]> BookStack Code Mirror - bookstack/commitdiff
Improved anchor updating/remove action
authorDan Brown <redacted>
Wed, 19 Jan 2022 23:22:48 +0000 (23:22 +0000)
committerDan Brown <redacted>
Wed, 19 Jan 2022 23:22:48 +0000 (23:22 +0000)
Now will update the link mark if you have a no-range selection on the
link.

resources/js/editor/menu/item-anchor-button.js
resources/js/editor/util.js

index f38985a9ceb94f24781939e0aefb25db2b126371..405d3ab4d610b2fc9eb6239ecfe8ff9241900fae 100644 (file)
@@ -6,6 +6,7 @@ import schema from "../schema";
 
 import {MenuItem} from "./menu";
 import {icons} from "./icons";
+import {markRangeAtPosition, nullifyEmptyValues} from "../util";
 
 /**
  * @param {PmMarkType} markType
@@ -46,7 +47,7 @@ function getLinkDialog(submitter, closer) {
             new DialogRadioOptions({
                 "Same tab or window": "",
                 "New tab or window": "_blank",
-            },{
+            }, {
                 label: 'Behaviour',
                 id: 'target',
                 value: getMarkAttribute(schema.marks.link, 'target'),
@@ -69,17 +70,29 @@ function getLinkDialog(submitter, closer) {
  */
 function applyLink(formData, state, dispatch) {
     const selection = state.selection;
-    const attrs = Object.fromEntries(formData);
-    if (dispatch) {
-        const tr = state.tr;
+    const attrs = nullifyEmptyValues(Object.fromEntries(formData));
+    if (!dispatch) return true;
 
-        if (attrs.href) {
-            tr.addMark(selection.from, selection.to, schema.marks.link.create(attrs));
-        } else {
-            tr.removeMark(selection.from, selection.to, schema.marks.link);
+    const tr = state.tr;
+    const noRange = (selection.from - selection.to === 0);
+    let from = selection.from;
+    let to = selection.to;
+
+    if (noRange) {
+        const linkRange = markRangeAtPosition(state, schema.marks.link, selection.from);
+        if (linkRange.from !== -1) {
+            from = linkRange.from;
+            to = linkRange.to;
         }
-        dispatch(tr);
     }
+
+    if (attrs.href) {
+        tr.addMark(from, to, schema.marks.link.create(attrs));
+    } else {
+        tr.removeMark(from, to, schema.marks.link);
+    }
+
+    dispatch(tr);
     return true;
 }
 
index 6d3fb74176d493212c77a628cfdbab5ff176f2e2..e1746a72517c0c599a3fcc338ac1c54436e068ee 100644 (file)
@@ -33,6 +33,49 @@ export function stateToHtml(state) {
     return renderDoc.body.innerHTML;
 }
 
+/**
+ * @param {Object} object
+ * @return {{}}
+ */
+export function nullifyEmptyValues(object) {
+    const clean = {};
+    for (const [key, value] of Object.entries(object)) {
+        clean[key] = (value === "") ? null : value;
+    }
+    return clean;
+}
+
+/**
+ * @param {PmEditorState} state
+ * @param {PmMarkType} markType
+ * @param {Number} pos
+ * @return {{from: Number, to: Number}}
+ */
+export function markRangeAtPosition(state, markType, pos) {
+    const $pos = state.doc.resolve(pos);
+
+    const { parent, parentOffset } = $pos;
+    const start = parent.childAfter(parentOffset);
+    if (!start.node) return {from: -1, to: -1};
+
+    const mark = start.node.marks.find((mark) => mark.type === markType);
+    if (!mark) return {from: -1, to: -1};
+
+    let startIndex = $pos.index();
+    let startPos = $pos.start() + start.offset;
+    let endIndex = startIndex + 1;
+    let endPos = startPos + start.node.nodeSize;
+    while (startIndex > 0 && mark.isInSet(parent.child(startIndex - 1).marks)) {
+        startIndex -= 1;
+        startPos -= parent.child(startIndex).nodeSize;
+    }
+    while (endIndex < parent.childCount && mark.isInSet(parent.child(endIndex).marks)) {
+        endPos += parent.child(endIndex).nodeSize;
+        endIndex += 1;
+    }
+    return { from: startPos, to: endPos };
+}
+
 /**
  * @class KeyedMultiStack
  * Holds many stacks, seperated via a key, with a simple