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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,9 @@
outline: none; outline: none;
} }
.dropdown__backdrop { }
.dropdown__backdrop {
position: fixed; position: fixed;
z-index: 999; z-index: 999;
display: none; display: none;
@ -21,15 +23,16 @@
top: 0; top: 0;
bottom: 0; bottom: 0;
content: ' '; content: ' ';
} //background: rgba(255, 0, 0, 0.1);
&.dropdown--open .dropdown__backdrop {
display: block;
}
} }
.dropdown__menu { .dropdown__menu {
z-index: 1000; &.dropdown__menu--open .dropdown__backdrop {
display: block;
}
ul {
z-index: 9999;
display: none; display: none;
position: fixed; position: fixed;
top: 0; top: 0;
@ -49,11 +52,6 @@
border-radius: @radius-md; border-radius: @radius-md;
overflow: auto; overflow: auto;
&.dropdown__menu--open {
display: block;
animation: fadeIn 200ms ease-out;
}
.dropdown__inner { .dropdown__inner {
width: 100%; width: 100%;
} }
@ -130,5 +128,11 @@
} }
} }
} }
}
&.dropdown__menu--open ul {
display: block;
animation: fadeIn 200ms ease-out;
}
} }

View File

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