From af8fb09244937c5ebb92044f230115f0f6289217 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 20 Nov 2024 13:53:30 +0800 Subject: [PATCH] ExtendedTextNode --- src/components/LexicalEditor/Index.jsx | 13 +- .../LexicalEditor/nodes/ExtendedTextNode.tsx | 129 ++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 src/components/LexicalEditor/nodes/ExtendedTextNode.tsx diff --git a/src/components/LexicalEditor/Index.jsx b/src/components/LexicalEditor/Index.jsx index 5aaa0da..261a369 100644 --- a/src/components/LexicalEditor/Index.jsx +++ b/src/components/LexicalEditor/Index.jsx @@ -32,15 +32,17 @@ import ImagesPlugin from './plugins/ImagesPlugin'; import InlineImagePlugin from './plugins/InlineImagePlugin'; import { ImageNode } from './nodes/ImageNode'; import {InlineImageNode} from './nodes/InlineImageNode/InlineImageNode'; +import { ExtendedTextNode } from './nodes/ExtendedTextNode'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; // import { useLexicalEditable } from '@lexical/react/useLexicalEditable'; import {TablePlugin} from '@lexical/react/LexicalTablePlugin'; +import TableCellResizer from './plugins/TableCellResizer'; import {useLexicalEditable} from '@lexical/react/useLexicalEditable'; -import { $getRoot, $getSelection, $createParagraphNode } from 'lexical'; +import { TextNode, $getRoot, $getSelection, $createParagraphNode } from 'lexical'; import { $generateHtmlFromNodes, $generateNodesFromDOM, } from '@lexical/html'; // import { } from '@lexical/clipboard'; import { isEmpty } from '@/utils/commons'; @@ -57,10 +59,17 @@ const editorConfig = { theme: ExampleTheme, // Handling of errors during update onError(error) { + console.log(error) throw error; }, // Any custom nodes go here nodes: [ + ExtendedTextNode, + { + replace: TextNode, + with: (node) => new ExtendedTextNode(node.__text), + withKlass: ExtendedTextNode, + }, HeadingNode, ListNode, ListItemNode, @@ -159,6 +168,8 @@ export default function Editor({ isRichText, isDebug, editorRef, onChange, defau + + {/* */} diff --git a/src/components/LexicalEditor/nodes/ExtendedTextNode.tsx b/src/components/LexicalEditor/nodes/ExtendedTextNode.tsx new file mode 100644 index 0000000..aca9622 --- /dev/null +++ b/src/components/LexicalEditor/nodes/ExtendedTextNode.tsx @@ -0,0 +1,129 @@ +import { + $applyNodeReplacement, + $isTextNode, + DOMConversion, + DOMConversionMap, + DOMConversionOutput, + NodeKey, + TextNode, + SerializedTextNode, + LexicalNode +} from 'lexical'; + +export class ExtendedTextNode extends TextNode { + constructor(text: string, key?: NodeKey) { + super(text, key); + } + + static getType(): string { + return 'extended-text'; + } + + static clone(node: ExtendedTextNode): ExtendedTextNode { + return new ExtendedTextNode(node.__text, node.__key); + } + + static importDOM(): DOMConversionMap | null { + const importers = TextNode.importDOM(); + return { + ...importers, + code: () => ({ + conversion: patchStyleConversion(importers?.code), + priority: 1 + }), + em: () => ({ + conversion: patchStyleConversion(importers?.em), + priority: 1 + }), + span: () => ({ + conversion: patchStyleConversion(importers?.span), + priority: 1 + }), + strong: () => ({ + conversion: patchStyleConversion(importers?.strong), + priority: 1 + }), + sub: () => ({ + conversion: patchStyleConversion(importers?.sub), + priority: 1 + }), + sup: () => ({ + conversion: patchStyleConversion(importers?.sup), + priority: 1 + }), + }; + } + + static importJSON(serializedNode: SerializedTextNode): TextNode { + return TextNode.importJSON(serializedNode); + } + + isSimpleText() { + return this.__type === 'extended-text' && this.__mode === 0; + } + + exportJSON(): SerializedTextNode { + return { + ...super.exportJSON(), + type: 'extended-text', + version: 1, + } + } +} + +export function $createExtendedTextNode(text: string): ExtendedTextNode { + return $applyNodeReplacement(new ExtendedTextNode(text)); +} + +export function $isExtendedTextNode(node: LexicalNode | null | undefined): node is ExtendedTextNode { + return node instanceof ExtendedTextNode; +} + +function patchStyleConversion( + originalDOMConverter?: (node: HTMLElement) => DOMConversion | null +): (node: HTMLElement) => DOMConversionOutput | null { + return (node) => { + const original = originalDOMConverter?.(node); + if (!original) { + return null; + } + const originalOutput = original.conversion(node); + + if (!originalOutput) { + return originalOutput; + } + + const background = node.style.background; + const backgroundColor = node.style.backgroundColor; + const color = node.style.color; + const fontFamily = node.style.fontFamily; + const fontWeight = node.style.fontWeight; + const fontSize = node.style.fontSize; + const textDecoration = node.style.textDecoration; + + return { + ...originalOutput, + forChild: (lexicalNode, parent) => { + const originalForChild = originalOutput?.forChild ?? ((x) => x); + const result = originalForChild(lexicalNode, parent); + if ($isTextNode(result)) { + const style = [ + background ? `background: ${background}` : null, + backgroundColor ? `background-color: ${backgroundColor}` : null, + color ? `color: ${color}` : null, + fontFamily ? `font-family: ${fontFamily}` : null, + fontWeight ? `font-weight: ${fontWeight}` : null, + fontSize ? `font-size: ${fontSize}` : null, + textDecoration ? `text-decoration: ${textDecoration}` : null, + ] + .filter((value) => value != null) + .join('; '); + if (style.length) { + return result.setStyle(style); + } + } + return result; + } + }; + }; +}