UNPKG

@platformos/pos-cli

Version:

Manage your platformOS application

297 lines (244 loc) 8.75 kB
"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 });