mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Started diagram support
This commit is contained in:
parent
d0a5a5ef37
commit
feca1f0502
@ -10,7 +10,6 @@ import {
|
||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||
import {el} from "../helpers";
|
||||
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
||||
import {code} from "../ui/defaults/button-definitions";
|
||||
|
||||
export type SerializedCodeBlockNode = Spread<{
|
||||
language: string;
|
||||
|
158
resources/js/wysiwyg/nodes/diagram.ts
Normal file
158
resources/js/wysiwyg/nodes/diagram.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import {
|
||||
DecoratorNode,
|
||||
DOMConversion,
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput,
|
||||
LexicalEditor, LexicalNode,
|
||||
SerializedLexicalNode,
|
||||
Spread
|
||||
} from "lexical";
|
||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||
import {el} from "../helpers";
|
||||
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
||||
|
||||
export type SerializedDiagramNode = Spread<{
|
||||
id: string;
|
||||
drawingId: string;
|
||||
drawingUrl: string;
|
||||
}, SerializedLexicalNode>
|
||||
|
||||
export class DiagramNode extends DecoratorNode<EditorDecoratorAdapter> {
|
||||
__id: string = '';
|
||||
__drawingId: string = '';
|
||||
__drawingUrl: string = '';
|
||||
|
||||
static getType(): string {
|
||||
return 'diagram';
|
||||
}
|
||||
|
||||
static clone(node: DiagramNode): DiagramNode {
|
||||
return new DiagramNode(node.__drawingId, node.__drawingUrl);
|
||||
}
|
||||
|
||||
constructor(drawingId: string, drawingUrl: string, key?: string) {
|
||||
super(key);
|
||||
this.__drawingId = drawingId;
|
||||
this.__drawingUrl = drawingUrl;
|
||||
}
|
||||
|
||||
setDrawingIdAndUrl(drawingId: string, drawingUrl: string): void {
|
||||
const self = this.getWritable();
|
||||
self.__drawingUrl = drawingUrl;
|
||||
self.__drawingId = drawingId;
|
||||
}
|
||||
|
||||
getDrawingIdAndUrl(): {id: string, url: string} {
|
||||
const self = this.getLatest();
|
||||
return {
|
||||
id: self.__drawingUrl,
|
||||
url: self.__drawingUrl,
|
||||
};
|
||||
}
|
||||
|
||||
setId(id: string) {
|
||||
const self = this.getWritable();
|
||||
self.__id = id;
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
const self = this.getLatest();
|
||||
return self.__id;
|
||||
}
|
||||
|
||||
decorate(editor: LexicalEditor, config: EditorConfig): EditorDecoratorAdapter {
|
||||
return {
|
||||
type: 'diagram',
|
||||
getNode: () => this,
|
||||
};
|
||||
}
|
||||
|
||||
isInline(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
isIsolated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
createDOM(_config: EditorConfig, _editor: LexicalEditor) {
|
||||
return el('div', {
|
||||
id: this.__id || null,
|
||||
'drawio-diagram': this.__drawingId,
|
||||
}, [
|
||||
el('img', {src: this.__drawingUrl}),
|
||||
]);
|
||||
}
|
||||
|
||||
updateDOM(prevNode: DiagramNode, dom: HTMLElement) {
|
||||
const img = dom.querySelector('img');
|
||||
if (!img) return false;
|
||||
|
||||
if (prevNode.__id !== this.__id) {
|
||||
dom.setAttribute('id', this.__id);
|
||||
}
|
||||
|
||||
if (prevNode.__drawingUrl !== this.__drawingUrl) {
|
||||
img.setAttribute('src', this.__drawingUrl);
|
||||
}
|
||||
|
||||
if (prevNode.__drawingId !== this.__drawingId) {
|
||||
dom.setAttribute('drawio-diagram', this.__drawingId);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static importDOM(): DOMConversionMap|null {
|
||||
return {
|
||||
div(node: HTMLElement): DOMConversion|null {
|
||||
|
||||
if (!node.hasAttribute('drawio-diagram')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
conversion: (element: HTMLElement): DOMConversionOutput|null => {
|
||||
|
||||
const img = element.querySelector('img');
|
||||
const drawingUrl = img?.getAttribute('src') || '';
|
||||
const drawingId = element.getAttribute('drawio-diagram') || '';
|
||||
|
||||
return {
|
||||
node: $createDiagramNode(drawingId, drawingUrl),
|
||||
};
|
||||
},
|
||||
priority: 3,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
exportJSON(): SerializedDiagramNode {
|
||||
return {
|
||||
type: 'diagram',
|
||||
version: 1,
|
||||
id: this.__id,
|
||||
drawingId: this.__drawingId,
|
||||
drawingUrl: this.__drawingUrl,
|
||||
};
|
||||
}
|
||||
|
||||
static importJSON(serializedNode: SerializedDiagramNode): DiagramNode {
|
||||
const node = $createDiagramNode(serializedNode.drawingId, serializedNode.drawingUrl);
|
||||
node.setId(serializedNode.id || '');
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
export function $createDiagramNode(drawingId: string = '', drawingUrl: string = ''): DiagramNode {
|
||||
return new DiagramNode(drawingId, drawingUrl);
|
||||
}
|
||||
|
||||
export function $isDiagramNode(node: LexicalNode | null | undefined) {
|
||||
return node instanceof DiagramNode;
|
||||
}
|
||||
|
||||
export function $openDrawingEditorForNode(editor: LexicalEditor, node: DiagramNode): void {
|
||||
// Todo
|
||||
}
|
@ -10,6 +10,7 @@ import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
|
||||
import {CustomTableNode} from "./custom-table";
|
||||
import {HorizontalRuleNode} from "./horizontal-rule";
|
||||
import {CodeBlockNode} from "./code-block";
|
||||
import {DiagramNode} from "./diagram";
|
||||
|
||||
/**
|
||||
* Load the nodes for lexical.
|
||||
@ -28,6 +29,7 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
|
||||
HorizontalRuleNode,
|
||||
DetailsNode, SummaryNode,
|
||||
CodeBlockNode,
|
||||
DiagramNode,
|
||||
CustomParagraphNode,
|
||||
LinkNode,
|
||||
{
|
||||
|
25
resources/js/wysiwyg/ui/decorators/diagram.ts
Normal file
25
resources/js/wysiwyg/ui/decorators/diagram.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {EditorDecorator} from "../framework/decorator";
|
||||
import {EditorUiContext} from "../framework/core";
|
||||
|
||||
|
||||
export class DiagramDecorator extends EditorDecorator {
|
||||
protected completedSetup: boolean = false;
|
||||
|
||||
setup(context: EditorUiContext, element: HTMLElement) {
|
||||
//
|
||||
|
||||
this.completedSetup = true;
|
||||
}
|
||||
|
||||
update() {
|
||||
//
|
||||
}
|
||||
|
||||
render(context: EditorUiContext, element: HTMLElement): void {
|
||||
if (this.completedSetup) {
|
||||
this.update();
|
||||
} else {
|
||||
this.setup(context, element);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import {image as imageFormDefinition, link as linkFormDefinition, source as sour
|
||||
import {ImageDecorator} from "./decorators/image";
|
||||
import {EditorUiContext} from "./framework/core";
|
||||
import {CodeBlockDecorator} from "./decorators/code-block";
|
||||
import {DiagramDecorator} from "./decorators/diagram";
|
||||
|
||||
export function buildEditorUI(container: HTMLElement, element: HTMLElement, editor: LexicalEditor) {
|
||||
const manager = new EditorUIManager();
|
||||
@ -51,4 +52,5 @@ export function buildEditorUI(container: HTMLElement, element: HTMLElement, edit
|
||||
// Register image decorator listener
|
||||
manager.registerDecoratorType('image', ImageDecorator);
|
||||
manager.registerDecoratorType('code', CodeBlockDecorator);
|
||||
manager.registerDecoratorType('diagram', DiagramDecorator);
|
||||
}
|
@ -46,7 +46,7 @@
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user