]> BookStack Code Mirror - bookstack/blobdiff - resources/js/wysiwyg/ui/defaults/buttons/objects.ts
Merge pull request #5731 from BookStackApp/lexical_jul25
[bookstack] / resources / js / wysiwyg / ui / defaults / buttons / objects.ts
index 88241e9262605b7053246c221c45bc85345e29f9..41f6061b64878075712671024a7ddd6ac532eb55 100644 (file)
@@ -2,58 +2,49 @@ import {EditorButtonDefinition} from "../../framework/buttons";
 import linkIcon from "@icons/editor/link.svg";
 import {EditorUiContext} from "../../framework/core";
 import {
-    $createNodeSelection,
-    $createTextNode,
     $getRoot,
-    $getSelection,
-    $setSelection,
+    $getSelection, $insertNodes,
     BaseSelection,
     ElementNode
 } from "lexical";
-import {$getNodeFromSelection, $insertNewBlockNodeAtSelection, $selectionContainsNodeType} from "../../../helpers";
 import {$isLinkNode, LinkNode} from "@lexical/link";
 import unlinkIcon from "@icons/editor/unlink.svg";
 import imageIcon from "@icons/editor/image.svg";
-import {$isImageNode, ImageNode} from "../../../nodes/image";
+import {$isImageNode, ImageNode} from "@lexical/rich-text/LexicalImageNode";
 import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg";
-import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../../nodes/horizontal-rule";
+import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "@lexical/rich-text/LexicalHorizontalRuleNode";
 import codeBlockIcon from "@icons/editor/code-block.svg";
-import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../../../nodes/code-block";
+import {$isCodeBlockNode} from "@lexical/rich-text/LexicalCodeBlockNode";
 import editIcon from "@icons/edit.svg";
 import diagramIcon from "@icons/editor/diagram.svg";
-import {$createDiagramNode, $isDiagramNode, $openDrawingEditorForNode, DiagramNode} from "../../../nodes/diagram";
+import {$createDiagramNode, DiagramNode} from "@lexical/rich-text/LexicalDiagramNode";
 import detailsIcon from "@icons/editor/details.svg";
