X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/a74e04141c7eee5daeb6a1a2e58de7c704b5020c..refs/pull/5312/head:/resources/js/wysiwyg/nodes/image.ts diff --git a/resources/js/wysiwyg/nodes/image.ts b/resources/js/wysiwyg/nodes/image.ts index 1e2cbd83c..b6d362b62 100644 --- a/resources/js/wysiwyg/nodes/image.ts +++ b/resources/js/wysiwyg/nodes/image.ts @@ -1,14 +1,14 @@ import { - DecoratorNode, DOMConversion, DOMConversionMap, - DOMConversionOutput, + DOMConversionOutput, ElementNode, LexicalEditor, LexicalNode, - SerializedLexicalNode, Spread } from "lexical"; import type {EditorConfig} from "lexical/LexicalEditor"; -import {el} from "../helpers"; +import {CommonBlockAlignment, extractAlignmentFromElement} from "./_common"; +import {$selectSingleNode} from "../utils/selection"; +import {SerializedElementNode} from "lexical/nodes/LexicalElementNode"; export interface ImageNodeOptions { alt?: string; @@ -21,25 +21,28 @@ export type SerializedImageNode = Spread<{ alt: string; width: number; height: number; -}, SerializedLexicalNode> + alignment: CommonBlockAlignment; +}, SerializedElementNode> -export class ImageNode extends DecoratorNode { +export class ImageNode extends ElementNode { __src: string = ''; __alt: string = ''; __width: number = 0; __height: number = 0; - // TODO - Alignment + __alignment: CommonBlockAlignment = ''; static getType(): string { return 'image'; } static clone(node: ImageNode): ImageNode { - return new ImageNode(node.__src, { + const newNode = new ImageNode(node.__src, { alt: node.__alt, width: node.__width, height: node.__height, - }); + }, node.__key); + newNode.__alignment = node.__alignment; + return newNode; } constructor(src: string, options: ImageNodeOptions, key?: string) { @@ -56,6 +59,16 @@ export class ImageNode extends DecoratorNode { } } + setSrc(src: string): void { + const self = this.getWritable(); + self.__src = src; + } + + getSrc(): string { + const self = this.getLatest(); + return self.__src; + } + setAltText(altText: string): void { const self = this.getWritable(); self.__alt = altText; @@ -86,21 +99,23 @@ export class ImageNode extends DecoratorNode { return self.__width; } - isInline(): boolean { - return true; + setAlignment(alignment: CommonBlockAlignment) { + const self = this.getWritable(); + self.__alignment = alignment; + } + + getAlignment(): CommonBlockAlignment { + const self = this.getLatest(); + return self.__alignment; } - decorate(editor: LexicalEditor, config: EditorConfig): HTMLElement { - console.log('decorate!'); - return el('div', { - class: 'editor-image-decorator', - }, ['decoration!!!']); + isInline(): boolean { + return true; } createDOM(_config: EditorConfig, _editor: LexicalEditor) { const element = document.createElement('img'); element.setAttribute('src', this.__src); - element.textContent if (this.__width) { element.setAttribute('width', String(this.__width)); @@ -111,14 +126,58 @@ export class ImageNode extends DecoratorNode { if (this.__alt) { element.setAttribute('alt', this.__alt); } - return el('span', {class: 'editor-image-wrap'}, [ - element, - ]); + + if (this.__alignment) { + element.classList.add('align-' + this.__alignment); + } + + element.addEventListener('click', e => { + _editor.update(() => { + $selectSingleNode(this); + }); + }); + + return element; } - updateDOM(prevNode: unknown, dom: HTMLElement) { - // Returning false tells Lexical that this node does not need its - // DOM element replacing with a new copy from createDOM. + updateDOM(prevNode: ImageNode, dom: HTMLElement) { + if (prevNode.__src !== this.__src) { + dom.setAttribute('src', this.__src); + } + + if (prevNode.__width !== this.__width) { + if (this.__width) { + dom.setAttribute('width', String(this.__width)); + } else { + dom.removeAttribute('width'); + } + } + + if (prevNode.__height !== this.__height) { + if (this.__height) { + dom.setAttribute('height', String(this.__height)); + } else { + dom.removeAttribute('height'); + } + } + + if (prevNode.__alt !== this.__alt) { + if (this.__alt) { + dom.setAttribute('alt', String(this.__alt)); + } else { + dom.removeAttribute('alt'); + } + } + + if (prevNode.__alignment !== this.__alignment) { + if (prevNode.__alignment) { + dom.classList.remove('align-' + prevNode.__alignment); + } + if (this.__alignment) { + dom.classList.add('align-' + this.__alignment); + } + } + return false; } @@ -135,9 +194,10 @@ export class ImageNode extends DecoratorNode { width: Number.parseInt(element.getAttribute('width') || '0'), } - return { - node: new ImageNode(src, options), - }; + const node = new ImageNode(src, options); + node.setAlignment(extractAlignmentFromElement(element)); + + return { node }; }, priority: 3, }; @@ -147,21 +207,25 @@ export class ImageNode extends DecoratorNode { exportJSON(): SerializedImageNode { return { + ...super.exportJSON(), type: 'image', version: 1, src: this.__src, alt: this.__alt, height: this.__height, - width: this.__width + width: this.__width, + alignment: this.__alignment, }; } static importJSON(serializedNode: SerializedImageNode): ImageNode { - return $createImageNode(serializedNode.src, { + const node = $createImageNode(serializedNode.src, { alt: serializedNode.alt, width: serializedNode.width, height: serializedNode.height, }); + node.setAlignment(serializedNode.alignment); + return node; } }