diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 170024c192e7f1..cf62de82bcf4b3 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n'; import { debounce } from 'lodash'; import { parse } from 'query-string'; import React, { CSSProperties, useCallback, useEffect, useRef, useState } from 'react'; +import { useUIAceKeyboardMode } from '../../../../../../../es_ui_shared/public'; // @ts-ignore import mappings from '../../../../../lib/mappings/mappings'; import { ConsoleMenu } from '../../../../components'; @@ -34,7 +35,6 @@ import { import * as senseEditor from '../../../../models/sense_editor'; import { autoIndent, getDocumentation } from '../console_menu_actions'; import { subscribeResizeChecker } from '../subscribe_console_resize_checker'; -import { useUIAceKeyboardMode } from '../use_ui_ace_keyboard_mode'; import { applyCurrentSettings } from './apply_editor_settings'; import { registerCommands } from './keyboard_shortcuts'; diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts index 8ed01b9b61c7e2..5935c7cc38d035 100644 --- a/src/plugins/es_ui_shared/public/index.ts +++ b/src/plugins/es_ui_shared/public/index.ts @@ -29,3 +29,5 @@ export { } from './request/np_ready_request'; export { indices } from './indices'; + +export { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode'; diff --git a/src/plugins/console/public/application/containers/editor/legacy/use_ui_ace_keyboard_mode.tsx b/src/plugins/es_ui_shared/public/use_ui_ace_keyboard_mode.tsx similarity index 99% rename from src/plugins/console/public/application/containers/editor/legacy/use_ui_ace_keyboard_mode.tsx rename to src/plugins/es_ui_shared/public/use_ui_ace_keyboard_mode.tsx index ca74b19b76f161..a93906d50b64af 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/use_ui_ace_keyboard_mode.tsx +++ b/src/plugins/es_ui_shared/public/use_ui_ace_keyboard_mode.tsx @@ -96,7 +96,7 @@ export function useUIAceKeyboardMode(aceTextAreaElement: HTMLTextAreaElement | n } return () => { if (aceTextAreaElement) { - document.removeEventListener('keydown', documentKeyDownListener); + document.removeEventListener('keydown', documentKeyDownListener, { capture: true }); aceTextAreaElement.removeEventListener('keydown', aceKeydownListener); const textAreaContainer = aceTextAreaElement.parentElement; if (textAreaContainer && textAreaContainer.contains(overlayMountNode.current!)) { diff --git a/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx b/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx index 5f8ab776a7672c..ece22905c64d92 100644 --- a/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx +++ b/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx @@ -8,7 +8,7 @@ import React, { memo, useRef, useEffect, useState } from 'react'; import { Editor as AceEditor } from 'brace'; import { initializeEditor } from './init_editor'; -import { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode'; +import { useUIAceKeyboardMode } from '../../../../../../src/plugins/es_ui_shared/public'; type EditorShim = ReturnType; diff --git a/x-pack/plugins/searchprofiler/public/application/editor/use_ui_ace_keyboard_mode.tsx b/x-pack/plugins/searchprofiler/public/application/editor/use_ui_ace_keyboard_mode.tsx deleted file mode 100644 index edf31c2e7c07f8..00000000000000 --- a/x-pack/plugins/searchprofiler/public/application/editor/use_ui_ace_keyboard_mode.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * Copied from Console plugin - */ - -import React, { useEffect, useRef } from 'react'; -import * as ReactDOM from 'react-dom'; -import { keyCodes, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -const OverlayText = () => ( - // The point of this element is for accessibility purposes, so ignore eslint error - // in this case - // - // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions - <> - - {i18n.translate('xpack.searchProfiler.aceAccessibilityOverlayInstructionEnter', { - defaultMessage: 'Press Enter to start editing.', - })} - - - {i18n.translate('xpack.searchProfiler.aceAccessibilityOverlayInstructionExit', { - defaultMessage: `When you are done, press Escape to stop editing.`, - })} - - -); - -export function useUIAceKeyboardMode(aceTextAreaElement: HTMLTextAreaElement | null) { - const overlayMountNode = useRef(null); - const autoCompleteVisibleRef = useRef(false); - - useEffect(() => { - function onDismissOverlay(event: KeyboardEvent) { - if (event.keyCode === keyCodes.ENTER) { - event.preventDefault(); - aceTextAreaElement!.focus(); - } - } - - function enableOverlay() { - if (overlayMountNode.current) { - overlayMountNode.current.focus(); - } - } - - const isAutoCompleteVisible = () => { - const autoCompleter = document.querySelector('.ace_autocomplete'); - if (!autoCompleter) { - return false; - } - // The autoComplete is just hidden when it's closed, not removed from the DOM. - return autoCompleter.style.display !== 'none'; - }; - - const documentKeyDownListener = () => { - autoCompleteVisibleRef.current = isAutoCompleteVisible(); - }; - - const aceKeydownListener = (event: KeyboardEvent) => { - if (event.keyCode === keyCodes.ESCAPE && !autoCompleteVisibleRef.current) { - event.preventDefault(); - event.stopPropagation(); - enableOverlay(); - } - }; - - if (aceTextAreaElement) { - // We don't control HTML elements inside of ace so we imperatively create an element - // that acts as a container and insert it just before ace's textarea element - // so that the overlay lives at the correct spot in the DOM hierarchy. - overlayMountNode.current = document.createElement('div'); - overlayMountNode.current.className = 'kbnUiAceKeyboardHint'; - overlayMountNode.current.setAttribute('role', 'application'); - overlayMountNode.current.tabIndex = 0; - overlayMountNode.current.addEventListener('focus', enableOverlay); - overlayMountNode.current.addEventListener('keydown', onDismissOverlay); - - ReactDOM.render(, overlayMountNode.current); - - aceTextAreaElement.parentElement!.insertBefore(overlayMountNode.current, aceTextAreaElement); - aceTextAreaElement.setAttribute('tabindex', '-1'); - - // Order of events: - // 1. Document capture event fires first and we check whether an autocomplete menu is open on keydown - // (not ideal because this is scoped to the entire document). - // 2. Ace changes it's state (like hiding or showing autocomplete menu) - // 3. We check what button was pressed and whether autocomplete was visible then determine - // whether it should act like a dismiss or if we should display an overlay. - document.addEventListener('keydown', documentKeyDownListener, { capture: true }); - aceTextAreaElement.addEventListener('keydown', aceKeydownListener); - } - return () => { - if (aceTextAreaElement) { - document.removeEventListener('keydown', documentKeyDownListener); - aceTextAreaElement.removeEventListener('keydown', aceKeydownListener); - const textAreaContainer = aceTextAreaElement.parentElement; - if (textAreaContainer && textAreaContainer.contains(overlayMountNode.current!)) { - textAreaContainer.removeChild(overlayMountNode.current!); - } - } - }; - }, [aceTextAreaElement]); -}