From f6196215d1779bb3db77b166064a0a5776346540 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus <50919443+bernhardoj@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:33:00 +0800 Subject: [PATCH] Handle context menu format bold and italic (#556) --- example/src/App.tsx | 12 +++++++++ src/MarkdownTextInput.tsx | 1 + src/MarkdownTextInput.web.tsx | 50 ++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 77d5e421..2d48a2bd 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -15,6 +15,17 @@ import {PlatformInfo} from './PlatformInfo'; // We don't need this workaround in New Expensify App since Reanimated is imported before Live Markdown. console.log(Animated); +function handleFormatSelection(selectedText: string, formatCommand: string) { + switch (formatCommand) { + case 'formatBold': + return `*${selectedText}*`; + case 'formatItalic': + return `_${selectedText}_`; + default: + return selectedText; + } +} + export default function App() { const [value, setValue] = React.useState(TEST_CONST.EXAMPLE_CONTENT); const [textColorState, setTextColorState] = React.useState(false); @@ -48,6 +59,7 @@ export default function App() { string; parser: (value: string) => MarkdownRange[]; } diff --git a/src/MarkdownTextInput.web.tsx b/src/MarkdownTextInput.web.tsx index b6902801..05316223 100644 --- a/src/MarkdownTextInput.web.tsx +++ b/src/MarkdownTextInput.web.tsx @@ -30,6 +30,7 @@ const useClientEffect = typeof window === 'undefined' ? useEffect : useLayoutEff interface MarkdownTextInputProps extends TextInputProps, InlineImagesInputProps { markdownStyle?: MarkdownStyle; parser: (text: string) => MarkdownRange[]; + formatSelection?: (selectedText: string, formatCommand: string) => string; onClick?: (e: MouseEvent) => void; dir?: string; disabled?: boolean; @@ -85,6 +86,7 @@ const MarkdownTextInput = React.forwardRef { + if (!contentSelection.current || contentSelection.current.end - contentSelection.current.start < 1) { + throw new Error('[react-native-live-markdown] Trying to apply format command on empty selection'); + } + + if (!formatSelection) { + return parseText(parser, target, parsedText, processedMarkdownStyle, cursorPosition); + } + + const selectedText = parsedText.slice(contentSelection.current.start, contentSelection.current.end); + const formattedText = formatSelection(selectedText, formatCommand); + + if (selectedText === formattedText) { + return parseText(parser, target, parsedText, processedMarkdownStyle, cursorPosition); + } + + const prefix = parsedText.slice(0, contentSelection.current.start); + const suffix = parsedText.slice(contentSelection.current.end); + const diffLength = formattedText.length - selectedText.length; + const text = `${prefix}${formattedText}${suffix}`; + + return parseText(parser, target, text, processedMarkdownStyle, cursorPosition + diffLength, true); + }, + [parser, parseText, formatSelection, processedMarkdownStyle], + ); + // Placeholder text color logic const updateTextColor = useCallback( (node: HTMLDivElement, text: string) => { @@ -361,6 +390,11 @@ const MarkdownTextInput = React.forwardRef