@platformos/pos-cli
Version:
Manage your platformOS application
297 lines (244 loc) • 8.75 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.QueryEditor = void 0;
var _react = _interopRequireDefault(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _graphql = require("graphql");
var _markdownIt = _interopRequireDefault(require("markdown-it"));
var _normalizeWhitespace = require("../utility/normalizeWhitespace");
var _onHasCompletion = _interopRequireDefault(require("../utility/onHasCompletion"));
var _commonKeys = _interopRequireDefault(require("../utility/commonKeys"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const md = new _markdownIt.default();
const AUTO_COMPLETE_AFTER_KEY = /^[a-zA-Z0-9_@(]$/;
/**
* QueryEditor
*
* Maintains an instance of CodeMirror responsible for editing a GraphQL query.
*
* Props:
*
* - schema: A GraphQLSchema instance enabling editor linting and hinting.
* - value: The text of the editor.
* - onEdit: A function called when the editor changes, given the edited text.
* - readOnly: Turns the editor to read-only mode.
*
*/
class QueryEditor extends _react.default.Component {
constructor(props) {
super(); // Keep a cached version of the value, this cache will be updated when the
// editor is updated, which can later be used to protect the editor from
// unnecessary updates during the update lifecycle.
_defineProperty(this, "_onKeyUp", (cm, event) => {
if (AUTO_COMPLETE_AFTER_KEY.test(event.key)) {
this.editor.execCommand('autocomplete');
}
});
_defineProperty(this, "_onEdit", () => {
if (!this.ignoreChangeEvent) {
this.cachedValue = this.editor.getValue();
if (this.props.onEdit) {
this.props.onEdit(this.cachedValue);
}
}
});
_defineProperty(this, "_onHasCompletion", (cm, data) => {
(0, _onHasCompletion.default)(cm, data, this.props.onHintInformationRender);
});
this.cachedValue = props.value || '';
}
componentDidMount() {
// Lazily require to ensure requiring GraphiQL outside of a Browser context
// does not produce an error.
const CodeMirror = require('codemirror');
require('codemirror/addon/hint/show-hint');
require('codemirror/addon/comment/comment');
require('codemirror/addon/edit/matchbrackets');
require('codemirror/addon/edit/closebrackets');
require('codemirror/addon/fold/foldgutter');
require('codemirror/addon/fold/brace-fold');
require('codemirror/addon/search/search');
require('codemirror/addon/search/searchcursor');
require('codemirror/addon/search/jump-to-line');
require('codemirror/addon/dialog/dialog');
require('codemirror/addon/lint/lint');
require('codemirror/keymap/sublime');
require('codemirror-graphql/hint');
require('codemirror-graphql/lint');
require('codemirror-graphql/info');
require('codemirror-graphql/jump');
require('codemirror-graphql/mode');
this.editor = CodeMirror(this._node, {
value: this.props.value || '',
lineNumbers: true,
tabSize: 2,
mode: 'graphql',
theme: this.props.editorTheme || 'graphiql',
keyMap: 'sublime',
autoCloseBrackets: true,
matchBrackets: true,
showCursorWhenSelecting: true,
readOnly: this.props.readOnly ? 'nocursor' : false,
foldGutter: {
minFoldSize: 4
},
lint: {
schema: this.props.schema
},
hintOptions: {
schema: this.props.schema,
closeOnUnfocus: false,
completeSingle: false,
container: this._node
},
info: {
schema: this.props.schema,
renderDescription: text => md.render(text),
onClick: reference => this.props.onClickReference(reference)
},
jump: {
schema: this.props.schema,
onClick: reference => this.props.onClickReference(reference)
},
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
extraKeys: {
'Cmd-Space': () => this.editor.showHint({
completeSingle: true,
container: this._node
}),
'Ctrl-Space': () => this.editor.showHint({
completeSingle: true,
container: this._node
}),
'Alt-Space': () => this.editor.showHint({
completeSingle: true,
container: this._node
}),
'Shift-Space': () => this.editor.showHint({
completeSingle: true,
container: this._node
}),
'Shift-Alt-Space': () => this.editor.showHint({
completeSingle: true,
container: this._node
}),
'Cmd-Enter': () => {
if (this.props.onRunQuery) {
this.props.onRunQuery();
}
},
'Ctrl-Enter': () => {
if (this.props.onRunQuery) {
this.props.onRunQuery();
}
},
'Shift-Ctrl-C': () => {
if (this.props.onCopyQuery) {
this.props.onCopyQuery();
}
},
'Shift-Ctrl-P': () => {
if (this.props.onPrettifyQuery) {
this.props.onPrettifyQuery();
}
},
/* Shift-Ctrl-P is hard coded in Firefox for private browsing so adding an alternative to Pretiffy */
'Shift-Ctrl-F': () => {
if (this.props.onPrettifyQuery) {
this.props.onPrettifyQuery();
}
},
'Shift-Ctrl-M': () => {
if (this.props.onMergeQuery) {
this.props.onMergeQuery();
}
},
..._commonKeys.default,
'Cmd-S': () => {
if (this.props.onRunQuery) {// empty
}
},
'Ctrl-S': () => {
if (this.props.onRunQuery) {// empty
}
}
}
});
this.editor.on('change', this._onEdit);
this.editor.on('keyup', this._onKeyUp);
this.editor.on('hasCompletion', this._onHasCompletion);
this.editor.on('beforeChange', this._onBeforeChange);
}
componentDidUpdate(prevProps) {
const CodeMirror = require('codemirror'); // Ensure the changes caused by this update are not interpretted as
// user-input changes which could otherwise result in an infinite
// event loop.
this.ignoreChangeEvent = true;
if (this.props.schema !== prevProps.schema) {
this.editor.options.lint.schema = this.props.schema;
this.editor.options.hintOptions.schema = this.props.schema;
this.editor.options.info.schema = this.props.schema;
this.editor.options.jump.schema = this.props.schema;
CodeMirror.signal(this.editor, 'change', this.editor);
}
if (this.props.value !== prevProps.value && this.props.value !== this.cachedValue) {
this.cachedValue = this.props.value;
this.editor.setValue(this.props.value);
}
this.ignoreChangeEvent = false;
}
componentWillUnmount() {
if (this.editor) {
this.editor.off('change', this._onEdit);
this.editor.off('keyup', this._onKeyUp);
this.editor.off('hasCompletion', this._onHasCompletion);
this.editor = null;
}
}
render() {
return _react.default.createElement("section", {
className: "query-editor",
"aria-label": "Query Editor",
ref: node => {
this._node = node;
}
});
}
/**
* Public API for retrieving the CodeMirror instance from this
* React component.
*/
getCodeMirror() {
return this.editor;
}
/**
* Public API for retrieving the DOM client height for this component.
*/
getClientHeight() {
return this._node && this._node.clientHeight;
}
_onBeforeChange(instance, change) {
// The update function is only present on non-redo, non-undo events.
if (change.origin === 'paste') {
const text = change.text.map(_normalizeWhitespace.normalizeWhitespace);
change.update(change.from, change.to, text);
}
}
}
exports.QueryEditor = QueryEditor;
_defineProperty(QueryEditor, "propTypes", {
schema: _propTypes.default.instanceOf(_graphql.GraphQLSchema),
value: _propTypes.default.string,
onEdit: _propTypes.default.func,
readOnly: _propTypes.default.bool,
onHintInformationRender: _propTypes.default.func,
onClickReference: _propTypes.default.func,
onCopyQuery: _propTypes.default.func,
onPrettifyQuery: _propTypes.default.func,
onMergeQuery: _propTypes.default.func,
onRunQuery: _propTypes.default.func,
editorTheme: _propTypes.default.string
});