From e1f0cd69aa31a1c54ae406f874f25fbc646de0eb Mon Sep 17 00:00:00 2001 From: Patryk Kopycinski Date: Tue, 24 Aug 2021 22:23:46 +0200 Subject: [PATCH] cleanup --- .../components/markdown_editor/editor.tsx | 89 +++---------- .../markdown_editor/plugins/lens/index.ts | 4 +- .../markdown_editor/plugins/lens/plugin.tsx | 4 +- .../plugins/lens/translations.ts | 6 +- .../plugins/lens/use_lens_button_toggle.ts | 123 +++++++++++++++--- .../plugins/lens/use_lens_draft_comment.ts | 6 +- 6 files changed, 127 insertions(+), 105 deletions(-) diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editor.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editor.tsx index c24ccfd9d9a46..b61fae25aa399 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editor.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editor.tsx @@ -9,21 +9,22 @@ import React, { memo, forwardRef, useCallback, - useEffect, useMemo, useRef, useState, useImperativeHandle, ElementRef, } from 'react'; -import { some } from 'lodash'; import { PluggableList } from 'unified'; -import { EuiMarkdownAstNode, EuiMarkdownEditor, EuiMarkdownEditorUiPlugin } from '@elastic/eui'; +import { + EuiMarkdownEditor, + EuiMarkdownEditorProps, + EuiMarkdownAstNode, + EuiMarkdownEditorUiPlugin, +} from '@elastic/eui'; import { ContextShape } from '@elastic/eui/src/components/markdown_editor/markdown_context'; -import useDebounce from 'react-use/lib/useDebounce'; import { usePlugins } from './use_plugins'; import { CommentEditorContext } from './context'; -import { PREFIX } from './plugins/lens/constants'; import { useLensButtonToggle } from './plugins/lens/use_lens_button_toggle'; interface MarkdownEditorProps { @@ -48,17 +49,21 @@ export interface MarkdownEditorRef { const MarkdownEditorComponent = forwardRef( ({ ariaLabel, dataTestSubj, editorId, height, onChange, value }, ref) => { - const astRef = useRef(null); + const astRef = useRef(undefined); const [markdownErrorMessages, setMarkdownErrorMessages] = useState([]); - const onParse = useCallback((err, { messages, ast }) => { + const onParse: EuiMarkdownEditorProps['onParse'] = useCallback((err, { messages, ast }) => { setMarkdownErrorMessages(err ? [err] : messages); astRef.current = ast; }, []); const { parsingPlugins, processingPlugins, uiPlugins } = usePlugins(); const editorRef = useRef(null); - const { enableLensButton, disableLensButton } = useLensButtonToggle(); - const [lensNodeSelected, setLensNodeSelected] = useState(false); - const lensPluginAvailable = useRef(false); + + useLensButtonToggle({ + astRef, + uiPlugins, + editorRef: ref as React.MutableRefObject, + value, + }); const commentEditorContextValue = useMemo( () => ({ @@ -68,10 +73,6 @@ const MarkdownEditorComponent = forwardRef { - lensPluginAvailable.current = some(uiPlugins, ['name', 'lens']); - }, [uiPlugins]); - // @ts-expect-error useImperativeHandle(ref, () => { if (!editorRef.current) { @@ -86,66 +87,6 @@ const MarkdownEditorComponent = forwardRef { - if (lensNodeSelected || !value.includes(PREFIX)) { - // @ts-expect-error - enableLensButton({ editorRef: ref?.current! }); - } else { - // @ts-expect-error - disableLensButton({ editorRef: ref?.current! }); - } - }, - 100, - [value, lensNodeSelected] - ); - - // Copied from https://github.com/elastic/eui/blob/master/src/components/markdown_editor/markdown_editor.tsx#L279 - useEffect(() => { - if ( - editorRef.current?.textarea == null || - astRef.current == null || - !lensPluginAvailable.current - ) { - return; - } - - const getCursorNode = () => { - const { selectionStart } = editorRef.current?.textarea!; - - let node: EuiMarkdownAstNode = astRef.current!; - - outer: while (true) { - if (node.children) { - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - if ( - child.position.start.offset < selectionStart && - selectionStart < child.position.end.offset - ) { - if (child.type === 'text') break outer; // don't dive into `text` nodes - node = child; - continue outer; - } - } - } - break; - } - - setLensNodeSelected(node.type === 'lens'); - }; - - const textarea = editorRef.current?.textarea; - - textarea.addEventListener('keyup', getCursorNode); - textarea.addEventListener('mouseup', getCursorNode); - - return () => { - textarea.removeEventListener('keyup', getCursorNode); - textarea.removeEventListener('mouseup', getCursorNode); - }; - }, [editorRef.current?.textarea]); - return ( { - const enableLensButton = useCallback(({ editorRef }) => { - if (editorRef && editorRef.textarea && editorRef.toolbar) { - const lensPluginButton = editorRef.toolbar?.querySelector( - `[aria-label="${ADD_VISUALIZATION}"]` +interface MarkdownEditorRef { + textarea: HTMLTextAreaElement | null; + replaceNode: ContextShape['replaceNode']; + toolbar: HTMLDivElement | null; +} + +interface UseLensButtonToggleProps { + astRef?: React.MutableRefObject; + uiPlugins?: EuiMarkdownEditorUiPlugin[] | undefined; + editorRef?: React.MutableRefObject; + value?: string; +} + +export const useLensButtonToggle = ({ + astRef, + editorRef, + uiPlugins, + value, +}: UseLensButtonToggleProps) => { + const lensPluginAvailable = useRef(false); + const [lensNodeSelected, setLensNodeSelected] = useState(false); + + const enableLensButton = useCallback(() => { + if (editorRef?.current?.textarea && editorRef.current?.toolbar) { + const lensPluginButton = editorRef.current?.toolbar?.querySelector( + `[aria-label="${VISUALIZATION}"]` ); if (lensPluginButton) { const isDisabled = lensPluginButton.className.includes(DISABLED_CLASSNAME); - - if (isDisabled) { + const buttonStyle = lensPluginButton.getAttribute('style'); + if (isDisabled && buttonStyle) { lensPluginButton.className = lensPluginButton.className.replace(DISABLED_CLASSNAME, ''); - lensPluginButton.setAttribute( - 'style', - lensPluginButton.getAttribute('style').replace('pointer-events: none;', '') - ); + lensPluginButton.setAttribute('style', buttonStyle.replace('pointer-events: none;', '')); } } } - }, []); + }, [editorRef]); - const disableLensButton = useCallback(({ editorRef }) => { - if (editorRef && editorRef.textarea && editorRef.toolbar) { - const lensPluginButton = editorRef.toolbar?.querySelector( - `[aria-label="${ADD_VISUALIZATION}"]` + const disableLensButton = useCallback(() => { + if (editorRef?.current?.textarea && editorRef.current.toolbar) { + const lensPluginButton = editorRef.current.toolbar?.querySelector( + `[aria-label="${VISUALIZATION}"]` ); if (lensPluginButton) { @@ -46,7 +69,67 @@ export const useLensButtonToggle = () => { } } } - }, []); + }, [editorRef]); + + useEffect(() => { + lensPluginAvailable.current = some(uiPlugins, ['name', 'lens']); + }, [uiPlugins]); + + useDebounce( + () => { + if (lensNodeSelected || !value?.includes(PREFIX)) { + enableLensButton(); + } else { + disableLensButton(); + } + }, + 100, + [value, lensNodeSelected] + ); + + // Copied from https://github.com/elastic/eui/blob/master/src/components/markdown_editor/markdown_editor.tsx#L279 + useEffect(() => { + if ( + editorRef?.current?.textarea == null || + astRef?.current == null || + !lensPluginAvailable.current + ) { + return; + } + + const getCursorNode = () => { + const { selectionStart } = editorRef.current?.textarea!; + + let node: EuiMarkdownAstNode = astRef.current!; + + outer: while (true) { + if (node.children) { + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + if ( + child.position.start.offset < selectionStart && + selectionStart < child.position.end.offset + ) { + if (child.type === 'text') break outer; // don't dive into `text` nodes + node = child; + continue outer; + } + } + } + break; + } + + setLensNodeSelected(node.type === 'lens'); + }; + + const textarea = editorRef.current?.textarea; + + textarea.addEventListener('keyup', getCursorNode); + textarea.addEventListener('mouseup', getCursorNode); - return { enableLensButton, disableLensButton }; + return () => { + textarea.removeEventListener('keyup', getCursorNode); + textarea.removeEventListener('mouseup', getCursorNode); + }; + }, [astRef, editorRef]); }; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_draft_comment.ts b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_draft_comment.ts index 26067918f9a9a..2a77037b300a3 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_draft_comment.ts +++ b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_draft_comment.ts @@ -10,7 +10,7 @@ import { useCallback, useEffect, useState } from 'react'; import { first } from 'rxjs/operators'; import { useKibana } from '../../../../common/lib/kibana'; import { DRAFT_COMMENT_STORAGE_ID } from './constants'; -import { ADD_VISUALIZATION } from './translations'; +import { VISUALIZATION } from './translations'; interface DraftComment { commentId: string; @@ -51,9 +51,7 @@ export const useLensDraftComment = () => { const openLensModal = useCallback(({ editorRef }) => { if (editorRef && editorRef.textarea && editorRef.toolbar) { - const lensPluginButton = editorRef.toolbar?.querySelector( - `[aria-label="${ADD_VISUALIZATION}"]` - ); + const lensPluginButton = editorRef.toolbar?.querySelector(`[aria-label="${VISUALIZATION}"]`); if (lensPluginButton) { lensPluginButton.click(); }