Lexical: Extracted & merged heading & quote nodes
This commit is contained in:
		
							parent
							
								
									f3fa63a5ae
								
							
						
					
					
						commit
						36a4d79120
					
				| 
						 | 
					@ -8,11 +8,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {$createLinkNode} from '@lexical/link';
 | 
					import {$createLinkNode} from '@lexical/link';
 | 
				
			||||||
import {$createListItemNode, $createListNode} from '@lexical/list';
 | 
					import {$createListItemNode, $createListNode} from '@lexical/list';
 | 
				
			||||||
import {$createHeadingNode, $createQuoteNode} from '@lexical/rich-text';
 | 
					 | 
				
			||||||
import {$createTableNodeWithDimensions} from '@lexical/table';
 | 
					import {$createTableNodeWithDimensions} from '@lexical/table';
 | 
				
			||||||
import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical';
 | 
					import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {initializeUnitTest} from '../utils';
 | 
					import {initializeUnitTest} from '../utils';
 | 
				
			||||||
 | 
					import {$createHeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					import {$createQuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function $createEditorContent() {
 | 
					function $createEditorContent() {
 | 
				
			||||||
  const root = $getRoot();
 | 
					  const root = $getRoot();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ import {createHeadlessEditor} from '@lexical/headless';
 | 
				
			||||||
import {AutoLinkNode, LinkNode} from '@lexical/link';
 | 
					import {AutoLinkNode, LinkNode} from '@lexical/link';
 | 
				
			||||||
import {ListItemNode, ListNode} from '@lexical/list';
 | 
					import {ListItemNode, ListNode} from '@lexical/list';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {HeadingNode, QuoteNode} from '@lexical/rich-text';
 | 
					 | 
				
			||||||
import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';
 | 
					import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
| 
						 | 
					@ -36,6 +35,8 @@ import {
 | 
				
			||||||
  LexicalNodeReplacement,
 | 
					  LexicalNodeReplacement,
 | 
				
			||||||
} from '../../LexicalEditor';
 | 
					} from '../../LexicalEditor';
 | 
				
			||||||
import {resetRandomKey} from '../../LexicalUtils';
 | 
					import {resetRandomKey} from '../../LexicalUtils';
 | 
				
			||||||
 | 
					import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TestEnv = {
 | 
					type TestEnv = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,7 @@ export class CommonBlockNode extends ElementNode {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function copyCommonBlockProperties(from: CommonBlockNode, to: CommonBlockNode): void {
 | 
					export function copyCommonBlockProperties(from: CommonBlockNode, to: CommonBlockNode): void {
 | 
				
			||||||
    to.__id = from.__id;
 | 
					    // to.__id = from.__id;
 | 
				
			||||||
    to.__alignment = from.__alignment;
 | 
					    to.__alignment = from.__alignment;
 | 
				
			||||||
    to.__inset = from.__inset;
 | 
					    to.__inset = from.__inset;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ import {
 | 
				
			||||||
  $insertDataTransferForRichText,
 | 
					  $insertDataTransferForRichText,
 | 
				
			||||||
} from '@lexical/clipboard';
 | 
					} from '@lexical/clipboard';
 | 
				
			||||||
import {$createListItemNode, $createListNode} from '@lexical/list';
 | 
					import {$createListItemNode, $createListNode} from '@lexical/list';
 | 
				
			||||||
import {$createHeadingNode, registerRichText} from '@lexical/rich-text';
 | 
					import {registerRichText} from '@lexical/rich-text';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  $createParagraphNode,
 | 
					  $createParagraphNode,
 | 
				
			||||||
  $createRangeSelection,
 | 
					  $createRangeSelection,
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@ import {
 | 
				
			||||||
  initializeUnitTest,
 | 
					  initializeUnitTest,
 | 
				
			||||||
  invariant,
 | 
					  invariant,
 | 
				
			||||||
} from '../../../__tests__/utils';
 | 
					} from '../../../__tests__/utils';
 | 
				
			||||||
 | 
					import {$createHeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('LexicalTabNode tests', () => {
 | 
					describe('LexicalTabNode tests', () => {
 | 
				
			||||||
  initializeUnitTest((testEnv) => {
 | 
					  initializeUnitTest((testEnv) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,13 +13,14 @@ import {createHeadlessEditor} from '@lexical/headless';
 | 
				
			||||||
import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html';
 | 
					import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html';
 | 
				
			||||||
import {LinkNode} from '@lexical/link';
 | 
					import {LinkNode} from '@lexical/link';
 | 
				
			||||||
import {ListItemNode, ListNode} from '@lexical/list';
 | 
					import {ListItemNode, ListNode} from '@lexical/list';
 | 
				
			||||||
import {HeadingNode, QuoteNode} from '@lexical/rich-text';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  $createParagraphNode,
 | 
					  $createParagraphNode,
 | 
				
			||||||
  $createRangeSelection,
 | 
					  $createRangeSelection,
 | 
				
			||||||
  $createTextNode,
 | 
					  $createTextNode,
 | 
				
			||||||
  $getRoot,
 | 
					  $getRoot,
 | 
				
			||||||
} from 'lexical';
 | 
					} from 'lexical';
 | 
				
			||||||
 | 
					import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('HTML', () => {
 | 
					describe('HTML', () => {
 | 
				
			||||||
  type Input = Array<{
 | 
					  type Input = Array<{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,202 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    $applyNodeReplacement,
 | 
				
			||||||
 | 
					    $createParagraphNode,
 | 
				
			||||||
 | 
					    type DOMConversionMap,
 | 
				
			||||||
 | 
					    DOMConversionOutput,
 | 
				
			||||||
 | 
					    type DOMExportOutput,
 | 
				
			||||||
 | 
					    type EditorConfig,
 | 
				
			||||||
 | 
					    isHTMLElement,
 | 
				
			||||||
 | 
					    type LexicalEditor,
 | 
				
			||||||
 | 
					    type LexicalNode,
 | 
				
			||||||
 | 
					    type NodeKey,
 | 
				
			||||||
 | 
					    type ParagraphNode,
 | 
				
			||||||
 | 
					    type RangeSelection,
 | 
				
			||||||
 | 
					    type SerializedElementNode,
 | 
				
			||||||
 | 
					    type Spread
 | 
				
			||||||
 | 
					} from "lexical";
 | 
				
			||||||
 | 
					import {addClassNamesToElement} from "@lexical/utils";
 | 
				
			||||||
 | 
					import {CommonBlockNode, copyCommonBlockProperties} from "lexical/nodes/CommonBlockNode";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    commonPropertiesDifferent, deserializeCommonBlockNode,
 | 
				
			||||||
 | 
					    SerializedCommonBlockNode, setCommonBlockPropsFromElement,
 | 
				
			||||||
 | 
					    updateElementWithCommonBlockProps
 | 
				
			||||||
 | 
					} from "../../nodes/_common";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type HeadingTagType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type SerializedHeadingNode = Spread<
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    SerializedCommonBlockNode
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** @noInheritDoc */
 | 
				
			||||||
 | 
					export class HeadingNode extends CommonBlockNode {
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    __tag: HeadingTagType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static getType(): string {
 | 
				
			||||||
 | 
					        return 'heading';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static clone(node: HeadingNode): HeadingNode {
 | 
				
			||||||
 | 
					        const clone = new HeadingNode(node.__tag, node.__key);
 | 
				
			||||||
 | 
					        copyCommonBlockProperties(node, clone);
 | 
				
			||||||
 | 
					        return clone;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(tag: HeadingTagType, key?: NodeKey) {
 | 
				
			||||||
 | 
					        super(key);
 | 
				
			||||||
 | 
					        this.__tag = tag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getTag(): HeadingTagType {
 | 
				
			||||||
 | 
					        return this.__tag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // View
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    createDOM(config: EditorConfig): HTMLElement {
 | 
				
			||||||
 | 
					        const tag = this.__tag;
 | 
				
			||||||
 | 
					        const element = document.createElement(tag);
 | 
				
			||||||
 | 
					        const theme = config.theme;
 | 
				
			||||||
 | 
					        const classNames = theme.heading;
 | 
				
			||||||
 | 
					        if (classNames !== undefined) {
 | 
				
			||||||
 | 
					            const className = classNames[tag];
 | 
				
			||||||
 | 
					            addClassNamesToElement(element, className);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        updateElementWithCommonBlockProps(element, this);
 | 
				
			||||||
 | 
					        return element;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    updateDOM(prevNode: HeadingNode, dom: HTMLElement): boolean {
 | 
				
			||||||
 | 
					        return commonPropertiesDifferent(prevNode, this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    exportDOM(editor: LexicalEditor): DOMExportOutput {
 | 
				
			||||||
 | 
					        const {element} = super.exportDOM(editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (element && isHTMLElement(element)) {
 | 
				
			||||||
 | 
					            if (this.isEmpty()) {
 | 
				
			||||||
 | 
					                element.append(document.createElement('br'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            element,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static importJSON(serializedNode: SerializedHeadingNode): HeadingNode {
 | 
				
			||||||
 | 
					        const node = $createHeadingNode(serializedNode.tag);
 | 
				
			||||||
 | 
					        deserializeCommonBlockNode(serializedNode, node);
 | 
				
			||||||
 | 
					        return node;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    exportJSON(): SerializedHeadingNode {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...super.exportJSON(),
 | 
				
			||||||
 | 
					            tag: this.getTag(),
 | 
				
			||||||
 | 
					            type: 'heading',
 | 
				
			||||||
 | 
					            version: 1,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Mutation
 | 
				
			||||||
 | 
					    insertNewAfter(
 | 
				
			||||||
 | 
					        selection?: RangeSelection,
 | 
				
			||||||
 | 
					        restoreSelection = true,
 | 
				
			||||||
 | 
					    ): ParagraphNode | HeadingNode {
 | 
				
			||||||
 | 
					        const anchorOffet = selection ? selection.anchor.offset : 0;
 | 
				
			||||||
 | 
					        const lastDesc = this.getLastDescendant();
 | 
				
			||||||
 | 
					        const isAtEnd =
 | 
				
			||||||
 | 
					            !lastDesc ||
 | 
				
			||||||
 | 
					            (selection &&
 | 
				
			||||||
 | 
					                selection.anchor.key === lastDesc.getKey() &&
 | 
				
			||||||
 | 
					                anchorOffet === lastDesc.getTextContentSize());
 | 
				
			||||||
 | 
					        const newElement =
 | 
				
			||||||
 | 
					            isAtEnd || !selection
 | 
				
			||||||
 | 
					                ? $createParagraphNode()
 | 
				
			||||||
 | 
					                : $createHeadingNode(this.getTag());
 | 
				
			||||||
 | 
					        const direction = this.getDirection();
 | 
				
			||||||
 | 
					        newElement.setDirection(direction);
 | 
				
			||||||
 | 
					        this.insertAfter(newElement, restoreSelection);
 | 
				
			||||||
 | 
					        if (anchorOffet === 0 && !this.isEmpty() && selection) {
 | 
				
			||||||
 | 
					            const paragraph = $createParagraphNode();
 | 
				
			||||||
 | 
					            paragraph.select();
 | 
				
			||||||
 | 
					            this.replace(paragraph, true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return newElement;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    collapseAtStart(): true {
 | 
				
			||||||
 | 
					        const newElement = !this.isEmpty()
 | 
				
			||||||
 | 
					            ? $createHeadingNode(this.getTag())
 | 
				
			||||||
 | 
					            : $createParagraphNode();
 | 
				
			||||||
 | 
					        const children = this.getChildren();
 | 
				
			||||||
 | 
					        children.forEach((child) => newElement.append(child));
 | 
				
			||||||
 | 
					        this.replace(newElement);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extractWithChild(): boolean {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 = $createHeadingNode(nodeName);
 | 
				
			||||||
 | 
					        setCommonBlockPropsFromElement(element, node);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return {node};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function $createHeadingNode(headingTag: HeadingTagType): HeadingNode {
 | 
				
			||||||
 | 
					    return $applyNodeReplacement(new HeadingNode(headingTag));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function $isHeadingNode(
 | 
				
			||||||
 | 
					    node: LexicalNode | null | undefined,
 | 
				
			||||||
 | 
					): node is HeadingNode {
 | 
				
			||||||
 | 
					    return node instanceof HeadingNode;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,129 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    $applyNodeReplacement,
 | 
				
			||||||
 | 
					    $createParagraphNode,
 | 
				
			||||||
 | 
					    type DOMConversionMap,
 | 
				
			||||||
 | 
					    type DOMConversionOutput,
 | 
				
			||||||
 | 
					    type DOMExportOutput,
 | 
				
			||||||
 | 
					    type EditorConfig,
 | 
				
			||||||
 | 
					    ElementNode,
 | 
				
			||||||
 | 
					    isHTMLElement,
 | 
				
			||||||
 | 
					    type LexicalEditor,
 | 
				
			||||||
 | 
					    LexicalNode,
 | 
				
			||||||
 | 
					    type NodeKey,
 | 
				
			||||||
 | 
					    type ParagraphNode,
 | 
				
			||||||
 | 
					    type RangeSelection,
 | 
				
			||||||
 | 
					    SerializedElementNode
 | 
				
			||||||
 | 
					} from "lexical";
 | 
				
			||||||
 | 
					import {addClassNamesToElement} from "@lexical/utils";
 | 
				
			||||||
 | 
					import {CommonBlockNode, copyCommonBlockProperties} from "lexical/nodes/CommonBlockNode";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    commonPropertiesDifferent, deserializeCommonBlockNode,
 | 
				
			||||||
 | 
					    SerializedCommonBlockNode, setCommonBlockPropsFromElement,
 | 
				
			||||||
 | 
					    updateElementWithCommonBlockProps
 | 
				
			||||||
 | 
					} from "../../nodes/_common";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type SerializedQuoteNode = SerializedCommonBlockNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** @noInheritDoc */
 | 
				
			||||||
 | 
					export class QuoteNode extends CommonBlockNode {
 | 
				
			||||||
 | 
					    static getType(): string {
 | 
				
			||||||
 | 
					        return 'quote';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static clone(node: QuoteNode): QuoteNode {
 | 
				
			||||||
 | 
					        const clone = new QuoteNode(node.__key);
 | 
				
			||||||
 | 
					        copyCommonBlockProperties(node, clone);
 | 
				
			||||||
 | 
					        return clone;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(key?: NodeKey) {
 | 
				
			||||||
 | 
					        super(key);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // View
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    createDOM(config: EditorConfig): HTMLElement {
 | 
				
			||||||
 | 
					        const element = document.createElement('blockquote');
 | 
				
			||||||
 | 
					        addClassNamesToElement(element, config.theme.quote);
 | 
				
			||||||
 | 
					        updateElementWithCommonBlockProps(element, this);
 | 
				
			||||||
 | 
					        return element;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    updateDOM(prevNode: QuoteNode, dom: HTMLElement): boolean {
 | 
				
			||||||
 | 
					        return commonPropertiesDifferent(prevNode, this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static importDOM(): DOMConversionMap | null {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            blockquote: (node: Node) => ({
 | 
				
			||||||
 | 
					                conversion: $convertBlockquoteElement,
 | 
				
			||||||
 | 
					                priority: 0,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    exportDOM(editor: LexicalEditor): DOMExportOutput {
 | 
				
			||||||
 | 
					        const {element} = super.exportDOM(editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (element && isHTMLElement(element)) {
 | 
				
			||||||
 | 
					            if (this.isEmpty()) {
 | 
				
			||||||
 | 
					                element.append(document.createElement('br'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            element,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static importJSON(serializedNode: SerializedQuoteNode): QuoteNode {
 | 
				
			||||||
 | 
					        const node = $createQuoteNode();
 | 
				
			||||||
 | 
					        deserializeCommonBlockNode(serializedNode, node);
 | 
				
			||||||
 | 
					        return node;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    exportJSON(): SerializedQuoteNode {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...super.exportJSON(),
 | 
				
			||||||
 | 
					            type: 'quote',
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Mutation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    insertNewAfter(_: RangeSelection, restoreSelection?: boolean): ParagraphNode {
 | 
				
			||||||
 | 
					        const newBlock = $createParagraphNode();
 | 
				
			||||||
 | 
					        const direction = this.getDirection();
 | 
				
			||||||
 | 
					        newBlock.setDirection(direction);
 | 
				
			||||||
 | 
					        this.insertAfter(newBlock, restoreSelection);
 | 
				
			||||||
 | 
					        return newBlock;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    collapseAtStart(): true {
 | 
				
			||||||
 | 
					        const paragraph = $createParagraphNode();
 | 
				
			||||||
 | 
					        const children = this.getChildren();
 | 
				
			||||||
 | 
					        children.forEach((child) => paragraph.append(child));
 | 
				
			||||||
 | 
					        this.replace(paragraph);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canMergeWhenEmpty(): true {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function $createQuoteNode(): QuoteNode {
 | 
				
			||||||
 | 
					    return $applyNodeReplacement(new QuoteNode());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function $isQuoteNode(
 | 
				
			||||||
 | 
					    node: LexicalNode | null | undefined,
 | 
				
			||||||
 | 
					): node is QuoteNode {
 | 
				
			||||||
 | 
					    return node instanceof QuoteNode;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput {
 | 
				
			||||||
 | 
					    const node = $createQuoteNode();
 | 
				
			||||||
 | 
					    setCommonBlockPropsFromElement(element, node);
 | 
				
			||||||
 | 
					    return {node};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,11 +6,6 @@
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  $createHeadingNode,
 | 
					 | 
				
			||||||
  $isHeadingNode,
 | 
					 | 
				
			||||||
  HeadingNode,
 | 
					 | 
				
			||||||
} from '@lexical/rich-text';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  $createTextNode,
 | 
					  $createTextNode,
 | 
				
			||||||
  $getRoot,
 | 
					  $getRoot,
 | 
				
			||||||
| 
						 | 
					@ -19,6 +14,7 @@ import {
 | 
				
			||||||
  RangeSelection,
 | 
					  RangeSelection,
 | 
				
			||||||
} from 'lexical';
 | 
					} from 'lexical';
 | 
				
			||||||
import {initializeUnitTest} from 'lexical/__tests__/utils';
 | 
					import {initializeUnitTest} from 'lexical/__tests__/utils';
 | 
				
			||||||
 | 
					import {$createHeadingNode, $isHeadingNode, HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const editorConfig = Object.freeze({
 | 
					const editorConfig = Object.freeze({
 | 
				
			||||||
  namespace: '',
 | 
					  namespace: '',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,9 @@
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {$createQuoteNode, QuoteNode} from '@lexical/rich-text';
 | 
					 | 
				
			||||||
import {$createRangeSelection, $getRoot, ParagraphNode} from 'lexical';
 | 
					import {$createRangeSelection, $getRoot, ParagraphNode} from 'lexical';
 | 
				
			||||||
import {initializeUnitTest} from 'lexical/__tests__/utils';
 | 
					import {initializeUnitTest} from 'lexical/__tests__/utils';
 | 
				
			||||||
 | 
					import {$createQuoteNode, QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const editorConfig = Object.freeze({
 | 
					const editorConfig = Object.freeze({
 | 
				
			||||||
  namespace: '',
 | 
					  namespace: '',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,42 +8,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import type {
 | 
					import type {
 | 
				
			||||||
  CommandPayloadType,
 | 
					  CommandPayloadType,
 | 
				
			||||||
  DOMConversionMap,
 | 
					 | 
				
			||||||
  DOMConversionOutput,
 | 
					 | 
				
			||||||
  DOMExportOutput,
 | 
					 | 
				
			||||||
  EditorConfig,
 | 
					 | 
				
			||||||
  ElementFormatType,
 | 
					  ElementFormatType,
 | 
				
			||||||
  LexicalCommand,
 | 
					  LexicalCommand,
 | 
				
			||||||
  LexicalEditor,
 | 
					  LexicalEditor,
 | 
				
			||||||
  LexicalNode,
 | 
					 | 
				
			||||||
  NodeKey,
 | 
					 | 
				
			||||||
  ParagraphNode,
 | 
					 | 
				
			||||||
  PasteCommandType,
 | 
					  PasteCommandType,
 | 
				
			||||||
  RangeSelection,
 | 
					  RangeSelection,
 | 
				
			||||||
  SerializedElementNode,
 | 
					 | 
				
			||||||
  Spread,
 | 
					 | 
				
			||||||
  TextFormatType,
 | 
					  TextFormatType,
 | 
				
			||||||
} from 'lexical';
 | 
					} from 'lexical';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  $insertDataTransferForRichText,
 | 
					 | 
				
			||||||
  copyToClipboard,
 | 
					 | 
				
			||||||
} from '@lexical/clipboard';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  $moveCharacter,
 | 
					 | 
				
			||||||
  $shouldOverrideDefaultCharacterSelection,
 | 
					 | 
				
			||||||
} from '@lexical/selection';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  $findMatchingParent,
 | 
					 | 
				
			||||||
  $getNearestBlockElementAncestorOrThrow,
 | 
					 | 
				
			||||||
  addClassNamesToElement,
 | 
					 | 
				
			||||||
  isHTMLElement,
 | 
					 | 
				
			||||||
  mergeRegister,
 | 
					 | 
				
			||||||
  objectKlassEquals,
 | 
					 | 
				
			||||||
} from '@lexical/utils';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  $applyNodeReplacement,
 | 
					 | 
				
			||||||
  $createParagraphNode,
 | 
					 | 
				
			||||||
  $createRangeSelection,
 | 
					  $createRangeSelection,
 | 
				
			||||||
  $createTabNode,
 | 
					  $createTabNode,
 | 
				
			||||||
  $getAdjacentNode,
 | 
					  $getAdjacentNode,
 | 
				
			||||||
| 
						 | 
					@ -55,7 +27,6 @@ import {
 | 
				
			||||||
  $isElementNode,
 | 
					  $isElementNode,
 | 
				
			||||||
  $isNodeSelection,
 | 
					  $isNodeSelection,
 | 
				
			||||||
  $isRangeSelection,
 | 
					  $isRangeSelection,
 | 
				
			||||||
  $isRootNode,
 | 
					 | 
				
			||||||
  $isTextNode,
 | 
					  $isTextNode,
 | 
				
			||||||
  $normalizeSelection__EXPERIMENTAL,
 | 
					  $normalizeSelection__EXPERIMENTAL,
 | 
				
			||||||
  $selectAll,
 | 
					  $selectAll,
 | 
				
			||||||
| 
						 | 
					@ -75,7 +46,6 @@ import {
 | 
				
			||||||
  ElementNode,
 | 
					  ElementNode,
 | 
				
			||||||
  FORMAT_ELEMENT_COMMAND,
 | 
					  FORMAT_ELEMENT_COMMAND,
 | 
				
			||||||
  FORMAT_TEXT_COMMAND,
 | 
					  FORMAT_TEXT_COMMAND,
 | 
				
			||||||
  INDENT_CONTENT_COMMAND,
 | 
					 | 
				
			||||||
  INSERT_LINE_BREAK_COMMAND,
 | 
					  INSERT_LINE_BREAK_COMMAND,
 | 
				
			||||||
  INSERT_PARAGRAPH_COMMAND,
 | 
					  INSERT_PARAGRAPH_COMMAND,
 | 
				
			||||||
  INSERT_TAB_COMMAND,
 | 
					  INSERT_TAB_COMMAND,
 | 
				
			||||||
| 
						 | 
					@ -88,327 +58,22 @@ import {
 | 
				
			||||||
  KEY_DELETE_COMMAND,
 | 
					  KEY_DELETE_COMMAND,
 | 
				
			||||||
  KEY_ENTER_COMMAND,
 | 
					  KEY_ENTER_COMMAND,
 | 
				
			||||||
  KEY_ESCAPE_COMMAND,
 | 
					  KEY_ESCAPE_COMMAND,
 | 
				
			||||||
  OUTDENT_CONTENT_COMMAND,
 | 
					 | 
				
			||||||
  PASTE_COMMAND,
 | 
					  PASTE_COMMAND,
 | 
				
			||||||
  REMOVE_TEXT_COMMAND,
 | 
					  REMOVE_TEXT_COMMAND,
 | 
				
			||||||
  SELECT_ALL_COMMAND,
 | 
					  SELECT_ALL_COMMAND,
 | 
				
			||||||
} from 'lexical';
 | 
					} from 'lexical';
 | 
				
			||||||
import caretFromPoint from 'lexical/shared/caretFromPoint';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  CAN_USE_BEFORE_INPUT,
 | 
					 | 
				
			||||||
  IS_APPLE_WEBKIT,
 | 
					 | 
				
			||||||
  IS_IOS,
 | 
					 | 
				
			||||||
  IS_SAFARI,
 | 
					 | 
				
			||||||
} from 'lexical/shared/environment';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SerializedHeadingNode = Spread<
 | 
					import {$insertDataTransferForRichText, copyToClipboard,} from '@lexical/clipboard';
 | 
				
			||||||
  {
 | 
					import {$moveCharacter, $shouldOverrideDefaultCharacterSelection,} from '@lexical/selection';
 | 
				
			||||||
    tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
 | 
					import {$findMatchingParent, mergeRegister, objectKlassEquals,} from '@lexical/utils';
 | 
				
			||||||
  },
 | 
					import caretFromPoint from 'lexical/shared/caretFromPoint';
 | 
				
			||||||
  SerializedElementNode
 | 
					import {CAN_USE_BEFORE_INPUT, IS_APPLE_WEBKIT, IS_IOS, IS_SAFARI,} from 'lexical/shared/environment';
 | 
				
			||||||
>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DRAG_DROP_PASTE: LexicalCommand<Array<File>> = createCommand(
 | 
					export const DRAG_DROP_PASTE: LexicalCommand<Array<File>> = createCommand(
 | 
				
			||||||
  'DRAG_DROP_PASTE_FILE',
 | 
					  'DRAG_DROP_PASTE_FILE',
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SerializedQuoteNode = SerializedElementNode;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** @noInheritDoc */
 | 
					 | 
				
			||||||
export class QuoteNode extends ElementNode {
 | 
					 | 
				
			||||||
  static getType(): string {
 | 
					 | 
				
			||||||
    return 'quote';
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static clone(node: QuoteNode): QuoteNode {
 | 
					 | 
				
			||||||
    return new QuoteNode(node.__key);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(key?: NodeKey) {
 | 
					 | 
				
			||||||
    super(key);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // View
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  createDOM(config: EditorConfig): HTMLElement {
 | 
					 | 
				
			||||||
    const element = document.createElement('blockquote');
 | 
					 | 
				
			||||||
    addClassNamesToElement(element, config.theme.quote);
 | 
					 | 
				
			||||||
    return element;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  updateDOM(prevNode: QuoteNode, dom: HTMLElement): boolean {
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static importDOM(): DOMConversionMap | null {
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      blockquote: (node: Node) => ({
 | 
					 | 
				
			||||||
        conversion: $convertBlockquoteElement,
 | 
					 | 
				
			||||||
        priority: 0,
 | 
					 | 
				
			||||||
      }),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  exportDOM(editor: LexicalEditor): DOMExportOutput {
 | 
					 | 
				
			||||||
    const {element} = super.exportDOM(editor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (element && isHTMLElement(element)) {
 | 
					 | 
				
			||||||
      if (this.isEmpty()) {
 | 
					 | 
				
			||||||
        element.append(document.createElement('br'));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      element,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static importJSON(serializedNode: SerializedQuoteNode): QuoteNode {
 | 
					 | 
				
			||||||
    const node = $createQuoteNode();
 | 
					 | 
				
			||||||
    return node;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  exportJSON(): SerializedElementNode {
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      ...super.exportJSON(),
 | 
					 | 
				
			||||||
      type: 'quote',
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Mutation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  insertNewAfter(_: RangeSelection, restoreSelection?: boolean): ParagraphNode {
 | 
					 | 
				
			||||||
    const newBlock = $createParagraphNode();
 | 
					 | 
				
			||||||
    const direction = this.getDirection();
 | 
					 | 
				
			||||||
    newBlock.setDirection(direction);
 | 
					 | 
				
			||||||
    this.insertAfter(newBlock, restoreSelection);
 | 
					 | 
				
			||||||
    return newBlock;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  collapseAtStart(): true {
 | 
					 | 
				
			||||||
    const paragraph = $createParagraphNode();
 | 
					 | 
				
			||||||
    const children = this.getChildren();
 | 
					 | 
				
			||||||
    children.forEach((child) => paragraph.append(child));
 | 
					 | 
				
			||||||
    this.replace(paragraph);
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  canMergeWhenEmpty(): true {
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function $createQuoteNode(): QuoteNode {
 | 
					 | 
				
			||||||
  return $applyNodeReplacement(new QuoteNode());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function $isQuoteNode(
 | 
					 | 
				
			||||||
  node: LexicalNode | null | undefined,
 | 
					 | 
				
			||||||
): node is QuoteNode {
 | 
					 | 
				
			||||||
  return node instanceof QuoteNode;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type HeadingTagType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** @noInheritDoc */
 | 
					 | 
				
			||||||
export class HeadingNode extends ElementNode {
 | 
					 | 
				
			||||||
  /** @internal */
 | 
					 | 
				
			||||||
  __tag: HeadingTagType;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static getType(): string {
 | 
					 | 
				
			||||||
    return 'heading';
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static clone(node: HeadingNode): HeadingNode {
 | 
					 | 
				
			||||||
    return new HeadingNode(node.__tag, node.__key);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(tag: HeadingTagType, key?: NodeKey) {
 | 
					 | 
				
			||||||
    super(key);
 | 
					 | 
				
			||||||
    this.__tag = tag;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  getTag(): HeadingTagType {
 | 
					 | 
				
			||||||
    return this.__tag;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // View
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  createDOM(config: EditorConfig): HTMLElement {
 | 
					 | 
				
			||||||
    const tag = this.__tag;
 | 
					 | 
				
			||||||
    const element = document.createElement(tag);
 | 
					 | 
				
			||||||
    const theme = config.theme;
 | 
					 | 
				
			||||||
    const classNames = theme.heading;
 | 
					 | 
				
			||||||
    if (classNames !== undefined) {
 | 
					 | 
				
			||||||
      const className = classNames[tag];
 | 
					 | 
				
			||||||
      addClassNamesToElement(element, className);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return element;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  updateDOM(prevNode: HeadingNode, dom: HTMLElement): boolean {
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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,
 | 
					 | 
				
			||||||
      }),
 | 
					 | 
				
			||||||
      p: (node: Node) => {
 | 
					 | 
				
			||||||
        // domNode is a <p> since we matched it by nodeName
 | 
					 | 
				
			||||||
        const paragraph = node as HTMLParagraphElement;
 | 
					 | 
				
			||||||
        const firstChild = paragraph.firstChild;
 | 
					 | 
				
			||||||
        if (firstChild !== null && isGoogleDocsTitle(firstChild)) {
 | 
					 | 
				
			||||||
          return {
 | 
					 | 
				
			||||||
            conversion: () => ({node: null}),
 | 
					 | 
				
			||||||
            priority: 3,
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      span: (node: Node) => {
 | 
					 | 
				
			||||||
        if (isGoogleDocsTitle(node)) {
 | 
					 | 
				
			||||||
          return {
 | 
					 | 
				
			||||||
            conversion: (domNode: Node) => {
 | 
					 | 
				
			||||||
              return {
 | 
					 | 
				
			||||||
                node: $createHeadingNode('h1'),
 | 
					 | 
				
			||||||
              };
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            priority: 3,
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  exportDOM(editor: LexicalEditor): DOMExportOutput {
 | 
					 | 
				
			||||||
    const {element} = super.exportDOM(editor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (element && isHTMLElement(element)) {
 | 
					 | 
				
			||||||
      if (this.isEmpty()) {
 | 
					 | 
				
			||||||
        element.append(document.createElement('br'));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      element,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static importJSON(serializedNode: SerializedHeadingNode): HeadingNode {
 | 
					 | 
				
			||||||
    return $createHeadingNode(serializedNode.tag);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  exportJSON(): SerializedHeadingNode {
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      ...super.exportJSON(),
 | 
					 | 
				
			||||||
      tag: this.getTag(),
 | 
					 | 
				
			||||||
      type: 'heading',
 | 
					 | 
				
			||||||
      version: 1,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Mutation
 | 
					 | 
				
			||||||
  insertNewAfter(
 | 
					 | 
				
			||||||
    selection?: RangeSelection,
 | 
					 | 
				
			||||||
    restoreSelection = true,
 | 
					 | 
				
			||||||
  ): ParagraphNode | HeadingNode {
 | 
					 | 
				
			||||||
    const anchorOffet = selection ? selection.anchor.offset : 0;
 | 
					 | 
				
			||||||
    const lastDesc = this.getLastDescendant();
 | 
					 | 
				
			||||||
    const isAtEnd =
 | 
					 | 
				
			||||||
      !lastDesc ||
 | 
					 | 
				
			||||||
      (selection &&
 | 
					 | 
				
			||||||
        selection.anchor.key === lastDesc.getKey() &&
 | 
					 | 
				
			||||||
        anchorOffet === lastDesc.getTextContentSize());
 | 
					 | 
				
			||||||
    const newElement =
 | 
					 | 
				
			||||||
      isAtEnd || !selection
 | 
					 | 
				
			||||||
        ? $createParagraphNode()
 | 
					 | 
				
			||||||
        : $createHeadingNode(this.getTag());
 | 
					 | 
				
			||||||
    const direction = this.getDirection();
 | 
					 | 
				
			||||||
    newElement.setDirection(direction);
 | 
					 | 
				
			||||||
    this.insertAfter(newElement, restoreSelection);
 | 
					 | 
				
			||||||
    if (anchorOffet === 0 && !this.isEmpty() && selection) {
 | 
					 | 
				
			||||||
      const paragraph = $createParagraphNode();
 | 
					 | 
				
			||||||
      paragraph.select();
 | 
					 | 
				
			||||||
      this.replace(paragraph, true);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return newElement;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  collapseAtStart(): true {
 | 
					 | 
				
			||||||
    const newElement = !this.isEmpty()
 | 
					 | 
				
			||||||
      ? $createHeadingNode(this.getTag())
 | 
					 | 
				
			||||||
      : $createParagraphNode();
 | 
					 | 
				
			||||||
    const children = this.getChildren();
 | 
					 | 
				
			||||||
    children.forEach((child) => newElement.append(child));
 | 
					 | 
				
			||||||
    this.replace(newElement);
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  extractWithChild(): boolean {
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function isGoogleDocsTitle(domNode: Node): boolean {
 | 
					 | 
				
			||||||
  if (domNode.nodeName.toLowerCase() === 'span') {
 | 
					 | 
				
			||||||
    return (domNode as HTMLSpanElement).style.fontSize === '26pt';
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 = $createHeadingNode(nodeName);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return {node};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput {
 | 
					 | 
				
			||||||
  const node = $createQuoteNode();
 | 
					 | 
				
			||||||
  return {node};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function $createHeadingNode(headingTag: HeadingTagType): HeadingNode {
 | 
					 | 
				
			||||||
  return $applyNodeReplacement(new HeadingNode(headingTag));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function $isHeadingNode(
 | 
					 | 
				
			||||||
  node: LexicalNode | null | undefined,
 | 
					 | 
				
			||||||
): node is HeadingNode {
 | 
					 | 
				
			||||||
  return node instanceof HeadingNode;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onPasteForRichText(
 | 
					function onPasteForRichText(
 | 
				
			||||||
  event: CommandPayloadType<typeof PASTE_COMMAND>,
 | 
					  event: CommandPayloadType<typeof PASTE_COMMAND>,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {$createLinkNode} from '@lexical/link';
 | 
					import {$createLinkNode} from '@lexical/link';
 | 
				
			||||||
import {$createListItemNode, $createListNode} from '@lexical/list';
 | 
					import {$createListItemNode, $createListNode} from '@lexical/list';
 | 
				
			||||||
import {$createHeadingNode, registerRichText} from '@lexical/rich-text';
 | 
					import {registerRichText} from '@lexical/rich-text';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  $addNodeStyle,
 | 
					  $addNodeStyle,
 | 
				
			||||||
  $getSelectionStyleValueForProperty,
 | 
					  $getSelectionStyleValueForProperty,
 | 
				
			||||||
| 
						 | 
					@ -74,6 +74,7 @@ import {
 | 
				
			||||||
} from '../utils';
 | 
					} from '../utils';
 | 
				
			||||||
import {createEmptyHistoryState, registerHistory} from "@lexical/history";
 | 
					import {createEmptyHistoryState, registerHistory} from "@lexical/history";
 | 
				
			||||||
import {mergeRegister} from "@lexical/utils";
 | 
					import {mergeRegister} from "@lexical/utils";
 | 
				
			||||||
 | 
					import {$createHeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ExpectedSelection {
 | 
					interface ExpectedSelection {
 | 
				
			||||||
  anchorPath: number[];
 | 
					  anchorPath: number[];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,6 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {$createLinkNode} from '@lexical/link';
 | 
					import {$createLinkNode} from '@lexical/link';
 | 
				
			||||||
import {$createHeadingNode, $isHeadingNode} from '@lexical/rich-text';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  $getSelectionStyleValueForProperty,
 | 
					  $getSelectionStyleValueForProperty,
 | 
				
			||||||
  $patchStyleText,
 | 
					  $patchStyleText,
 | 
				
			||||||
| 
						 | 
					@ -44,6 +43,7 @@ import {
 | 
				
			||||||
} from 'lexical/__tests__/utils';
 | 
					} from 'lexical/__tests__/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {$setAnchorPoint, $setFocusPoint} from '../utils';
 | 
					import {$setAnchorPoint, $setFocusPoint} from '../utils';
 | 
				
			||||||
 | 
					import {$createHeadingNode, $isHeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Range.prototype.getBoundingClientRect = function (): DOMRect {
 | 
					Range.prototype.getBoundingClientRect = function (): DOMRect {
 | 
				
			||||||
  const rect = {
 | 
					  const rect = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import {AutoLinkNode, LinkNode} from '@lexical/link';
 | 
					import {AutoLinkNode, LinkNode} from '@lexical/link';
 | 
				
			||||||
import {ListItemNode, ListNode} from '@lexical/list';
 | 
					import {ListItemNode, ListNode} from '@lexical/list';
 | 
				
			||||||
import {HeadingNode, QuoteNode, registerRichText} from '@lexical/rich-text';
 | 
					import {registerRichText} from '@lexical/rich-text';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  applySelectionInputs,
 | 
					  applySelectionInputs,
 | 
				
			||||||
  pasteHTML,
 | 
					  pasteHTML,
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,8 @@ import {
 | 
				
			||||||
import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';
 | 
					import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';
 | 
				
			||||||
import {$createParagraphNode, $insertNodes, LexicalEditor} from 'lexical';
 | 
					import {$createParagraphNode, $insertNodes, LexicalEditor} from 'lexical';
 | 
				
			||||||
import {createTestEditor, initializeClipboard} from 'lexical/__tests__/utils';
 | 
					import {createTestEditor, initializeClipboard} from 'lexical/__tests__/utils';
 | 
				
			||||||
 | 
					import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jest.mock('lexical/shared/environment', () => {
 | 
					jest.mock('lexical/shared/environment', () => {
 | 
				
			||||||
  const originalModule = jest.requireActual('lexical/shared/environment');
 | 
					  const originalModule = jest.requireActual('lexical/shared/environment');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,146 +0,0 @@
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    DOMConversionMap,
 | 
					 | 
				
			||||||
    DOMConversionOutput,
 | 
					 | 
				
			||||||
    LexicalNode,
 | 
					 | 
				
			||||||
    Spread
 | 
					 | 
				
			||||||
} from "lexical";
 | 
					 | 
				
			||||||
import {EditorConfig} from "lexical/LexicalEditor";
 | 
					 | 
				
			||||||
import {HeadingNode, HeadingTagType, SerializedHeadingNode} from "@lexical/rich-text";
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    CommonBlockAlignment, commonPropertiesDifferent, deserializeCommonBlockNode,
 | 
					 | 
				
			||||||
    SerializedCommonBlockNode,
 | 
					 | 
				
			||||||
    setCommonBlockPropsFromElement,
 | 
					 | 
				
			||||||
    updateElementWithCommonBlockProps
 | 
					 | 
				
			||||||
} from "./_common";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type SerializedCustomHeadingNode = Spread<SerializedCommonBlockNode, SerializedHeadingNode>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class CustomHeadingNode extends HeadingNode {
 | 
					 | 
				
			||||||
    __id: string = '';
 | 
					 | 
				
			||||||
    __alignment: CommonBlockAlignment = '';
 | 
					 | 
				
			||||||
    __inset: number = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static getType() {
 | 
					 | 
				
			||||||
        return 'custom-heading';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setId(id: string) {
 | 
					 | 
				
			||||||
        const self = this.getWritable();
 | 
					 | 
				
			||||||
        self.__id = id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getId(): string {
 | 
					 | 
				
			||||||
        const self = this.getLatest();
 | 
					 | 
				
			||||||
        return self.__id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setAlignment(alignment: CommonBlockAlignment) {
 | 
					 | 
				
			||||||
        const self = this.getWritable();
 | 
					 | 
				
			||||||
        self.__alignment = alignment;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getAlignment(): CommonBlockAlignment {
 | 
					 | 
				
			||||||
        const self = this.getLatest();
 | 
					 | 
				
			||||||
        return self.__alignment;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setInset(size: number) {
 | 
					 | 
				
			||||||
        const self = this.getWritable();
 | 
					 | 
				
			||||||
        self.__inset = size;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getInset(): number {
 | 
					 | 
				
			||||||
        const self = this.getLatest();
 | 
					 | 
				
			||||||
        return self.__inset;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static clone(node: CustomHeadingNode) {
 | 
					 | 
				
			||||||
        const newNode = new CustomHeadingNode(node.__tag, node.__key);
 | 
					 | 
				
			||||||
        newNode.__alignment = node.__alignment;
 | 
					 | 
				
			||||||
        newNode.__inset = node.__inset;
 | 
					 | 
				
			||||||
        return newNode;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    createDOM(config: EditorConfig): HTMLElement {
 | 
					 | 
				
			||||||
        const dom = super.createDOM(config);
 | 
					 | 
				
			||||||
        updateElementWithCommonBlockProps(dom, this);
 | 
					 | 
				
			||||||
        return dom;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    updateDOM(prevNode: CustomHeadingNode, dom: HTMLElement): boolean {
 | 
					 | 
				
			||||||
        return super.updateDOM(prevNode, dom)
 | 
					 | 
				
			||||||
            || commonPropertiesDifferent(prevNode, this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    exportJSON(): SerializedCustomHeadingNode {
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            ...super.exportJSON(),
 | 
					 | 
				
			||||||
            type: 'custom-heading',
 | 
					 | 
				
			||||||
            version: 1,
 | 
					 | 
				
			||||||
            id: this.__id,
 | 
					 | 
				
			||||||
            alignment: this.__alignment,
 | 
					 | 
				
			||||||
            inset: this.__inset,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static importJSON(serializedNode: SerializedCustomHeadingNode): CustomHeadingNode {
 | 
					 | 
				
			||||||
        const node = $createCustomHeadingNode(serializedNode.tag);
 | 
					 | 
				
			||||||
        deserializeCommonBlockNode(serializedNode, node);
 | 
					 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
        setCommonBlockPropsFromElement(element, node);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,115 +0,0 @@
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    DOMConversionMap,
 | 
					 | 
				
			||||||
    DOMConversionOutput,
 | 
					 | 
				
			||||||
    LexicalNode,
 | 
					 | 
				
			||||||
    Spread
 | 
					 | 
				
			||||||
} from "lexical";
 | 
					 | 
				
			||||||
import {EditorConfig} from "lexical/LexicalEditor";
 | 
					 | 
				
			||||||
import {QuoteNode, SerializedQuoteNode} from "@lexical/rich-text";
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    CommonBlockAlignment, commonPropertiesDifferent, deserializeCommonBlockNode,
 | 
					 | 
				
			||||||
    SerializedCommonBlockNode,
 | 
					 | 
				
			||||||
    setCommonBlockPropsFromElement,
 | 
					 | 
				
			||||||
    updateElementWithCommonBlockProps
 | 
					 | 
				
			||||||
} from "./_common";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type SerializedCustomQuoteNode = Spread<SerializedCommonBlockNode, SerializedQuoteNode>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class CustomQuoteNode extends QuoteNode {
 | 
					 | 
				
			||||||
    __id: string = '';
 | 
					 | 
				
			||||||
    __alignment: CommonBlockAlignment = '';
 | 
					 | 
				
			||||||
    __inset: number = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static getType() {
 | 
					 | 
				
			||||||
        return 'custom-quote';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setId(id: string) {
 | 
					 | 
				
			||||||
        const self = this.getWritable();
 | 
					 | 
				
			||||||
        self.__id = id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getId(): string {
 | 
					 | 
				
			||||||
        const self = this.getLatest();
 | 
					 | 
				
			||||||
        return self.__id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setAlignment(alignment: CommonBlockAlignment) {
 | 
					 | 
				
			||||||
        const self = this.getWritable();
 | 
					 | 
				
			||||||
        self.__alignment = alignment;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getAlignment(): CommonBlockAlignment {
 | 
					 | 
				
			||||||
        const self = this.getLatest();
 | 
					 | 
				
			||||||
        return self.__alignment;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setInset(size: number) {
 | 
					 | 
				
			||||||
        const self = this.getWritable();
 | 
					 | 
				
			||||||
        self.__inset = size;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getInset(): number {
 | 
					 | 
				
			||||||
        const self = this.getLatest();
 | 
					 | 
				
			||||||
        return self.__inset;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static clone(node: CustomQuoteNode) {
 | 
					 | 
				
			||||||
        const newNode = new CustomQuoteNode(node.__key);
 | 
					 | 
				
			||||||
        newNode.__id = node.__id;
 | 
					 | 
				
			||||||
        newNode.__alignment = node.__alignment;
 | 
					 | 
				
			||||||
        newNode.__inset = node.__inset;
 | 
					 | 
				
			||||||
        return newNode;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    createDOM(config: EditorConfig): HTMLElement {
 | 
					 | 
				
			||||||
        const dom = super.createDOM(config);
 | 
					 | 
				
			||||||
        updateElementWithCommonBlockProps(dom, this);
 | 
					 | 
				
			||||||
        return dom;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    updateDOM(prevNode: CustomQuoteNode): boolean {
 | 
					 | 
				
			||||||
        return commonPropertiesDifferent(prevNode, this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    exportJSON(): SerializedCustomQuoteNode {
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            ...super.exportJSON(),
 | 
					 | 
				
			||||||
            type: 'custom-quote',
 | 
					 | 
				
			||||||
            version: 1,
 | 
					 | 
				
			||||||
            id: this.__id,
 | 
					 | 
				
			||||||
            alignment: this.__alignment,
 | 
					 | 
				
			||||||
            inset: this.__inset,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static importJSON(serializedNode: SerializedCustomQuoteNode): CustomQuoteNode {
 | 
					 | 
				
			||||||
        const node = $createCustomQuoteNode();
 | 
					 | 
				
			||||||
        deserializeCommonBlockNode(serializedNode, node);
 | 
					 | 
				
			||||||
        return node;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static importDOM(): DOMConversionMap | null {
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            blockquote: (node: Node) => ({
 | 
					 | 
				
			||||||
                conversion: $convertBlockquoteElement,
 | 
					 | 
				
			||||||
                priority: 0,
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput {
 | 
					 | 
				
			||||||
    const node = $createCustomQuoteNode();
 | 
					 | 
				
			||||||
    setCommonBlockPropsFromElement(element, node);
 | 
					 | 
				
			||||||
    return {node};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function $createCustomQuoteNode() {
 | 
					 | 
				
			||||||
    return new CustomQuoteNode();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function $isCustomQuoteNode(node: LexicalNode | null | undefined): node is CustomQuoteNode {
 | 
					 | 
				
			||||||
    return node instanceof CustomQuoteNode;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import {HeadingNode, QuoteNode} from '@lexical/rich-text';
 | 
					 | 
				
			||||||
import {CalloutNode} from './callout';
 | 
					import {CalloutNode} from './callout';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ElementNode,
 | 
					    ElementNode,
 | 
				
			||||||
| 
						 | 
					@ -21,9 +20,9 @@ import {MediaNode} from "./media";
 | 
				
			||||||
import {CustomListItemNode} from "./custom-list-item";
 | 
					import {CustomListItemNode} from "./custom-list-item";
 | 
				
			||||||
import {CustomTableCellNode} from "./custom-table-cell";
 | 
					import {CustomTableCellNode} from "./custom-table-cell";
 | 
				
			||||||
import {CustomTableRowNode} from "./custom-table-row";
 | 
					import {CustomTableRowNode} from "./custom-table-row";
 | 
				
			||||||
import {CustomHeadingNode} from "./custom-heading";
 | 
					 | 
				
			||||||
import {CustomQuoteNode} from "./custom-quote";
 | 
					 | 
				
			||||||
import {CustomListNode} from "./custom-list";
 | 
					import {CustomListNode} from "./custom-list";
 | 
				
			||||||
 | 
					import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Load the nodes for lexical.
 | 
					 * Load the nodes for lexical.
 | 
				
			||||||
| 
						 | 
					@ -31,8 +30,8 @@ import {CustomListNode} from "./custom-list";
 | 
				
			||||||
export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> | LexicalNodeReplacement)[] {
 | 
					export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> | LexicalNodeReplacement)[] {
 | 
				
			||||||
    return [
 | 
					    return [
 | 
				
			||||||
        CalloutNode,
 | 
					        CalloutNode,
 | 
				
			||||||
        CustomHeadingNode,
 | 
					        HeadingNode,
 | 
				
			||||||
        CustomQuoteNode,
 | 
					        QuoteNode,
 | 
				
			||||||
        CustomListNode,
 | 
					        CustomListNode,
 | 
				
			||||||
        CustomListItemNode, // TODO - Alignment?
 | 
					        CustomListItemNode, // TODO - Alignment?
 | 
				
			||||||
        CustomTableNode,
 | 
					        CustomTableNode,
 | 
				
			||||||
| 
						 | 
					@ -46,18 +45,6 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
 | 
				
			||||||
        MediaNode, // TODO - Alignment
 | 
					        MediaNode, // TODO - Alignment
 | 
				
			||||||
        ParagraphNode,
 | 
					        ParagraphNode,
 | 
				
			||||||
        LinkNode,
 | 
					        LinkNode,
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            replace: HeadingNode,
 | 
					 | 
				
			||||||
            with: (node: HeadingNode) => {
 | 
					 | 
				
			||||||
                return new CustomHeadingNode(node.__tag);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            replace: QuoteNode,
 | 
					 | 
				
			||||||
            with: (node: QuoteNode) => {
 | 
					 | 
				
			||||||
                return new CustomQuoteNode();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            replace: ListNode,
 | 
					            replace: ListNode,
 | 
				
			||||||
            with: (node: ListNode) => {
 | 
					            with: (node: ListNode) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,12 +6,12 @@ import {
 | 
				
			||||||
    toggleSelectionAsHeading, toggleSelectionAsList,
 | 
					    toggleSelectionAsHeading, toggleSelectionAsList,
 | 
				
			||||||
    toggleSelectionAsParagraph
 | 
					    toggleSelectionAsParagraph
 | 
				
			||||||
} from "../utils/formats";
 | 
					} from "../utils/formats";
 | 
				
			||||||
import {HeadingTagType} from "@lexical/rich-text";
 | 
					 | 
				
			||||||
import {EditorUiContext} from "../ui/framework/core";
 | 
					import {EditorUiContext} from "../ui/framework/core";
 | 
				
			||||||
import {$getNodeFromSelection} from "../utils/selection";
 | 
					import {$getNodeFromSelection} from "../utils/selection";
 | 
				
			||||||
import {$isLinkNode, LinkNode} from "@lexical/link";
 | 
					import {$isLinkNode, LinkNode} from "@lexical/link";
 | 
				
			||||||
import {$showLinkForm} from "../ui/defaults/forms/objects";
 | 
					import {$showLinkForm} from "../ui/defaults/forms/objects";
 | 
				
			||||||
import {showLinkSelector} from "../utils/links";
 | 
					import {showLinkSelector} from "../utils/links";
 | 
				
			||||||
 | 
					import {HeadingTagType} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function headerHandler(editor: LexicalEditor, tag: HeadingTagType): boolean {
 | 
					function headerHandler(editor: LexicalEditor, tag: HeadingTagType): boolean {
 | 
				
			||||||
    toggleSelectionAsHeading(editor, tag);
 | 
					    toggleSelectionAsHeading(editor, tag);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,18 +2,14 @@ import {$createCalloutNode, $isCalloutNodeOfCategory, CalloutCategory} from "../
 | 
				
			||||||
import {EditorButtonDefinition} from "../../framework/buttons";
 | 
					import {EditorButtonDefinition} from "../../framework/buttons";
 | 
				
			||||||
import {EditorUiContext} from "../../framework/core";
 | 
					import {EditorUiContext} from "../../framework/core";
 | 
				
			||||||
import {$isParagraphNode, BaseSelection, LexicalNode} from "lexical";
 | 
					import {$isParagraphNode, BaseSelection, LexicalNode} from "lexical";
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    $isHeadingNode,
 | 
					 | 
				
			||||||
    $isQuoteNode,
 | 
					 | 
				
			||||||
    HeadingNode,
 | 
					 | 
				
			||||||
    HeadingTagType
 | 
					 | 
				
			||||||
} from "@lexical/rich-text";
 | 
					 | 
				
			||||||
import {$selectionContainsNodeType, $toggleSelectionBlockNodeType} from "../../../utils/selection";
 | 
					import {$selectionContainsNodeType, $toggleSelectionBlockNodeType} from "../../../utils/selection";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    toggleSelectionAsBlockquote,
 | 
					    toggleSelectionAsBlockquote,
 | 
				
			||||||
    toggleSelectionAsHeading,
 | 
					    toggleSelectionAsHeading,
 | 
				
			||||||
    toggleSelectionAsParagraph
 | 
					    toggleSelectionAsParagraph
 | 
				
			||||||
} from "../../../utils/formats";
 | 
					} from "../../../utils/formats";
 | 
				
			||||||
 | 
					import {$isHeadingNode, HeadingNode, HeadingTagType} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					import {$isQuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function buildCalloutButton(category: CalloutCategory, name: string): EditorButtonDefinition {
 | 
					function buildCalloutButton(category: CalloutCategory, name: string): EditorButtonDefinition {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,13 @@
 | 
				
			||||||
import {EditorContainerUiElement} from "../core";
 | 
					import {EditorContainerUiElement} from "../core";
 | 
				
			||||||
import {el} from "../../../utils/dom";
 | 
					import {el} from "../../../utils/dom";
 | 
				
			||||||
import {EditorFormField} from "../forms";
 | 
					import {EditorFormField} from "../forms";
 | 
				
			||||||
import {CustomHeadingNode} from "../../../nodes/custom-heading";
 | 
					 | 
				
			||||||
import {$getAllNodesOfType} from "../../../utils/nodes";
 | 
					import {$getAllNodesOfType} from "../../../utils/nodes";
 | 
				
			||||||
import {$isHeadingNode} from "@lexical/rich-text";
 | 
					 | 
				
			||||||
import {uniqueIdSmall} from "../../../../services/util";
 | 
					import {uniqueIdSmall} from "../../../../services/util";
 | 
				
			||||||
 | 
					import {$isHeadingNode, HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class LinkField extends EditorContainerUiElement {
 | 
					export class LinkField extends EditorContainerUiElement {
 | 
				
			||||||
    protected input: EditorFormField;
 | 
					    protected input: EditorFormField;
 | 
				
			||||||
    protected headerMap = new Map<string, CustomHeadingNode>();
 | 
					    protected headerMap = new Map<string, HeadingNode>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(input: EditorFormField) {
 | 
					    constructor(input: EditorFormField) {
 | 
				
			||||||
        super([input]);
 | 
					        super([input]);
 | 
				
			||||||
| 
						 | 
					@ -43,7 +42,7 @@ export class LinkField extends EditorContainerUiElement {
 | 
				
			||||||
        return container;
 | 
					        return container;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updateFormFromHeader(header: CustomHeadingNode) {
 | 
					    updateFormFromHeader(header: HeadingNode) {
 | 
				
			||||||
        this.getHeaderIdAndText(header).then(({id, text}) => {
 | 
					        this.getHeaderIdAndText(header).then(({id, text}) => {
 | 
				
			||||||
            console.log('updating form', id, text);
 | 
					            console.log('updating form', id, text);
 | 
				
			||||||
            const modal =  this.getContext().manager.getActiveModal('link');
 | 
					            const modal =  this.getContext().manager.getActiveModal('link');
 | 
				
			||||||
| 
						 | 
					@ -57,7 +56,7 @@ export class LinkField extends EditorContainerUiElement {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getHeaderIdAndText(header: CustomHeadingNode): Promise<{id: string, text: string}> {
 | 
					    getHeaderIdAndText(header: HeadingNode): Promise<{id: string, text: string}> {
 | 
				
			||||||
        return new Promise((res) => {
 | 
					        return new Promise((res) => {
 | 
				
			||||||
            this.getContext().editor.update(() => {
 | 
					            this.getContext().editor.update(() => {
 | 
				
			||||||
                let id = header.getId();
 | 
					                let id = header.getId();
 | 
				
			||||||
| 
						 | 
					@ -75,7 +74,7 @@ export class LinkField extends EditorContainerUiElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updateDataList(listEl: HTMLElement) {
 | 
					    updateDataList(listEl: HTMLElement) {
 | 
				
			||||||
        this.getContext().editor.getEditorState().read(() => {
 | 
					        this.getContext().editor.getEditorState().read(() => {
 | 
				
			||||||
            const headers = $getAllNodesOfType($isHeadingNode) as CustomHeadingNode[];
 | 
					            const headers = $getAllNodesOfType($isHeadingNode) as HeadingNode[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.headerMap.clear();
 | 
					            this.headerMap.clear();
 | 
				
			||||||
            const listEls: HTMLElement[] = [];
 | 
					            const listEls: HTMLElement[] = [];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import {$isQuoteNode, HeadingNode, HeadingTagType} from "@lexical/rich-text";
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    $createParagraphNode,
 | 
					    $createParagraphNode,
 | 
				
			||||||
    $createTextNode,
 | 
					    $createTextNode,
 | 
				
			||||||
| 
						 | 
					@ -15,23 +14,23 @@ import {
 | 
				
			||||||
    $toggleSelectionBlockNodeType,
 | 
					    $toggleSelectionBlockNodeType,
 | 
				
			||||||
    getLastSelection
 | 
					    getLastSelection
 | 
				
			||||||
} from "./selection";
 | 
					} from "./selection";
 | 
				
			||||||
import {$createCustomHeadingNode, $isCustomHeadingNode} from "../nodes/custom-heading";
 | 
					 | 
				
			||||||
import {$createCustomQuoteNode} from "../nodes/custom-quote";
 | 
					 | 
				
			||||||
import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../nodes/code-block";
 | 
					import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../nodes/code-block";
 | 
				
			||||||
import {$createCalloutNode, $isCalloutNode, CalloutCategory} from "../nodes/callout";
 | 
					import {$createCalloutNode, $isCalloutNode, CalloutCategory} from "../nodes/callout";
 | 
				
			||||||
import {insertList, ListNode, ListType, removeList} from "@lexical/list";
 | 
					import {insertList, ListNode, ListType, removeList} from "@lexical/list";
 | 
				
			||||||
import {$isCustomListNode} from "../nodes/custom-list";
 | 
					import {$isCustomListNode} from "../nodes/custom-list";
 | 
				
			||||||
import {$createLinkNode, $isLinkNode} from "@lexical/link";
 | 
					import {$createLinkNode, $isLinkNode} from "@lexical/link";
 | 
				
			||||||
 | 
					import {$createHeadingNode, $isHeadingNode, HeadingTagType} from "@lexical/rich-text/LexicalHeadingNode";
 | 
				
			||||||
 | 
					import {$createQuoteNode, $isQuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const $isHeaderNodeOfTag = (node: LexicalNode | null | undefined, tag: HeadingTagType) => {
 | 
					const $isHeaderNodeOfTag = (node: LexicalNode | null | undefined, tag: HeadingTagType) => {
 | 
				
			||||||
    return $isCustomHeadingNode(node) && (node as HeadingNode).getTag() === tag;
 | 
					    return $isHeadingNode(node) && node.getTag() === tag;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function toggleSelectionAsHeading(editor: LexicalEditor, tag: HeadingTagType) {
 | 
					export function toggleSelectionAsHeading(editor: LexicalEditor, tag: HeadingTagType) {
 | 
				
			||||||
    editor.update(() => {
 | 
					    editor.update(() => {
 | 
				
			||||||
        $toggleSelectionBlockNodeType(
 | 
					        $toggleSelectionBlockNodeType(
 | 
				
			||||||
            (node) => $isHeaderNodeOfTag(node, tag),
 | 
					            (node) => $isHeaderNodeOfTag(node, tag),
 | 
				
			||||||
            () => $createCustomHeadingNode(tag),
 | 
					            () => $createHeadingNode(tag),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -44,7 +43,7 @@ export function toggleSelectionAsParagraph(editor: LexicalEditor) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function toggleSelectionAsBlockquote(editor: LexicalEditor) {
 | 
					export function toggleSelectionAsBlockquote(editor: LexicalEditor) {
 | 
				
			||||||
    editor.update(() => {
 | 
					    editor.update(() => {
 | 
				
			||||||
        $toggleSelectionBlockNodeType($isQuoteNode, $createCustomQuoteNode);
 | 
					        $toggleSelectionBlockNodeType($isQuoteNode, $createQuoteNode);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue