diff --git a/src/MarkdownTextInput.tsx b/src/MarkdownTextInput.tsx index c4ec2467..51a8368a 100644 --- a/src/MarkdownTextInput.tsx +++ b/src/MarkdownTextInput.tsx @@ -10,7 +10,7 @@ import type {MarkdownStyle} from './MarkdownTextInputDecoratorViewNativeComponen import NativeLiveMarkdownModule from './NativeLiveMarkdownModule'; import {mergeMarkdownStyleWithDefault} from './styleUtils'; import type {PartialMarkdownStyle} from './styleUtils'; -import type {InlineImagesInputProps, MarkdownRange} from './commonTypes'; +import type {FormatType, InlineImagesInputProps, MarkdownRange} from './commonTypes'; declare global { // eslint-disable-next-line no-var @@ -53,6 +53,7 @@ function unregisterParser(parserId: number) { interface MarkdownTextInputProps extends TextInputProps, InlineImagesInputProps { markdownStyle?: PartialMarkdownStyle; + formatSelection?: (selectedText: string, formatType: FormatType) => string; parser: (value: string) => MarkdownRange[]; } diff --git a/src/MarkdownTextInput.web.tsx b/src/MarkdownTextInput.web.tsx index b6902801..4db86b68 100644 --- a/src/MarkdownTextInput.web.tsx +++ b/src/MarkdownTextInput.web.tsx @@ -23,13 +23,14 @@ import type {MarkdownStyle} from './MarkdownTextInputDecoratorViewNativeComponen import {getElementHeight, getPlaceholderValue, isEventComposing, normalizeValue, parseInnerHTMLToText} from './web/utils/inputUtils'; import {parseToReactDOMStyle, processMarkdownStyle} from './web/utils/webStyleUtils'; import {forceRefreshAllImages} from './web/inputElements/inlineImage'; -import type {MarkdownRange, InlineImagesInputProps} from './commonTypes'; +import type {MarkdownRange, InlineImagesInputProps, FormatType} from './commonTypes'; const useClientEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect; interface MarkdownTextInputProps extends TextInputProps, InlineImagesInputProps { markdownStyle?: MarkdownStyle; parser: (text: string) => MarkdownRange[]; + formatSelection?: (selectedText: string, formatType: FormatType) => string; onClick?: (e: MouseEvent) => void; dir?: string; disabled?: boolean; @@ -85,6 +86,7 @@ const MarkdownTextInput = React.forwardRef { + if (!contentSelection.current) { + return { + text: '', + cursorPosition: 0, + }; + } + + let formatType: FormatType; + switch (formatCommand) { + case 'formatBold': + formatType = 'bold'; + break; + case 'formatItalic': + formatType = 'italic'; + break; + default: + formatType = 'underline'; + break; + } + + const beforeSelectedText = parsedText.slice(0, contentSelection.current.start); + const selectedText = parsedText.slice(contentSelection.current.start, contentSelection.current.end); + const formattedText = formatSelection?.(selectedText, formatType) ?? selectedText; + const formattedTextDiffLength = formattedText.length - selectedText.length; + const afterSelectedText = parsedText.slice(contentSelection.current.end); + const text = `${beforeSelectedText}${formattedText}${afterSelectedText}`; + + return parseText(parser, target, text, processedMarkdownStyle, cursorPosition + formattedTextDiffLength, true); + }, + [parser, parseText, formatSelection, processedMarkdownStyle], + ); + // Placeholder text color logic const updateTextColor = useCallback( (node: HTMLDivElement, text: string) => { @@ -361,6 +397,11 @@ const MarkdownTextInput = React.forwardRef