Fixed some editor bugs (#97)

* Fixed some editor bugs

* Some fixes

* Update index.js
This commit is contained in:
Gregory Schier
2017-02-28 16:10:23 -08:00
committed by GitHub
parent b198e6170b
commit bce0e5fec1
9 changed files with 154 additions and 68 deletions

View File

@@ -0,0 +1,23 @@
import React, {PureComponent, PropTypes} from 'react';
class Lazy extends PureComponent {
constructor (props) {
super(props);
this.state = {show: false};
}
componentDidMount () {
const delay = this.props.delay || 0;
setTimeout(() => this.setState({show: true}), delay);
}
render () {
return this.state.show ? this.props.children : null;
}
}
Lazy.propTypes = {
delay: PropTypes.number,
};
export default Lazy;

View File

@@ -132,7 +132,11 @@ class Editor extends PureComponent {
* @returns {boolean}
*/
hasFocus () {
return this.codeMirror.hasFocus();
if (this.codeMirror) {
return this.codeMirror.hasFocus();
} else {
return false;
}
}
/**
@@ -162,7 +166,6 @@ class Editor extends PureComponent {
}
const {value, debounceMillis: ms} = this.props;
this.codeMirror = CodeMirror.fromTextArea(textarea, BASE_CODEMIRROR_OPTIONS);
// Set default listeners
@@ -183,11 +186,11 @@ class Editor extends PureComponent {
});
}
// Do this a bit later so we don't block the render process
window.requestAnimationFrame(() => {
// Set editor options
this._codemirrorSetOptions();
// Set editor options
this._codemirrorSetOptions();
// Do this a bit later so we don't block the render process
setTimeout(() => {
// Actually set the value
this._codemirrorSetValue(value || '');
@@ -198,15 +201,9 @@ class Editor extends PureComponent {
// Unset default cursor of [0, 0];
this.codeMirror.setCursor({line: -1, ch: -1});
});
}, 10);
};
_debounce (fn) {
const {debounceMillis} = this.props;
const ms = typeof debounceMillis === 'number' ? debounceMillis : DEBOUNCE_MILLIS;
return misc.debounce(fn, ms);
}
_isJSON (mode) {
if (!mode) {
return false;
@@ -568,10 +565,12 @@ class Editor extends PureComponent {
return (
<div className={classes}>
<div className="editor__container input" style={{fontSize: `${fontSize || 12}px`}}>
<textarea ref={this._handleInitTextarea}
defaultValue=" "
readOnly={readOnly}
autoComplete="off"/>
<textarea
ref={this._handleInitTextarea}
style={{display: 'none'}}
defaultValue=" "
readOnly={readOnly}
autoComplete="off"/>
</div>
{toolbar}
</div>

View File

@@ -18,6 +18,23 @@ class OneLineEditor extends PureComponent {
this.props.onChange && this.props.onChange(value);
};
_handleKeyDown = e => {
// submit form if needed
if (e.keyCode === 13) {
let node = e.target;
for (let i = 0; i < 20 && node; i++) {
if (node.tagName === 'FORM') {
node.dispatchEvent(new Event('submit'));
break;
}
node = node.parentNode;
}
}
// Also call the original if there was one
this.props.onKeyDown && this.props.onKeyDown(e);
};
_setRef = n => this.editor = n;
render () {
@@ -33,6 +50,7 @@ class OneLineEditor extends PureComponent {
noMatchBrackets
singleLine
tabIndex={0}
onKeyDown={this._handleKeyDown}
onChange={this._handleChange}
className="editor--single-line"
value={defaultValue}

View File

@@ -2,6 +2,7 @@ import React, {PureComponent, PropTypes} from 'react';
import KeyValueEditor from '../keyvalueeditor/Editor';
import Editor from '../codemirror/Editor';
import Lazy from '../base/Lazy';
import {trackEvent} from '../../../analytics/index';
class RequestHeadersEditor extends PureComponent {
@@ -65,29 +66,31 @@ class RequestHeadersEditor extends PureComponent {
const {bulk, headers, onChange, handleRender} = this.props;
return bulk ? (
<div className="tall">
<Editor
onChange={this._handleBulkUpdate}
value={this._getHeadersString()}
/>
</div>
) : (
<div className="pad-bottom scrollable-container">
<div className="scrollable">
<KeyValueEditor
namePlaceholder="My-Header"
valuePlaceholder="Value"
pairs={headers}
sortable={true}
handleRender={handleRender}
onToggleDisable={this._handleTrackToggle}
onCreate={this._handleTrackCreate}
onDelete={this._handleTrackDelete}
onChange={onChange}
<div className="tall">
<Editor
onChange={this._handleBulkUpdate}
value={this._getHeadersString()}
/>
</div>
</div>
);
) : (
<div className="pad-bottom scrollable-container">
<div className="scrollable">
<Lazy>
<KeyValueEditor
namePlaceholder="My-Header"
valuePlaceholder="Value"
pairs={headers}
sortable={true}
handleRender={handleRender}
onToggleDisable={this._handleTrackToggle}
onCreate={this._handleTrackCreate}
onDelete={this._handleTrackDelete}
onChange={onChange}
/>
</Lazy>
</div>
</div>
);
}
}

View File

@@ -3,6 +3,7 @@ import classnames from 'classnames';
import {DEBOUNCE_MILLIS} from '../../../common/constants';
import KeyValueEditorRow from './Row';
import {generateId, nullFn} from '../../../common/misc';
import * as misc from '../../../common/misc';
const NAME = 'name';
const VALUE = 'value';
@@ -17,7 +18,7 @@ class KeyValueEditor extends PureComponent {
constructor (props) {
super(props);
this._focusedPair = null;
this._focusedPairId = null;
this._focusedField = NAME;
this._rows = [];
@@ -70,12 +71,12 @@ class KeyValueEditor extends PureComponent {
};
_handleFocusName = pair => {
this._focusedPair = pair;
this._setFocusedPair(pair);
this._focusedField = NAME;
};
_handleFocusValue = pair => {
this._focusedPair = pair;
this._setFocusedPair(pair);
this._focusedField = VALUE;
};
@@ -144,21 +145,27 @@ class KeyValueEditor extends PureComponent {
...this.state.pairs.slice(position)
];
this._focusedPair = pair;
this._setFocusedPair(pair);
this._onChange(pairs);
this.props.onCreate && this.props.onCreate();
}
_deletePair (position, breakFocus = false) {
if (this._focusedPair >= position) {
this._focusedPair = breakFocus ? -1 : this._focusedPair - 1;
const focusedPosition = this._getFocusedPairIndex();
if (focusedPosition >= position) {
const newPosition = breakFocus ? -1 : focusedPosition - 1;
this._setFocusedPair(this.state.pairs[newPosition]);
}
const pair = this.state.pairs[position];
this.props.onDelete && this.props.onDelete(pair);
const pairs = this.state.pairs.filter((_, i) => i !== position);
const pairs = [
...this.state.pairs.slice(0, position),
...this.state.pairs.slice(position + 1),
];
this._onChange(pairs);
};
@@ -170,8 +177,7 @@ class KeyValueEditor extends PureComponent {
} else if (this._focusedField === VALUE) {
this._focusedField = NAME;
if (addIfValue) {
const i = this._getPairIndex(this._focusedPair);
this._addPair(i + 1);
this._addPair(this._getFocusedPairIndex() + 1);
} else {
this._focusNextPair();
}
@@ -183,10 +189,10 @@ class KeyValueEditor extends PureComponent {
this._focusedField = NAME;
this._updateFocus();
} else if (this._focusedField === NAME) {
const p = this._focusedPair;
const p = this._getFocusedPair();
if (!p.name && !p.value && !p.fileName && deleteIfEmpty) {
this._focusedField = VALUE;
this._deletePair(this._focusedPair);
this._deletePair(this._getFocusedPairIndex());
} else if (!p.name) {
this._focusedField = VALUE;
this._focusPreviousPair();
@@ -195,25 +201,32 @@ class KeyValueEditor extends PureComponent {
}
_focusNextPair () {
const i = this._getPairIndex(this._focusedPair);
const i = this._getFocusedPairIndex();
if (i === -1) {
// No focused pair currently
return;
}
if (i >= this.state.pairs.length - 1) {
// Focused on last one, so add another
this._addPair();
} else {
this._focusedPair = this.state.pairs[i + 1];
this._setFocusedPair(this.state.pairs[i + 1]);
this._updateFocus();
}
}
_focusPreviousPair () {
const i = this._getPairIndex(this._focusedPair);
const i = this._getFocusedPairIndex();
if (i > 0) {
this._focusedPair = this.state.pairs[i - 1];
this._setFocusedPair(this.state.pairs[i - 1]);
this._updateFocus();
}
}
_updateFocus () {
const row = this._focusedPair && this._rows[this._focusedPair.id];
const row = this._getFocusedPair() && this._rows[this._focusedPairId];
if (!row) {
return;
@@ -227,7 +240,27 @@ class KeyValueEditor extends PureComponent {
}
_getPairIndex (pair) {
return this.props.pairs.findIndex(p => p.id === pair.id);
if (pair) {
return this.props.pairs.findIndex(p => p.id === pair.id);
} else {
return -1;
}
}
_getFocusedPairIndex () {
return this._getPairIndex(this._getFocusedPair());
}
_getFocusedPair () {
return this.props.pairs.find(p => p.id === this._focusedPairId) || null;
}
_setFocusedPair (pair) {
if (pair) {
this._focusedPairId = pair.id;
} else {
this._focusedPairId = null;
}
}
componentDidUpdate () {

View File

@@ -97,7 +97,7 @@ class KeyValueEditorRow extends PureComponent {
) : null
}
<div className="key-value-editor__row" onMouseDown={preventDefault/* Don't trigger drag*/}>
<div className="key-value-editor__row">
<div className="form-control form-control--underlined form-control--wide">
<OneLineEditor
ref={this._setNameInputRef}

View File

@@ -1,10 +1,9 @@
import React, {PureComponent, PropTypes} from 'react';
import {debounce} from '../../../common/misc';
class ResponseRaw extends PureComponent {
_update (value) {
// Use a timeout so it doesn't block the UI
window.requestAnimationFrame(() => this._setTextAreaValue(value))
}
// Use a timeout so it doesn't block the UI
_update = debounce(value => this._setTextAreaValue(value));
_setTextAreaValue (value) {
// Bail if we're not mounted

View File

@@ -13,18 +13,18 @@ class ResponseViewer extends PureComponent {
blockingBecauseTooLarge: false
};
_handleOpenLink (link) {
_handleOpenLink = link => {
shell.openExternal(link);
}
};
_handleDismissBlocker () {
_handleDismissBlocker = () => {
this.setState({blockingBecauseTooLarge: false});
}
};
_handleDisableBlocker () {
_handleDisableBlocker = () => {
alwaysShowLargeResponses = true;
this._handleDismissBlocker();
}
};
_checkResponseBlocker (props) {
if (alwaysShowLargeResponses) {
@@ -101,13 +101,13 @@ class ResponseViewer extends PureComponent {
slowdowns on some computers
</p>
<p>
<button onClick={e => this._handleDismissBlocker()}
<button onClick={this._handleDismissBlocker}
className="inline-block btn btn--clicky">
Show Response
</button>
{" "}
<button className="faint inline-block btn btn--super-compact"
onClick={e => this._handleDisableBlocker()}>
onClick={this._handleDisableBlocker}>
Always Show
</button>
</p>

View File

@@ -12,6 +12,8 @@
flex-direction: row;
width: 100%;
padding-left: @padding-md;
margin-top: @padding-xs;
margin-bottom: @padding-xs;
box-sizing: border-box;
&.key-value-editor__row-wrapper--disabled .input,
@@ -43,6 +45,11 @@
top: 100%;
}
&.key-value-editor__row-wrapper--dragging-above::before {
// So the line appears on the next row
bottom: 100%;
}
// Style last row the same no matter if focused or not.
&.key-value-editor__row-wrapper--clicker .CodeMirror {
border-color: var(--hl-sm) !important;
@@ -70,6 +77,10 @@
padding-left: 0;
}
.form-control {
margin-bottom: 0;
}
.input {
background-color: transparent;
height: @line-height-sm;