From 5c87e4ee399afc6a68bcc79a2e00e166f057eb24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 22 Aug 2024 15:22:30 +0200 Subject: [PATCH 1/3] Fix cursor selection event --- WebExample/__tests__/utils.ts | 8 +++++++- src/MarkdownTextInput.web.tsx | 37 +++++++++++++---------------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/WebExample/__tests__/utils.ts b/WebExample/__tests__/utils.ts index 2085afad..dfc97bd8 100644 --- a/WebExample/__tests__/utils.ts +++ b/WebExample/__tests__/utils.ts @@ -11,7 +11,13 @@ const setupInput = async (page: Page, action?: 'clear' | 'reset') => { }; const getCursorPosition = async (elementHandle: Locator) => { - const inputSelectionHandle = await elementHandle.evaluateHandle((div: HTMLInputElement) => ({start: div.selectionStart, end: div.selectionEnd})); + const inputSelectionHandle = await elementHandle.evaluateHandle( + ( + div: HTMLInputElement & { + selection: {start: number; end: number}; + }, + ) => div.selection, + ); const selection = await inputSelectionHandle.jsonValue(); return selection; }; diff --git a/src/MarkdownTextInput.web.tsx b/src/MarkdownTextInput.web.tsx index 01de8eb1..9fe9a7ae 100644 --- a/src/MarkdownTextInput.web.tsx +++ b/src/MarkdownTextInput.web.tsx @@ -57,6 +57,7 @@ let focusTimeout: NodeJS.Timeout | null = null; type MarkdownTextInputElement = HTMLDivElement & HTMLInputElement & { tree: TreeNode; + selection: Selection; }; type HTMLMarkdownElement = HTMLElement & { @@ -233,26 +234,25 @@ const MarkdownTextInput = React.forwardRef( ); const updateRefSelectionVariables = useCallback((newSelection: Selection) => { + if (!divRef.current) { + return; + } const {start, end} = newSelection; - const markdownHTMLInput = divRef.current as HTMLInputElement; - markdownHTMLInput.selectionStart = start; - markdownHTMLInput.selectionEnd = end; + divRef.current.selection = {start, end}; }, []); const updateSelection = useCallback( - (e: SyntheticEvent | null = null, predefinedSelection: Selection | null = null) => { + (e: SyntheticEvent) => { if (!divRef.current) { return; } - const newSelection = predefinedSelection || getCurrentCursorPosition(divRef.current); + const newSelection = getCurrentCursorPosition(divRef.current); if (newSelection && (!contentSelection.current || contentSelection.current.start !== newSelection.start || contentSelection.current.end !== newSelection.end)) { updateRefSelectionVariables(newSelection); contentSelection.current = newSelection; - if (e) { - handleSelectionChange(e); - } + handleSelectionChange(e); } }, [handleSelectionChange, updateRefSelectionVariables], @@ -315,10 +315,6 @@ const MarkdownTextInput = React.forwardRef( } const {text, cursorPosition} = newInputUpdate; updateTextColor(divRef.current, text); - updateSelection(e, { - start: cursorPosition ?? 0, - end: cursorPosition ?? 0, - }); if (onChange) { const event = e as unknown as NativeSyntheticEvent<{ @@ -363,7 +359,7 @@ const MarkdownTextInput = React.forwardRef( handleContentSizeChange(); }, - [updateTextColor, updateSelection, onChange, onChangeText, handleContentSizeChange, undo, redo, parseText, processedMarkdownStyle, setEventProps], + [updateTextColor, onChange, onChangeText, handleContentSizeChange, undo, redo, parseText, processedMarkdownStyle, setEventProps], ); const insertText = useCallback( @@ -382,9 +378,8 @@ const MarkdownTextInput = React.forwardRef( (e.nativeEvent as MarkdownNativeEvent).inputType = 'pasteText'; handleOnChangeText(e); - updateSelection(e); }, - [handleOnChangeText, updateSelection], + [handleOnChangeText], ); const handleKeyPress = useCallback( @@ -421,8 +416,6 @@ const MarkdownTextInput = React.forwardRef( onKeyPress(event); } - updateSelection(event as unknown as SyntheticEvent); - if ( e.key === 'Enter' && // Do not call submit if composition is occuring. @@ -443,7 +436,7 @@ const MarkdownTextInput = React.forwardRef( } } }, - [multiline, blurOnSubmit, setEventProps, onKeyPress, updateSelection, handleOnChangeText, onSubmitEditing, insertText], + [multiline, blurOnSubmit, setEventProps, onKeyPress, handleOnChangeText, onSubmitEditing, insertText], ); const handleFocus: FocusEventHandler = useCallback( @@ -459,7 +452,6 @@ const MarkdownTextInput = React.forwardRef( const valueLength = value ? value.length : divRef.current.value.length; setCursorPosition(divRef.current, valueLength, null); } - updateSelection(event, contentSelection.current); } if (onFocus) { @@ -485,7 +477,7 @@ const MarkdownTextInput = React.forwardRef( } } }, - [clearTextOnFocus, onFocus, selectTextOnFocus, setEventProps, updateSelection, value], + [clearTextOnFocus, onFocus, selectTextOnFocus, setEventProps, value], ); const handleBlur: FocusEventHandler = useCallback( @@ -503,14 +495,13 @@ const MarkdownTextInput = React.forwardRef( const handleClick = useCallback( (e: MouseEvent) => { - updateSelection(e); if (!onClick || !divRef.current) { return; } (e.target as HTMLInputElement).value = divRef.current.value; onClick(e); }, - [onClick, updateSelection], + [onClick], ); const handleCopy: ClipboardEventHandler = useCallback((e) => { @@ -660,7 +651,6 @@ const MarkdownTextInput = React.forwardRef( onKeyDown={handleKeyPress} onCompositionStart={startComposition} onCompositionEnd={endComposition} - onKeyUp={updateSelection} onInput={handleOnChangeText} onClick={handleClick} onFocus={handleFocus} @@ -672,6 +662,7 @@ const MarkdownTextInput = React.forwardRef( spellCheck={spellCheck} dir={dir} inputMode={inputMode} + onSelect={updateSelection} /> ); }, From b36dab7afac5d7ba80b8c540c1a174ced1374451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 22 Aug 2024 19:50:02 +0200 Subject: [PATCH 2/3] Fix cursor positioning on pasting --- src/MarkdownTextInput.web.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/MarkdownTextInput.web.tsx b/src/MarkdownTextInput.web.tsx index 9fe9a7ae..cb1bac2d 100644 --- a/src/MarkdownTextInput.web.tsx +++ b/src/MarkdownTextInput.web.tsx @@ -242,11 +242,11 @@ const MarkdownTextInput = React.forwardRef( }, []); const updateSelection = useCallback( - (e: SyntheticEvent) => { + (e: SyntheticEvent, predefinedSelection: Selection | null = null) => { if (!divRef.current) { return; } - const newSelection = getCurrentCursorPosition(divRef.current); + const newSelection = predefinedSelection || getCurrentCursorPosition(divRef.current); if (newSelection && (!contentSelection.current || contentSelection.current.start !== newSelection.start || contentSelection.current.end !== newSelection.end)) { updateRefSelectionVariables(newSelection); @@ -315,6 +315,10 @@ const MarkdownTextInput = React.forwardRef( } const {text, cursorPosition} = newInputUpdate; updateTextColor(divRef.current, text); + updateSelection(e, { + start: cursorPosition ?? 0, + end: cursorPosition ?? 0, + }); if (onChange) { const event = e as unknown as NativeSyntheticEvent<{ @@ -359,7 +363,7 @@ const MarkdownTextInput = React.forwardRef( handleContentSizeChange(); }, - [updateTextColor, onChange, onChangeText, handleContentSizeChange, undo, redo, parseText, processedMarkdownStyle, setEventProps], + [updateTextColor, updateSelection, onChange, onChangeText, handleContentSizeChange, undo, redo, parseText, processedMarkdownStyle, setEventProps], ); const insertText = useCallback( From e8037640e39596c0d88d99f3a61d86b084093545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Tue, 27 Aug 2024 10:29:16 +0200 Subject: [PATCH 3/3] Fix cursor position setting when element was just focused --- src/web/utils/cursorUtils.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/web/utils/cursorUtils.ts b/src/web/utils/cursorUtils.ts index e0b4e23b..9ad986c9 100644 --- a/src/web/utils/cursorUtils.ts +++ b/src/web/utils/cursorUtils.ts @@ -45,6 +45,10 @@ function setCursorPosition(target: MarkdownTextInputElement, startIndex: number, selection.setBaseAndExtent(range.startContainer, range.startOffset, range.endContainer, range.endOffset); } + // Update the focused elements zIndex to make sure they are on top and the cursor is beeing set correctly + startTreeNode.element.style.zIndex = '1'; + endTreeNode.element.style.zIndex = '1'; + scrollIntoView(startTreeNode); }