app/addons/documents/mango/components/MangoQueryEditor.js (115 lines of code) (raw):

// Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. import PropTypes from 'prop-types'; import React, { Component } from "react"; import { Button } from 'react-bootstrap'; import ReactSelect from "react-select"; import "../../../../../assets/js/plugins/prettify"; import app from "../../../../app"; import FauxtonAPI from "../../../../core/api"; import ReactComponents from "../../../components/react-components"; import ExecutionStatsPanel from './ExecutionStatsPanel'; import MangoQueryCheatsheetModal from './MangoQueryCheatsheetModal'; const PaddedBorderedBox = ReactComponents.PaddedBorderedBox; const CodeEditorPanel = ReactComponents.CodeEditorPanel; const getDocUrl = app.helpers.getDocUrl; export default class MangoQueryEditor extends Component { constructor(props) { super(props); } componentDidMount() { prettyPrint(); this.props.loadQueryHistory({ databaseName: this.props.databaseName }); // Clear results list in case it was populated by other pages this.props.clearResults(); this.props.checkExecutionStatsSupport({ databaseName: this.props.databaseName }); // Add key binding to run query when doing Ctrl-Enter const editor = this.codeEditor.codeEditor.editor; const runQueryCmdName = "runQuery"; if (!editor.commands.byName[runQueryCmdName]) { editor.commands.addCommand({ name: runQueryCmdName, bindKey: {win: 'Ctrl-Enter', mac: 'Command-Option-Enter'}, exec: () => { this.runQuery({preventDefault: () => {}}); }, readOnly: true }); } } componentDidUpdate (prevProps) { prettyPrint(); if (prevProps.history != this.props.history) { // Explicitly set value because updating 'CodeEditorPanel.defaultCode' won't change the editor once it's already loaded. this.setEditorValue(this.props.history[0].value); } } setEditorValue (newValue = '') { return this.codeEditor.getEditor().setValue(newValue); } getEditorValue () { return this.codeEditor.getValue(); } editorHasErrors () { return this.codeEditor.getEditor().hasErrors(); } onHistorySelected(selectedItem) { this.setEditorValue(selectedItem.value); } state = { isCheatsheetVisible: false, }; hideCheatsheetModal = () => { this.setState({isCheatsheetVisible: false}); }; showCheatsheetModal = () => { this.setState({isCheatsheetVisible: true}); }; editor() { return ( <div className="mango-editor-wrapper"> <MangoQueryCheatsheetModal isVisible={this.state.isCheatsheetVisible} onHide={this.hideCheatsheetModal}/> <form className="form-horizontal" onSubmit={(ev) => {this.runQuery(ev);}}> <div className="padded-box"> <ReactSelect className="mango-select" options={this.props.history} ref={node => this.history = node} placeholder="Query history" searchable={false} clearable={false} autosize={false} onChange={(elem) => {this.onHistorySelected(elem);}} /> </div> <PaddedBorderedBox> <CodeEditorPanel id="query-field" ref={node => this.codeEditor = node} title={this.props.editorTitle} docLink={getDocUrl('MANGO_SEARCH')} syntaxMode="mango" defaultCode={this.props.queryFindCode} showCheatSheetIcon={true} onCheatsheatIconClick={this.showCheatsheetModal} setHeightToLineCount={false} className="mango-code-editor"/> </PaddedBorderedBox> <div className="padded-box"> <div className="actions-panel"> <Button type="submit" id="create-index-btn" variant="cf-primary" className="btn-space">Run Query</Button> <Button type="button" id="explain-btn" variant="cf-secondary" className="btn-space" onClick={(ev) => {this.runExplain(ev);} }>Explain</Button> <div className="right-side-actions"> <Button variant="cf-secondary" className="edit-link" onClick={(ev) => {this.manageIndexes(ev);}}>Manage Indexes</Button> </div> </div> <div> <ExecutionStatsPanel {...this.props} /> </div> </div> </form> </div> ); } render () { if (this.props.isLoading) { return ( <div className="mango-editor-wrapper"> <ReactComponents.LoadLines /> </div> ); } return this.editor(); } notifyOnQueryError() { if (this.editorHasErrors()) { FauxtonAPI.addNotification({ msg: 'Please fix the Javascript errors and try again.', type: 'error', clear: true }); return true; } return false; } manageIndexes(event) { event.preventDefault(); this.props.manageIndexes(); const encodedPartKey = this.props.partitionKey ? encodeURIComponent(this.props.partitionKey) : ''; const manageIndexURL = '#' + FauxtonAPI.urls('mango', 'index-app', encodeURIComponent(this.props.databaseName), encodedPartKey); FauxtonAPI.navigate(manageIndexURL); } runExplain(event) { event.preventDefault(); if (this.notifyOnQueryError()) { return; } this.props.runExplainQuery({ databaseName: this.props.databaseName, partitionKey: this.props.partitionKey, queryCode: this.getEditorValue() }); } runQuery (event) { event.preventDefault(); if (this.notifyOnQueryError()) { return; } this.props.clearResults(); this.props.runQuery({ databaseName: this.props.databaseName, partitionKey: this.props.partitionKey, queryCode: JSON.parse(this.getEditorValue()), fetchParams: {...this.props.fetchParams, skip: 0} }); } } MangoQueryEditor.propTypes = { description: PropTypes.string.isRequired, editorTitle: PropTypes.string.isRequired, queryFindCode: PropTypes.string.isRequired, queryFindCodeChanged: PropTypes.bool, databaseName: PropTypes.string.isRequired, partitionKey: PropTypes.string, executionStatsSupported: PropTypes.bool.isRequired, runExplainQuery: PropTypes.func.isRequired, runQuery: PropTypes.func.isRequired, manageIndexes: PropTypes.func.isRequired, loadQueryHistory: PropTypes.func.isRequired, clearResults: PropTypes.func.isRequired, checkExecutionStatsSupport: PropTypes.func.isRequired, };