MentionNode.ts (3583B)
1 import type { Spread } from "lexical"; 2 3 import { 4 DOMConversionMap, 5 DOMConversionOutput, 6 DOMExportOutput, 7 EditorConfig, 8 LexicalNode, 9 NodeKey, 10 SerializedTextNode, 11 TextNode 12 } from "lexical"; 13 import { IRoomMemberEvent } from "../../../../../app/sdk/api/events"; 14 15 export type SerializedMentionNode = Spread< 16 { 17 mentionEvent: IRoomMemberEvent; 18 type: "mention"; 19 version: 1; 20 }, 21 SerializedTextNode 22 >; 23 24 function convertMentionElement( 25 domNode: HTMLElement 26 ): DOMConversionOutput | null { 27 const textContent = domNode.textContent; 28 const mxid = domNode.getAttribute("data-lexical-mention-mxid"); 29 30 if (textContent && mxid) { 31 const node = $createMentionNode({ 32 state_key: mxid, 33 content: { 34 displayname: textContent, 35 } 36 } as IRoomMemberEvent); 37 return { 38 node 39 }; 40 } 41 42 return null; 43 } 44 45 export class MentionNode extends TextNode { 46 __mention: IRoomMemberEvent; 47 48 static getType(): string { 49 return "mention"; 50 } 51 52 static clone(node: MentionNode): MentionNode { 53 return new MentionNode(node.__mention, node.__text, node.__key); 54 } 55 static importJSON(serializedNode: SerializedMentionNode): MentionNode { 56 const node = $createMentionNode(serializedNode.mentionEvent); 57 node.setTextContent(serializedNode.text); 58 node.setFormat(serializedNode.format); 59 node.setDetail(serializedNode.detail); 60 node.setMode(serializedNode.mode); 61 node.setStyle(serializedNode.style); 62 return node; 63 } 64 65 constructor(memberEvent: IRoomMemberEvent, text?: string, key?: NodeKey) { 66 super(text ?? (memberEvent.content.displayname || memberEvent.state_key), key); 67 this.__mention = memberEvent; 68 } 69 70 exportJSON(): SerializedMentionNode { 71 return { 72 ...super.exportJSON(), 73 mentionEvent: this.__mention, 74 type: "mention", 75 version: 1 76 }; 77 } 78 79 createDOM(config: EditorConfig): HTMLElement { 80 const dom = super.createDOM(config); 81 dom.className = "mention"; 82 return dom; 83 } 84 85 exportDOM(): DOMExportOutput { 86 const element = document.createElement("a"); 87 element.setAttribute("href", `matrix:u/${this.__mention.state_key.replace("@", "")}`); 88 element.setAttribute("data-lexical-mention", "true"); 89 element.setAttribute("data-lexical-mention-mxid", this.__mention.state_key); 90 element.textContent = this.__text; 91 return { element }; 92 } 93 94 isSegmented(): false { 95 return false; 96 } 97 98 static importDOM(): DOMConversionMap | null { 99 return { 100 a: (domNode: HTMLElement) => { 101 if (!domNode.hasAttribute("data-lexical-mention") && !domNode.hasAttribute("data-lexical-mention-mxid")) { 102 return null; 103 } 104 return { 105 conversion: convertMentionElement, 106 priority: 1 107 }; 108 } 109 }; 110 } 111 112 isTextEntity(): true { 113 return true; 114 } 115 116 isToken(): true { 117 return true; 118 } 119 } 120 121 export function $createMentionNode(memberEvent: IRoomMemberEvent,): MentionNode { 122 const mentionNode = new MentionNode(memberEvent); 123 mentionNode.setMode("segmented").toggleDirectionless(); 124 return mentionNode; 125 } 126 127 export function $isMentionNode( 128 node: LexicalNode | null | undefined 129 ): node is MentionNode { 130 return node instanceof MentionNode; 131 }