From 37fe064c144284f81ea8fdddcd1bc1c88af8e40d Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Nov 2024 14:03:15 +0100 Subject: [PATCH] fix selection when rerendering because of markdownStyle change --- src/MarkdownTextInput.web.tsx | 22 +++++++++++++++++----- src/web/utils/parserUtils.ts | 17 ++++++++++++++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/MarkdownTextInput.web.tsx b/src/MarkdownTextInput.web.tsx index bc2f4ba8..40549243 100644 --- a/src/MarkdownTextInput.web.tsx +++ b/src/MarkdownTextInput.web.tsx @@ -165,6 +165,7 @@ const MarkdownTextInput = React.forwardRef { if (!divRef.current) { return {text: text || '', cursorPosition: null}; @@ -173,10 +174,21 @@ const MarkdownTextInput = React.forwardRef { const newMarkdownStyle = processMarkdownStyle(memoizedMarkdownStyle); if (divRef.current) { - parseText(divRef.current, divRef.current.value, newMarkdownStyle, null, false, false); + parseText(divRef.current, divRef.current.value, newMarkdownStyle, null, false, false, false, true); } return newMarkdownStyle; }, [memoizedMarkdownStyle, parseText]); diff --git a/src/web/utils/parserUtils.ts b/src/web/utils/parserUtils.ts index 4f0c5793..8895cc4d 100644 --- a/src/web/utils/parserUtils.ts +++ b/src/web/utils/parserUtils.ts @@ -259,7 +259,14 @@ function parseRangesToHTMLNodes( return {dom: rootElement, tree: rootNode}; } -function moveCursor(isFocused: boolean, alwaysMoveCursorToTheEnd: boolean, cursorPosition: number | null, target: MarkdownTextInputElement, shouldScrollIntoView = false) { +function moveCursor( + isFocused: boolean, + alwaysMoveCursorToTheEnd: boolean, + cursorPosition: number | null, + target: MarkdownTextInputElement, + selectionEnd: number | null = null, + shouldScrollIntoView = false, +) { if (!isFocused) { return; } @@ -267,7 +274,7 @@ function moveCursor(isFocused: boolean, alwaysMoveCursorToTheEnd: boolean, curso if (alwaysMoveCursorToTheEnd || cursorPosition === null) { moveCursorToEnd(target); } else if (cursorPosition !== null) { - setCursorPosition(target, cursorPosition, null, shouldScrollIntoView); + setCursorPosition(target, cursorPosition, selectionEnd, shouldScrollIntoView); } } @@ -281,15 +288,19 @@ function updateInputStructure( shouldForceDOMUpdate = false, shouldScrollIntoView = false, inlineImagesProps: InlineImagesInputProps = {}, + shouldPreserveSelection = false, ) { const targetElement = target; // in case the cursorPositionIndex is larger than text length, cursorPosition will be null, i.e: move the caret to the end let cursorPosition: number | null = cursorPositionIndex !== null && cursorPositionIndex <= text.length ? cursorPositionIndex : null; + let selectionEndPosition: number | null = null; const isFocused = document.activeElement === target; if (isFocused && cursorPositionIndex === null) { const selection = getCurrentCursorPosition(target); cursorPosition = selection ? selection.start : null; + // in some cases like rerendering because style was changed we want to preserve selection + selectionEndPosition = shouldPreserveSelection && selection ? selection.end : null; } const markdownRanges = global.parseExpensiMarkToRanges(text); if (!text || targetElement.innerHTML === '
' || (targetElement && targetElement.innerHTML === '\n')) { @@ -312,7 +323,7 @@ function updateInputStructure( updateTreeElementRefs(tree, targetElement); targetElement.tree = tree; - moveCursor(isFocused, alwaysMoveCursorToTheEnd, cursorPosition, targetElement, shouldScrollIntoView); + moveCursor(isFocused, alwaysMoveCursorToTheEnd, cursorPosition, targetElement, selectionEndPosition, shouldScrollIntoView); } else { targetElement.tree = createRootTreeNode(targetElement); }