]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Added id support for all main block types
authorDan Brown <redacted>
Sun, 11 Aug 2024 15:08:51 +0000 (16:08 +0100)
committerDan Brown <redacted>
Sun, 11 Aug 2024 15:08:51 +0000 (16:08 +0100)
13 files changed:
resources/js/wysiwyg/index.ts
resources/js/wysiwyg/nodes/callout.ts
resources/js/wysiwyg/nodes/code-block.ts
resources/js/wysiwyg/nodes/custom-heading.ts [new file with mode: 0644]
resources/js/wysiwyg/nodes/custom-list.ts [new file with mode: 0644]
resources/js/wysiwyg/nodes/custom-paragraph.ts
resources/js/wysiwyg/nodes/custom-quote.ts [new file with mode: 0644]
resources/js/wysiwyg/nodes/details.ts
resources/js/wysiwyg/nodes/diagram.ts
resources/js/wysiwyg/nodes/horizontal-rule.ts
resources/js/wysiwyg/nodes/index.ts
resources/js/wysiwyg/nodes/media.ts
resources/js/wysiwyg/todo.md

index 9da646a771aae72e3b3ef6b43d1b2b785c8d9569..a07fbd78980bc12e86bcae14be29e1e106ae22f9 100644 (file)
@@ -82,6 +82,11 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
         }
     });
 
+    // @ts-ignore
+    window.debugEditorState = () => {
+        console.log(editor.getEditorState().toJSON());
+    };
+
     const context: EditorUiContext = buildEditorUI(container, editArea, editWrap, editor, options);
     registerCommonNodeMutationListeners(context);
 
index e39dcc3ee94d5f4ced4a550836d4390d27aef017..b720b5c43ce91c123da63c5ca7f89937eb1e5296 100644 (file)
@@ -9,15 +9,17 @@ import {
 } from 'lexical';
 import type {EditorConfig} from "lexical/LexicalEditor";
 import type {RangeSelection} from "lexical/LexicalSelection";
+import {el} from "../utils/dom";
 
 export type CalloutCategory = 'info' | 'danger' | 'warning' | 'success';
 
 export type SerializedCalloutNode = Spread<{
     category: CalloutCategory;
+    id: string;
 }, SerializedElementNode>
 
 export class CalloutNode extends ElementNode {
-
+    __id: string = '';
     __category: CalloutCategory = 'info';
 
     static getType() {
@@ -25,7 +27,9 @@ export class CalloutNode extends ElementNode {
     }
 
     static clone(node: CalloutNode) {
-        return new CalloutNode(node.__category, node.__key);
+        const newNode = new CalloutNode(node.__category, node.__key);
+        newNode.__id = node.__id;
+        return newNode;
     }
 
     constructor(category: CalloutCategory, key?: string) {
@@ -43,9 +47,22 @@ export class CalloutNode extends ElementNode {
         return self.__category;
     }
 
+    setId(id: string) {
+        const self = this.getWritable();
+        self.__id = id;
+    }
+
+    getId(): string {
+        const self = this.getLatest();
+        return self.__id;
+    }
+
     createDOM(_config: EditorConfig, _editor: LexicalEditor) {
         const element = document.createElement('p');
         element.classList.add('callout', this.__category || '');
+        if (this.__id) {
+            element.setAttribute('id', this.__id);
+        }
         return element;
     }
 
@@ -88,8 +105,13 @@ export class CalloutNode extends ElementNode {
                                 }
                             }
 
+                            const node = new CalloutNode(category);
+                            if (element.id) {
+                                node.setId(element.id);
+                            }
+
                             return {
-                                node: new CalloutNode(category),
+                                node,
                             };
                         },
                         priority: 3,
@@ -106,11 +128,14 @@ export class CalloutNode extends ElementNode {
             type: 'callout',
             version: 1,
             category: this.__category,
+            id: this.__id,
         };
     }
 
     static importJSON(serializedNode: SerializedCalloutNode): CalloutNode {
-        return $createCalloutNode(serializedNode.category);
+        const node = $createCalloutNode(serializedNode.category);
+        node.setId(serializedNode.id);
+        return node;
     }
 
 }
@@ -119,7 +144,7 @@ export function $createCalloutNode(category: CalloutCategory = 'info') {
     return new CalloutNode(category);
 }
 
