Spaces:
Runtime error
Runtime error
| /** | |
| * Copyright (c) Meta Platforms, Inc. and affiliates. | |
| * | |
| * This source code is licensed under the Chameleon License found in the | |
| * LICENSE file in the root directory of this source tree. | |
| * | |
| */ | |
| import type { | |
| DOMConversionMap, | |
| DOMConversionOutput, | |
| DOMExportOutput, | |
| EditorConfig, | |
| LexicalEditor, | |
| LexicalNode, | |
| NodeKey, | |
| SerializedEditor, | |
| SerializedLexicalNode, | |
| Spread, | |
| } from "lexical"; | |
| import { $applyNodeReplacement, createEditor, DecoratorNode } from "lexical"; | |
| import * as React from "react"; | |
| import { Suspense } from "react"; | |
| const ImageComponent = React.lazy( | |
| () => import("./ImageComponent"), | |
| ); | |
| export interface ImagePayload { | |
| altText: string; | |
| caption?: LexicalEditor; | |
| height?: number; | |
| key?: NodeKey; | |
| maxWidth?: number; | |
| showCaption?: boolean; | |
| src: string; | |
| width?: number; | |
| } | |
| function convertImageElement(domNode: Node): null | DOMConversionOutput { | |
| if (domNode instanceof HTMLImageElement) { | |
| const { alt: altText, src, width, height } = domNode; | |
| const node = $createImageNode({ altText, height, src, width }); | |
| return { node }; | |
| } | |
| return null; | |
| } | |
| export type SerializedImageNode = Spread< | |
| { | |
| altText: string; | |
| caption: SerializedEditor; | |
| height?: number; | |
| maxWidth: number; | |
| showCaption: boolean; | |
| src: string; | |
| width?: number; | |
| }, | |
| SerializedLexicalNode | |
| >; | |
| export class ImageNode extends DecoratorNode<JSX.Element> { | |
| __src: string; | |
| __altText: string; | |
| __width: "inherit" | number; | |
| __height: "inherit" | number; | |
| __maxWidth: number; | |
| __showCaption: boolean; | |
| __caption: LexicalEditor; | |
| static getType(): string { | |
| return "image"; | |
| } | |
| static clone(node: ImageNode): ImageNode { | |
| return new ImageNode( | |
| node.__src, | |
| node.__altText, | |
| node.__maxWidth, | |
| node.__width, | |
| node.__height, | |
| node.__showCaption, | |
| node.__caption, | |
| node.__key, | |
| ); | |
| } | |
| static importJSON(serializedNode: SerializedImageNode): ImageNode { | |
| const { altText, height, width, maxWidth, caption, src, showCaption } = | |
| serializedNode; | |
| const node = $createImageNode({ | |
| altText, | |
| height, | |
| maxWidth, | |
| showCaption, | |
| src, | |
| width, | |
| }); | |
| const nestedEditor = node.__caption; | |
| const editorState = nestedEditor.parseEditorState(caption.editorState); | |
| if (!editorState.isEmpty()) { | |
| nestedEditor.setEditorState(editorState); | |
| } | |
| return node; | |
| } | |
| exportDOM(): DOMExportOutput { | |
| const element = document.createElement("img"); | |
| element.setAttribute("src", this.__src); | |
| element.setAttribute("alt", this.__altText); | |
| element.setAttribute("width", this.__width.toString()); | |
| element.setAttribute("height", this.__height.toString()); | |
| return { element }; | |
| } | |
| static importDOM(): DOMConversionMap | null { | |
| return { | |
| img: () => ({ | |
| conversion: convertImageElement, | |
| priority: 0, | |
| }), | |
| }; | |
| } | |
| constructor( | |
| src: string, | |
| altText: string, | |
| maxWidth: number, | |
| width?: "inherit" | number, | |
| height?: "inherit" | number, | |
| showCaption?: boolean, | |
| caption?: LexicalEditor, | |
| key?: NodeKey, | |
| ) { | |
| super(key); | |
| this.__src = src; | |
| this.__altText = altText; | |
| this.__maxWidth = maxWidth; | |
| this.__width = width || "inherit"; | |
| this.__height = height || "inherit"; | |
| this.__showCaption = showCaption || false; | |
| this.__caption = caption || createEditor(); | |
| } | |
| exportJSON(): SerializedImageNode { | |
| return { | |
| altText: this.getAltText(), | |
| caption: this.__caption.toJSON(), | |
| height: this.__height === "inherit" ? 0 : this.__height, | |
| maxWidth: this.__maxWidth, | |
| showCaption: this.__showCaption, | |
| src: this.getSrc(), | |
| type: "image", | |
| version: 1, | |
| width: this.__width === "inherit" ? 0 : this.__width, | |
| }; | |
| } | |
| setWidthAndHeight( | |
| width: "inherit" | number, | |
| height: "inherit" | number, | |
| ): void { | |
| const writable = this.getWritable(); | |
| writable.__width = width; | |
| writable.__height = height; | |
| } | |
| setShowCaption(showCaption: boolean): void { | |
| const writable = this.getWritable(); | |
| writable.__showCaption = showCaption; | |
| } | |
| // View | |
| createDOM(config: EditorConfig): HTMLElement { | |
| const span = document.createElement("span"); | |
| const theme = config.theme; | |
| const className = theme.image; | |
| if (className !== undefined) { | |
| span.className = className; | |
| } | |
| return span; | |
| } | |
| updateDOM(): false { | |
| return false; | |
| } | |
| getSrc(): string { | |
| return this.__src; | |
| } | |
| getAltText(): string { | |
| return this.__altText; | |
| } | |
| decorate(): JSX.Element { | |
| return ( | |
| <Suspense fallback={null}> | |
| <ImageComponent | |
| src={this.__src} | |
| altText={this.__altText} | |
| width={this.__width} | |
| height={this.__height} | |
| maxWidth={this.__maxWidth} | |
| nodeKey={this.getKey()} | |
| showCaption={this.__showCaption} | |
| caption={this.__caption} | |
| /> | |
| </Suspense> | |
| ); | |
| } | |
| } | |
| export function $createImageNode({ | |
| altText, | |
| height, | |
| maxWidth = 500, | |
| src, | |
| width, | |
| showCaption, | |
| caption, | |
| key, | |
| }: ImagePayload): ImageNode { | |
| return $applyNodeReplacement( | |
| new ImageNode( | |
| src, | |
| altText, | |
| maxWidth, | |
| width, | |
| height, | |
| showCaption, | |
| caption, | |
| key, | |
| ), | |
| ); | |
| } | |
| export function $isImageNode( | |
| node: LexicalNode | null | undefined, | |
| ): node is ImageNode { | |
| return node instanceof ImageNode; | |
| } | |