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