-export function $isCalloutNode(node: LexicalNode | null | undefined) {
+export function $isCalloutNode(node: LexicalNode | null | undefined): node is CalloutNode {
     return node instanceof CalloutNode;
 }
 
index e240a38870a16670ae5947c304be949d0b81a72b..a71e21e2e7307e6392b4e77aa872460d0087cf84 100644 (file)
@@ -2,7 +2,7 @@ import {
     DecoratorNode,
     DOMConversion,
     DOMConversionMap,
-    DOMConversionOutput,
+    DOMConversionOutput, DOMExportOutput,
     LexicalEditor, LexicalNode,
     SerializedLexicalNode,
     Spread
@@ -33,7 +33,9 @@ export class CodeBlockNode extends DecoratorNode<EditorDecoratorAdapter> {
     }
 
     static clone(node: CodeBlockNode): CodeBlockNode {
-        return new CodeBlockNode(node.__language, node.__code);
+        const newNode = new CodeBlockNode(node.__language, node.__code);
+        newNode.__id = node.__id;
+        return newNode;
     }
 
     constructor(language: string = '', code: string = '', key?: string) {
@@ -118,6 +120,13 @@ export class CodeBlockNode extends DecoratorNode<EditorDecoratorAdapter> {
         return false;
     }
 
+    exportDOM(editor: LexicalEditor): DOMExportOutput {
+        const dom = this.createDOM(editor._config, editor);
+        return {
+            element: dom.querySelector('pre') as HTMLElement,
+        };
+    }
+
     static importDOM(): DOMConversionMap|null {
         return {
             pre(node: HTMLElement): DOMConversion|null {
@@ -130,10 +139,13 @@ export class CodeBlockNode extends DecoratorNode<EditorDecoratorAdapter> {
                                         || '';
 
                         const code = codeEl ? (codeEl.textContent || '').trim() : (element.textContent || '').trim();
+                        const node = $createCodeBlockNode(language, code);
+
+                        if (element.id) {
+                            node.setId(element.id);
+                        }
 
-                        return {
-                            node: $createCodeBlockNode(language, code),
-                        };
+                        return { node };
                     },
                     priority: 3,
                 };
diff --git a/resources/js/wysiwyg/nodes/custom-heading.ts b/resources/js/wysiwyg/nodes/custom-heading.ts
new file mode 100644 (file)
index 0000000..dba4989
--- /dev/null
@@ -0,0 +1,120 @@
+import {
+    DOMConversionMap,
+    DOMConversionOutput, ElementFormatType,
+    LexicalNode,
+    Spread
+} from "lexical";
+import {EditorConfig} from "lexical/LexicalEditor";
+import {HeadingNode, HeadingTagType, SerializedHeadingNode} from "@lexical/rich-text";
+
+
+export type SerializedCustomHeadingNode = Spread<{
+    id: string;
+}, SerializedHeadingNode>
+
+export class CustomHeadingNode extends HeadingNode {
+    __id: string = '';
+
+    static getType() {
+        return 'custom-heading';
+    }
+
+    setId(id: string) {
+        const self = this.getWritable();
+        self.__id = id;
+    }
+
+    getId(): string {
+        const self = this.getLatest();
+        return self.__id;
+    }
+
+    static clone(node: CustomHeadingNode) {
+        const newNode = new CustomHeadingNode(node.__tag, node.__key);
+        newNode.__id = node.__id;
+        return newNode;
+    }
+
+    createDOM(config: EditorConfig): HTMLElement {
+        const dom = super.createDOM(config);
+        if (this.__id) {
+            dom.setAttribute('id', this.__id);
+        }
+
+        return dom;
+    }
+
+    exportJSON(): SerializedCustomHeadingNode {
+        return {
+            ...super.exportJSON(),
+            type: 'custom-heading',
+            version: 1,
+            id: this.__id,
+        };
+    }
+
+    static importJSON(serializedNode: SerializedCustomHeadingNode): CustomHeadingNode {
+        const node = $createCustomHeadingNode(serializedNode.tag);
+        node.setId(serializedNode.id);
+        return node;
+    }
+
+    static importDOM(): DOMConversionMap | null {
+        return {
+            h1: (node: Node) => ({
+                conversion: $convertHeadingElement,
+                priority: 0,
+            }),
+            h2: (node: Node) => ({
+                conversion: $convertHeadingElement,
+                priority: 0,
+            }),
+            h3: (node: Node) => ({
+                conversion: $convertHeadingElement,
+                priority: 0,
+            }),
+            h4: (node: Node) => ({
+                conversion: $convertHeadingElement,
+                priority: 0,
+            }),
+            h5: (node: Node) => ({
+                conversion: $convertHeadingElement,
+                priority: 0,
+            }),
+            h6: (node: Node) => ({
+                conversion: $convertHeadingElement,
+                priority: 0,
+            }),
+        };
+    }
+}
+
+function $convertHeadingElement(element: HTMLElement): DOMConversionOutput {
+    const nodeName = element.nodeName.toLowerCase();
+    let node = null;
+    if (
+        nodeName === 'h1' ||
+        nodeName === 'h2' ||
+        nodeName === 'h3' ||
+        nodeName === 'h4' ||
+        nodeName === 'h5' ||
+        nodeName === 'h6'
+    ) {
+        node = $createCustomHeadingNode(nodeName);
+        if (element.style !== null) {
+            node.setFormat(element.style.textAlign as ElementFormatType);
+        }
+        if (element.id) {
+            node.setId(element.id);
+        }
+    }
+    return {node};
+}
+
+export function $createCustomHeadingNode(tag: HeadingTagType) {
+    return new CustomHeadingNode(tag);
+}
+
+export function $isCustomHeadingNode(node: LexicalNode | null | undefined): node is CustomHeadingNode {
+    return node instanceof CustomHeadingNode;
+}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/nodes/custom-list.ts b/resources/js/wysiwyg/nodes/custom-list.ts
new file mode 100644 (file)
index 0000000..953bcb8
--- /dev/null
@@ -0,0 +1,92 @@
+import {
+    DOMConversionFn,
+    DOMConversionMap,
+    LexicalNode,
+    Spread
+} from "lexical";
+import {EditorConfig} from "lexical/LexicalEditor";
+import {ListNode, ListType, SerializedListNode} from "@lexical/list";
+
+
+export type SerializedCustomListNode = Spread<{
+    id: string;
+}, SerializedListNode>
+
+export class CustomListNode extends ListNode {
+    __id: string = '';
+
+    static getType() {
+        return 'custom-list';
+    }
+
+    setId(id: string) {
+        const self = this.getWritable();
+        self.__id = id;
+    }
+
+    getId(): string {
+        const self = this.getLatest();
+        return self.__id;
+    }
+
+    static clone(node: CustomListNode) {
+        const newNode = new CustomListNode(node.__listType, 0, node.__key);
+        newNode.__id = node.__id;
+        return newNode;
+    }
+
+    createDOM(config: EditorConfig): HTMLElement {
+        const dom = super.createDOM(config);
+        if (this.__id) {
+            dom.setAttribute('id', this.__id);
+        }
+
+        return dom;
+    }
+
+    exportJSON(): SerializedCustomListNode {
+        return {
+            ...super.exportJSON(),
+            type: 'custom-list',
+            version: 1,
+            id: this.__id,
+        };
+    }
+
+    static importJSON(serializedNode: SerializedCustomListNode): CustomListNode {
+        const node = $createCustomListNode(serializedNode.listType);
+        node.setId(serializedNode.id);
+        return node;
+    }
+
+    static importDOM(): DOMConversionMap | null {
+        // @ts-ignore
+        const converter = super.importDOM().ol().conversion as DOMConversionFn<HTMLElement>;
+        const customConvertFunction = (element: HTMLElement) => {
+            const baseResult = converter(element);
+            if (element.id && baseResult?.node) {
+                (baseResult.node as CustomListNode).setId(element.id);
+            }
+            return baseResult;
+        };
+
+        return {
+            ol: () => ({
+                conversion: customConvertFunction,
+                priority: 0,
+            }),
+            ul: () => ({
+                conversion: customConvertFunction,
+                priority: 0,
+            }),
+        };
+    }
+}
+
+export function $createCustomListNode(type: ListType): CustomListNode {
+    return new CustomListNode(type, 0);
+}
+
+export function $isCustomListNode(node: LexicalNode | null | undefined): node is CustomListNode {
+    return node instanceof CustomListNode;
+}
\ No newline at end of file
index 97647bf5e8d4a6bc34b04962b709cde6ccbf3d2a..cb936a5599b3dfe11c86b5cbdf72d9853d392ef2 100644 (file)
@@ -31,7 +31,7 @@ export class CustomParagraphNode extends ParagraphNode {
         return self.__id;
     }
 
-    static clone(node: CustomParagraphNode) {
+    static clone(node: CustomParagraphNode): CustomParagraphNode {
         const newNode = new CustomParagraphNode(node.__key);
         newNode.__id = node.__id;
         return newNode;
@@ -39,9 +39,8 @@ export class CustomParagraphNode extends ParagraphNode {
 
     createDOM(config: EditorConfig): HTMLElement {
         const dom = super.createDOM(config);
-        const id = this.getId();
-        if (id) {
-            dom.setAttribute('id', id);
+        if (this.__id) {
+            dom.setAttribute('id', this.__id);
         }
 
         return dom;
@@ -89,7 +88,7 @@ export class CustomParagraphNode extends ParagraphNode {
     }
 }
 
-export function $createCustomParagraphNode() {
+export function $createCustomParagraphNode(): CustomParagraphNode {
     return new CustomParagraphNode();
 }
 
diff --git a/resources/js/wysiwyg/nodes/custom-quote.ts b/resources/js/wysiwyg/nodes/custom-quote.ts
new file mode 100644 (file)
index 0000000..58c62f7
--- /dev/null
@@ -0,0 +1,89 @@
+import {
+    DOMConversionMap,
+    DOMConversionOutput, ElementFormatType,
+    LexicalNode,
+    Spread
+} from "lexical";
+import {EditorConfig} from "lexical/LexicalEditor";
+import {QuoteNode, SerializedQuoteNode} from "@lexical/rich-text";
+
+
+export type SerializedCustomQuoteNode = Spread<{
+    id: string;
+}, SerializedQuoteNode>
+
+export class CustomQuoteNode extends QuoteNode {
+    __id: string = '';
+
+    static getType() {
+        return 'custom-quote';
+    }
+
+    setId(id: string) {
+        const self = this.getWritable();
+        self.__id = id;
+    }
+
+    getId(): string {
+        const self = this.getLatest();
+        return self.__id;
+    }
+
+    static clone(node: CustomQuoteNode) {
+        const newNode = new CustomQuoteNode(node.__key);
+        newNode.__id = node.__id;
+        return newNode;
+    }
+
+    createDOM(config: EditorConfig): HTMLElement {
+        const dom = super.createDOM(config);
+        if (this.__id) {
+            dom.setAttribute('id', this.__id);
+        }
+
+        return dom;
+    }
+
+    exportJSON(): SerializedCustomQuoteNode {
+        return {
+            ...super.exportJSON(),
+            type: 'custom-quote',
+            version: 1,
+            id: this.__id,
+        };
+    }
+
+    static importJSON(serializedNode: SerializedCustomQuoteNode): CustomQuoteNode {
+        const node = $createCustomQuoteNode();
+        node.setId(serializedNode.id);
+        return node;
+    }
+
+    static importDOM(): DOMConversionMap | null {
+        return {
+            blockquote: (node: Node) => ({
+                conversion: $convertBlockquoteElement,
+                priority: 0,
+            }),
+        };
+    }
+}
+
+function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput {
+    const node = $createCustomQuoteNode();
+    if (element.style !== null) {
+        node.setFormat(element.style.textAlign as ElementFormatType);
+    }
+    if (element.id) {
+        node.setId(element.id);
+    }
+    return {node};
+}
+
+export function $createCustomQuoteNode() {
+    return new CustomQuoteNode();
+}
+
+export function $isCustomQuoteNode(node: LexicalNode | null | undefined): node is CustomQuoteNode {
+    return node instanceof CustomQuoteNode;
+}
\ No newline at end of file
index 8071d5e8fd98017368fba2c0ef97cdc42bec87cd..119619da6adb61ae0d5e4feffacfc9a01f7d3d2e 100644 (file)
@@ -4,28 +4,50 @@ import {
     ElementNode,
     LexicalEditor,
     LexicalNode,
-    SerializedElementNode,
+    SerializedElementNode, Spread,
 } from 'lexical';
 import type {EditorConfig} from "lexical/LexicalEditor";
 
 import {el} from "../utils/dom";
 
+export type SerializedDetailsNode = Spread<{
+    id: string;
+}, SerializedElementNode>
+
 export class DetailsNode extends ElementNode {
+    __id: string = '';
 
     static getType() {
         return 'details';
     }
 
-    static clone(node: DetailsNode) {
-        return new DetailsNode(node.__key);
+    setId(id: string) {
+        const self = this.getWritable();
+        self.__id = id;
+    }
+
+    getId(): string {
+        const self = this.getLatest();
+        return self.__id;
+    }
+
+    static clone(node: DetailsNode): DetailsNode {
+        const newNode =  new DetailsNode(node.__key);
+        newNode.__id = node.__id;
+        return newNode;
     }
 
     createDOM(_config: EditorConfig, _editor: LexicalEditor) {
-        return el('details');
+        const el = document.createElement('details');
+        if (this.__id) {
+            el.setAttribute('id', this.__id);
+        }
+
+        return el;
     }
 
     updateDOM(prevNode: DetailsNode, dom: HTMLElement) {
-        return false;
+        return prevNode.__id !== this.__id;
     }
 
     static importDOM(): DOMConversionMap|null {
@@ -33,9 +55,12 @@ export class DetailsNode extends ElementNode {
             details(node: HTMLElement): DOMConversion|null {
                 return {
                     conversion: (element: HTMLElement): DOMConversionOutput|null => {
-                        return {
-                            node: new DetailsNode(),
-                        };
+                        const node = new DetailsNode();
+                        if (element.id) {
+                            node.setId(element.id);
+                        }
+
+                        return {node};
                     },
                     priority: 3,
                 };
@@ -43,16 +68,19 @@ export class DetailsNode extends ElementNode {
         };
     }
 
-    exportJSON(): SerializedElementNode {
+    exportJSON(): SerializedDetailsNode {
         return {
             ...super.exportJSON(),
             type: 'details',
             version: 1,
+            id: this.__id,
         };
     }
 
-    static importJSON(serializedNode: SerializedElementNode): DetailsNode {
-        return $createDetailsNode();
+    static importJSON(serializedNode: SerializedDetailsNode): DetailsNode {
+        const node = $createDetailsNode();
+        node.setId(serializedNode.id);
+        return node;
     }
 
 }
@@ -61,7 +89,7 @@ export function $createDetailsNode() {
     return new DetailsNode();
 }
 
-export function $isDetailsNode(node: LexicalNode | null | undefined) {
+export function $isDetailsNode(node: LexicalNode | null | undefined): node is DetailsNode {
     return node instanceof DetailsNode;
 }
 
@@ -106,16 +134,16 @@ export class SummaryNode extends ElementNode {
         };
     }
 
-    static importJSON(serializedNode: SerializedElementNode): DetailsNode {
+    static importJSON(serializedNode: SerializedElementNode): SummaryNode {
         return $createSummaryNode();
     }
 
 }
 
-export function $createSummaryNode() {
+export function $createSummaryNode(): SummaryNode {
     return new SummaryNode();
 }
 
-export function $isSummaryNode(node: LexicalNode | null | undefined) {
+export function $isSummaryNode(node: LexicalNode | null | undefined): node is SummaryNode {
     return node instanceof SummaryNode;
 }
index 76d9392480fc1560576afaac3baa565b33f084b8..e2ffeaaddafcc63caed328736516ee9dee60784d 100644 (file)
@@ -30,7 +30,9 @@ export class DiagramNode extends DecoratorNode<EditorDecoratorAdapter> {
     }
 
     static clone(node: DiagramNode): DiagramNode {
-        return new DiagramNode(node.__drawingId, node.__drawingUrl);
+        const newNode = new DiagramNode(node.__drawingId, node.__drawingUrl);
+        newNode.__id = node.__id;
+        return newNode;
     }
 
     constructor(drawingId: string, drawingUrl: string, key?: string) {
@@ -120,10 +122,13 @@ export class DiagramNode extends DecoratorNode<EditorDecoratorAdapter> {
                         const img = element.querySelector('img');
                         const drawingUrl = img?.getAttribute('src') || '';
                         const drawingId = element.getAttribute('drawio-diagram') || '';
+                        const node = $createDiagramNode(drawingId, drawingUrl);
 
-                        return {
-                            node: $createDiagramNode(drawingId, drawingUrl),
-                        };
+                        if (element.id) {
+                            node.setId(element.id);
+                        }
+
+                        return { node };
                     },
                     priority: 3,
                 };
@@ -152,7 +157,7 @@ export function $createDiagramNode(drawingId: string = '', drawingUrl: string =
     return new DiagramNode(drawingId, drawingUrl);
 }
 
-export function $isDiagramNode(node: LexicalNode | null | undefined) {
+export function $isDiagramNode(node: LexicalNode | null | undefined): node is DiagramNode {
     return node instanceof DiagramNode;
 }
 
index fbd019e729900cb8f9e365d1c97e88e52244761e..e881d4688df72361a851f46aa9420c847235eab1 100644 (file)
@@ -4,26 +4,48 @@ import {
     ElementNode,
     LexicalEditor,
     LexicalNode,
-    SerializedElementNode,
+    SerializedElementNode, Spread,
 } from 'lexical';
 import type {EditorConfig} from "lexical/LexicalEditor";
 
+export type SerializedHorizontalRuleNode = Spread<{
+    id: string;
+}, SerializedElementNode>
+
 export class HorizontalRuleNode extends ElementNode {
+    __id: string = '';
 
     static getType() {
         return 'horizontal-rule';
     }
 
+    setId(id: string) {
+        const self = this.getWritable();
+        self.__id = id;
+    }
+
+    getId(): string {
+        const self = this.getLatest();
+        return self.__id;
+    }
+
     static clone(node: HorizontalRuleNode): HorizontalRuleNode {
-        return new HorizontalRuleNode(node.__key);
+        const newNode = new HorizontalRuleNode(node.__key);
+        newNode.__id = node.__id;
+        return newNode;
     }
 
-    createDOM(_config: EditorConfig, _editor: LexicalEditor) {
-        return document.createElement('hr');
+    createDOM(_config: EditorConfig, _editor: LexicalEditor): HTMLElement {
+        const el = document.createElement('hr');
+        if (this.__id) {
+            el.setAttribute('id', this.__id);
+        }
+
+        return el;
     }
 
-    updateDOM(prevNode: unknown, dom: HTMLElement) {
-        return false;
+    updateDOM(prevNode: HorizontalRuleNode, dom: HTMLElement) {
+        return prevNode.__id !== this.__id;
     }
 
     static importDOM(): DOMConversionMap|null {
@@ -31,9 +53,12 @@ export class HorizontalRuleNode extends ElementNode {
             hr(node: HTMLElement): DOMConversion|null {
                 return {
                     conversion: (element: HTMLElement): DOMConversionOutput|null => {
-                        return {
-                            node: new HorizontalRuleNode(),
-                        };
+                        const node = new HorizontalRuleNode();
+                        if (element.id) {
+                            node.setId(element.id);
+                        }
+
+                        return {node};
                     },
                     priority: 3,
                 };
@@ -41,24 +66,27 @@ export class HorizontalRuleNode extends ElementNode {
         };
     }
 
-    exportJSON(): SerializedElementNode {
+    exportJSON(): SerializedHorizontalRuleNode {
         return {
             ...super.exportJSON(),
             type: 'horizontal-rule',
             version: 1,
+            id: this.__id,
         };
     }
 
-    static importJSON(serializedNode: SerializedElementNode): HorizontalRuleNode {
-        return $createHorizontalRuleNode();
+    static importJSON(serializedNode: SerializedHorizontalRuleNode): HorizontalRuleNode {
+        const node = $createHorizontalRuleNode();
+        node.setId(serializedNode.id);
+        return node;
     }
 
 }
 
-export function $createHorizontalRuleNode() {
+export function $createHorizontalRuleNode(): HorizontalRuleNode {
     return new HorizontalRuleNode();
 }
 
-export function $isHorizontalRuleNode(node: LexicalNode | null | undefined) {
+export function $isHorizontalRuleNode(node: LexicalNode | null | undefined): node is HorizontalRuleNode {
     return node instanceof HorizontalRuleNode;
 }
\ No newline at end of file
index 81a0c1a0d8e8e1dbd19f9312b8f9e4f40402539e..8cbec20dad21f58460fc638ee72b78c311c32068 100644 (file)
@@ -22,16 +22,19 @@ import {MediaNode} from "./media";
 import {CustomListItemNode} from "./custom-list-item";
 import {CustomTableCellNode} from "./custom-table-cell";
 import {CustomTableRowNode} from "./custom-table-row";
+import {CustomHeadingNode} from "./custom-heading";
+import {CustomQuoteNode} from "./custom-quote";
+import {CustomListNode} from "./custom-list";
 
 /**
  * Load the nodes for lexical.
  */
 export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> | LexicalNodeReplacement)[] {
     return [
-        CalloutNode, // Todo - Create custom
-        HeadingNode, // Todo - Create custom
-        QuoteNode, // Todo - Create custom
-        ListNode, // Todo - Create custom
+        CalloutNode,
+        CustomHeadingNode,
+        CustomQuoteNode,
+        CustomListNode,
         CustomListItemNode,
         CustomTableNode,
         CustomTableRowNode,
@@ -42,7 +45,7 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
         CodeBlockNode,
         DiagramNode,
         MediaNode,
-        CustomParagraphNode,
+        CustomParagraphNode, // TODO - ID
         LinkNode,
         {
             replace: ParagraphNode,
@@ -50,6 +53,24 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
                 return new CustomParagraphNode();
             }
         },
+        {
+            replace: HeadingNode,
+            with: (node: HeadingNode) => {
+                return new CustomHeadingNode(node.__tag);
+            }
+        },
+        {
+            replace: QuoteNode,
+            with: (node: QuoteNode) => {
+                return new CustomQuoteNode();
+            }
+        },
+        {
+            replace: ListNode,
+            with: (node: ListNode) => {
+                return new CustomListNode(node.getListType(), node.getStart());
+            }
+        },
         {
             replace: ListItemNode,
             with: (node: ListItemNode) => {
index aba4f6c37b46b5b53cc176a5524852271f3d65d3..73208cb2e433dc00a45c663db2b7373e92791807 100644 (file)
@@ -66,7 +66,6 @@ function domElementToNode(tag: MediaNodeTag, element: Element): MediaNode {
 }
 
 export class MediaNode extends ElementNode {
-
     __tag: MediaNodeTag;
     __attributes: Record<string, string> = {};
     __sources: MediaNodeSource[] = [];
@@ -76,7 +75,10 @@ export class MediaNode extends ElementNode {
     }
 
     static clone(node: MediaNode) {
-        return new MediaNode(node.__tag, node.__key);
+        const newNode = new MediaNode(node.__tag, node.__key);
+        newNode.__attributes = Object.assign({}, node.__attributes);
+        newNode.__sources = node.__sources.map(s => Object.assign({}, s));
+        return newNode;
     }
 
     constructor(tag: MediaNodeTag, key?: string) {
@@ -226,10 +228,10 @@ export function $createMediaNodeFromSrc(src: string): MediaNode {
     return new MediaNode(nodeTag);
 }
 
-export function $isMediaNode(node: LexicalNode | null | undefined) {
+export function $isMediaNode(node: LexicalNode | null | undefined): node is MediaNode {
     return node instanceof MediaNode;
 }
 
-export function $isMediaNodeOfTag(node: LexicalNode | null | undefined, tag: MediaNodeTag) {
+export function $isMediaNodeOfTag(node: LexicalNode | null | undefined, tag: MediaNodeTag): boolean {
     return node instanceof MediaNode && (node as MediaNode).getTag() === tag;
 }
\ No newline at end of file
index 9e501fb2421c4f1102129807165f3afd0fa12c36..c8a0293d51d44438e991dc723bfc1f112a5b3b87 100644 (file)
@@ -2,13 +2,14 @@
 
 ## In progress
 
+// 
+
 ## Main Todo
 
 - Alignments: Use existing classes for blocks (including table cells)
 - Alignments: Handle inline block content (image, video)
 - Image paste upload
 - Keyboard shortcuts support
-- Add ID support to all block types
 - Link popup menu for cross-content reference
 - Link heading-based ID reference menu
 - Image gallery integration for insert
Morty Proxy This is a proxified and sanitized view of the page, visit original site.