mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
Support multiline text in form data editors (#299)
* Support multiline text in form data editors * Some tweaks around rendering and syntax * Embed markdown editor
This commit is contained in:
parent
4129c1ec8e
commit
3056921fda
@ -221,26 +221,26 @@ export function debounce (callback, millis = DEBOUNCE_MILLIS) {
|
||||
}, millis).bind(null, '__key__');
|
||||
}
|
||||
|
||||
export function describeByteSize (bytes) {
|
||||
export function describeByteSize (bytes, long) {
|
||||
bytes = Math.round(bytes * 10) / 10;
|
||||
let size;
|
||||
|
||||
// NOTE: We multiply these by 2 so we don't end up with
|
||||
// values like 0 GB
|
||||
|
||||
let unit = 'B';
|
||||
let unit = long ? 'bytes' : 'B';
|
||||
if (bytes < 1024 * 2) {
|
||||
size = bytes;
|
||||
unit = 'B';
|
||||
unit = long ? 'bytes' : 'B';
|
||||
} else if (bytes < 1024 * 1024 * 2) {
|
||||
size = bytes / 1024;
|
||||
unit = 'KB';
|
||||
unit = long ? 'kilobytes' : 'KB';
|
||||
} else if (bytes < 1024 * 1024 * 1024 * 2) {
|
||||
size = bytes / 1024 / 1024;
|
||||
unit = 'MB';
|
||||
unit = long ? 'megabytes' : 'MB';
|
||||
} else {
|
||||
size = bytes / 1024 / 1024 / 1024;
|
||||
unit = 'GB';
|
||||
unit = long ? 'gigabytes' : 'GB';
|
||||
}
|
||||
|
||||
const rounded = (Math.round(size * 10) / 10);
|
||||
|
@ -36,13 +36,14 @@ class FileInputButton extends PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {showFileName, path, name, ...extraProps} = this.props;
|
||||
const {showFileName, showFileIcon, path, name, ...extraProps} = this.props;
|
||||
const fileName = pathBasename(path);
|
||||
return (
|
||||
<button type="button"
|
||||
ref={this._setRef}
|
||||
onClick={this._handleChooseFile}
|
||||
{...extraProps}>
|
||||
{showFileIcon && <i className="fa fa-file-o space-right"/>}
|
||||
{showFileName && fileName ? `${fileName}` : `Choose ${name || 'File'}`}
|
||||
</button>
|
||||
);
|
||||
@ -56,6 +57,7 @@ FileInputButton.propTypes = {
|
||||
|
||||
// Optional
|
||||
showFileName: PropTypes.bool,
|
||||
showFileIcon: PropTypes.bool,
|
||||
name: PropTypes.string
|
||||
};
|
||||
|
||||
|
@ -63,6 +63,24 @@ class OneLineEditor extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
getSelectionStart () {
|
||||
if (this._editor) {
|
||||
return this._editor.getSelectionStart();
|
||||
} else {
|
||||
console.warn('Tried to get selection start of one-line-editor when <input>');
|
||||
return this._input.value.length;
|
||||
}
|
||||
}
|
||||
|
||||
getSelectionEnd () {
|
||||
if (this._editor) {
|
||||
return this._editor.getSelectionEnd();
|
||||
} else {
|
||||
console.warn('Tried to get selection end of one-line-editor when <input>');
|
||||
return this._input.value.length;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.body.addEventListener('click', this._handleDocumentClick);
|
||||
}
|
||||
|
@ -23,9 +23,10 @@ class ContentTypeDropdown extends PureComponent {
|
||||
const hasFile = body.fileName && body.fileName.length;
|
||||
const isEmpty = !hasParams && !hasText && !hasFile;
|
||||
const isFile = body.mimeType === CONTENT_TYPE_FILE;
|
||||
const isMultipart = body.mimeType === CONTENT_TYPE_FORM_DATA;
|
||||
const isMultipartWithFiles = body.mimeType === CONTENT_TYPE_FORM_DATA &&
|
||||
body.params.find(p => p.type === 'file');
|
||||
const isFormUrlEncoded = body.mimeType === CONTENT_TYPE_FORM_URLENCODED;
|
||||
const isText = !isFile && !isMultipart;
|
||||
const isText = !isFile && !isMultipartWithFiles;
|
||||
|
||||
const willBeFile = mimeType === CONTENT_TYPE_FILE;
|
||||
const willBeMultipart = mimeType === CONTENT_TYPE_FORM_DATA;
|
||||
|
@ -42,6 +42,8 @@ class FormEditor extends PureComponent {
|
||||
<div className="scrollable">
|
||||
<KeyValueEditor
|
||||
sortable
|
||||
allowFile
|
||||
allowMultiline
|
||||
namePlaceholder="name"
|
||||
valuePlaceholder="value"
|
||||
handleRender={handleRender}
|
||||
@ -53,7 +55,6 @@ class FormEditor extends PureComponent {
|
||||
onDelete={this._handleTrackDelete}
|
||||
onChange={onChange}
|
||||
pairs={parameters}
|
||||
multipart
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -34,6 +34,7 @@ class UrlEncodedEditor extends PureComponent {
|
||||
<div className="scrollable">
|
||||
<KeyValueEditor
|
||||
sortable
|
||||
allowMultiline
|
||||
namePlaceholder="name"
|
||||
valuePlaceholder="value"
|
||||
onChange={onChange}
|
||||
|
@ -324,7 +324,8 @@ class Editor extends PureComponent {
|
||||
handleGetRenderContext,
|
||||
handleGetAutocompleteNameConstants,
|
||||
handleGetAutocompleteValueConstants,
|
||||
multipart,
|
||||
allowFile,
|
||||
allowMultiline,
|
||||
sortable,
|
||||
disableDelete
|
||||
} = this.props;
|
||||
@ -357,7 +358,8 @@ class Editor extends PureComponent {
|
||||
handleGetRenderContext={handleGetRenderContext}
|
||||
handleGetAutocompleteNameConstants={handleGetAutocompleteNameConstants}
|
||||
handleGetAutocompleteValueConstants={handleGetAutocompleteValueConstants}
|
||||
multipart={multipart}
|
||||
allowMultiline={allowMultiline}
|
||||
allowFile={allowFile}
|
||||
pair={pair}
|
||||
/>
|
||||
))}
|
||||
@ -378,7 +380,8 @@ class Editor extends PureComponent {
|
||||
valuePlaceholder={`New ${valuePlaceholder}`}
|
||||
onFocusName={this._handleAddFromName}
|
||||
onFocusValue={this._handleAddFromValue}
|
||||
multipart={multipart}
|
||||
allowMultiline={allowMultiline}
|
||||
allowFile={allowFile}
|
||||
pair={{name: '', value: ''}}
|
||||
/> : null
|
||||
}
|
||||
@ -397,7 +400,8 @@ Editor.propTypes = {
|
||||
handleGetRenderContext: PropTypes.func,
|
||||
handleGetAutocompleteNameConstants: PropTypes.func,
|
||||
handleGetAutocompleteValueConstants: PropTypes.func,
|
||||
multipart: PropTypes.bool,
|
||||
allowFile: PropTypes.bool,
|
||||
allowMultiline: PropTypes.bool,
|
||||
sortable: PropTypes.bool,
|
||||
maxPairs: PropTypes.number,
|
||||
namePlaceholder: PropTypes.string,
|
||||
|
@ -1,14 +1,16 @@
|
||||
// eslint-disable-next-line filenames/match-exported
|
||||
import React, {PureComponent, PropTypes} from 'react';
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import autobind from 'autobind-decorator';
|
||||
import {DragSource, DropTarget} from 'react-dnd';
|
||||
import classnames from 'classnames';
|
||||
import FileInputButton from '../base/file-input-button';
|
||||
import {Dropdown, DropdownItem, DropdownButton} from '../base/dropdown/index';
|
||||
import {Dropdown, DropdownButton, DropdownItem} from '../base/dropdown/index';
|
||||
import PromptButton from '../base/prompt-button';
|
||||
import CodePromptModal from '../modals/code-prompt-modal';
|
||||
import Button from '../base/button';
|
||||
import OneLineEditor from '../codemirror/one-line-editor';
|
||||
import {showModal} from '../modals/index';
|
||||
|
||||
@autobind
|
||||
class KeyValueEditorRow extends PureComponent {
|
||||
@ -25,16 +27,12 @@ class KeyValueEditorRow extends PureComponent {
|
||||
focusNameEnd () {
|
||||
if (this._nameInput) {
|
||||
this._nameInput.focusEnd();
|
||||
} else {
|
||||
console.warn('Unable to focus non-existing nameInput');
|
||||
}
|
||||
}
|
||||
|
||||
focusValueEnd () {
|
||||
if (this._valueInput) {
|
||||
this._valueInput.focusEnd();
|
||||
} else {
|
||||
console.warn('Unable to focus non-existing valueInput');
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +59,24 @@ class KeyValueEditorRow extends PureComponent {
|
||||
this._sendChange({name});
|
||||
}
|
||||
|
||||
_handleValuePaste (e) {
|
||||
const value = e.clipboardData.getData('text/plain');
|
||||
if (value && value.includes('\n')) {
|
||||
e.preventDefault();
|
||||
|
||||
// Insert the pasted text into the current selection. Unfortunately, this
|
||||
// is the easiest way to do this.
|
||||
const currentValue = this._valueInput.getValue();
|
||||
const prefix = currentValue.slice(0, this._valueInput.getSelectionStart());
|
||||
const suffix = currentValue.slice(this._valueInput.getSelectionEnd());
|
||||
const finalValue = `${prefix}${value}${suffix}`;
|
||||
|
||||
// Update type and value
|
||||
this._handleTypeChange({type: 'text', multiline: 'text/plain'});
|
||||
this._handleValueChange(finalValue);
|
||||
}
|
||||
}
|
||||
|
||||
_handleValueChange (value) {
|
||||
this._sendChange({value});
|
||||
}
|
||||
@ -69,8 +85,14 @@ class KeyValueEditorRow extends PureComponent {
|
||||
this._sendChange({fileName});
|
||||
}
|
||||
|
||||
_handleTypeChange (type) {
|
||||
this._sendChange({type});
|
||||
_handleTypeChange (def) {
|
||||
// Remove newlines if converting to text
|
||||
let value = this.props.pair.value || '';
|
||||
if (def.type === 'text' && !def.multiline && value.includes('\n')) {
|
||||
value = value.replace(/\n/g, '');
|
||||
}
|
||||
|
||||
this._sendChange({type: def.type, multiline: def.multiline, value});
|
||||
}
|
||||
|
||||
_handleDisableChange (disabled) {
|
||||
@ -123,15 +145,130 @@ class KeyValueEditorRow extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
_handleEditMultiline () {
|
||||
const {pair, handleRender, handleGetRenderContext} = this.props;
|
||||
|
||||
showModal(CodePromptModal, {
|
||||
submitName: 'Done',
|
||||
title: `Edit ${pair.name}`,
|
||||
defaultValue: pair.value,
|
||||
onChange: this._handleValueChange,
|
||||
enableRender: handleRender || handleGetRenderContext,
|
||||
onModeChange: mode => {
|
||||
this._handleTypeChange(Object.assign({}, pair, {multiline: mode}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderPairValue () {
|
||||
const {
|
||||
pair,
|
||||
readOnly,
|
||||
forceInput,
|
||||
valueInputType,
|
||||
valuePlaceholder,
|
||||
handleRender,
|
||||
handleGetRenderContext
|
||||
} = this.props;
|
||||
|
||||
if (pair.type === 'file') {
|
||||
return (
|
||||
<FileInputButton
|
||||
ref={this._setValueInputRef}
|
||||
showFileName
|
||||
className="btn btn--outlined btn--super-duper-compact wide ellipsis"
|
||||
path={pair.fileName || ''}
|
||||
onChange={this._handleFileNameChange}
|
||||
/>
|
||||
);
|
||||
} else if (pair.type === 'text' && pair.multiline) {
|
||||
const numWords = (pair.value || '').replace(/\s+/g, ' ').trim().split(' ').length;
|
||||
return (
|
||||
<button className="btn btn--outlined btn--super-duper-compact wide ellipsis no-min-width"
|
||||
onClick={this._handleEditMultiline}>
|
||||
<i className="fa fa-pencil-square-o space-right"/>
|
||||
{pair.value
|
||||
? `${numWords} word${numWords === 1 ? '' : 's'}`
|
||||
: 'Click to Edit'
|
||||
}
|
||||
</button>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<OneLineEditor
|
||||
ref={this._setValueInputRef}
|
||||
readOnly={readOnly}
|
||||
forceInput={forceInput}
|
||||
type={valueInputType || 'text'}
|
||||
placeholder={valuePlaceholder || 'Value'}
|
||||
defaultValue={pair.value}
|
||||
onPaste={this._handleValuePaste}
|
||||
onChange={this._handleValueChange}
|
||||
onBlur={this._handleBlurValue}
|
||||
onKeyDown={this._handleKeyDown}
|
||||
onFocus={this._handleFocusValue}
|
||||
render={handleRender}
|
||||
getRenderContext={handleGetRenderContext}
|
||||
getAutocompleteConstants={this._handleAutocompleteValues}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderPairSelector () {
|
||||
const {
|
||||
hideButtons,
|
||||
allowMultiline,
|
||||
allowFile
|
||||
} = this.props;
|
||||
|
||||
const showDropdown = allowMultiline || allowFile;
|
||||
|
||||
// Put a spacer in for dropdown if needed
|
||||
if (hideButtons && showDropdown) {
|
||||
return (
|
||||
<button>
|
||||
<i className="fa fa-empty"/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (hideButtons) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (showDropdown) {
|
||||
return (
|
||||
<Dropdown right>
|
||||
<DropdownButton className="tall">
|
||||
<i className="fa fa-caret-down"/>
|
||||
</DropdownButton>
|
||||
<DropdownItem onClick={this._handleTypeChange} value={{type: 'text', multiline: false}}>
|
||||
Text
|
||||
</DropdownItem>
|
||||
{allowMultiline && (
|
||||
<DropdownItem onClick={this._handleTypeChange} value={{type: 'text', multiline: true}}>
|
||||
Text (Multi-line)
|
||||
</DropdownItem>
|
||||
)}
|
||||
{allowFile && (
|
||||
<DropdownItem onClick={this._handleTypeChange} value={{type: 'file'}}>
|
||||
File
|
||||
</DropdownItem>
|
||||
)}
|
||||
</Dropdown>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
pair,
|
||||
namePlaceholder,
|
||||
valuePlaceholder,
|
||||
handleRender,
|
||||
handleGetRenderContext,
|
||||
valueInputType,
|
||||
multipart,
|
||||
sortable,
|
||||
noDropZone,
|
||||
hideButtons,
|
||||
@ -185,53 +322,11 @@ class KeyValueEditorRow extends PureComponent {
|
||||
onKeyDown={this._handleKeyDown}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-control form-control--wide wide form-control--underlined">
|
||||
{pair.type === 'file' ? (
|
||||
<FileInputButton
|
||||
ref={this._setValueInputRef}
|
||||
showFileName
|
||||
className="btn btn--clicky wide ellipsis txt-sm no-margin"
|
||||
path={pair.fileName || ''}
|
||||
onChange={this._handleFileNameChange}
|
||||
/>
|
||||
) : (
|
||||
<OneLineEditor
|
||||
ref={this._setValueInputRef}
|
||||
readOnly={readOnly}
|
||||
forceInput={forceInput}
|
||||
type={valueInputType || 'text'}
|
||||
placeholder={valuePlaceholder || 'Value'}
|
||||
defaultValue={pair.value}
|
||||
onChange={this._handleValueChange}
|
||||
onBlur={this._handleBlurValue}
|
||||
onKeyDown={this._handleKeyDown}
|
||||
onFocus={this._handleFocusValue}
|
||||
render={handleRender}
|
||||
getRenderContext={handleGetRenderContext}
|
||||
getAutocompleteConstants={this._handleAutocompleteValues}
|
||||
/>
|
||||
)}
|
||||
<div className="form-control form-control--wide form-control--underlined">
|
||||
{this.renderPairValue()}
|
||||
</div>
|
||||
|
||||
{multipart && (
|
||||
!hideButtons ? (
|
||||
<Dropdown right>
|
||||
<DropdownButton className="tall">
|
||||
<i className="fa fa-caret-down"></i>
|
||||
</DropdownButton>
|
||||
<DropdownItem onClick={this._handleTypeChange} value="text">
|
||||
Text
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleTypeChange} value="file">
|
||||
File
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
) : (
|
||||
<button>
|
||||
<i className="fa fa-empty"/>
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
{this.renderPairSelector()}
|
||||
|
||||
{!hideButtons ? (
|
||||
<Button onClick={this._handleDisableChange}
|
||||
@ -303,7 +398,8 @@ KeyValueEditorRow.propTypes = {
|
||||
valuePlaceholder: PropTypes.string,
|
||||
valueInputType: PropTypes.string,
|
||||
forceInput: PropTypes.bool,
|
||||
multipart: PropTypes.bool,
|
||||
allowMultiline: PropTypes.bool,
|
||||
allowFile: PropTypes.bool,
|
||||
sortable: PropTypes.bool,
|
||||
noDelete: PropTypes.bool,
|
||||
noDropZone: PropTypes.bool,
|
||||
|
@ -93,17 +93,26 @@ class MarkdownEditor extends PureComponent {
|
||||
lineWrapping,
|
||||
indentSize,
|
||||
keyMap,
|
||||
mode,
|
||||
placeholder,
|
||||
defaultPreviewMode,
|
||||
className,
|
||||
tall,
|
||||
handleRender,
|
||||
handleGetRenderContext
|
||||
} = this.props;
|
||||
|
||||
const {markdown, compiled, renderError} = this.state;
|
||||
|
||||
const classes = classnames(
|
||||
'markdown-editor',
|
||||
'outlined',
|
||||
className,
|
||||
{'markdown-editor--dynamic-height': !tall}
|
||||
);
|
||||
|
||||
return (
|
||||
<Tabs className={classnames('markdown-editor', 'outlined', className)}
|
||||
<Tabs className={classes}
|
||||
forceRenderTabPanel
|
||||
selectedIndex={defaultPreviewMode ? 1 : 0}>
|
||||
<TabList>
|
||||
@ -123,10 +132,10 @@ class MarkdownEditor extends PureComponent {
|
||||
<CodeEditor
|
||||
hideGutters
|
||||
hideLineNumbers
|
||||
dynamicHeight
|
||||
dynamicHeight={!tall}
|
||||
manualPrettify
|
||||
noStyleActiveLine
|
||||
mode="text/plain"
|
||||
mode={mode || 'text/plain'}
|
||||
placeholder={placeholder}
|
||||
debounceMillis={300}
|
||||
keyMap={keyMap}
|
||||
@ -170,7 +179,9 @@ MarkdownEditor.propTypes = {
|
||||
// Optional
|
||||
placeholder: PropTypes.string,
|
||||
defaultPreviewMode: PropTypes.bool,
|
||||
className: PropTypes.string
|
||||
className: PropTypes.string,
|
||||
mode: PropTypes.string,
|
||||
tall: PropTypes.bool
|
||||
};
|
||||
|
||||
export default MarkdownEditor;
|
||||
|
180
app/ui/components/modals/code-prompt-modal.js
Normal file
180
app/ui/components/modals/code-prompt-modal.js
Normal file
@ -0,0 +1,180 @@
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Modal from '../base/modal';
|
||||
import ModalBody from '../base/modal-body';
|
||||
import ModalHeader from '../base/modal-header';
|
||||
import ModalFooter from '../base/modal-footer';
|
||||
import CodeEditor from '../codemirror/code-editor';
|
||||
import Dropdown from '../base/dropdown/dropdown';
|
||||
import DropdownButton from '../base/dropdown/dropdown-button';
|
||||
import DropdownItem from '../base/dropdown/dropdown-item';
|
||||
import DropdownDivider from '../base/dropdown/dropdown-divider';
|
||||
import MarkdownEditor from '../markdown-editor';
|
||||
|
||||
const MODES = {
|
||||
'text/plain': 'Plain Text',
|
||||
'application/json': 'JSON',
|
||||
'application/xml': 'XML',
|
||||
'text/x-markdown': 'Markdown',
|
||||
'text/html': 'HTML'
|
||||
};
|
||||
|
||||
@autobind
|
||||
class CodePromptModal extends PureComponent {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
title: 'Not Set',
|
||||
defaultValue: '',
|
||||
submitName: 'Not Set',
|
||||
placeholder: '',
|
||||
hint: '',
|
||||
mode: 'text/plain',
|
||||
enableRender: false
|
||||
};
|
||||
}
|
||||
|
||||
_setModalRef (n) {
|
||||
this.modal = n;
|
||||
}
|
||||
|
||||
_handleChange (value) {
|
||||
this._onChange(value);
|
||||
}
|
||||
|
||||
_handleChangeMode (mode) {
|
||||
this.setState({mode});
|
||||
this._onModeChange && this._onModeChange(mode);
|
||||
}
|
||||
|
||||
hide () {
|
||||
this.modal.hide();
|
||||
}
|
||||
|
||||
show (options) {
|
||||
const {
|
||||
title,
|
||||
defaultValue,
|
||||
submitName,
|
||||
placeholder,
|
||||
hint,
|
||||
mode,
|
||||
enableRender,
|
||||
onChange,
|
||||
onModeChange
|
||||
} = options;
|
||||
|
||||
this.modal.show();
|
||||
|
||||
this._onChange = onChange;
|
||||
this._onModeChange = onModeChange;
|
||||
|
||||
this.setState({
|
||||
title,
|
||||
defaultValue,
|
||||
submitName,
|
||||
placeholder,
|
||||
hint,
|
||||
enableRender,
|
||||
mode: mode || this.state.mode
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
handleGetRenderContext,
|
||||
handleRender,
|
||||
editorKeyMap,
|
||||
editorIndentSize,
|
||||
editorFontSize,
|
||||
editorLineWrapping
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
submitName,
|
||||
title,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
hint,
|
||||
mode,
|
||||
enableRender
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Modal ref={this._setModalRef} freshState tall>
|
||||
<ModalHeader>{title}</ModalHeader>
|
||||
<ModalBody className="wide tall" style={{minHeight: '10rem'}}>
|
||||
{mode === 'text/x-markdown' ? (
|
||||
<div className="pad-sm tall">
|
||||
<MarkdownEditor
|
||||
tall
|
||||
defaultValue={defaultValue}
|
||||
placeholder={placeholder}
|
||||
onChange={this._handleChange}
|
||||
handleGetRenderContext={enableRender && handleGetRenderContext}
|
||||
handleRender={enableRender && handleRender}
|
||||
mode={mode}
|
||||
keyMap={editorKeyMap}
|
||||
indentSize={editorIndentSize}
|
||||
fontSize={editorFontSize}
|
||||
lineWrapping={editorLineWrapping}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pad-sm pad-bottom tall">
|
||||
<div className="form-control form-control--outlined form-control--tall tall">
|
||||
<CodeEditor
|
||||
hideLineNumbers
|
||||
className="tall"
|
||||
defaultValue={defaultValue}
|
||||
placeholder={placeholder}
|
||||
onChange={this._handleChange}
|
||||
getRenderContext={enableRender && handleGetRenderContext}
|
||||
render={enableRender && handleRender}
|
||||
mode={mode}
|
||||
keyMap={editorKeyMap}
|
||||
indentSize={editorIndentSize}
|
||||
fontSize={editorFontSize}
|
||||
lineWrapping={editorLineWrapping}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Dropdown>
|
||||
<DropdownButton className="btn btn--clicky margin-left-sm">
|
||||
{MODES[mode]}
|
||||
<i className="fa fa-caret-down space-left"/>
|
||||
</DropdownButton>
|
||||
<DropdownDivider>Editor Syntax</DropdownDivider>
|
||||
{Object.keys(MODES).map(mode => (
|
||||
<DropdownItem key={mode} value={mode} onClick={this._handleChangeMode}>
|
||||
<i className="fa fa-code"/>
|
||||
{MODES[mode]}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</Dropdown>
|
||||
<div className="margin-left faint italic txt-sm tall">{hint ? `* ${hint}` : ''}</div>
|
||||
<button className="btn" onClick={this.hide}>
|
||||
{submitName || 'Submit'}
|
||||
</button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CodePromptModal.propTypes = {
|
||||
// Required
|
||||
editorFontSize: PropTypes.number.isRequired,
|
||||
editorIndentSize: PropTypes.number.isRequired,
|
||||
editorKeyMap: PropTypes.string.isRequired,
|
||||
editorLineWrapping: PropTypes.bool.isRequired,
|
||||
|
||||
// Optional
|
||||
handleGetRenderContext: PropTypes.func,
|
||||
handleRender: PropTypes.func
|
||||
};
|
||||
|
||||
export default CodePromptModal;
|
@ -24,8 +24,7 @@ class PromptModal extends PureComponent {
|
||||
|
||||
_done (rawValue) {
|
||||
const value = this.state.upperCase ? rawValue.toUpperCase() : rawValue;
|
||||
this._onSubmitCallback && this._onSubmitCallback(value);
|
||||
this._onSubmitCallback2 && this._onSubmitCallback2(value);
|
||||
this._onComplete && this._onComplete(value);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@ -75,22 +74,19 @@ class PromptModal extends PureComponent {
|
||||
selectText && this._input.select();
|
||||
}, 100);
|
||||
|
||||
return new Promise(resolve => {
|
||||
this._onSubmitCallback = resolve;
|
||||
this._onSubmitCallback2 = onComplete;
|
||||
this._onComplete = onComplete;
|
||||
|
||||
this.setState({
|
||||
headerName,
|
||||
defaultValue,
|
||||
submitName,
|
||||
selectText,
|
||||
placeholder,
|
||||
upperCase,
|
||||
hint,
|
||||
inputType,
|
||||
label,
|
||||
hints
|
||||
});
|
||||
this.setState({
|
||||
headerName,
|
||||
defaultValue,
|
||||
submitName,
|
||||
selectText,
|
||||
placeholder,
|
||||
upperCase,
|
||||
hint,
|
||||
inputType,
|
||||
label,
|
||||
hints
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ class RequestCreateModal extends PureComponent {
|
||||
true,
|
||||
);
|
||||
|
||||
this._onSubmitCallback(finalRequest);
|
||||
this._onComplete(finalRequest);
|
||||
|
||||
this.hide();
|
||||
}
|
||||
@ -75,7 +75,7 @@ class RequestCreateModal extends PureComponent {
|
||||
this.modal.hide();
|
||||
}
|
||||
|
||||
show ({parentId}) {
|
||||
show ({parentId, onComplete}) {
|
||||
this.modal.show();
|
||||
|
||||
this._input.value = 'My Request';
|
||||
@ -91,9 +91,7 @@ class RequestCreateModal extends PureComponent {
|
||||
this._input.select();
|
||||
}, 200);
|
||||
|
||||
return new Promise(resolve => {
|
||||
this._onSubmitCallback = resolve;
|
||||
});
|
||||
this._onComplete = onComplete;
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -5,6 +5,7 @@ import * as sync from '../../../sync/index';
|
||||
import Link from '../base/link';
|
||||
import LoginModal from '../modals/login-modal';
|
||||
import {hideAllModals, showModal} from '../modals/index';
|
||||
import PromptButton from '../base/prompt-button';
|
||||
|
||||
@autobind
|
||||
class Account extends PureComponent {
|
||||
@ -45,7 +46,7 @@ class Account extends PureComponent {
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Or <a href="" onClick={this._handleLogin}>Login</a>
|
||||
Or <a href="#" onClick={this._handleLogin}>Login</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@ -63,15 +64,12 @@ class Account extends PureComponent {
|
||||
<code className="code--compact">{session.getEmail()}</code>
|
||||
</p>
|
||||
<br/>
|
||||
<Link button
|
||||
href="https://insomnia.rest/app/"
|
||||
className="btn btn--clicky">
|
||||
<Link button href="https://insomnia.rest/app/" className="btn btn--clicky">
|
||||
Manage Account
|
||||
</Link>
|
||||
<button className="margin-left-sm btn btn--clicky"
|
||||
onClick={this._handleLogout}>
|
||||
<PromptButton className="margin-left-sm btn btn--clicky" onClick={this._handleLogout}>
|
||||
Sign Out
|
||||
</button>
|
||||
</PromptButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -90,7 +90,10 @@ class General extends PureComponent {
|
||||
</div>
|
||||
|
||||
<div className="form-control form-control--thin">
|
||||
<label className="inline-block">Disable Analytics Tracking (requires restart)
|
||||
<label className="inline-block">
|
||||
Disable Usage Tracking
|
||||
{' '}
|
||||
<HelpTooltip>Requires restart to take effect</HelpTooltip>
|
||||
<input type="checkbox"
|
||||
name="disableAnalyticsTracking"
|
||||
checked={settings.disableAnalyticsTracking}
|
||||
|
@ -47,7 +47,7 @@ class Plugins extends PureComponent {
|
||||
return (
|
||||
<div>
|
||||
<p className="notice info no-margin-top">
|
||||
Plugins are still experimental and may fail to work in future releases
|
||||
Plugins are experimental and compatibility may break in future releases
|
||||
</p>
|
||||
<table className="table--fancy table--striped margin-top margin-bottom">
|
||||
<thead>
|
||||
|
@ -24,6 +24,7 @@ import Sidebar from './sidebar/sidebar';
|
||||
import WorkspaceEnvironmentsEditModal from './modals/workspace-environments-edit-modal';
|
||||
import WorkspaceSettingsModal from './modals/workspace-settings-modal';
|
||||
import WorkspaceShareSettingsModal from './modals/workspace-share-settings-modal';
|
||||
import CodePromptModal from './modals/code-prompt-modal';
|
||||
import * as models from '../../models/index';
|
||||
import {updateMimeType} from '../../models/request';
|
||||
import {trackEvent} from '../../analytics/index';
|
||||
@ -389,6 +390,16 @@ class Wrapper extends PureComponent {
|
||||
<FilterHelpModal ref={registerModal}/>
|
||||
<RequestRenderErrorModal ref={registerModal}/>
|
||||
|
||||
<CodePromptModal
|
||||
ref={registerModal}
|
||||
handleRender={handleRender}
|
||||
handleGetRenderContext={handleGetRenderContext}
|
||||
editorFontSize={settings.editorFontSize}
|
||||
editorIndentSize={settings.editorIndentSize}
|
||||
editorKeyMap={settings.editorKeyMap}
|
||||
editorLineWrapping={settings.editorLineWrapping}
|
||||
/>
|
||||
|
||||
<RequestSettingsModal
|
||||
ref={registerModal}
|
||||
editorFontSize={settings.editorFontSize}
|
||||
|
@ -157,10 +157,10 @@ class App extends PureComponent {
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: KEY_N,
|
||||
callback: async () => {
|
||||
callback: () => {
|
||||
const {activeRequest, activeWorkspace} = this.props;
|
||||
const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
|
||||
await this._requestCreate(parentId);
|
||||
this._requestCreate(parentId);
|
||||
trackEvent('HotKey', 'Request Create');
|
||||
}
|
||||
}, {
|
||||
@ -218,9 +218,13 @@ class App extends PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
async _requestCreate (parentId) {
|
||||
const request = await showModal(RequestCreateModal, {parentId});
|
||||
await this._handleSetActiveRequest(request._id);
|
||||
_requestCreate (parentId) {
|
||||
showModal(RequestCreateModal, {
|
||||
parentId,
|
||||
onComplete: request => {
|
||||
this._handleSetActiveRequest(request._id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async _requestGroupDuplicate (requestGroup) {
|
||||
|
@ -73,10 +73,12 @@
|
||||
background-color: @hl-xxs;
|
||||
}
|
||||
|
||||
.input,
|
||||
input,
|
||||
select {
|
||||
height: @line-height-xs;
|
||||
&:not(.form-control--tall) {
|
||||
.input,
|
||||
input,
|
||||
select {
|
||||
height: @line-height-xs;
|
||||
}
|
||||
}
|
||||
|
||||
.input[data-focused="on"],
|
||||
|
@ -58,8 +58,9 @@
|
||||
}
|
||||
|
||||
.key-value-editor__drag {
|
||||
padding-left: @padding-md;
|
||||
padding-right: @padding-md;
|
||||
width: @line-height-sm;
|
||||
min-width: @line-height-sm;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
|
||||
|
@ -8,9 +8,11 @@
|
||||
|
||||
.markdown-editor__edit {
|
||||
padding: @padding-xs @padding-sm;
|
||||
}
|
||||
|
||||
// Not sure why this style doesn't work on .CodeMirror...
|
||||
&.markdown-editor--dynamic-height {
|
||||
.CodeMirror-scroll {
|
||||
// Not sure why this style doesn't work on .CodeMirror...
|
||||
max-height: 30vh;
|
||||
}
|
||||
|
||||
@ -19,6 +21,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.markdown-editor--dynamic-height) .markdown-editor__edit {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
|
||||
.input {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-editor__preview {
|
||||
max-height: 35vh;
|
||||
padding: @padding-sm;
|
||||
|
@ -69,8 +69,8 @@
|
||||
color: var(--hl-xl) !important;
|
||||
}
|
||||
|
||||
.CodeMirror-gutter {
|
||||
background-color: var(--color-bg);
|
||||
.CodeMirror-gutters {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
|
@ -322,6 +322,10 @@ p.notice {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.no-min-width {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.width-auto {
|
||||
width: auto !important;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user