/** * Copyright (c) 2021 GraphQL Contributors. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import React from 'react'; import StyledWrapper from './StyledWrapper'; let CodeMirror; const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true; if (!SERVER_RENDERED) { CodeMirror = require('codemirror'); } export default class QueryEditor extends React.Component { constructor(props) { super(props); // 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. this.cachedValue = props.value || ''; } componentDidMount() { const editor = (this.editor = CodeMirror(this._node, { value: this.props.value || '', lineNumbers: true, lineWrapping: true, tabSize: 2, mode: this.props.mode || 'application/ld+json', keyMap: 'sublime', autoCloseBrackets: true, matchBrackets: true, showCursorWhenSelecting: true, foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], readOnly: this.props.readOnly, theme: this.props.theme === 'dark' ? 'monokai' : 'default', extraKeys: { 'Cmd-Enter': () => { if (this.props.onRun) { this.props.onRun(); } }, 'Ctrl-Enter': () => { if (this.props.onRun) { this.props.onRun(); } }, 'Cmd-S': () => { if (this.props.onSave) { this.props.onSave(); } }, 'Ctrl-S': () => { if (this.props.onSave) { this.props.onSave(); } }, 'Cmd-F': 'findPersistent', 'Ctrl-F': 'findPersistent', Tab: function (cm) { cm.replaceSelection(' ', 'end'); } } })); if (editor) { editor.on('change', this._onEdit); } } componentDidUpdate(prevProps) { // 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) { 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.editor) { this.cachedValue = this.props.value; this.editor.setValue(this.props.value); this.editor.setOption('mode', this.props.mode); } if (this.props.theme !== prevProps.theme && this.editor) { this.cachedValue = this.props.value; this.editor.setValue(this.props.value); this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default'); } this.ignoreChangeEvent = false; } componentWillUnmount() { if (this.editor) { this.editor.off('change', this._onEdit); this.editor = null; } } render() { return ( { this._node = node; }} /> ); } _onEdit = () => { if (!this.ignoreChangeEvent && this.editor) { this.cachedValue = this.editor.getValue(); if (this.props.onEdit) { this.props.onEdit(this.cachedValue); } } }; }