Skip to content

Commit

Permalink
feat: handle context menu format command
Browse files Browse the repository at this point in the history
  • Loading branch information
bernhardoj committed Dec 10, 2024
1 parent c1a08de commit 292f1d8
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 4 deletions.
3 changes: 2 additions & 1 deletion src/MarkdownTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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[];
}

Expand Down
45 changes: 43 additions & 2 deletions src/MarkdownTextInput.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLDivElement>) => void;
dir?: string;
disabled?: boolean;
Expand Down Expand Up @@ -85,6 +86,7 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
multiline = false,
markdownStyle,
parser,
formatSelection,
onBlur,
onChange,
onChangeText,
Expand Down Expand Up @@ -236,6 +238,40 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
[parser, parseText, processedMarkdownStyle],
);

const format = useCallback(
(target: MarkdownTextInputElement, parsedText: string, cursorPosition: number, formatCommand: string): ParseTextResult => {
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) => {
Expand Down Expand Up @@ -361,6 +397,11 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
case 'historyRedo':
newInputUpdate = redo(divRef.current);
break;
case 'formatBold':
case 'formatItalic':
case 'formatUnderline':
newInputUpdate = format(divRef.current, parsedText, newCursorPosition, inputType);
break;
default:
newInputUpdate = parseText(parser, divRef.current, parsedText, processedMarkdownStyle, newCursorPosition, true, !inputType, inputType === 'pasteText');
}
Expand Down Expand Up @@ -414,7 +455,7 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP

handleContentSizeChange();
},
[parser, updateTextColor, updateSelection, onChange, onChangeText, handleContentSizeChange, undo, redo, parseText, processedMarkdownStyle, setEventProps, maxLength],
[parser, updateTextColor, updateSelection, onChange, onChangeText, handleContentSizeChange, undo, redo, format, parseText, processedMarkdownStyle, setEventProps, maxLength],
);

const insertText = useCallback(
Expand Down
4 changes: 3 additions & 1 deletion src/commonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type MarkdownType =
| 'syntax'
| 'inline-image';

type FormatType = 'bold' | 'italic' | 'underline';

interface MarkdownRange {
type: MarkdownType;
start: number;
Expand All @@ -26,4 +28,4 @@ type InlineImagesInputProps = {
imagePreviewAuthRequiredURLs?: string[];
};

export type {MarkdownType, MarkdownRange, InlineImagesInputProps};
export type {MarkdownType, FormatType, MarkdownRange, InlineImagesInputProps};

0 comments on commit 292f1d8

Please sign in to comment.