| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |     DOMConversion, | 
					
						
							|  |  |  |     DOMConversionMap, | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |     DOMConversionOutput, ElementNode, | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |     LexicalEditor, LexicalNode, | 
					
						
							|  |  |  |     Spread | 
					
						
							|  |  |  | } from "lexical"; | 
					
						
							|  |  |  | import type {EditorConfig} from "lexical/LexicalEditor"; | 
					
						
							| 
									
										
										
										
											2024-12-05 02:53:59 +08:00
										 |  |  | import {CommonBlockAlignment, extractAlignmentFromElement} from "lexical/nodes/common"; | 
					
						
							|  |  |  | import {$selectSingleNode} from "../../utils/selection"; | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  | import {SerializedElementNode} from "lexical/nodes/LexicalElementNode"; | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export interface ImageNodeOptions { | 
					
						
							|  |  |  |     alt?: string; | 
					
						
							|  |  |  |     width?: number; | 
					
						
							|  |  |  |     height?: number; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export type SerializedImageNode = Spread<{ | 
					
						
							|  |  |  |     src: string; | 
					
						
							|  |  |  |     alt: string; | 
					
						
							|  |  |  |     width: number; | 
					
						
							|  |  |  |     height: number; | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |     alignment: CommonBlockAlignment; | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  | }, SerializedElementNode> | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  | export class ImageNode extends ElementNode { | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |     __src: string = ''; | 
					
						
							|  |  |  |     __alt: string = ''; | 
					
						
							|  |  |  |     __width: number = 0; | 
					
						
							|  |  |  |     __height: number = 0; | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |     __alignment: CommonBlockAlignment = ''; | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     static getType(): string { | 
					
						
							|  |  |  |         return 'image'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static clone(node: ImageNode): ImageNode { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |         const newNode = new ImageNode(node.__src, { | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |             alt: node.__alt, | 
					
						
							|  |  |  |             width: node.__width, | 
					
						
							|  |  |  |             height: node.__height, | 
					
						
							| 
									
										
										
										
											2024-09-08 22:54:59 +08:00
										 |  |  |         }, node.__key); | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |         newNode.__alignment = node.__alignment; | 
					
						
							|  |  |  |         return newNode; | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(src: string, options: ImageNodeOptions, key?: string) { | 
					
						
							|  |  |  |         super(key); | 
					
						
							|  |  |  |         this.__src = src; | 
					
						
							|  |  |  |         if (options.alt) { | 
					
						
							|  |  |  |             this.__alt = options.alt; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (options.width) { | 
					
						
							|  |  |  |             this.__width = options.width; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (options.height) { | 
					
						
							|  |  |  |             this.__height = options.height; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-06 01:43:42 +08:00
										 |  |  |     setSrc(src: string): void { | 
					
						
							|  |  |  |         const self = this.getWritable(); | 
					
						
							|  |  |  |         self.__src = src; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getSrc(): string { | 
					
						
							|  |  |  |         const self = this.getLatest(); | 
					
						
							|  |  |  |         return self.__src; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |     setAltText(altText: string): void { | 
					
						
							|  |  |  |         const self = this.getWritable(); | 
					
						
							|  |  |  |         self.__alt = altText; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getAltText(): string { | 
					
						
							|  |  |  |         const self = this.getLatest(); | 
					
						
							|  |  |  |         return self.__alt; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setHeight(height: number): void { | 
					
						
							|  |  |  |         const self = this.getWritable(); | 
					
						
							|  |  |  |         self.__height = height; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getHeight(): number { | 
					
						
							|  |  |  |         const self = this.getLatest(); | 
					
						
							|  |  |  |         return self.__height; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setWidth(width: number): void { | 
					
						
							|  |  |  |         const self = this.getWritable(); | 
					
						
							|  |  |  |         self.__width = width; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getWidth(): number { | 
					
						
							|  |  |  |         const self = this.getLatest(); | 
					
						
							|  |  |  |         return self.__width; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |     setAlignment(alignment: CommonBlockAlignment) { | 
					
						
							|  |  |  |         const self = this.getWritable(); | 
					
						
							|  |  |  |         self.__alignment = alignment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getAlignment(): CommonBlockAlignment { | 
					
						
							|  |  |  |         const self = this.getLatest(); | 
					
						
							|  |  |  |         return self.__alignment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |     isInline(): boolean { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     createDOM(_config: EditorConfig, _editor: LexicalEditor) { | 
					
						
							|  |  |  |         const element = document.createElement('img'); | 
					
						
							|  |  |  |         element.setAttribute('src', this.__src); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this.__width) { | 
					
						
							|  |  |  |             element.setAttribute('width', String(this.__width)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (this.__height) { | 
					
						
							|  |  |  |             element.setAttribute('height', String(this.__height)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (this.__alt) { | 
					
						
							|  |  |  |             element.setAttribute('alt', this.__alt); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (this.__alignment) { | 
					
						
							|  |  |  |             element.classList.add('align-' + this.__alignment); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |         element.addEventListener('click', e => { | 
					
						
							|  |  |  |             _editor.update(() => { | 
					
						
							|  |  |  |                 $selectSingleNode(this); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return element; | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |     updateDOM(prevNode: ImageNode, dom: HTMLElement) { | 
					
						
							|  |  |  |         if (prevNode.__src !== this.__src) { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |             dom.setAttribute('src', this.__src); | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (prevNode.__width !== this.__width) { | 
					
						
							|  |  |  |             if (this.__width) { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.setAttribute('width', String(this.__width)); | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.removeAttribute('width'); | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (prevNode.__height !== this.__height) { | 
					
						
							|  |  |  |             if (this.__height) { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.setAttribute('height', String(this.__height)); | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.removeAttribute('height'); | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (prevNode.__alt !== this.__alt) { | 
					
						
							|  |  |  |             if (this.__alt) { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.setAttribute('alt', String(this.__alt)); | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.removeAttribute('alt'); | 
					
						
							| 
									
										
										
										
											2024-06-05 20:04:49 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |         if (prevNode.__alignment !== this.__alignment) { | 
					
						
							|  |  |  |             if (prevNode.__alignment) { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.classList.remove('align-' + prevNode.__alignment); | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |             if (this.__alignment) { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |                 dom.classList.add('align-' + this.__alignment); | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static importDOM(): DOMConversionMap|null { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             img(node: HTMLElement): DOMConversion|null { | 
					
						
							|  |  |  |                 return { | 
					
						
							|  |  |  |                     conversion: (element: HTMLElement): DOMConversionOutput|null => { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         const src = element.getAttribute('src') || ''; | 
					
						
							|  |  |  |                         const options: ImageNodeOptions = { | 
					
						
							|  |  |  |                             alt: element.getAttribute('alt') || '', | 
					
						
							|  |  |  |                             height: Number.parseInt(element.getAttribute('height') || '0'), | 
					
						
							|  |  |  |                             width: Number.parseInt(element.getAttribute('width') || '0'), | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |                         const node = new ImageNode(src, options); | 
					
						
							|  |  |  |                         node.setAlignment(extractAlignmentFromElement(element)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         return { node }; | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |                     }, | 
					
						
							|  |  |  |                     priority: 3, | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     exportJSON(): SerializedImageNode { | 
					
						
							|  |  |  |         return { | 
					
						
							| 
									
										
										
										
											2024-09-08 01:39:58 +08:00
										 |  |  |             ...super.exportJSON(), | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |             type: 'image', | 
					
						
							|  |  |  |             version: 1, | 
					
						
							|  |  |  |             src: this.__src, | 
					
						
							|  |  |  |             alt: this.__alt, | 
					
						
							|  |  |  |             height: this.__height, | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |             width: this.__width, | 
					
						
							|  |  |  |             alignment: this.__alignment, | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static importJSON(serializedNode: SerializedImageNode): ImageNode { | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |         const node = $createImageNode(serializedNode.src, { | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |             alt: serializedNode.alt, | 
					
						
							|  |  |  |             width: serializedNode.width, | 
					
						
							|  |  |  |             height: serializedNode.height, | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2024-09-06 21:07:10 +08:00
										 |  |  |         node.setAlignment(serializedNode.alignment); | 
					
						
							|  |  |  |         return node; | 
					
						
							| 
									
										
										
										
											2024-06-03 23:56:31 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function $createImageNode(src: string, options: ImageNodeOptions = {}): ImageNode { | 
					
						
							|  |  |  |     return new ImageNode(src, options); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function $isImageNode(node: LexicalNode | null | undefined) { | 
					
						
							|  |  |  |     return node instanceof ImageNode; | 
					
						
							|  |  |  | } |