From aec9c85861c64e452e5719bd5ef124387f3d1db0 Mon Sep 17 00:00:00 2001 From: Olaf Sulich Date: Wed, 18 Dec 2024 10:22:04 +0100 Subject: [PATCH] fix: rich text editor [WPB-12089] (#18520) * feat(RichTextEditor): replace markdown transformers with custom implementation * fix(EditedMessagePlugin): editting a message with mentions included spacing * fix: lists * chore: remove unused code * fix: reorder lists * fix: heading inserting * fix: heading margin * fix: restore avatar only on text value + fix styles * feat: update list styles to use lower-alpha and lower-roman * fix: lint * fix: adjust emoji picker position calculations * feat: update lexical to v0.21.0 --- package.json | 14 +- src/script/components/InputBar/InputBar.tsx | 24 +- .../hooks/useEmojiPicker/useEmojiPicker.ts | 6 +- .../RichTextEditor/RichTextEditor.tsx | 27 +- .../FormatToolbar/FormatToolbar.styles.ts | 2 - .../FormatToolbar/FormatToolbar.tsx | 12 +- .../useHeadingState/useHeadingState.ts | 18 +- .../SendMessageButton/SendMessageButton.tsx | 5 +- .../EditedMessagePlugin.tsx | 8 +- .../getMentionMarkdownTransformer.ts | 10 +- .../wrapMentionsWithTags.ts | 33 ++ .../ListIndentationPlugin.ts | 90 +++++ .../ListMaxIndentLevelPlugin.tsx | 82 +++++ .../utils/markdownTransformers.ts | 42 +++ src/style/components/lexical-input.less | 39 +- src/style/content/conversation/input-bar.less | 56 ++- .../content/conversation/message-list.less | 16 +- yarn.lock | 342 +++++++++--------- 18 files changed, 582 insertions(+), 244 deletions(-) create mode 100644 src/script/components/RichTextEditor/plugins/EditedMessagePlugin/wrapMentionsWithTags/wrapMentionsWithTags.ts create mode 100644 src/script/components/RichTextEditor/plugins/ListIndentationPlugin/ListIndentationPlugin.ts create mode 100644 src/script/components/RichTextEditor/plugins/ListMaxIndentLevelPlugin/ListMaxIndentLevelPlugin.tsx create mode 100644 src/script/components/RichTextEditor/utils/markdownTransformers.ts diff --git a/package.json b/package.json index 180a3ff796f..36ea39eecdf 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "@datadog/browser-logs": "5.33.0", "@datadog/browser-rum": "5.33.0", "@emotion/react": "11.11.4", - "@lexical/code": "0.20.2", - "@lexical/history": "0.20.2", - "@lexical/list": "0.20.2", - "@lexical/markdown": "0.20.2", - "@lexical/react": "0.20.2", - "@lexical/rich-text": "0.20.2", + "@lexical/code": "0.21.0", + "@lexical/history": "0.21.0", + "@lexical/list": "0.21.0", + "@lexical/markdown": "0.21.0", + "@lexical/react": "0.21.0", + "@lexical/rich-text": "0.21.0", "@mediapipe/tasks-vision": "0.10.20", "@wireapp/avs": "10.0.4", "@wireapp/avs-debugger": "0.0.7", @@ -35,7 +35,7 @@ "kalium-backup": "./TEMP-crossplatform-backup", "keyboardjs": "2.7.0", "knockout": "3.5.1", - "lexical": "0.20.2", + "lexical": "0.21.0", "libsodium-wrappers": "0.7.15", "linkify-it": "5.0.0", "long": "5.2.3", diff --git a/src/script/components/InputBar/InputBar.tsx b/src/script/components/InputBar/InputBar.tsx index ecd73797558..f56988e9294 100644 --- a/src/script/components/InputBar/InputBar.tsx +++ b/src/script/components/InputBar/InputBar.tsx @@ -562,7 +562,7 @@ export const InputBar = ({ const enableSending = textValue.length > 0; - const showAvatar = messageFormatButtonsEnabled || !!textValue.length; + const showAvatar = !!textValue.length; return (
@@ -627,14 +627,20 @@ export const InputBar = ({ onSend={handleSendMessage} onBlur={() => isTypingRef.current && conversationRepository.sendTypingStop(conversation)} > - +
+
    + +
+ +
)} diff --git a/src/script/components/InputBar/hooks/useEmojiPicker/useEmojiPicker.ts b/src/script/components/InputBar/hooks/useEmojiPicker/useEmojiPicker.ts index c6c002634e0..94e79612730 100644 --- a/src/script/components/InputBar/hooks/useEmojiPicker/useEmojiPicker.ts +++ b/src/script/components/InputBar/hooks/useEmojiPicker/useEmojiPicker.ts @@ -26,6 +26,10 @@ interface EmojiPickerParams { onEmojiPicked: (emoji: string) => void; } +const TRIGGER_WIDTH = 40; +const TRIGGER_HEIGHT = 32; +const Y_OFFSET = 8; + export const useEmojiPicker = ({wrapperRef, onEmojiPicked}: EmojiPickerParams) => { const [open, setOpen] = useState(false); @@ -43,7 +47,7 @@ export const useEmojiPicker = ({wrapperRef, onEmojiPicked}: EmojiPickerParams) = const handleToggle = (event: MouseEvent) => { const rect = event.currentTarget.getBoundingClientRect(); // eslint-disable-next-line id-length - emojiPickerPosition.current = {x: rect.x, y: rect.y}; + emojiPickerPosition.current = {x: rect.x + TRIGGER_WIDTH, y: rect.y - TRIGGER_HEIGHT - Y_OFFSET}; setOpen(prev => !prev); }; diff --git a/src/script/components/RichTextEditor/RichTextEditor.tsx b/src/script/components/RichTextEditor/RichTextEditor.tsx index e87253d87d4..81e2fd30395 100644 --- a/src/script/components/RichTextEditor/RichTextEditor.tsx +++ b/src/script/components/RichTextEditor/RichTextEditor.tsx @@ -22,7 +22,7 @@ import {ReactElement, useRef} from 'react'; import {CodeHighlightNode, CodeNode} from '@lexical/code'; import {LinkNode} from '@lexical/link'; import {ListItemNode, ListNode} from '@lexical/list'; -import {$convertToMarkdownString, TRANSFORMERS} from '@lexical/markdown'; +import {$convertToMarkdownString} from '@lexical/markdown'; import {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin'; import {InitialConfigType, LexicalComposer} from '@lexical/react/LexicalComposer'; import {ContentEditable} from '@lexical/react/LexicalContentEditable'; @@ -52,9 +52,12 @@ import {EmojiPickerPlugin} from './plugins/EmojiPickerPlugin'; import {GlobalEventsPlugin} from './plugins/GlobalEventsPlugin'; import {HistoryPlugin} from './plugins/HistoryPlugin'; import {findAndTransformEmoji, ReplaceEmojiPlugin} from './plugins/InlineEmojiReplacementPlugin'; +import {ListItemTabIndentationPlugin} from './plugins/ListIndentationPlugin/ListIndentationPlugin'; +import {ListMaxIndentLevelPlugin} from './plugins/ListMaxIndentLevelPlugin/ListMaxIndentLevelPlugin'; import {MentionsPlugin} from './plugins/MentionsPlugin'; import {ReplaceCarriageReturnPlugin} from './plugins/ReplaceCarriageReturnPlugin/ReplaceCarriageReturnPlugin'; import {SendPlugin} from './plugins/SendPlugin'; +import {markdownTransformers} from './utils/markdownTransformers'; import {MentionEntity} from '../../message/MentionEntity'; @@ -75,8 +78,13 @@ const theme = { code: 'editor-code', }, list: { - ul: 'editor-list editor-list--unordered', - ol: 'editor-list editor-list--ordered', + ul: 'editor-list editor-list-unordered', + ol: 'editor-list editor-list-ordered', + listitem: 'editor-list__item', + nested: { + listitem: 'editor-list__item--nested', + }, + olDepth: ['editor-list-ordered--1', 'editor-list-ordered--2', 'editor-list-ordered--3'], }, heading: { h1: 'editor-heading editor-heading--1', @@ -187,7 +195,7 @@ export const RichTextEditor = ({ return; } - const markdown = $convertToMarkdownString(TRANSFORMERS); + const markdown = $convertToMarkdownString(markdownTransformers); onUpdate({ text: replaceEmojis ? findAndTransformEmoji(markdown) : markdown, @@ -210,14 +218,15 @@ export const RichTextEditor = ({ /> - + + {replaceEmojis && } - + } @@ -242,7 +251,11 @@ export const RichTextEditor = ({ />
- {showFormatToolbar && } + {showFormatToolbar && ( +
+ +
+ )} {children} ); diff --git a/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.styles.ts b/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.styles.ts index d090a351563..686272aaec4 100644 --- a/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.styles.ts +++ b/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.styles.ts @@ -22,6 +22,4 @@ import {CSSObject} from '@emotion/react'; export const wrapperStyles: CSSObject = { display: 'flex', alignItems: 'center', - margin: '8px 0 8px auto', - gridArea: 'toolbar', }; diff --git a/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.tsx b/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.tsx index 78cc63660d5..d1b9f1b90ae 100644 --- a/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.tsx +++ b/src/script/components/RichTextEditor/components/FormatToolbar/FormatToolbar.tsx @@ -77,18 +77,18 @@ export const FormatToolbar = () => { active={activeFormats.includes('strikethrough')} onClick={() => formatText('strikethrough')} /> - toggleList('unordered')} - /> toggleList('ordered')} /> + toggleList('unordered')} + /> { const anchorNode = selection.anchor.getNode(); const isHeading = isNodeHeading(anchorNode); - if (!isHeading) { - editor.dispatchCommand(INSERT_HEADING_COMMAND, {}); + if (isHeading) { + $setBlocksType(selection, () => $createParagraphNode()); return; } - const paragraphNode = $createParagraphNode(); - const headingNode = anchorNode.getParent(); - - if (!headingNode) { - return; - } - - headingNode.replace(paragraphNode); - paragraphNode.append(...headingNode.getChildren()); + $setBlocksType(selection, () => $createHeadingNode('h1')); }); }; diff --git a/src/script/components/RichTextEditor/components/SendMessageButton/SendMessageButton.tsx b/src/script/components/RichTextEditor/components/SendMessageButton/SendMessageButton.tsx index c4b3776e5ec..62293db3082 100644 --- a/src/script/components/RichTextEditor/components/SendMessageButton/SendMessageButton.tsx +++ b/src/script/components/RichTextEditor/components/SendMessageButton/SendMessageButton.tsx @@ -25,13 +25,14 @@ import {t} from 'Util/LocalizerUtil'; interface SendMessageButtonProps { disabled?: boolean; onSend: () => void; + className?: string; } -export const SendMessageButton = ({disabled, onSend}: SendMessageButtonProps) => { +export const SendMessageButton = ({disabled, onSend, className}: SendMessageButtonProps) => { return (