]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/rich-text/LexicalQuoteNode.ts
Lexical: Extracted & merged heading & quote nodes
[bookstack] / resources / js / wysiwyg / lexical / rich-text / LexicalQuoteNode.ts
1 import {
2     $applyNodeReplacement,
3     $createParagraphNode,
4     type DOMConversionMap,
5     type DOMConversionOutput,
6     type DOMExportOutput,
7     type EditorConfig,
8     ElementNode,
9     isHTMLElement,
10     type LexicalEditor,
11     LexicalNode,
12     type NodeKey,
13     type ParagraphNode,
14     type RangeSelection,
15     SerializedElementNode
16 } from "lexical";
17 import {addClassNamesToElement} from "@lexical/utils";
18 import {CommonBlockNode, copyCommonBlockProperties} from "lexical/nodes/CommonBlockNode";
19 import {
20     commonPropertiesDifferent, deserializeCommonBlockNode,
21     SerializedCommonBlockNode, setCommonBlockPropsFromElement,
22     updateElementWithCommonBlockProps
23 } from "../../nodes/_common";
24
25 export type SerializedQuoteNode = SerializedCommonBlockNode;
26
27 /** @noInheritDoc */
28 export class QuoteNode extends CommonBlockNode {
29     static getType(): string {
30         return 'quote';
31     }
32
33     static clone(node: QuoteNode): QuoteNode {
34         const clone = new QuoteNode(node.__key);
35         copyCommonBlockProperties(node, clone);
36         return clone;
37     }
38
39     constructor(key?: NodeKey) {
40         super(key);
41     }
42
43     // View
44
45     createDOM(config: EditorConfig): HTMLElement {
46         const element = document.createElement('blockquote');
47         addClassNamesToElement(element, config.theme.quote);
48         updateElementWithCommonBlockProps(element, this);
49         return element;
50     }
51
52     updateDOM(prevNode: QuoteNode, dom: HTMLElement): boolean {
53         return commonPropertiesDifferent(prevNode, this);
54     }
55
56     static importDOM(): DOMConversionMap | null {
57         return {
58             blockquote: (node: Node) => ({
59                 conversion: $convertBlockquoteElement,
60                 priority: 0,
61             }),
62         };
63     }
64
65     exportDOM(editor: LexicalEditor): DOMExportOutput {
66         const {element} = super.exportDOM(editor);
67
68         if (element && isHTMLElement(element)) {
69             if (this.isEmpty()) {
70                 element.append(document.createElement('br'));
71             }
72         }
73
74         return {
75             element,
76         };
77     }
78
79     static importJSON(serializedNode: SerializedQuoteNode): QuoteNode {
80         const node = $createQuoteNode();
81         deserializeCommonBlockNode(serializedNode, node);
82         return node;
83     }
84
85     exportJSON(): SerializedQuoteNode {
86         return {
87             ...super.exportJSON(),
88             type: 'quote',
89         };
90     }
91
92     // Mutation
93
94     insertNewAfter(_: RangeSelection, restoreSelection?: boolean): ParagraphNode {
95         const newBlock = $createParagraphNode();
96         const direction = this.getDirection();
97         newBlock.setDirection(direction);
98         this.insertAfter(newBlock, restoreSelection);
99         return newBlock;
100     }
101
102     collapseAtStart(): true {
103         const paragraph = $createParagraphNode();
104         const children = this.getChildren();
105         children.forEach((child) => paragraph.append(child));
106         this.replace(paragraph);
107         return true;
108     }
109
110     canMergeWhenEmpty(): true {
111         return true;
112     }
113 }
114
115 export function $createQuoteNode(): QuoteNode {
116     return $applyNodeReplacement(new QuoteNode());
117 }
118
119 export function $isQuoteNode(
120     node: LexicalNode | null | undefined,
121 ): node is QuoteNode {
122     return node instanceof QuoteNode;
123 }
124
125 function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput {
126     const node = $createQuoteNode();
127     setCommonBlockPropsFromElement(element, node);
128     return {node};
129 }