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:
Gregory Schier 2017-06-09 14:42:19 -07:00 committed by GitHub
parent 4129c1ec8e
commit 3056921fda
22 changed files with 463 additions and 120 deletions

View File

@ -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);

View File

@ -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
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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>

View File

@ -34,6 +34,7 @@ class UrlEncodedEditor extends PureComponent {
<div className="scrollable">
<KeyValueEditor
sortable
allowMultiline
namePlaceholder="name"
valuePlaceholder="value"
onChange={onChange}

View File

@ -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,

View File

@ -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,

View File

@ -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;

View 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;

View File

@ -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
});
}

View File

@ -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 () {

View File

@ -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>
);
}

View File

@ -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}

View File

@ -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>

View File

@ -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}

View File

@ -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) {

View File

@ -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"],

View File

@ -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;

View File

@ -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;

View File

@ -69,8 +69,8 @@
color: var(--hl-xl) !important;
}
.CodeMirror-gutter {
background-color: var(--color-bg);
.CodeMirror-gutters {
background-color: transparent;
}
.CodeMirror-scroll {

View File

@ -322,6 +322,10 @@ p.notice {
box-sizing: border-box;
}
.no-min-width {
min-width: 0;
}
.width-auto {
width: auto !important;
}