]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/utils/diagrams.ts
Lexical: Changed table esacpe handling
[bookstack] / resources / js / wysiwyg / utils / diagrams.ts
1 import {$insertNodes, LexicalEditor, LexicalNode} from "lexical";
2 import {HttpError} from "../../services/http";
3 import {EditorUiContext} from "../ui/framework/core";
4 import * as DrawIO from "../../services/drawio";
5 import {$createDiagramNode, DiagramNode} from "@lexical/rich-text/LexicalDiagramNode";
6 import {ImageManager} from "../../components";
7 import {EditorImageData} from "./images";
8 import {$getNodeFromSelection, getLastSelection} from "./selection";
9
10 export function $isDiagramNode(node: LexicalNode | null | undefined): node is DiagramNode {
11     return node instanceof DiagramNode;
12 }
13
14 function handleUploadError(error: HttpError, context: EditorUiContext): void {
15     if (error.status === 413) {
16         window.$events.emit('error', context.options.translations.serverUploadLimitText || '');
17     } else {
18         window.$events.emit('error', context.options.translations.imageUploadErrorText || '');
19     }
20     console.error(error);
21 }
22
23 async function loadDiagramIdFromNode(editor: LexicalEditor, node: DiagramNode): Promise<string> {
24     const drawingId = await new Promise<string>((res, rej) => {
25         editor.getEditorState().read(() => {
26             const {id: drawingId} = node.getDrawingIdAndUrl();
27             res(drawingId);
28         });
29     });
30
31     return drawingId || '';
32 }
33
34 async function updateDrawingNodeFromData(context: EditorUiContext, node: DiagramNode, pngData: string, isNew: boolean): Promise<void> {
35     DrawIO.close();
36
37     if (isNew) {
38         const loadingImage: string = window.baseUrl('/loading.gif');
39         context.editor.update(() => {
40             node.setDrawingIdAndUrl('', loadingImage);
41         });
42     }
43
44     try {
45         const img = await DrawIO.upload(pngData, context.options.pageId);
46         context.editor.update(() => {
47             node.setDrawingIdAndUrl(String(img.id), img.url);
48         });
49     } catch (err) {
50         if (err instanceof HttpError) {
51             handleUploadError(err, context);
52         }
53
54         if (isNew) {
55             context.editor.update(() => {
56                 node.remove();
57             });
58         }
59
60         throw new Error(`Failed to save image with error: ${err}`);
61     }
62 }
63
64 export function $openDrawingEditorForNode(context: EditorUiContext, node: DiagramNode): void {
65     let isNew = false;
66     DrawIO.show(context.options.drawioUrl, async () => {
67         const drawingId = await loadDiagramIdFromNode(context.editor, node);
68         isNew = !drawingId;
69         return isNew ? '' : DrawIO.load(drawingId);
70     }, async (pngData: string) => {
71         return updateDrawingNodeFromData(context, node, pngData, isNew);
72     });
73 }
74
75 export function showDiagramManager(callback: (image: EditorImageData) => any) {
76     const imageManager: ImageManager = window.$components.first('image-manager') as ImageManager;
77     imageManager.show((image: EditorImageData) => {
78         callback(image);
79     }, 'drawio');
80 }
81
82 export function showDiagramManagerForInsert(context: EditorUiContext) {
83     const selection = getLastSelection(context.editor);
84     showDiagramManager((image: EditorImageData) => {
85         context.editor.update(() => {
86             const diagramNode = $createDiagramNode(image.id, image.url);
87             const selectedDiagram = $getNodeFromSelection(selection, $isDiagramNode);
88             if ($isDiagramNode(selectedDiagram)) {
89                 selectedDiagram.replace(diagramNode);
90             } else {
91                 $insertNodes([diagramNode]);
92             }
93         });
94     });
95 }