]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Added context toolbar placement, added link toolbar
authorDan Brown <redacted>
Sun, 30 Jun 2024 18:52:09 +0000 (19:52 +0100)
committerDan Brown <redacted>
Sun, 30 Jun 2024 18:52:09 +0000 (19:52 +0100)
Also added some basic context toolbar styling

resources/icons/editor/unlink.svg [new file with mode: 0644]
resources/js/wysiwyg/ui/defaults/button-definitions.ts
resources/js/wysiwyg/ui/framework/manager.ts
resources/js/wysiwyg/ui/framework/toolbars.ts
resources/js/wysiwyg/ui/index.ts
resources/js/wysiwyg/ui/toolbars.ts
resources/sass/_editor.scss

diff --git a/resources/icons/editor/unlink.svg b/resources/icons/editor/unlink.svg
new file mode 100644 (file)
index 0000000..28f47fd
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m770-302-60-62q40-11 65-42.5t25-73.5q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 57-29.5 105T770-302ZM634-440l-80-80h86v80h-6ZM792-56 56-792l56-56 736 736-56 56ZM440-280H280q-83 0-141.5-58.5T80-480q0-69 42-123t108-71l74 74h-24q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h65l79 80H320Z"/></svg>
\ No newline at end of file
index aa8b27ec56c7c625d47dd3df4754eca7de11ced1..cebf2580721a589f7ef195ecfc9691600c271907 100644 (file)
@@ -1,7 +1,7 @@
 import {EditorBasicButtonDefinition, EditorButton, EditorButtonDefinition} from "../framework/buttons";
 import {
     $createNodeSelection,
-    $createParagraphNode, $getRoot, $getSelection,
+    $createParagraphNode, $createTextNode, $getRoot, $getSelection,
     $isParagraphNode, $isTextNode, $setSelection,
     BaseSelection, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_LOW, ElementNode, FORMAT_TEXT_COMMAND,
     LexicalNode,
@@ -45,12 +45,13 @@ import listBulletIcon from "@icons/editor/list-bullet.svg"
 import listNumberedIcon from "@icons/editor/list-numbered.svg"
 import listCheckIcon from "@icons/editor/list-check.svg"
 import linkIcon from "@icons/editor/link.svg"
+import unlinkIcon from "@icons/editor/unlink.svg"
 import tableIcon from "@icons/editor/table.svg"
 import imageIcon from "@icons/editor/image.svg"
 import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg"
 import detailsIcon from "@icons/editor/details.svg"
 import sourceIcon from "@icons/editor/source-view.svg"
-import {$createHorizontalRuleNode, $isHorizontalRuleNode, HorizontalRuleNode} from "../../nodes/horizontal-rule";
+import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../nodes/horizontal-rule";
 
 export const undo: EditorButtonDefinition = {
     label: 'Undo',
@@ -258,6 +259,31 @@ export const link: EditorButtonDefinition = {
     }
 };
 
+export const unlink: EditorButtonDefinition = {
+    label: 'Remove link',
+    icon: unlinkIcon,
+    action(context: EditorUiContext) {
+        context.editor.update(() => {
+            const selection = context.lastSelection;
+            const selectedLink = getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
+            const selectionPoints = selection?.getStartEndPoints();
+
+            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();
+                }
+            }
+        });
+    },
+    isActive(selection: BaseSelection|null): boolean {
+        return false;
+    }
+};
+
 export const table: EditorBasicButtonDefinition = {
     label: 'Table',
     icon: tableIcon,
index 685031ff871770333e1b6089e06f34afb5217ca8..4ca90a12a1a046157b58c41467761e404809c428 100644 (file)
@@ -94,6 +94,7 @@ export class EditorUIManager {
         for (const toolbar of this.activeContextToolbars) {
             toolbar.updateState(update);
         }
+        // console.log('selection update', update.selection);
     }
 
     protected updateContextToolbars(update: EditorUiStateUpdate): void {
index a844161f468e7767399636ee876b71629f2f56e3..c9db0d6bd7f95040117a96e1611ae8f7432346b5 100644 (file)
@@ -16,8 +16,14 @@ export class EditorContextToolbar extends EditorContainerUiElement {
     }
 
     attachTo(target: HTMLElement) {
-        // Todo - attach to target position
-        console.log('attaching context toolbar to', target);
+        const targetBounds = target.getBoundingClientRect();
+        const dom = this.getDOMElement();
+        const domBounds = dom.getBoundingClientRect();
+
+        const targetMid = targetBounds.left + (targetBounds.width / 2);
+        const targetLeft = targetMid - (domBounds.width / 2);
+        dom.style.top = (targetBounds.bottom + 6) + 'px';
+        dom.style.left = targetLeft + 'px';
     }
 
     insert(children: EditorUiElement[]) {
index 9f05e211ed06949b2d993a0f1b565c4b8afe1075..1f07fe710567d422ca787884dbba32b6e07ac229 100644 (file)
@@ -1,5 +1,5 @@
 import {LexicalEditor} from "lexical";
-import {getImageToolbarContent, getMainEditorFullToolbar} from "./toolbars";
+import {getImageToolbarContent, getLinkToolbarContent, getMainEditorFullToolbar} from "./toolbars";
 import {EditorUIManager} from "./framework/manager";
 import {image as imageFormDefinition, link as linkFormDefinition, source as sourceFormDefinition} from "./defaults/form-definitions";
 import {ImageDecorator} from "./decorators/image";
@@ -41,6 +41,10 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
             return originalTarget.closest('a') || originalTarget;
         }
     });
+    manager.registerContextToolbar('link', {
+        selector: 'a',
+        content: getLinkToolbarContent(),
+    });
 
     // Register image decorator listener
     manager.registerDecoratorType('image', ImageDecorator);
index 6e822e9c1396d04f28be689b0e4e591c56b1c1c6..bb3b436b9e155bbcdb6ab7acdaa21c9aa1ce75bb 100644 (file)
@@ -6,7 +6,7 @@ import {
     infoCallout, italic, link, numberList, paragraph,
     redo, source, strikethrough, subscript,
     successCallout, superscript, table, taskList, textColor, underline,
-    undo,
+    undo, unlink,
     warningCallout
 } from "./defaults/button-definitions";
 import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiContext, EditorUiElement} from "./framework/core";
@@ -91,4 +91,11 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
 
 export function getImageToolbarContent(): EditorUiElement[] {
     return [new EditorButton(image)];
+}
+
+export function getLinkToolbarContent(): EditorUiElement[] {
+    return [
+        new EditorButton(link),
+        new EditorButton(unlink),
+    ];
 }
\ No newline at end of file
index e0a2bac90018057ab441ee02c84be489046ece9e..2fcf4edb39720482a95d2e05034191b9a61ae738 100644 (file)
   display: flex;
 }
 
+.editor-context-toolbar {
+  position: fixed;
+  background-color: #FFF;
+  border: 1px solid #DDD;
+  padding: .2rem;
+  border-radius: 4px;
+  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12);
+  &:before {
+    content: '';
+    z-index: -1;
+    display: block;
+    width: 8px;
+    height: 8px;
+    position: absolute;
+    background-color: #FFF;
+    border-top: 1px solid #DDD;
+    border-left: 1px solid #DDD;
+    transform: rotate(45deg);
+    left: 50%;
+    margin-left: -4px;
+    top: -5px;
+  }
+}
+
 // Modals
 .editor-modal-wrapper {
   position: fixed;
Morty Proxy This is a proxified and sanitized view of the page, visit original site.