]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Integrated diagram manager, added menu split button
authorDan Brown <redacted>
Sat, 17 Aug 2024 09:48:34 +0000 (10:48 +0100)
committerDan Brown <redacted>
Sat, 17 Aug 2024 09:48:34 +0000 (10:48 +0100)
resources/icons/caret-down-large.svg [new file with mode: 0644]
resources/js/wysiwyg/todo.md
resources/js/wysiwyg/ui/defaults/buttons/objects.ts
resources/js/wysiwyg/ui/framework/blocks/button-with-menu.ts [new file with mode: 0644]
resources/js/wysiwyg/ui/toolbars.ts
resources/js/wysiwyg/utils/diagrams.ts
resources/js/wysiwyg/utils/images.ts
resources/sass/_editor.scss

diff --git a/resources/icons/caret-down-large.svg b/resources/icons/caret-down-large.svg
new file mode 100644 (file)
index 0000000..a15ec42
--- /dev/null
@@ -0,0 +1 @@
+<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m2 6.9159 10 10.168 10-10.168z" stroke-width="2.0168"/></svg>
index 70a3744f3bc9668b802a387fb0dfc88d881e2750..f3a8da404e695e59aea717dc3f841f9643640099 100644 (file)
@@ -10,7 +10,6 @@
 - Alignments: Handle inline block content (image, video)
 - Image paste upload
 - Keyboard shortcuts support
-- Drawing gallery integration
 - Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
 - Media resize support (like images)
 - Table caption text support
index f4075a7403d5b428c414561db771107bea1500d7..96a92ff228518a4f8c49c25a4de03fd35b672511 100644 (file)
@@ -30,7 +30,7 @@ import {
     $insertNewBlockNodeAtSelection,
     $selectionContainsNodeType
 } from "../../../utils/selection";
-import {$isDiagramNode, $openDrawingEditorForNode} from "../../../utils/diagrams";
+import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
 import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
 import {$showImageForm} from "../forms/objects";
 
@@ -184,6 +184,16 @@ export const diagram: EditorButtonDefinition = {
     }
 };
 
