]> BookStack Code Mirror - bookstack/commitdiff
Got link insert/editor working
authorDan Brown <redacted>
Sun, 16 Jan 2022 14:37:58 +0000 (14:37 +0000)
committerDan Brown <redacted>
Sun, 16 Jan 2022 14:37:58 +0000 (14:37 +0000)
TODO
resources/js/editor/menu/DialogBox.js
resources/js/editor/menu/icons.js
resources/js/editor/menu/index.js
resources/js/editor/menu/item-anchor-button.js [new file with mode: 0644]
resources/js/editor/schema-marks.js

diff --git a/TODO b/TODO
index fbba22f5098ab874d6f421ad58f612291112ac84..ca678d1dc8c2f9e8456d2f12aa65d37656a47882 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,15 +1,8 @@
 ### In-Progress
 
-- Modal Dialogs for details such as links
-  - Got dialog + form + input ready
-  - Next stage is creating a button (eg, anchor insert) which toggles/shows dialog box.
-    - Dialog box should attach at bottom of dom (Prevent z-index issues).
-    - At some level a layer is needed to wire up the existing components.
-
 ### Features
 
 - Tables
-- Links
 - Images
   - Image Resizing in editor
 - Drawings
@@ -33,4 +26,7 @@
 - Color picker buttons should be split, with button to re-apply last selected color.
 - Color picker options should change color if different instead of remove.
 - Clear formatting, If no selection range, clear the formatting of parent block.
-  - If no marks, clear the block type if text type? 
\ No newline at end of file
+  - If no marks, clear the block type if text type?
+- Remove links button? (Action already in place if link href is empty).
+- Links - Limit target attribute options and validate URL.
+- Links - Integrate entity picker.
\ No newline at end of file
index a9aa443da95ac91b35b018c908aa225011f5c400..7af50c62e34cb0b6887e828783d9f70619bbbeee 100644 (file)
@@ -53,10 +53,6 @@ class DialogBox {
         if (this.options.closer) {
             this.options.closer();
         }
-
-        if (this.closeMouseDownListener) {
-            this.wrap.removeEventListener("click", this.closeMouseDownListener);
-        }
     }
 }
 
