diff --git a/src/legacy/core_plugins/console/np_ready/public/application/components/console_menu.tsx b/src/legacy/core_plugins/console/np_ready/public/application/components/console_menu.tsx index 1fb23ffbc8897..f4b292280056d 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/components/console_menu.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/components/console_menu.tsx @@ -26,7 +26,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; interface Props { getCurl: (cb: (text: string) => void) => void; getDocumentation: () => Promise; - autoIndent: (ev: React.MouseEvent) => void; + autoIndent: (ev?: React.MouseEvent) => void; } interface State { diff --git a/src/legacy/core_plugins/console/np_ready/public/application/components/settings_modal.tsx b/src/legacy/core_plugins/console/np_ready/public/application/components/settings_modal.tsx index 9ecfc1bc4d446..05afdc9b79e0f 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/components/settings_modal.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/components/settings_modal.tsx @@ -41,7 +41,7 @@ import { DevToolsSettings } from '../../services'; export type AutocompleteOptions = 'fields' | 'indices' | 'templates'; interface Props { - onSaveSettings: (newSettings: DevToolsSettings) => Promise; + onSaveSettings: (newSettings: DevToolsSettings) => void; onClose: () => void; refreshAutocompleteSettings: (selectedSettings: any) => void; settings: DevToolsSettings; diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/editor_context.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/editor_context.tsx new file mode 100644 index 0000000000000..aa04a5ff3dd96 --- /dev/null +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/editor_context.tsx @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 React, { createContext, Dispatch, useContext, useReducer } from 'react'; +import { Action, reducer } from './reducer'; +import { DevToolsSettings } from '../../../../services'; + +export interface ContextValue { + editorsReady: boolean; + settings: DevToolsSettings; +} + +const EditorReadContext = createContext(null as any); +const EditorActionContext = createContext>(null as any); + +export interface EditorContextArgs { + children: any; + settings: DevToolsSettings; +} + +const initialValue: ContextValue = { + editorsReady: false, + settings: null as any, +}; + +export function EditorContextProvider({ children, settings }: EditorContextArgs) { + const [state, dispatch] = useReducer(reducer, initialValue, value => ({ + ...value, + settings, + })); + return ( + + {children} + + ); +} + +export const useEditorActionContext = () => { + const context = useContext(EditorActionContext); + if (context === undefined) { + throw new Error('useEditorActionContext must be used inside EditorActionContext'); + } + return context; +}; + +export const useEditorReadContext = () => { + const context = useContext(EditorReadContext); + if (context === undefined) { + throw new Error('useEditorReadContext must be used inside EditorContextProvider'); + } + return context; +}; diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/editor_registry.ts b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/editor_registry.ts new file mode 100644 index 0000000000000..6f14c6fc84150 --- /dev/null +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/editor_registry.ts @@ -0,0 +1,42 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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. + */ + +export class EditorRegistry { + inputEditor: any; + outputEditor: any; + + setInputEditor(inputEditor: any) { + this.inputEditor = inputEditor; + } + + setOutputEditor(outputEditor: any) { + this.outputEditor = outputEditor; + } + + getInputEditor() { + return this.inputEditor; + } + + getOutputEditor() { + return this.outputEditor; + } +} + +// Create a single instance of this and use as private state. +export const instance = new EditorRegistry(); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/index.ts b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/index.ts new file mode 100644 index 0000000000000..c6e059731332a --- /dev/null +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/index.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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. + */ + +export { + EditorContextProvider, + useEditorReadContext, + useEditorActionContext, +} from './editor_context'; diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/reducer.ts b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/reducer.ts new file mode 100644 index 0000000000000..caed6b24c3c11 --- /dev/null +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/context/reducer.ts @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 { Reducer } from 'react'; + +import { instance as registry } from './editor_registry'; +import { ContextValue } from './editor_context'; + +import { restoreRequestFromHistory } from '../legacy/console_history/restore_request_from_history'; +import { + sendCurrentRequestToES, + EsRequestArgs, +} from '../legacy/console_editor/send_current_request_to_es'; +import { DevToolsSettings } from '../../../../services'; + +export type Action = + | { type: 'setInputEditor'; value: any } + | { type: 'setOutputEditor'; value: any } + | { type: 'restoreRequest'; value: any } + | { type: 'updateSettings'; value: DevToolsSettings } + | { type: 'sendRequestToEs'; value: EsRequestArgs } + | { type: 'updateRequestHistory'; value: any }; + +export const reducer: Reducer = (state, action) => { + const nextState = { ...state }; + + if (action.type === 'setInputEditor') { + registry.setInputEditor(action.value); + if (registry.getOutputEditor()) { + nextState.editorsReady = true; + } + } + + if (action.type === 'setOutputEditor') { + registry.setOutputEditor(action.value); + if (registry.getInputEditor()) { + nextState.editorsReady = true; + } + } + + if (action.type === 'restoreRequest') { + restoreRequestFromHistory(registry.getInputEditor(), action.value); + } + + if (action.type === 'updateSettings') { + nextState.settings = action.value; + } + + if (action.type === 'sendRequestToEs') { + const { callback, isPolling, isUsingTripleQuotes } = action.value; + sendCurrentRequestToES({ + input: registry.getInputEditor(), + output: registry.getOutputEditor(), + callback, + isUsingTripleQuotes, + isPolling, + }); + } + + return nextState; +}; diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/index.ts b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/index.ts index 346b748e1be91..b3cab3d13b3a3 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/index.ts +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/index.ts @@ -18,3 +18,4 @@ */ export { Editor, EditorOutput, ConsoleHistory, autoIndent, getDocumentation } from './legacy'; +export { useEditorActionContext, useEditorReadContext, EditorContextProvider } from './context'; diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/apply_editor_settings.ts b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/apply_editor_settings.ts new file mode 100644 index 0000000000000..8445863b6dfc3 --- /dev/null +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/apply_editor_settings.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 { DevToolsSettings } from '../../../../../services'; + +export function applyCurrentSettings(editor: any, settings: DevToolsSettings) { + editor.getSession().setUseWrapMode(settings.wrapMode); + editor.$el.css('font-size', settings.fontSize + 'px'); +} diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx index 0ce3d44caccd9..4dacaa739592e 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -27,16 +27,22 @@ import { EuiIcon } from '@elastic/eui'; import { useAppContext } from '../../../../context'; import { useUIAceKeyboardMode } from '../use_ui_ace_keyboard_mode'; import { ConsoleMenu } from '../../../../components'; + import { autoIndent, getDocumentation } from '../console_menu_actions'; import { registerCommands } from './keyboard_shortcuts'; +import { applyCurrentSettings } from './apply_editor_settings'; // @ts-ignore import { initializeInput } from '../../../../../../../public/quarantined/src/input'; +// @ts-ignore +import mappings from '../../../../../../../public/quarantined/src/mappings'; + +import { useEditorActionContext, useEditorReadContext } from '../../context'; +import { subscribeResizeChecker } from '../subscribe_console_resize_checker'; +import { loadRemoteState } from './load_remote_editor_state'; export interface EditorProps { - onEditorReady?: (editor: any) => void; - sendCurrentRequest?: () => void; - docLinkVersion: string; + previousStateLocation?: 'stored' | string; } const abs: CSSProperties = { @@ -47,11 +53,23 @@ const abs: CSSProperties = { right: '0', }; -function Component({ onEditorReady, docLinkVersion, sendCurrentRequest = () => {} }: EditorProps) { +const DEFAULT_INPUT_VALUE = `GET _search +{ + "query": { + "match_all": {} + } +}`; + +function _Editor({ previousStateLocation = 'stored' }: EditorProps) { const { - services: { history, settings }, + services: { history }, + ResizeChecker, + docLinkVersion, } = useAppContext(); + const { settings } = useEditorReadContext(); + const dispatch = useEditorActionContext(); + const editorRef = useRef(null); const actionsRef = useRef(null); const editorInstanceRef = useRef(null); @@ -70,21 +88,86 @@ function Component({ onEditorReady, docLinkVersion, sendCurrentRequest = () => { useEffect(() => { const $editor = $(editorRef.current!); const $actions = $(actionsRef.current!); - editorInstanceRef.current = initializeInput($editor, $actions, history, settings); - if (onEditorReady) { - onEditorReady({ editor: editorInstanceRef.current, element: editorRef.current! }); + editorInstanceRef.current = initializeInput($editor, $actions); + + if (previousStateLocation === 'stored') { + const { content } = history.getSavedEditorState() || { + content: DEFAULT_INPUT_VALUE, + }; + editorInstanceRef.current.update(content); + } else { + loadRemoteState({ url: previousStateLocation, input: editorInstanceRef.current }); + } + + function setupAutosave() { + let timer: number; + const saveDelay = 500; + + return editorInstanceRef.current.getSession().on('change', function onChange() { + if (timer) { + clearTimeout(timer); + } + timer = window.setTimeout(saveCurrentState, saveDelay); + }); + } + + function saveCurrentState() { + try { + const content = editorInstanceRef.current.getValue(); + history.updateCurrentState(content); + } catch (e) { + // Ignoring saving error + } } + dispatch({ + type: 'setInputEditor', + value: editorInstanceRef.current, + }); + setTextArea(editorRef.current!.querySelector('textarea')); + + mappings.retrieveAutoCompleteInfo(); + + const unsubscribeResizer = subscribeResizeChecker( + ResizeChecker, + editorRef.current!, + editorInstanceRef.current + ); + const unsubscribeAutoSave = setupAutosave(); + + return () => { + unsubscribeResizer(); + unsubscribeAutoSave(); + mappings.clearSubscriptions(); + }; }, []); + const sendCurrentRequestToES = () => { + dispatch({ + type: 'sendRequestToEs', + value: { + isUsingTripleQuotes: settings.tripleQuotes, + isPolling: settings.polling, + callback: (esPath: any, esMethod: any, esData: any) => + history.addToHistory(esPath, esMethod, esData), + }, + }); + }; + + useEffect(() => { + applyCurrentSettings(editorInstanceRef.current!, settings); + // Preserve legacy focus behavior after settings have updated. + editorInstanceRef.current!.focus(); + }, [settings]); + useEffect(() => { registerCommands({ input: editorInstanceRef.current, - sendCurrentRequestToES: sendCurrentRequest, + sendCurrentRequestToES, openDocumentation, }); - }, [sendCurrentRequest]); + }, []); return (
@@ -97,7 +180,7 @@ function Component({ onEditorReady, docLinkVersion, sendCurrentRequest = () => { })} >