4 DOMConversionMap, DOMConversionOutput,
10 import type {EditorConfig} from "lexical/LexicalEditor";
11 import type {RangeSelection} from "lexical/LexicalSelection";
13 CommonBlockAlignment, commonPropertiesDifferent,
14 SerializedCommonBlockNode,
15 setCommonBlockPropsFromElement,
16 updateElementWithCommonBlockProps
19 export type CalloutCategory = 'info' | 'danger' | 'warning' | 'success';
21 export type SerializedCalloutNode = Spread<{
22 category: CalloutCategory;
23 }, SerializedCommonBlockNode>
25 export class CalloutNode extends ElementNode {
27 __category: CalloutCategory = 'info';
28 __alignment: CommonBlockAlignment = '';
34 static clone(node: CalloutNode) {
35 const newNode = new CalloutNode(node.__category, node.__key);
36 newNode.__id = node.__id;
37 newNode.__alignment = node.__alignment;
41 constructor(category: CalloutCategory, key?: string) {
43 this.__category = category;
46 setCategory(category: CalloutCategory) {
47 const self = this.getWritable();
48 self.__category = category;
51 getCategory(): CalloutCategory {
52 const self = this.getLatest();
53 return self.__category;
57 const self = this.getWritable();
62 const self = this.getLatest();
66 setAlignment(alignment: CommonBlockAlignment) {
67 const self = this.getWritable();
68 self.__alignment = alignment;
71 getAlignment(): CommonBlockAlignment {
72 const self = this.getLatest();
73 return self.__alignment;
76 createDOM(_config: EditorConfig, _editor: LexicalEditor) {
77 const element = document.createElement('p');
78 element.classList.add('callout', this.__category || '');
79 updateElementWithCommonBlockProps(element, this);
83 updateDOM(prevNode: CalloutNode): boolean {
84 return prevNode.__category !== this.__category ||
85 commonPropertiesDifferent(prevNode, this);
88 insertNewAfter(selection: RangeSelection, restoreSelection?: boolean): CalloutNode|ParagraphNode {
89 const anchorOffset = selection ? selection.anchor.offset : 0;
90 const newElement = anchorOffset === this.getTextContentSize() || !selection
91 ? $createParagraphNode() : $createCalloutNode(this.__category);
93 newElement.setDirection(this.getDirection());
94 this.insertAfter(newElement, restoreSelection);
96 if (anchorOffset === 0 && !this.isEmpty() && selection) {
97 const paragraph = $createParagraphNode();
99 this.replace(paragraph, true);
105 static importDOM(): DOMConversionMap|null {
107 p(node: HTMLElement): DOMConversion|null {
108 if (node.classList.contains('callout')) {
110 conversion: (element: HTMLElement): DOMConversionOutput|null => {
111 let category: CalloutCategory = 'info';
112 const categories: CalloutCategory[] = ['info', 'success', 'warning', 'danger'];
114 for (const c of categories) {
115 if (element.classList.contains(c)) {
121 const node = new CalloutNode(category);
122 setCommonBlockPropsFromElement(element, node);
136 exportJSON(): SerializedCalloutNode {
138 ...super.exportJSON(),
141 category: this.__category,
143 alignment: this.__alignment,
147 static importJSON(serializedNode: SerializedCalloutNode): CalloutNode {
148 const node = $createCalloutNode(serializedNode.category);
149 node.setId(serializedNode.id);
150 node.setAlignment(serializedNode.alignment);
156 export function $createCalloutNode(category: CalloutCategory = 'info') {
157 return new CalloutNode(category);
160 export function $isCalloutNode(node: LexicalNode | null | undefined): node is CalloutNode {
161 return node instanceof CalloutNode;
164 export function $isCalloutNodeOfCategory(node: LexicalNode | null | undefined, category: CalloutCategory = 'info') {
165 return node instanceof CalloutNode && (node as CalloutNode).getCategory() === category;