index 022a0078b9552cef19347f67c72f5fe0475045bd..030ac75bfe52386bdfbfb1ad67159c84ca077b2d 100644 (file)
 // holding an object that can be used as the `icon` option to
 // `MenuItem`.
 export const icons = {
-  join: {
-    width: 800, height: 900,
-    path: "M0 75h800v125h-800z M0 825h800v-125h-800z M250 400h100v-100h100v100h100v100h-100v100h-100v-100h-100z"
-  },
-  lift: {
-    width: 1024, height: 1024,
-    path: "M219 310v329q0 7-5 12t-12 5q-8 0-13-5l-164-164q-5-5-5-13t5-13l164-164q5-5 13-5 7 0 12 5t5 12zM1024 749v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12zM1024 530v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 310v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 91v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12z"
-  },
-  selectParentNode: {text: "\u2b1a", css: "font-weight: bold"},
   undo: {
     width: 24, height: 24,
     path: "M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"
@@ -37,13 +28,9 @@ export const icons = {
     width: 24, height: 24,
     path: "M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z"
   },
-  code: {
-    width: 896, height: 1024,
-    path: "M608 192l-96 96 224 224-224 224 96 96 288-320-288-320zM288 192l-288 320 288 320 96-96-224-224 224-224-96-96z"
-  },
   link: {
-    width: 951, height: 1024,
-    path: "M832 694q0-22-16-38l-118-118q-16-16-38-16-24 0-41 18 1 1 10 10t12 12 8 10 7 14 2 15q0 22-16 38t-38 16q-8 0-15-2t-14-7-10-8-12-12-10-10q-18 17-18 41 0 22 16 38l117 118q15 15 38 15 22 0 38-14l84-83q16-16 16-38zM430 292q0-22-16-38l-117-118q-16-16-38-16-22 0-38 15l-84 83q-16 16-16 38 0 22 16 38l118 118q15 15 38 15 24 0 41-17-1-1-10-10t-12-12-8-10-7-14-2-15q0-22 16-38t38-16q8 0 15 2t14 7 10 8 12 12 10 10q18-17 18-41zM941 694q0 68-48 116l-84 83q-47 47-116 47-69 0-116-48l-117-118q-47-47-47-116 0-70 50-119l-50-50q-49 50-118 50-68 0-116-48l-118-118q-48-48-48-116t48-116l84-83q47-47 116-47 69 0 116 48l117 118q47 47 47 116 0 70-50 119l50 50q49-50 118-50 68 0 116 48l118 118q48 48 48 116z"
+    width: 24, height: 24,
+    path: "M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"
   },
   bullet_list: {
     width: 24, height: 24,
@@ -57,10 +44,6 @@ export const icons = {
     width: 24, height: 24,
     path: "M22,7h-9v2h9V7z M22,15h-9v2h9V15z M5.54,11L2,7.46l1.41-1.41l2.12,2.12l4.24-4.24l1.41,1.41L5.54,11z M5.54,19L2,15.46 l1.41-1.41l2.12,2.12l4.24-4.24l1.41,1.41L5.54,19z"
   },
-  blockquote: {
-    width: 640, height: 896,
-    path: "M0 448v256h256v-256h-128c0 0 0-128 128-128v-128c0 0-256 0-256 256zM640 320v-128c0 0-256 0-256 256v256h256v-256h-128c0 0 0-128 128-128z"
-  },
   underline: {
     width: 24, height: 24,
     path: "M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z"
index 290e6f6f0c50e537f9156c4b7bd3e1fc14143412..cefa678fea120b08dd9bbeba3140610744345fda 100644 (file)
@@ -12,6 +12,8 @@ import {removeMarks} from "../commands";
 import DialogForm from "./DialogForm";
 import DialogInput from "./DialogInput";
 
+import itemAnchorButtonItem from "./item-anchor-button";
+
 
 function cmdItem(cmd, options) {
     const passedOptions = {
@@ -149,6 +151,7 @@ const lists = [
 ];
 
 const inserts = [
+    itemAnchorButtonItem(),
     insertBlockBeforeItem(schema.nodes.horizontal_rule, {
         title: "Horizontal Rule",
         icon: icons.horizontal_rule,
@@ -164,37 +167,6 @@ const utilities = [
     }),
 ];
 
-function getMarkAttribute(markType, attribute) {
-    return function(state) {
-        const marks = state.selection.$head.marks();
-        for (const mark of marks) {
-            if (mark.type === markType) {
-                return mark.attrs[attribute];
-            }
-        }
-
-        return null;
-    };
-}
-
-let box = new DialogBox([
-    new DialogForm([
-        new DialogInput({
-            label: 'URL',
-            id: 'url',
-            value: getMarkAttribute(schema.marks.link, 'href'),
-        }),
-        new DialogInput({
-            label: 'Title',
-            id: 'title',
-            value: getMarkAttribute(schema.marks.link, 'title'),
-        })
-    ], {
-        canceler: () =>  box.close(),
-        action: (data) => console.log('submit', data),
-    }),
-], {label: 'Insert Link', closer: () => {console.log('close')}});
-
 const menu = menuBar({
     floating: false,
     content: [
@@ -206,7 +178,6 @@ const menu = menuBar({
         lists,
         inserts,
         utilities,
-        [box]
     ],
 });
 
diff --git a/resources/js/editor/menu/item-anchor-button.js b/resources/js/editor/menu/item-anchor-button.js
new file mode 100644 (file)
index 0000000..f419314
--- /dev/null
@@ -0,0 +1,89 @@
+import DialogBox from "./DialogBox";
+import DialogForm from "./DialogForm";
+import DialogInput from "./DialogInput";
+import schema from "../schema";
+
+import {MenuItem} from "./menu";
+import {icons} from "./icons";
+
+
+function getMarkAttribute(markType, attribute) {
+    return function (state) {
+        const marks = state.selection.$head.marks();
+        for (const mark of marks) {
+            if (mark.type === markType) {
+                return mark.attrs[attribute];
+            }
+        }
+
+        return null;
+    };
+}
+
+function getLinkDialog(submitter, closer) {
+    return new DialogBox([
+        new DialogForm([
+            new DialogInput({
+                label: 'URL',
+                id: 'href',
+                value: getMarkAttribute(schema.marks.link, 'href'),
+            }),
+            new DialogInput({
+                label: 'Title',
+                id: 'title',
+                value: getMarkAttribute(schema.marks.link, 'title'),
+            }),
+            new DialogInput({
+                label: 'Target',
+                id: 'target',
+                value: getMarkAttribute(schema.marks.link, 'target'),
+            })
+        ], {
+            canceler: closer,
+            action: submitter,
+        }),
+    ], {
+        label: 'Insert Link',
+        closer: closer,
+    });
+}
+
+function applyLink(formData, state, dispatch) {
+    const selection = state.selection;
+    const attrs = Object.fromEntries(formData);
+    if (dispatch) {
+        const tr = state.tr;
+
+        if (attrs.href) {
+            tr.addMark(selection.from, selection.to, schema.marks.link.create(attrs));
+        } else {
+            tr.removeMark(selection.from, selection.to, schema.marks.link);
+        }
+        dispatch(tr);
+    }
+    return true;
+}
+
+function onPress(state, dispatch, view, e) {
+    const dialog = getLinkDialog((data) => {
+        applyLink(data, state, dispatch);
+        dom.remove();
+    }, () => {
+        dom.remove();
+    })
+
+    const {dom, update} = dialog.render(view);
+    update(state);
+    document.body.appendChild(dom);
+}
+
+function anchorButtonItem() {
+    return new MenuItem({
+        title: "Insert/Edit Anchor Link",
+        run: onPress,
+        enable: state => true,
+        icon: icons.link,
+    });
+}
+
+export default anchorButtonItem;
\ No newline at end of file
index c8b8da3464a20052dd73b79c771774b756f342f2..f33fc741a584cfe7de79104ba61d1c29e2280673 100644 (file)
@@ -1,19 +1,25 @@
 const link = {
     attrs: {
         href: {},
-        title: {default: null}
+        title: {default: null},
+        target: {default: null}
     },
     inclusive: false,
     parseDOM: [{
         tag: "a[href]", getAttrs: function getAttrs(dom) {
-            return {href: dom.getAttribute("href"), title: dom.getAttribute("title")}
+            return {
+                href: dom.getAttribute("href"),
+                title: dom.getAttribute("title"),
+                target: dom.getAttribute("target"),
+            }
         }
     }],
     toDOM: function toDOM(node) {
         const ref = node.attrs;
         const href = ref.href;
         const title = ref.title;
-        return ["a", {href: href, title: title}, 0]
+        const target = ref.target;
+        return ["a", {href, title, target}, 0]
     }
 };