+export const diagramManager: EditorButtonDefinition = {
+    label: 'Drawing manager',
+    action(context: EditorUiContext) {
+        showDiagramManagerForInsert(context);
+    },
+    isActive(): boolean {
+        return false;
+    }
+};
+
 export const media: EditorButtonDefinition = {
     label: 'Insert/edit Media',
     icon: mediaIcon,
diff --git a/resources/js/wysiwyg/ui/framework/blocks/button-with-menu.ts b/resources/js/wysiwyg/ui/framework/blocks/button-with-menu.ts
new file mode 100644 (file)
index 0000000..30dd237
--- /dev/null
@@ -0,0 +1,31 @@
+import {EditorContainerUiElement, EditorUiElement} from "../core";
+import {el} from "../../../utils/dom";
+import {EditorButton} from "../buttons";
+import {EditorDropdownButton} from "./dropdown-button";
+import caretDownIcon from "@icons/caret-down-large.svg";
+
+export class EditorButtonWithMenu extends EditorContainerUiElement {
+    protected button: EditorButton;
+    protected dropdownButton: EditorDropdownButton;
+
+    constructor(button: EditorButton, menuItems: EditorUiElement[]) {
+        super([button]);
+
+        this.button = button;
+        this.dropdownButton = new EditorDropdownButton({
+            button: {label: 'Menu', icon: caretDownIcon},
+            showOnHover: false,
+            direction: 'vertical',
+        }, menuItems);
+        this.addChildren(this.dropdownButton);
+    }
+
+    buildDOM(): HTMLElement {
+        return el('div', {
+            class: 'editor-button-with-menu-container',
+        }, [
+            this.button.getDOMElement(),
+            this.dropdownButton.getDOMElement()
+        ]);
+    }
+}
index 48e11837c953445894deb1d9da08be6e0015d0db..87ecae03e2be775eccbed96bf4b86e8bb3900ede 100644 (file)
@@ -56,16 +56,15 @@ import {bulletList, numberList, taskList} from "./defaults/buttons/lists";
 import {
     codeBlock,
     details,
-    diagram,
+    diagram, diagramManager,
     editCodeBlock,
     horizontalRule,
     image,
     link, media,
     unlink
 } from "./defaults/buttons/objects";
-import {$isTableNode} from "@lexical/table";
-import {$selectionContainsNodeType} from "../utils/selection";
 import {el} from "../utils/dom";
+import {EditorButtonWithMenu} from "./framework/blocks/button-with-menu";
 
 export function getMainEditorFullToolbar(): EditorContainerUiElement {
     return new EditorSimpleClassContainer('editor-toolbar-main', [
@@ -166,7 +165,10 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
             new EditorButton(image),
             new EditorButton(horizontalRule),
             new EditorButton(codeBlock),
-            new EditorButton(diagram),
+            new EditorButtonWithMenu(
+                new EditorButton(diagram),
+                [new EditorButton(diagramManager)],
+            ),
             new EditorButton(media),
             new EditorButton(details),
         ]),
index 50d7d5b3f36e536dcbb2e28374e31794fe5f8ec8..2dee3ab6b0d3cc12219dfe9097648a21b10d9ed4 100644 (file)
@@ -1,8 +1,11 @@
-import {LexicalEditor, LexicalNode} from "lexical";
+import {$getSelection, $insertNodes, LexicalEditor, LexicalNode} from "lexical";
 import {HttpError} from "../../services/http";
 import {EditorUiContext} from "../ui/framework/core";
 import * as DrawIO from "../../services/drawio";
-import {DiagramNode} from "../nodes/diagram";
+import {$createDiagramNode, DiagramNode} from "../nodes/diagram";
+import {ImageManager} from "../../components";
+import {EditorImageData} from "./images";
+import {$getNodeFromSelection} from "./selection";
 
 export function $isDiagramNode(node: LexicalNode | null | undefined): node is DiagramNode {
     return node instanceof DiagramNode;
@@ -67,4 +70,26 @@ export function $openDrawingEditorForNode(context: EditorUiContext, node: Diagra
     }, async (pngData: string) => {
         return updateDrawingNodeFromData(context, node, pngData, isNew);
     });
+}
+
+export function showDiagramManager(callback: (image: EditorImageData) => any) {
+    const imageManager: ImageManager = window.$components.first('image-manager') as ImageManager;
+    imageManager.show((image: EditorImageData) => {
+        callback(image);
+    }, 'drawio');
+}
+
+export function showDiagramManagerForInsert(context: EditorUiContext) {
+    const selection = context.lastSelection;
+    showDiagramManager((image: EditorImageData) => {
+        context.editor.update(() => {
+            const diagramNode = $createDiagramNode(image.id, image.url);
+            const selectedDiagram = $getNodeFromSelection(selection, $isDiagramNode);
+            if ($isDiagramNode(selectedDiagram)) {
+                selectedDiagram.replace(diagramNode);
+            } else {
+                $insertNodes([diagramNode]);
+            }
+        });
+    });
 }
\ No newline at end of file
index 89a4a60f091abca5440c74863da746ab07102d4b..a83d554186eec470bfba886886de9e1a81977c27 100644 (file)
@@ -2,7 +2,8 @@ import {ImageManager} from "../../components";
 import {$createImageNode} from "../nodes/image";
 import {$createLinkNode, LinkNode} from "@lexical/link";
 
-type EditorImageData = {
+export type EditorImageData = {
+    id: string;
     url: string;
     thumbs?: {display: string};
     name: string;
index 379c436f466070aac4a384bf54893283e70eab43..78e518bd566e45f8b27fbe70a866b6f49e1524a7 100644 (file)
@@ -82,6 +82,31 @@ body.editor-is-fullscreen {
   fill: currentColor;
   display: block;
 }
+.editor-button-with-menu-container {
+  display: flex;
+  flex-direction: row;
+  gap: 0;
+  align-items: stretch;
+  border-radius: 4px;
+  .editor-dropdown-menu-container {
+    display: flex;
+  }
+  .editor-dropdown-menu-container > .editor-dropdown-menu {
+    top: 100%;
+  }
+  .editor-dropdown-menu-container > .editor-button {
+    padding-inline: 4px;
+    margin-inline-start: -3px;
+    svg {
+      width: 12px;
+      height: 12px;
+    }
+  }
+  &:hover {
+    outline: 1px solid #DDD;
+    outline-offset: -3px;
+  }
+}
 
 // Containers
 .editor-dropdown-menu-container {