markupeditor 0.9.53 → 0.9.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/markup-editor.js +101 -60
  2. package/package.json +1 -1
@@ -35519,17 +35519,11 @@ function _getMarkTypes() {
35519
35519
  */
35520
35520
  function getLinkAttributes() {
35521
35521
  const view = activeView();
35522
- const selection = view.state.selection;
35523
- const selectedNodes = [];
35524
- view.state.doc.nodesBetween(selection.from, selection.to, node => {
35525
- if (node.isText) selectedNodes.push(node);
35526
- });
35527
- const selectedNode = (selectedNodes.length === 1) && selectedNodes[0];
35528
- if (selectedNode) {
35529
- const linkMarks = selectedNode.marks.filter(mark => mark.type === schema.marks.link);
35530
- if (linkMarks.length === 1) {
35531
- return {href: linkMarks[0].attrs.href, link: selectedNode.text};
35532
- } } // A linked image carries the link mark on the image node, which has no text to report as the link.
35522
+ const link = linkRangeAt(view.state);
35523
+ if (link) {
35524
+ const text = view.state.doc.textBetween(link.from, link.to);
35525
+ if (text) return {href: link.mark.attrs.href, link: text};
35526
+ } // A linked image carries the link mark on the image node, which has no text to report as the link.
35533
35527
  const image = _selectedImageNode(view.state);
35534
35528
  if (image) {
35535
35529
  const linkMarks = image.marks.filter(mark => mark.type === schema.marks.link);
@@ -36182,6 +36176,9 @@ function testCodeLanguageTabInfo() {
36182
36176
  * Insert a link to url. When the selection is collapsed, the url is inserted
36183
36177
  * at the selection point as a link.
36184
36178
  *
36179
+ * When the selection is in an existing link, or contains one, the href of the whole link is
36180
+ * changed to url and its text is left alone.
36181
+ *
36185
36182
  * When done, leave the link selected.
36186
36183
  *
36187
36184
  * @param {string} url The url/href to use for the link
@@ -36196,25 +36193,94 @@ function insertLinkCommand(url) {
36196
36193
  const commandAdapter = (state, dispatch, view) => {
36197
36194
  const selection = state.selection;
36198
36195
  const linkMark = state.schema.marks.link.create({ href: url });
36199
- if (selection.empty) {
36196
+ const transaction = state.tr;
36197
+ if (!addLinkMark(transaction, state, linkMark)) {
36198
+ // A collapsed selection outside any link
36200
36199
  const textNode = state.schema.text(url).mark([linkMark]);
36201
- const transaction = state.tr.replaceSelectionWith(textNode, false);
36200
+ transaction.replaceSelectionWith(textNode, false);
36202
36201
  const linkSelection = TextSelection.create(transaction.doc, selection.from, selection.from + textNode.nodeSize);
36203
36202
  transaction.setSelection(linkSelection);
36204
- dispatch(transaction);
36205
- stateChanged(view);
36206
- } else {
36207
- const toggle = toggleMark(linkMark.type, linkMark.attrs);
36208
- if (toggle) {
36209
- toggle(state, dispatch);
36210
- stateChanged(view);
36211
- }
36212
36203
  }
36204
+ dispatch(transaction);
36205
+ stateChanged(view);
36213
36206
  return true;
36214
36207
  };
36215
36208
  return commandAdapter;
36216
36209
  }
36217
36210
 
36211
+ /**
36212
+ * Add `linkMark` to `transaction`, replacing the href of the link the selection is in if there is one.
36213
+ *
36214
+ * The selection can be anywhere within a link or contain the full link. A link that spans several
36215
+ * text nodes, because part of it has other marks, is changed as a whole.
36216
+ *
36217
+ * @ignore
36218
+ * @param {Transaction} transaction
36219
+ * @param {EditorState} state The state `transaction` was created from
36220
+ * @param {Mark} linkMark
36221
+ * @returns {boolean} False, with `transaction` unchanged, when the selection is collapsed and not in a link
36222
+ */
36223
+ function addLinkMark(transaction, state, linkMark) {
36224
+ const range = linkRangeAt(state);
36225
+ if (range) {
36226
+ transaction.addMark(range.from, range.to, linkMark);
36227
+ const {from, to} = state.selection;
36228
+ if (range.from !== from || range.to !== to) {
36229
+ transaction.setSelection(TextSelection.create(transaction.doc, range.from, range.to));
36230
+ } return true;
36231
+ } if (state.selection.empty) return false;
36232
+ state.selection.ranges.forEach(range => transaction.addMark(range.$from.pos, range.$to.pos, linkMark));
36233
+ return true;
36234
+ }
36235
+
36236
+ /**
36237
+ * The extent of the link the selection is in, or null if it is not in one.
36238
+ *
36239
+ * Every inline node in the selection has to carry the same link, and a collapsed selection has to be inside the
36240
+ * link, not at its edge.
36241
+ *
36242
+ * @ignore
36243
+ * @param {EditorState} state
36244
+ * @returns {{from: number, to: number, mark: Mark}|null}
36245
+ */
36246
+ function linkRangeAt(state) {
36247
+ const linkType = state.schema.marks.link;
36248
+ const {from, to, $from, $to} = state.selection;
36249
+ if (!$from.sameParent($to)) return null;
36250
+ let target = null;
36251
+ let uniform = true;
36252
+ const check = node => {
36253
+ const mark = linkType.isInSet(node.marks);
36254
+ if (!mark || (target && !target.eq(mark))) uniform = false;
36255
+ else target = mark;
36256
+ };
36257
+ if (state.selection.empty) {
36258
+ // A collapsed selection is in a link when the same link is on both sides of it, including between its text nodes
36259
+ if (!$from.nodeBefore || !$from.nodeAfter) return null;
36260
+ check($from.nodeBefore);
36261
+ check($from.nodeAfter);
36262
+ } else {
36263
+ state.doc.nodesBetween(from, to, node => {
36264
+ if (!node.isInline) return true;
36265
+ check(node);
36266
+ return false;
36267
+ });
36268
+ } if (!uniform || !target) return null;
36269
+ // Widen to the contiguous children carrying the same link
36270
+ const base = $from.start();
36271
+ const spans = [];
36272
+ $from.parent.forEach((child, offset) => {
36273
+ spans.push({start: base + offset, end: base + offset + child.nodeSize, linked: !!target.isInSet(child.marks)});
36274
+ });
36275
+ const collapsed = state.selection.empty;
36276
+ let first = spans.findIndex(span => collapsed ? span.end >= from : span.end > from);
36277
+ let last = spans.findLastIndex(span => collapsed ? span.start <= to : span.start < to);
36278
+ if (first < 0 || last < first) return null;
36279
+ while (first > 0 && spans[first - 1].linked) first--;
36280
+ while (last < spans.length - 1 && spans[last + 1].linked) last++;
36281
+ return {from: spans[first].start, to: spans[last].end, mark: target};
36282
+ }
36283
+
36218
36284
  /**
36219
36285
  * Insert a link to the header identified by hTag ('H1'-'H6') and index (its position among
36220
36286
  * headers with that tag), assigning the header a unique id if it doesn't already have one.
@@ -36237,27 +36303,16 @@ function insertInternalLinkCommand(hTag, index) {
36237
36303
  let id = idForHeader(node, state);
36238
36304
  // Same transaction as the mark insertion below, so both land as one undo step.
36239
36305
  let tr = state.tr.setNodeMarkup(pos, undefined, { ...node.attrs, id });
36240
- const selection = state.selection;
36241
36306
  const linkMark = state.schema.marks.link.create({ href: '#' + id });
36242
- if (selection.empty) {
36307
+ if (!addLinkMark(tr, state, linkMark)) {
36243
36308
  // In case of an empty selection, insert the textContent of the header and then use
36244
36309
  // that to link-to the header
36245
36310
  const textNode = state.schema.text(node.textContent, [linkMark]);
36246
36311
  tr.replaceSelectionWith(textNode, false);
36247
- dispatch(tr);
36248
- stateChanged(view);
36249
- return true;
36250
- } else {
36251
- // Capture toggleMark's steps rather than let it dispatch, then replay them onto
36252
- // `tr` (setNodeMarkup doesn't shift positions, so they apply cleanly).
36253
- let captured = null;
36254
- toggleMark(linkMark.type, linkMark.attrs)(state, t => { captured = t; });
36255
- if (!captured) return false;
36256
- for (const step of captured.steps) tr.step(step);
36257
- dispatch(tr);
36258
- stateChanged(view);
36259
- return true;
36260
36312
  }
36313
+ dispatch(tr);
36314
+ stateChanged(view);
36315
+ return true;
36261
36316
  };
36262
36317
  return commandAdapter;
36263
36318
  }
@@ -36445,29 +36500,14 @@ function deleteLinkCommand() {
36445
36500
  }
36446
36501
 
36447
36502
  function selectFullLink(view) {
36448
- const linkType = view.state.schema.marks.link;
36449
- const selection = view.state.selection;
36450
-
36451
- // Make sure the selection is in a single text node with a linkType Mark
36452
- const nodePos = [];
36453
- view.state.doc.nodesBetween(selection.from, selection.to, (node, pos) => {
36454
- if (node.isText) {
36455
- nodePos.push({node: node, pos: pos});
36456
- return false;
36457
- } return true;
36458
- });
36459
- if (nodePos.length !== 1) return;
36460
- const selectedNode = nodePos[0].node;
36461
- const selectedPos = nodePos[0].pos;
36462
- const linkMarks = selectedNode && selectedNode.marks.filter(mark => mark.type === linkType);
36463
- if (linkMarks.length !== 1) return;
36464
-
36465
- // Select the entire text of selectedNode
36466
- const anchor = selectedPos;
36467
- const head = anchor + selectedNode.nodeSize;
36468
- const linkSelection = TextSelection.create(view.state.doc, anchor, head);
36469
- const transaction = view.state.tr.setSelection(linkSelection);
36470
- view.dispatch(transaction);
36503
+ // A node selection (a linked image, for example) is left alone
36504
+ if (view.state.selection instanceof NodeSelection) return;
36505
+ const range = linkRangeAt(view.state);
36506
+ if (!range) return;
36507
+ const {from, to} = view.state.selection;
36508
+ if (range.from === from && range.to === to) return;
36509
+ const linkSelection = TextSelection.create(view.state.doc, range.from, range.to);
36510
+ view.dispatch(view.state.tr.setSelection(linkSelection));
36471
36511
  }
36472
36512
 
36473
36513
  /********************************************************************************
@@ -40806,7 +40846,8 @@ function baseKeyString(itemName, keymap) {
40806
40846
  keyString = keyString.replaceAll('Shift', '\u21E7'); // ⇧
40807
40847
  keyString = keyString.replaceAll('Alt', '\u2325'); // ⌥
40808
40848
  keyString = keyString.replaceAll('-', '');
40809
- return keyString
40849
+ // Show a letter key in capitals, as the menu bar does
40850
+ return keyString.replace(/(^|[\u2318\u2303\u21E7\u2325])([a-z])$/, (_, lead, letter) => lead + letter.toUpperCase())
40810
40851
  }
40811
40852
 
40812
40853
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markupeditor",
3
- "version": "0.9.53",
3
+ "version": "0.9.55",
4
4
  "description": "A web component and API for WYSIWYG HTML editing.",
5
5
  "keywords": [
6
6
  "wysiwyg",