From e1981f4eae83b9349d2708743bdc9da63e70370b Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Tue, 12 Mar 2024 16:51:08 +0530 Subject: [PATCH] chore: add "Clear formatting" option to text format menu and make selected options clearer by showing check if selected [skip e2e] --- packages/web/package.json | 2 + .../Plugins/ToolbarPlugin/ToolbarPlugin.tsx | 61 +++++++++++++++++-- yarn.lock | 2 + 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/packages/web/package.json b/packages/web/package.json index ce122b492..5fc5d42a3 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -111,6 +111,8 @@ "@ariakit/react": "^0.3.9", "@lexical/headless": "0.13.1", "@lexical/list": "0.13.1", + "@lexical/rich-text": "0.13.1", + "@lexical/utils": "0.13.1", "@radix-ui/react-slot": "^1.0.1", "@react-pdf/renderer": "^3.3.2", "comlink": "^4.4.1", diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx index a360b46a9..c869d5544 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx @@ -20,11 +20,18 @@ import { ElementFormatType, $isElementNode, COMMAND_PRIORITY_LOW, + $createParagraphNode, + $isTextNode, } from 'lexical' -import { mergeRegister, $findMatchingParent, $getNearestNodeOfType } from '@lexical/utils' +import { + mergeRegister, + $findMatchingParent, + $getNearestNodeOfType, + $getNearestBlockElementAncestorOrThrow, +} from '@lexical/utils' import { $isLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link' import { $isListNode, ListNode } from '@lexical/list' -import { $isHeadingNode } from '@lexical/rich-text' +import { $isHeadingNode, $isQuoteNode } from '@lexical/rich-text' import { ComponentPropsWithoutRef, ForwardedRef, @@ -66,6 +73,7 @@ import { getDOMRangeRect } from '../../Lexical/Utils/getDOMRangeRect' import { getPositionedPopoverStyles } from '@/Components/Popover/GetPositionedPopoverStyles' import usePreference from '@/Hooks/usePreference' import { ElementIds } from '@/Constants/ElementIDs' +import { $isDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode' const TOGGLE_LINK_AND_EDIT_COMMAND = createCommand('TOGGLE_LINK_AND_EDIT_COMMAND') @@ -181,6 +189,7 @@ const ToolbarMenuItem = ({ name, iconName, active, onClick, ...props }: ToolbarM > {name} + {active && } ) } @@ -378,6 +387,49 @@ const ToolbarPlugin = () => { containerElement.style.removeProperty('opacity') }, []) + const clearFormatting = useCallback(() => { + activeEditor.update(() => { + const selection = $getSelection() + if ($isRangeSelection(selection)) { + const anchor = selection.anchor + const focus = selection.focus + const nodes = selection.getNodes() + + if (anchor.key === focus.key && anchor.offset === focus.offset) { + return + } + + nodes.forEach((node, idx) => { + // We split the first and last node by the selection + // So that we don't format unselected text inside those nodes + if ($isTextNode(node)) { + // Use a separate variable to ensure TS does not lose the refinement + let textNode = node + if (idx === 0 && anchor.offset !== 0) { + textNode = textNode.splitText(anchor.offset)[1] || textNode + } + if (idx === nodes.length - 1) { + textNode = textNode.splitText(focus.offset)[0] || textNode + } + + if (textNode.__style !== '') { + textNode.setStyle('') + } + if (textNode.__format !== 0) { + textNode.setFormat(0) + $getNearestBlockElementAncestorOrThrow(textNode).setFormat('') + } + node = textNode + } else if ($isHeadingNode(node) || $isQuoteNode(node)) { + node.replace($createParagraphNode(), true) + } else if ($isDecoratorBlockNode(node)) { + node.setFormat('') + } + }) + } + }) + }, [activeEditor]) + useEffect(() => { if (isMobile) { return @@ -848,10 +900,11 @@ const ToolbarPlugin = () => { active={isSuperscript} onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'superscript')} /> + setIsTextStyleMenuOpen(!isTextStyleMenuOpen)} @@ -864,7 +917,7 @@ const ToolbarPlugin = () => { portal={false} documentElement={popoverDocumentElement} > - setIsTextStyleMenuOpen(false)}> + setIsTextStyleMenuOpen(false)}>