mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-08-09 10:22:51 +03:00
Lexical: Reorganised custom node code into lexical codebase
Also cleaned up old unused imports.
This commit is contained in:
238
resources/js/wysiwyg/lexical/rich-text/LexicalImageNode.ts
Normal file
238
resources/js/wysiwyg/lexical/rich-text/LexicalImageNode.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
import {
|
||||
DOMConversion,
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput, ElementNode,
|
||||
LexicalEditor, LexicalNode,
|
||||
Spread
|
||||
} from "lexical";
|
||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||
import {CommonBlockAlignment, extractAlignmentFromElement} from "lexical/nodes/common";
|
||||
import {$selectSingleNode} from "../../utils/selection";
|
||||
import {SerializedElementNode} from "lexical/nodes/LexicalElementNode";
|
||||
|
||||
export interface ImageNodeOptions {
|
||||
alt?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export type SerializedImageNode = Spread<{
|
||||
src: string;
|
||||
alt: string;
|
||||
width: number;
|
||||
height: number;
|
||||
alignment: CommonBlockAlignment;
|
||||
}, SerializedElementNode>
|
||||
|
||||
export class ImageNode extends ElementNode {
|
||||
__src: string = '';
|
||||
__alt: string = '';
|
||||
__width: number = 0;
|
||||
__height: number = 0;
|
||||
__alignment: CommonBlockAlignment = '';
|
||||
|
||||
static getType(): string {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
static clone(node: ImageNode): ImageNode {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
setAlignment(alignment: CommonBlockAlignment) {
|
||||
const self = this.getWritable();
|
||||
self.__alignment = alignment;
|
||||
}
|
||||
|
||||
getAlignment(): CommonBlockAlignment {
|
||||
const self = this.getLatest();
|
||||
return self.__alignment;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (this.__alignment) {
|
||||
element.classList.add('align-' + this.__alignment);
|
||||
}
|
||||
|
||||
element.addEventListener('click', e => {
|
||||
_editor.update(() => {
|
||||
$selectSingleNode(this);
|
||||
});
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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'),
|
||||
}
|
||||
|
||||
const node = new ImageNode(src, options);
|
||||
node.setAlignment(extractAlignmentFromElement(element));
|
||||
|
||||
return { node };
|
||||
},
|
||||
priority: 3,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
exportJSON(): SerializedImageNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
type: 'image',
|
||||
version: 1,
|
||||
src: this.__src,
|
||||
alt: this.__alt,
|
||||
height: this.__height,
|
||||
width: this.__width,
|
||||
alignment: this.__alignment,
|
||||
};
|
||||
}
|
||||
|
||||
static importJSON(serializedNode: SerializedImageNode): ImageNode {
|
||||
const node = $createImageNode(serializedNode.src, {
|
||||
alt: serializedNode.alt,
|
||||
width: serializedNode.width,
|
||||
height: serializedNode.height,
|
||||
});
|
||||
node.setAlignment(serializedNode.alignment);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
export function $createImageNode(src: string, options: ImageNodeOptions = {}): ImageNode {
|
||||
return new ImageNode(src, options);
|
||||
}
|
||||
|
||||
export function $isImageNode(node: LexicalNode | null | undefined) {
|
||||
return node instanceof ImageNode;
|
||||
}
|
Reference in New Issue
Block a user