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 type {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {el} from "../helpers";
|
import {el} from "../helpers";
|
||||||
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
||||||
import {code} from "../ui/defaults/button-definitions";
|
|
||||||
|
|
||||||
export type SerializedCodeBlockNode = Spread<{
|
export type SerializedCodeBlockNode = Spread<{
|
||||||
language: string;
|
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 {CustomTableNode} from "./custom-table";
|
||||||
import {HorizontalRuleNode} from "./horizontal-rule";
|
import {HorizontalRuleNode} from "./horizontal-rule";
|
||||||
import {CodeBlockNode} from "./code-block";
|
import {CodeBlockNode} from "./code-block";
|
||||||
|
import {DiagramNode} from "./diagram";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the nodes for lexical.
|
* Load the nodes for lexical.
|
||||||
@ -28,6 +29,7 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
|
|||||||
HorizontalRuleNode,
|
HorizontalRuleNode,
|
||||||
DetailsNode, SummaryNode,
|
DetailsNode, SummaryNode,
|
||||||
CodeBlockNode,
|
CodeBlockNode,
|
||||||
|
DiagramNode,
|
||||||
CustomParagraphNode,
|
CustomParagraphNode,
|
||||||
LinkNode,
|
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 {ImageDecorator} from "./decorators/image";
|
||||||
import {EditorUiContext} from "./framework/core";
|
import {EditorUiContext} from "./framework/core";
|
||||||
import {CodeBlockDecorator} from "./decorators/code-block";
|
import {CodeBlockDecorator} from "./decorators/code-block";
|
||||||
|
import {DiagramDecorator} from "./decorators/diagram";
|
||||||
|
|
||||||
export function buildEditorUI(container: HTMLElement, element: HTMLElement, editor: LexicalEditor) {
|
export function buildEditorUI(container: HTMLElement, element: HTMLElement, editor: LexicalEditor) {
|
||||||
const manager = new EditorUIManager();
|
const manager = new EditorUIManager();
|
||||||
@ -51,4 +52,5 @@ export function buildEditorUI(container: HTMLElement, element: HTMLElement, edit
|
|||||||
// Register image decorator listener
|
// Register image decorator listener
|
||||||
manager.registerDecoratorType('image', ImageDecorator);
|
manager.registerDecoratorType('image', ImageDecorator);
|
||||||
manager.registerDecoratorType('code', CodeBlockDecorator);
|
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. */
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
/* JavaScript Support */
|
/* 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. */
|
// "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'. */
|
// "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