Lots more dropdown fixes

This commit is contained in:
Gregory Schier 2017-06-16 15:21:41 -07:00
parent 26d7423fa2
commit 27fcd29531
12 changed files with 185 additions and 154 deletions

View File

@ -36,7 +36,7 @@ class Dropdown extends PureComponent {
}
_checkSizeAndPosition () {
if (!this.state.open) {
if (!this.state.open || !this._dropdownList) {
return;
}
@ -58,15 +58,19 @@ class Dropdown extends PureComponent {
const {right, wide} = this.props;
if (right || wide) {
const {right} = dropdownRect;
this._dropdownList.style.right = `${bodyRect.width - right}px`;
this._dropdownList.style.maxWidth = `${right}px`;
const {beside} = this.props;
const offset = beside ? dropdownRect.width - dropdownRect.height : 0;
this._dropdownList.style.right = `${bodyRect.width - right + offset}px`;
this._dropdownList.style.maxWidth = `${right + offset}px`;
this._dropdownList.style.minWidth = `${Math.min(right, 200)}px`;
}
if (!right || wide) {
const {left} = dropdownRect;
this._dropdownList.style.left = `${left}px`;
this._dropdownList.style.maxWidth = `${bodyRect.width - left - 5}px`;
const {beside} = this.props;
const offset = beside ? dropdownRect.width - dropdownRect.height : 0;
this._dropdownList.style.left = `${left + offset}px`;
this._dropdownList.style.maxWidth = `${bodyRect.width - left - 5 - offset}px`;
this._dropdownList.style.minWidth = `${Math.min(bodyRect.width - left, 200)}px`;
}
@ -93,6 +97,10 @@ class Dropdown extends PureComponent {
_addDropdownListRef (n) {
this._dropdownList = n;
}
_addDropdownMenuRef (n) {
this._dropdownMenu = n;
// Move the element to the body so we can position absolutely
if (n) {
@ -103,7 +111,7 @@ class Dropdown extends PureComponent {
document.body.appendChild(container);
}
container.appendChild(ReactDOM.findDOMNode(n));
container.appendChild(ReactDOM.findDOMNode(this._dropdownMenu));
}
}
@ -163,7 +171,7 @@ class Dropdown extends PureComponent {
}
render () {
const {right, outline, wide, className, style, children, rightOffset} = this.props;
const {right, outline, wide, className, style, children} = this.props;
const {dropUp, open} = this.state;
const classes = classnames('dropdown', className, {
@ -202,16 +210,14 @@ class Dropdown extends PureComponent {
{allChildren}
);
} else {
const styles = {};
if (typeof rightOffset === 'number') {
styles.marginRight = `-${rightOffset}px`;
}
finalChildren = [
dropdownButtons[0],
<ul key="items" className={menuClasses} style={styles} ref={this._addDropdownListRef}>
{dropdownItems}
</ul>
<div key="item" className={menuClasses} ref={this._addDropdownMenuRef}>
<div className="dropdown__backdrop"></div>
<ul ref={this._addDropdownListRef}>
{dropdownItems}
</ul>
</div>
];
}
@ -222,7 +228,6 @@ class Dropdown extends PureComponent {
onClick={this._handleClick}
onMouseDown={this._handleMouseDown}>
{finalChildren}
<div className="dropdown__backdrop"></div>
</div>
);
}
@ -240,7 +245,7 @@ Dropdown.propTypes = {
onHide: PropTypes.func,
className: PropTypes.string,
style: PropTypes.string,
rightOffset: PropTypes.number
beside: PropTypes.bool
};
export default Dropdown;

View File

@ -19,6 +19,7 @@ const TYPE_CONSTANT = 'constant';
const MAX_CONSTANTS = -1;
const MAX_VARIABLES = -1;
const MAX_TAGS = -1;
const MIN_CHAR_MATCH = 1;
const ICONS = {
[TYPE_CONSTANT]: {char: '&#x1d484;', title: 'Constant'},
@ -308,6 +309,11 @@ function matchSegments (listOfThings, segment, type, limit = -1) {
continue;
}
// Throw away short matches
if (matchSegment.length < MIN_CHAR_MATCH) {
continue;
}
matches.push({
// Custom Insomnia keys
type,

View File

@ -56,7 +56,7 @@ class AuthDropdown extends PureComponent {
render () {
const {children, className} = this.props;
return (
<Dropdown debug="true">
<Dropdown beside debug="true">
<DropdownDivider>Auth Types</DropdownDivider>
<DropdownButton className={className}>
{children}

View File

@ -22,11 +22,12 @@ class PreviewModeDropdown extends PureComponent {
}
render () {
const {download, fullDownload} = this.props;
const {download, fullDownload, previewMode} = this.props;
return (
<Dropdown>
<Dropdown beside>
<DropdownButton className="tall">
<i className="fa fa-caret-down"/>
{getPreviewModeName(previewMode)}
<i className="fa fa-caret-down space-left"/>
</DropdownButton>
<DropdownDivider>Preview Mode</DropdownDivider>
{PREVIEW_MODES.map(this.renderPreviewMode)}
@ -36,7 +37,7 @@ class PreviewModeDropdown extends PureComponent {
Save Response Body
</DropdownItem>
<DropdownItem onClick={fullDownload}>
<i className="fa fa-save" />
<i className="fa fa-save"/>
Save Full Response
</DropdownItem>
</Dropdown>

View File

@ -91,7 +91,7 @@ class WorkspaceDropdown extends PureComponent {
const classes = classnames(className, 'wide', 'workspace-dropdown');
return (
<Dropdown right className={classes} rightOffset={150} onOpen={this._handleDropdownOpen} {...other}>
<Dropdown beside className={classes} onOpen={this._handleDropdownOpen} {...other}>
<DropdownButton className="btn wide">
<h1 className="no-pad text-left">
<div className="pull-right">

View File

@ -39,6 +39,10 @@ class MarkdownEditor extends PureComponent {
this._editor && this._editor.focusEnd();
}
focus () {
this._editor && this._editor.focus();
}
render () {
const {
fontSize,
@ -88,7 +92,7 @@ class MarkdownEditor extends PureComponent {
dynamicHeight={!tall}
manualPrettify
noStyleActiveLine
mode={mode || 'text/plain'}
mode={mode || 'text/x-markdown'}
placeholder={placeholder}
debounceMillis={300}
keyMap={keyMap}

View File

@ -16,19 +16,25 @@ class MarkdownPreview extends PureComponent {
};
}
async _compileMarkdown (markdown) {
try {
const rendered = await this.props.handleRender(markdown);
this.setState({
compiled: markdownToHTML(rendered),
renderError: ''
});
} catch (err) {
this.setState({
renderError: err.message,
compiled: ''
});
}
/**
* Debounce and compile the markdown (won't debounce first render)
*/
_compileMarkdown (markdown) {
clearTimeout(this._compileTimeout);
this._compileTimeout = setTimeout(async () => {
try {
const rendered = await this.props.handleRender(markdown);
this.setState({
compiled: markdownToHTML(rendered),
renderError: ''
});
} catch (err) {
this.setState({
renderError: err.message,
compiled: ''
});
}
}, this.state.compiled ? this.props.debounceMillis : 0);
}
_setPreviewRef (n) {
@ -61,14 +67,14 @@ class MarkdownPreview extends PureComponent {
this._compileMarkdown(this.props.markdown);
}
componentDidUpdate () {
this._highlightCodeBlocks();
}
componentWillReceiveProps (nextProps) {
this._compileMarkdown(nextProps.markdown);
}
componentDidUpdate () {
this._highlightCodeBlocks();
}
componentDidMount () {
this._highlightCodeBlocks();
}
@ -99,7 +105,8 @@ MarkdownPreview.propTypes = {
handleRender: PropTypes.func.isRequired,
// Optional
className: PropTypes.string
className: PropTypes.string,
debounceMillis: PropTypes.number
};
export default MarkdownPreview;

View File

@ -62,7 +62,7 @@ class RequestSettingsModal extends PureComponent {
if (forceEditMode) {
setTimeout(() => {
this._editor.focusEnd();
this._editor.focus();
}, 400);
}
}

View File

@ -235,26 +235,22 @@ class RequestPane extends PureComponent {
<Tabs className="pane__body" forceRenderTabPanel>
<TabList>
<Tab onClick={this._trackTabBody}>
<button>
{getContentTypeName(request.body.mimeType) || 'Body'}
{' '}
{numBodyParams ? <span className="bubble">{numBodyParams}</span> : null}
</button>
<ContentTypeDropdown onChange={updateRequestMimeType}
contentType={request.body.mimeType}
request={request}
className="tall">
<i className="fa fa-caret-down"/>
{getContentTypeName(request.body.mimeType) || 'Body'}
{' '}
{numBodyParams ? <span className="bubble">{numBodyParams}</span> : null}
<i className="fa fa-caret-down space-left"/>
</ContentTypeDropdown>
</Tab>
<Tab onClick={this._trackTabAuthentication}>
<button>
{getAuthTypeName(request.authentication.type) || 'Auth'}
</button>
<AuthDropdown onChange={updateRequestAuthentication}
authentication={request.authentication}
className="tall">
<i className="fa fa-caret-down"/>
{getAuthTypeName(request.authentication.type) || 'Auth'}
<i className="fa fa-caret-down space-left"/>
</AuthDropdown>
</Tab>
<Tab onClick={this._trackTabQuery}>
@ -372,6 +368,7 @@ class RequestPane extends PureComponent {
</div>
<MarkdownPreview
className="pad"
debounceMillis={1000}
markdown={request.description}
handleRender={handleRender}
/>

View File

@ -16,7 +16,7 @@ import ResponseTimelineViewer from './viewers/response-timeline-viewer';
import ResponseHeadersViewer from './viewers/response-headers-viewer';
import ResponseCookiesViewer from './viewers/response-cookies-viewer';
import * as models from '../../models';
import {getPreviewModeName, PREVIEW_MODE_SOURCE} from '../../common/constants';
import {PREVIEW_MODE_SOURCE} from '../../common/constants';
import {getSetCookieHeaders, nullFn} from '../../common/misc';
import {cancelCurrentRequest} from '../../network/network';
import {trackEvent} from '../../analytics';
@ -240,9 +240,6 @@ class ResponsePane extends PureComponent {
<Tabs className="pane__body" forceRenderTabPanel>
<TabList>
<Tab>
<Button onClick={this._trackTab} value="Response">
{getPreviewModeName(previewMode)}
</Button>
<PreviewModeDropdown
download={this._handleDownloadResponseBody}
fullDownload={this._handleDownloadFullResponseBody}

View File

@ -12,123 +12,127 @@
outline: none;
}
.dropdown__backdrop {
position: fixed;
z-index: 999;
display: none;
left: 0;
right: 0;
top: 0;
bottom: 0;
content: ' ';
}
}
&.dropdown--open .dropdown__backdrop {
display: block;
}
.dropdown__backdrop {
position: fixed;
z-index: 999;
display: none;
left: 0;
right: 0;
top: 0;
bottom: 0;
content: ' ';
//background: rgba(255, 0, 0, 0.1);
}
.dropdown__menu {
z-index: 1000;
display: none;
position: fixed;
top: 0;
left: 0;
border: 1px solid @hl-sm;
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, 0.1);
box-sizing: border-box;
background: var(--color-bg);
// Separate it from the button a bit
margin-top: @padding-xxs;
margin-bottom: @padding-xxs;
padding-top: @radius-md;
padding-bottom: @radius-md;
border-radius: @radius-md;
overflow: auto;
&.dropdown__menu--open {
&.dropdown__menu--open .dropdown__backdrop {
display: block;
animation: fadeIn 200ms ease-out;
}
.dropdown__inner {
width: 100%;
}
ul {
z-index: 9999;
display: none;
position: fixed;
top: 0;
left: 0;
border: 1px solid @hl-sm;
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, 0.1);
box-sizing: border-box;
background: var(--color-bg);
.dropdown__text {
white-space: nowrap;
display: flex;
flex-direction: row;
align-items: center;
// Separate it from the button a bit
margin-top: @padding-xxs;
margin-bottom: @padding-xxs;
& > *:not(:first-child) {
margin-left: 0.3em;
padding-top: @radius-md;
padding-bottom: @radius-md;
border-radius: @radius-md;
overflow: auto;
.dropdown__inner {
width: 100%;
}
.dropdown__hint,
.dropdown__right {
color: @hl-xl;
margin-left: auto;
padding-left: @padding-lg;
}
}
.dropdown__text {
white-space: nowrap;
display: flex;
flex-direction: row;
align-items: center;
li > button {
font-size: @font-size-md;
text-align: left;
padding-right: @padding-md;
padding-left: @padding-sm;
height: @line-height-xs;
width: 100%;
display: block;
color: var(--color-font) !important;
white-space: nowrap;
margin: 0;
& > *:not(:first-child) {
margin-left: 0.3em;
}
&:hover:not(:disabled),
&:focus:not(:disabled) {
background: @hl-sm;
.dropdown__hint,
.dropdown__right {
color: @hl-xl;
margin-left: auto;
padding-left: @padding-lg;
}
}
&:active:not(:disabled) {
background: @hl-md;
li > button {
font-size: @font-size-md;
text-align: left;
padding-right: @padding-md;
padding-left: @padding-sm;
height: @line-height-xs;
width: 100%;
display: block;
color: var(--color-font) !important;
white-space: nowrap;
margin: 0;
&:hover:not(:disabled),
&:focus:not(:disabled) {
background: @hl-sm;
}
&:active:not(:disabled) {
background: @hl-md;
}
i.fa:first-child {
display: inline-block;
width: 2.2em;
text-align: center;
}
}
i.fa:first-child {
display: inline-block;
width: 2.2em;
text-align: center;
}
}
li.dropdown__divider {
border-bottom: 1px solid @hl-lg;
overflow: visible !important;
height: 0;
margin: @padding-md @padding-md @padding-md @padding-md;
min-width: @dropdown-min-width - @padding-md * 2;
.dropdown__divider__label {
position: relative;
left: -@padding-sm / 2;
top: -0.7rem;
color: @hl;
padding-right: 1em;
background: var(--color-bg);
font-size: @font-size-xs;
text-transform: uppercase;
}
&.dropdown__divider--no-name {
margin: @padding-xs 0;
li.dropdown__divider {
border-bottom: 1px solid @hl-lg;
overflow: visible !important;
height: 0;
margin: @padding-md @padding-md @padding-md @padding-md;
min-width: @dropdown-min-width - @padding-md * 2;
.dropdown__divider__label {
display: none;
position: relative;
left: -@padding-sm / 2;
top: -0.7rem;
color: @hl;
padding-right: 1em;
background: var(--color-bg);
font-size: @font-size-xs;
text-transform: uppercase;
}
&.dropdown__divider--no-name {
margin: @padding-xs 0;
.dropdown__divider__label {
display: none;
}
}
}
}
&.dropdown__menu--open ul {
display: block;
animation: fadeIn 200ms ease-out;
}
}

View File

@ -19,6 +19,7 @@
box-sizing: border-box;
background-color: var(--color-bg);
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
@ -38,7 +39,16 @@
border-top: 0 !important;
white-space: nowrap;
display: flex;
position: static;
position: relative;
&:not(.ReactTabs__Tab--selected)::after {
content: ' ';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.bubble {
position: relative;