import {MenuItem} from "./menu";
import {icons} from "./icons";
+import {markRangeAtPosition, nullifyEmptyValues} from "../util";
/**
* @param {PmMarkType} markType
new DialogRadioOptions({
"Same tab or window": "",
"New tab or window": "_blank",
- },{
+ }, {
label: 'Behaviour',
id: 'target',
value: getMarkAttribute(schema.marks.link, 'target'),
*/
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;
}
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