-import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
+import detailsToggleIcon from "@icons/editor/details-toggle.svg";
+import tableDeleteIcon from "@icons/editor/table-delete.svg";
+import tagIcon from "@icons/tag.svg";
+import mediaIcon from "@icons/editor/media.svg";
+import {$createDetailsNode, $isDetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
+import {$isMediaNode, MediaNode} from "@lexical/rich-text/LexicalMediaNode";
+import {
+    $getNodeFromSelection,
+    $insertNewBlockNodeAtSelection,
+    $selectionContainsNodeType, getLastSelection
+} from "../../../utils/selection";
+import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
+import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
+import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
+import {formatCodeBlock} from "../../../utils/formats";
 
 export const link: EditorButtonDefinition = {
     label: 'Insert/edit link',
     icon: linkIcon,
     action(context: EditorUiContext) {
-        const linkModal = context.manager.createModal('link');
         context.editor.getEditorState().read(() => {
-            const selection = $getSelection();
-            const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
-
-            let formDefaults = {};
-            if (selectedLink) {
-                formDefaults = {
-                    url: selectedLink.getURL(),
-                    text: selectedLink.getTextContent(),
-                    title: selectedLink.getTitle(),
-                    target: selectedLink.getTarget(),
-                }
-
-                context.editor.update(() => {
-                    const selection = $createNodeSelection();
-                    selection.add(selectedLink.getKey());
-                    $setSelection(selection);
-                });
-            }
-
-            linkModal.show(formDefaults);
+            const selectedLink = $getNodeFromSelection($getSelection(), $isLinkNode) as LinkNode | null;
+            $showLinkForm(selectedLink, context);
         });
     },
-    isActive(selection: BaseSelection|null): boolean {
+    isActive(selection: BaseSelection | null): boolean {
         return $selectionContainsNodeType(selection, $isLinkNode);
     }
 };
@@ -63,57 +54,50 @@ export const unlink: EditorButtonDefinition = {
     icon: unlinkIcon,
     action(context: EditorUiContext) {
         context.editor.update(() => {
-            const selection = context.lastSelection;
-            const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
-            const selectionPoints = selection?.getStartEndPoints();
+            const selection = getLastSelection(context.editor);
+            const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode | null;
 
             if (selectedLink) {
-                const newNode = $createTextNode(selectedLink.getTextContent());
-                selectedLink.replace(newNode);
-                if (selectionPoints?.length === 2) {
-                    newNode.select(selectionPoints[0].offset, selectionPoints[1].offset);
-                } else {
-                    newNode.select();
+                const contents = selectedLink.getChildren().reverse();
+                for (const child of contents) {
+                    selectedLink.insertAfter(child);
                 }
+                selectedLink.remove();
+
+                contents[contents.length - 1].selectStart();
+
+                context.manager.triggerFutureStateRefresh();
             }
         });
     },
-    isActive(selection: BaseSelection|null): boolean {
+    isActive(selection: BaseSelection | null): boolean {
         return false;
     }
 };
 
 
-
 export const image: EditorButtonDefinition = {
     label: 'Insert/Edit Image',
     icon: imageIcon,
     action(context: EditorUiContext) {
-        const imageModal = context.manager.createModal('image');
-        const selection = context.lastSelection;
-        const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode|null;
-
         context.editor.getEditorState().read(() => {
-            let formDefaults = {};
+            const selection = getLastSelection(context.editor);
+            const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode | null;
             if (selectedImage) {
-                formDefaults = {
-                    src: selectedImage.getSrc(),
-                    alt: selectedImage.getAltText(),
-                    height: selectedImage.getHeight(),
-                    width: selectedImage.getWidth(),
-                }
+                $showImageForm(selectedImage, context);
+                return;
+            }
 
+            showImageManager((image) => {
                 context.editor.update(() => {
-                    const selection = $createNodeSelection();
-                    selection.add(selectedImage.getKey());
-                    $setSelection(selection);
+                    const link = $createLinkedImageNodeFromImageData(image);
+                    $insertNodes([link]);
+                    link.select();
                 });
-            }
-
-            imageModal.show(formDefaults);
+            })
         });
     },
-    isActive(selection: BaseSelection|null): boolean {
+    isActive(selection: BaseSelection | null): boolean {
         return $selectionContainsNodeType(selection, $isImageNode);
     }
 };
@@ -126,7 +110,7 @@ export const horizontalRule: EditorButtonDefinition = {
             $insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
         });
     },
-    isActive(selection: BaseSelection|null): boolean {
+    isActive(selection: BaseSelection | null): boolean {
         return $selectionContainsNodeType(selection, $isHorizontalRuleNode);
     }
 };
@@ -135,23 +119,9 @@ export const codeBlock: EditorButtonDefinition = {
     label: 'Insert code block',
     icon: codeBlockIcon,
     action(context: EditorUiContext) {
-        context.editor.getEditorState().read(() => {
-            const selection = $getSelection();
-            const codeBlock = $getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode|null);
-            if (codeBlock === null) {
-                context.editor.update(() => {
-                    const codeBlock = $createCodeBlockNode();
-                    codeBlock.setCode(selection?.getTextContent() || '');
-                    $insertNewBlockNodeAtSelection(codeBlock, true);
-                    $openCodeEditorForNode(context.editor, codeBlock);
-                    codeBlock.selectStart();
-                });
-            } else {
-                $openCodeEditorForNode(context.editor, codeBlock);
-            }
-        });
+        formatCodeBlock(context.editor);
     },
-    isActive(selection: BaseSelection|null): boolean {
+    isActive(selection: BaseSelection | null): boolean {
         return $selectionContainsNodeType(selection, $isCodeBlockNode);
     }
 };
@@ -166,8 +136,8 @@ export const diagram: EditorButtonDefinition = {
     icon: diagramIcon,
     action(context: EditorUiContext) {
         context.editor.getEditorState().read(() => {
-            const selection = $getSelection();
-            const diagramNode = $getNodeFromSelection(context.lastSelection, $isDiagramNode) as (DiagramNode|null);
+            const selection = getLastSelection(context.editor);
+            const diagramNode = $getNodeFromSelection(selection, $isDiagramNode) as (DiagramNode | null);
             if (diagramNode === null) {
                 context.editor.update(() => {
                     const diagram = $createDiagramNode();
@@ -180,11 +150,36 @@ export const diagram: EditorButtonDefinition = {
             }
         });
     },
-    isActive(selection: BaseSelection|null): boolean {
+    isActive(selection: BaseSelection | null): boolean {
         return $selectionContainsNodeType(selection, $isDiagramNode);
     }
 };
 
+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,
+    action(context: EditorUiContext) {
+        context.editor.getEditorState().read(() => {
+            const selection = $getSelection();
+            const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
+
+            $showMediaForm(selectedNode, context);
+        });
+    },
+    isActive(selection: BaseSelection | null): boolean {
+        return $selectionContainsNodeType(selection, $isMediaNode);
+    }
+};
 
 export const details: EditorButtonDefinition = {
     label: 'Insert collapsible block',
@@ -209,7 +204,61 @@ export const details: EditorButtonDefinition = {
             }
         });
     },
-    isActive(selection: BaseSelection|null): boolean {
+    isActive(selection: BaseSelection | null): boolean {
         return $selectionContainsNodeType(selection, $isDetailsNode);
     }
+}
+
+export const detailsEditLabel: EditorButtonDefinition = {
+    label: 'Edit label',
+    icon: tagIcon,
+    action(context: EditorUiContext) {
+        context.editor.getEditorState().read(() => {
+            const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
+            if ($isDetailsNode(details)) {
+                $showDetailsForm(details, context);
+            }
+        })
+    },
+    isActive(selection: BaseSelection | null): boolean {
+        return false;
+    }
+}
+
+export const detailsToggle: EditorButtonDefinition = {
+    label: 'Toggle open/closed',
+    icon: detailsToggleIcon,
+    action(context: EditorUiContext) {
+        context.editor.update(() => {
+            const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
+            if ($isDetailsNode(details)) {
+                details.setOpen(!details.getOpen());
+                context.manager.triggerLayoutUpdate();
+            }
+        })
+    },
+    isActive(selection: BaseSelection | null): boolean {
+        return false;
+    }
+}
+
+export const detailsUnwrap: EditorButtonDefinition = {
+    label: 'Unwrap',
+    icon: tableDeleteIcon,
+    action(context: EditorUiContext) {
+        context.editor.update(() => {
+            const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
+            if ($isDetailsNode(details)) {
+                const children = details.getChildren();
+                for (const child of children) {
+                    details.insertBefore(child);
+                }
+                details.remove();
+                context.manager.triggerLayoutUpdate();
+            }
+        })
+    },
+    isActive(selection: BaseSelection | null): boolean {
+        return false;
+    }
 }
\ No newline at end of file