prosemirror-view 1.24.0-beta.3 → 1.25.0

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## 1.25.0 (2022-06-01)
2
+
3
+ ### New features
4
+
5
+ Mark views can now be specified with a separate editor prop, `markViews`, to make it easier to get the types right.
6
+
7
+ ## 1.24.1 (2022-05-30)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix a regression that caused the key handler to crash.
12
+
13
+ ## 1.24.0 (2022-05-30)
14
+
15
+ ### Bug fixes
16
+
17
+ Fix an issue where bogus table wrappers could be added when copy-pasting table content nodes within an editor.
18
+
19
+ Fix a crash that could occur when two textblocks were joined in the editor DOM.
20
+
21
+ Treat ctrl-b/f/p/n on macOS similar to arrow key presses, anticipating cursor motion.
22
+
23
+ ### New features
24
+
25
+ Include TypeScript type declarations.
26
+
1
27
  ## 1.23.13 (2022-04-26)
2
28
 
3
29
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -2866,13 +2866,13 @@ function captureKeyDown(view, event) {
2866
2866
  return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodesRight(view);
2867
2867
  } else if (code == 13 || code == 27) {
2868
2868
  return true;
2869
- } else if (code == 37) {
2869
+ } else if (code == 37 || mac && code == 66 && mods == "c") {
2870
2870
  return selectHorizontally(view, -1, mods) || skipIgnoredNodesLeft(view);
2871
- } else if (code == 39) {
2871
+ } else if (code == 39 || mac && code == 70 && mods == "c") {
2872
2872
  return selectHorizontally(view, 1, mods) || skipIgnoredNodesRight(view);
2873
- } else if (code == 38) {
2873
+ } else if (code == 38 || mac && code == 80 && mods == "c") {
2874
2874
  return selectVertically(view, -1, mods) || skipIgnoredNodesLeft(view);
2875
- } else if (code == 40) {
2875
+ } else if (code == 40 || mac && code == 78 && mods == "c") {
2876
2876
  return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesRight(view);
2877
2877
  } else if (mods == (mac ? "m" : "c") && (code == 66 || code == 73 || code == 89 || code == 90)) {
2878
2878
  return true;
@@ -2902,7 +2902,8 @@ function serializeForClipboard(view, slice) {
2902
2902
  document: doc
2903
2903
  }));
2904
2904
  var firstChild = wrap.firstChild,
2905
- needsWrap;
2905
+ needsWrap,
2906
+ wrappers = 0;
2906
2907
 
2907
2908
  while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
2908
2909
  for (var i = needsWrap.length - 1; i >= 0; i--) {
@@ -2913,17 +2914,13 @@ function serializeForClipboard(view, slice) {
2913
2914
  }
2914
2915
 
2915
2916
  wrap.appendChild(wrapper);
2916
-
2917
- if (needsWrap[i] != "tbody") {
2918
- openStart++;
2919
- openEnd++;
2920
- }
2917
+ wrappers++;
2921
2918
  }
2922
2919
 
2923
2920
  firstChild = wrap.firstChild;
2924
2921
  }
2925
2922
 
2926
- if (firstChild && firstChild.nodeType == 1) firstChild.setAttribute("data-pm-slice", "".concat(openStart, " ").concat(openEnd, " ").concat(JSON.stringify(context)));
2923
+ if (firstChild && firstChild.nodeType == 1) firstChild.setAttribute("data-pm-slice", "".concat(openStart, " ").concat(openEnd).concat(wrappers ? " -".concat(wrappers) : "", " ").concat(JSON.stringify(context)));
2927
2924
  var text = view.someProp("clipboardTextSerializer", function (f) {
2928
2925
  return f(slice);
2929
2926
  }) || slice.content.textBetween(0, slice.content.size, "\n\n");
@@ -2969,7 +2966,10 @@ function parseFromClipboard(view, text, html, plainText, $context) {
2969
2966
  }
2970
2967
 
2971
2968
  var contextNode = dom && dom.querySelector("[data-pm-slice]");
2972
- var sliceData = contextNode && /^(\d+) (\d+) (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
2969
+ var sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
2970
+ if (sliceData && sliceData[3]) for (var i = +sliceData[3]; i > 0 && dom.firstChild; i--) {
2971
+ dom = dom.firstChild;
2972
+ }
2973
2973
 
2974
2974
  if (!slice) {
2975
2975
  var parser = view.someProp("clipboardParser") || view.someProp("domParser") || prosemirrorModel.DOMParser.fromSchema(view.state.schema);
@@ -2986,7 +2986,7 @@ function parseFromClipboard(view, text, html, plainText, $context) {
2986
2986
  }
2987
2987
 
2988
2988
  if (sliceData) {
2989
- slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[3]);
2989
+ slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4]);
2990
2990
  } else {
2991
2991
  slice = prosemirrorModel.Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
2992
2992
 
@@ -5111,7 +5111,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5111
5111
 
5112
5112
  var $from = parse.doc.resolveNoCache(change.start - parse.from);
5113
5113
  var $to = parse.doc.resolveNoCache(change.endB - parse.from);
5114
- var inlineChange = $from.sameParent($to) && $from.parent.inlineContent;
5114
+ var $fromA = doc.resolve(change.start);
5115
+ var inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA;
5115
5116
  var nextSel;
5116
5117
 
5117
5118
  if ((ios && view.input.lastIOSEnter > Date.now() - 225 && (!inlineChange || addedNodes.some(function (n) {
@@ -5144,7 +5145,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5144
5145
 
5145
5146
  var chFrom = change.start,
5146
5147
  chTo = change.endA;
5147
- var tr, storedMarks, markChange, $from1;
5148
+ var tr, storedMarks, markChange;
5148
5149
 
5149
5150
  if (inlineChange) {
5150
5151
  if ($from.pos == $to.pos) {
@@ -5157,7 +5158,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5157
5158
 
5158
5159
  tr = view.state.tr["delete"](chFrom, chTo);
5159
5160
  storedMarks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA));
5160
- } else if (change.endA == change.endB && ($from1 = doc.resolve(change.start)) && (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $from1.parent.content.cut($from1.parentOffset, change.endA - $from1.start())))) {
5161
+ } else if (change.endA == change.endB && (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))) {
5161
5162
  tr = view.state.tr;
5162
5163
  if (markChange.type == "add") tr.addMark(chFrom, chTo, markChange.mark);else tr.removeMark(chFrom, chTo, markChange.mark);
5163
5164
  } else if ($from.parent.child($from.index()).isText && $from.index() == $to.index() - ($to.textOffset ? 0 : 1)) {
@@ -5697,11 +5698,15 @@ function selectionContextChanged(sel1, sel2) {
5697
5698
 
5698
5699
  function buildNodeViews(view) {
5699
5700
  var result = Object.create(null);
5700
- view.someProp("nodeViews", function (obj) {
5701
+
5702
+ function add(obj) {
5701
5703
  for (var _prop3 in obj) {
5702
5704
  if (!Object.prototype.hasOwnProperty.call(result, _prop3)) result[_prop3] = obj[_prop3];
5703
5705
  }
5704
- });
5706
+ }
5707
+
5708
+ view.someProp("nodeViews", add);
5709
+ view.someProp("markViews", add);
5705
5710
  return result;
5706
5711
  }
5707
5712
 
package/dist/index.d.ts CHANGED
@@ -536,7 +536,11 @@ declare class EditorView {
536
536
  */
537
537
  dispatch(tr: Transaction): void;
538
538
  }
539
- declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
539
+ declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
540
+ declare type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
541
+ dom: HTMLElement;
542
+ contentDOM?: HTMLElement;
543
+ };
540
544
  /**
541
545
  Helper type that maps event names to event object types, but
542
546
  includes events that TypeScript's HTMLElementEventMap doesn't know
@@ -676,14 +680,12 @@ interface EditorProps {
676
680
  */
677
681
  transformPasted?: (slice: Slice) => Slice;
678
682
  /**
679
- Allows you to pass custom rendering and behavior logic for nodes
680
- and marks. Should map node and mark names to constructor
681
- functions that produce a [`NodeView`](https://prosemirror.net/docs/ref/#view.NodeView) object
682
- implementing the node's display behavior. For nodes, the third
683
- argument `getPos` is a function that can be called to get the
684
- node's current position, which can be useful when creating
685
- transactions to update it. For marks, the third argument is a
686
- boolean that indicates whether the mark's content is inline.
683
+ Allows you to pass custom rendering and behavior logic for
684
+ nodes. Should map node names to constructor functions that
685
+ produce a [`NodeView`](https://prosemirror.net/docs/ref/#view.NodeView) object implementing the
686
+ node's display behavior. The third argument `getPos` is a
687
+ function that can be called to get the node's current position,
688
+ which can be useful when creating transactions to update it.
687
689
 
688
690
  `decorations` is an array of node or inline decorations that are
689
691
  active around the node. They are automatically drawn in the
@@ -697,11 +699,25 @@ interface EditorProps {
697
699
  on the content. But if you, for example, want to create a nested
698
700
  editor with the content, it may make sense to provide it with the
699
701
  inner decorations.
702
+
703
+ (For backwards compatibility reasons, [mark
704
+ views](https://prosemirror.net/docs/ref/#view.ViewProps.markViews) can also be included in this
705
+ object.)
700
706
  */
701
707
  nodeViews?: {
702
708
  [node: string]: NodeViewConstructor;
703
709
  };
704
710
  /**
711
+ Pass custom mark rendering functions. Note that these cannot
712
+ provide the kind of dynamic behavior that [node
713
+ views](https://prosemirror.net/docs/ref/#view.NodeView) can—they just provide custom rendering
714
+ logic. The third argument indicates whether the mark's content
715
+ is inline.
716
+ */
717
+ markViews?: {
718
+ [mark: string]: MarkViewConstructor;
719
+ };
720
+ /**
705
721
  The DOM serializer to use when putting content onto the
706
722
  clipboard. If not given, the result of
707
723
  [`DOMSerializer.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMSerializer^fromSchema)
package/dist/index.js CHANGED
@@ -2509,16 +2509,16 @@ function captureKeyDown(view, event) {
2509
2509
  else if (code == 13 || code == 27) { // Enter, Esc
2510
2510
  return true;
2511
2511
  }
2512
- else if (code == 37) { // Left arrow
2512
+ else if (code == 37 || (mac && code == 66 && mods == "c")) { // Left arrow, Ctrl-b on Mac
2513
2513
  return selectHorizontally(view, -1, mods) || skipIgnoredNodesLeft(view);
2514
2514
  }
2515
- else if (code == 39) { // Right arrow
2515
+ else if (code == 39 || (mac && code == 70 && mods == "c")) { // Right arrow, Ctrl-f on Mac
2516
2516
  return selectHorizontally(view, 1, mods) || skipIgnoredNodesRight(view);
2517
2517
  }
2518
- else if (code == 38) { // Up arrow
2518
+ else if (code == 38 || (mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
2519
2519
  return selectVertically(view, -1, mods) || skipIgnoredNodesLeft(view);
2520
2520
  }
2521
- else if (code == 40) { // Down arrow
2521
+ else if (code == 40 || (mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
2522
2522
  return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesRight(view);
2523
2523
  }
2524
2524
  else if (mods == (mac ? "m" : "c") &&
@@ -2540,22 +2540,19 @@ function serializeForClipboard(view, slice) {
2540
2540
  let serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema);
2541
2541
  let doc = detachedDoc(), wrap = doc.createElement("div");
2542
2542
  wrap.appendChild(serializer.serializeFragment(content, { document: doc }));
2543
- let firstChild = wrap.firstChild, needsWrap;
2543
+ let firstChild = wrap.firstChild, needsWrap, wrappers = 0;
2544
2544
  while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
2545
2545
  for (let i = needsWrap.length - 1; i >= 0; i--) {
2546
2546
  let wrapper = doc.createElement(needsWrap[i]);
2547
2547
  while (wrap.firstChild)
2548
2548
  wrapper.appendChild(wrap.firstChild);
2549
2549
  wrap.appendChild(wrapper);
2550
- if (needsWrap[i] != "tbody") {
2551
- openStart++;
2552
- openEnd++;
2553
- }
2550
+ wrappers++;
2554
2551
  }
2555
2552
  firstChild = wrap.firstChild;
2556
2553
  }
2557
2554
  if (firstChild && firstChild.nodeType == 1)
2558
- firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd} ${JSON.stringify(context)}`);
2555
+ firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`);
2559
2556
  let text = view.someProp("clipboardTextSerializer", f => f(slice)) ||
2560
2557
  slice.content.textBetween(0, slice.content.size, "\n\n");
2561
2558
  return { dom: wrap, text };
@@ -2593,7 +2590,10 @@ function parseFromClipboard(view, text, html, plainText, $context) {
2593
2590
  restoreReplacedSpaces(dom);
2594
2591
  }
2595
2592
  let contextNode = dom && dom.querySelector("[data-pm-slice]");
2596
- let sliceData = contextNode && /^(\d+) (\d+) (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
2593
+ let sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
2594
+ if (sliceData && sliceData[3])
2595
+ for (let i = +sliceData[3]; i > 0 && dom.firstChild; i--)
2596
+ dom = dom.firstChild;
2597
2597
  if (!slice) {
2598
2598
  let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema);
2599
2599
  slice = parser.parseSlice(dom, {
@@ -2608,7 +2608,7 @@ function parseFromClipboard(view, text, html, plainText, $context) {
2608
2608
  });
2609
2609
  }
2610
2610
  if (sliceData) {
2611
- slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[3]);
2611
+ slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4]);
2612
2612
  }
2613
2613
  else { // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
2614
2614
  slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
@@ -4566,7 +4566,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4566
4566
  }
4567
4567
  let $from = parse.doc.resolveNoCache(change.start - parse.from);
4568
4568
  let $to = parse.doc.resolveNoCache(change.endB - parse.from);
4569
- let inlineChange = $from.sameParent($to) && $from.parent.inlineContent;
4569
+ let $fromA = doc.resolve(change.start);
4570
+ let inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA;
4570
4571
  let nextSel;
4571
4572
  // If this looks like the effect of pressing Enter (or was recorded
4572
4573
  // as being an iOS enter press), just dispatch an Enter key instead.
@@ -4609,7 +4610,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4609
4610
  }, 20);
4610
4611
  }
4611
4612
  let chFrom = change.start, chTo = change.endA;
4612
- let tr, storedMarks, markChange, $from1;
4613
+ let tr, storedMarks, markChange;
4613
4614
  if (inlineChange) {
4614
4615
  if ($from.pos == $to.pos) { // Deletion
4615
4616
  // IE11 sometimes weirdly moves the DOM selection around after
@@ -4622,8 +4623,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4622
4623
  storedMarks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA));
4623
4624
  }
4624
4625
  else if ( // Adding or removing a mark
4625
- change.endA == change.endB && ($from1 = doc.resolve(change.start)) &&
4626
- (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $from1.parent.content.cut($from1.parentOffset, change.endA - $from1.start())))) {
4626
+ change.endA == change.endB &&
4627
+ (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset), $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))) {
4627
4628
  tr = view.state.tr;
4628
4629
  if (markChange.type == "add")
4629
4630
  tr.addMark(chFrom, chTo, markChange.mark);
@@ -5225,11 +5226,13 @@ function selectionContextChanged(sel1, sel2) {
5225
5226
  }
5226
5227
  function buildNodeViews(view) {
5227
5228
  let result = Object.create(null);
5228
- view.someProp("nodeViews", obj => {
5229
+ function add(obj) {
5229
5230
  for (let prop in obj)
5230
5231
  if (!Object.prototype.hasOwnProperty.call(result, prop))
5231
5232
  result[prop] = obj[prop];
5232
- });
5233
+ }
5234
+ view.someProp("nodeViews", add);
5235
+ view.someProp("markViews", add);
5233
5236
  return result;
5234
5237
  }
5235
5238
  function changedNodeViews(a, b) {
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.24.0-beta.3",
3
+ "version": "1.25.0",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "exports": {
10
- "import": "./dist/index.js",
11
- "require": "./dist/index.cjs"
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ },
14
+ "./style/prosemirror.css": "./style/prosemirror.css"
12
15
  },
13
16
  "sideEffects": false,
14
17
  "style": "style/prosemirror.css",
@@ -256,13 +256,13 @@ export function captureKeyDown(view: EditorView, event: KeyboardEvent) {
256
256
  return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodesRight(view)
257
257
  } else if (code == 13 || code == 27) { // Enter, Esc
258
258
  return true
259
- } else if (code == 37) { // Left arrow
259
+ } else if (code == 37 || (browser.mac && code == 66 && mods == "c")) { // Left arrow, Ctrl-b on Mac
260
260
  return selectHorizontally(view, -1, mods) || skipIgnoredNodesLeft(view)
261
- } else if (code == 39) { // Right arrow
261
+ } else if (code == 39 || (browser.mac && code == 70 && mods == "c")) { // Right arrow, Ctrl-f on Mac
262
262
  return selectHorizontally(view, 1, mods) || skipIgnoredNodesRight(view)
263
- } else if (code == 38) { // Up arrow
263
+ } else if (code == 38 || (browser.mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
264
264
  return selectVertically(view, -1, mods) || skipIgnoredNodesLeft(view)
265
- } else if (code == 40) { // Down arrow
265
+ } else if (code == 40 || (browser.mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
266
266
  return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesRight(view)
267
267
  } else if (mods == (browser.mac ? "m" : "c") &&
268
268
  (code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
package/src/clipboard.ts CHANGED
@@ -16,22 +16,20 @@ export function serializeForClipboard(view: EditorView, slice: Slice) {
16
16
  let doc = detachedDoc(), wrap = doc.createElement("div")
17
17
  wrap.appendChild(serializer.serializeFragment(content, {document: doc}))
18
18
 
19
- let firstChild = wrap.firstChild, needsWrap
19
+ let firstChild = wrap.firstChild, needsWrap, wrappers = 0
20
20
  while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
21
21
  for (let i = needsWrap.length - 1; i >= 0; i--) {
22
22
  let wrapper = doc.createElement(needsWrap[i])
23
23
  while (wrap.firstChild) wrapper.appendChild(wrap.firstChild)
24
24
  wrap.appendChild(wrapper)
25
- if (needsWrap[i] != "tbody") {
26
- openStart++
27
- openEnd++
28
- }
25
+ wrappers++
29
26
  }
30
27
  firstChild = wrap.firstChild
31
28
  }
32
29
 
33
30
  if (firstChild && firstChild.nodeType == 1)
34
- (firstChild as HTMLElement).setAttribute("data-pm-slice", `${openStart} ${openEnd} ${JSON.stringify(context)}`)
31
+ (firstChild as HTMLElement).setAttribute(
32
+ "data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`)
35
33
 
36
34
  let text = view.someProp("clipboardTextSerializer", f => f(slice)) ||
37
35
  slice.content.textBetween(0, slice.content.size, "\n\n")
@@ -67,7 +65,10 @@ export function parseFromClipboard(view: EditorView, text: string, html: string
67
65
  }
68
66
 
69
67
  let contextNode = dom && dom.querySelector("[data-pm-slice]")
70
- let sliceData = contextNode && /^(\d+) (\d+) (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "")
68
+ let sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "")
69
+ if (sliceData && sliceData[3]) for (let i = +sliceData[3]; i > 0 && dom!.firstChild; i--)
70
+ dom = dom!.firstChild as HTMLElement
71
+
71
72
  if (!slice) {
72
73
  let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema)
73
74
  slice = parser.parseSlice(dom!, {
@@ -81,7 +82,7 @@ export function parseFromClipboard(view: EditorView, text: string, html: string
81
82
  })
82
83
  }
83
84
  if (sliceData) {
84
- slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[3])
85
+ slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4])
85
86
  } else { // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
86
87
  slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true)
87
88
  if (slice.openStart || slice.openEnd) {
package/src/domchange.ts CHANGED
@@ -167,7 +167,8 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
167
167
 
168
168
  let $from = parse.doc.resolveNoCache(change.start - parse.from)
169
169
  let $to = parse.doc.resolveNoCache(change.endB - parse.from)
170
- let inlineChange = $from.sameParent($to) && $from.parent.inlineContent
170
+ let $fromA = doc.resolve(change.start)
171
+ let inlineChange = $from.sameParent($to) && $from.parent.inlineContent && $fromA.end() >= change.endA
171
172
  let nextSel
172
173
  // If this looks like the effect of pressing Enter (or was recorded
173
174
  // as being an iOS enter press), just dispatch an Enter key instead.
@@ -213,7 +214,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
213
214
 
214
215
  let chFrom = change.start, chTo = change.endA
215
216
 
216
- let tr, storedMarks, markChange, $from1
217
+ let tr, storedMarks, markChange
217
218
  if (inlineChange) {
218
219
  if ($from.pos == $to.pos) { // Deletion
219
220
  // IE11 sometimes weirdly moves the DOM selection around after
@@ -225,9 +226,9 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
225
226
  tr = view.state.tr.delete(chFrom, chTo)
226
227
  storedMarks = doc.resolve(change.start).marksAcross(doc.resolve(change.endA))
227
228
  } else if ( // Adding or removing a mark
228
- change.endA == change.endB && ($from1 = doc.resolve(change.start)) &&
229
+ change.endA == change.endB &&
229
230
  (markChange = isMarkChange($from.parent.content.cut($from.parentOffset, $to.parentOffset),
230
- $from1.parent.content.cut($from1.parentOffset, change.endA - $from1.start())))
231
+ $fromA.parent.content.cut($fromA.parentOffset, change.endA - $fromA.start())))
231
232
  ) {
232
233
  tr = view.state.tr
233
234
  if (markChange.type == "add") tr.addMark(chFrom, chTo, markChange.mark)
package/src/index.ts CHANGED
@@ -42,7 +42,7 @@ export class EditorView {
42
42
  /// @internal
43
43
  cursorWrapper: {dom: DOMNode, deco: Decoration} | null = null
44
44
  /// @internal
45
- nodeViews: {[node: string]: NodeViewConstructor}
45
+ nodeViews: NodeViewSet
46
46
  /// @internal
47
47
  lastSelectedViewDesc: ViewDesc | undefined = undefined
48
48
  /// @internal
@@ -472,15 +472,17 @@ function selectionContextChanged(sel1: Selection, sel2: Selection) {
472
472
  }
473
473
 
474
474
  function buildNodeViews(view: EditorView) {
475
- let result: {[node: string]: NodeViewConstructor} = Object.create(null)
476
- view.someProp("nodeViews", obj => {
475
+ let result: NodeViewSet = Object.create(null)
476
+ function add(obj: NodeViewSet) {
477
477
  for (let prop in obj) if (!Object.prototype.hasOwnProperty.call(result, prop))
478
478
  result[prop] = obj[prop]
479
- })
479
+ }
480
+ view.someProp("nodeViews", add)
481
+ view.someProp("markViews", add)
480
482
  return result
481
483
  }
482
484
 
483
- function changedNodeViews(a: {[node: string]: NodeViewConstructor}, b: {[node: string]: NodeViewConstructor}) {
485
+ function changedNodeViews(a: NodeViewSet, b: NodeViewSet) {
484
486
  let nA = 0, nB = 0
485
487
  for (let prop in a) {
486
488
  if (a[prop] != b[prop]) return true
@@ -495,8 +497,11 @@ function checkStateComponent(plugin: Plugin) {
495
497
  throw new RangeError("Plugins passed directly to the view must not have a state component")
496
498
  }
497
499
 
498
- type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined,
500
+ type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number,
499
501
  decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView
502
+ type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {dom: HTMLElement, contentDOM?: HTMLElement}
503
+
504
+ type NodeViewSet = {[name: string]: NodeViewConstructor | MarkViewConstructor}
500
505
 
501
506
  /// Helper type that maps event names to event object types, but
502
507
  /// includes events that TypeScript's HTMLElementEventMap doesn't know
@@ -613,14 +618,12 @@ export interface EditorProps {
613
618
  /// the document.
614
619
  transformPasted?: (slice: Slice) => Slice
615
620
 
616
- /// Allows you to pass custom rendering and behavior logic for nodes
617
- /// and marks. Should map node and mark names to constructor
618
- /// functions that produce a [`NodeView`](#view.NodeView) object
619
- /// implementing the node's display behavior. For nodes, the third
620
- /// argument `getPos` is a function that can be called to get the
621
- /// node's current position, which can be useful when creating
622
- /// transactions to update it. For marks, the third argument is a
623
- /// boolean that indicates whether the mark's content is inline.
621
+ /// Allows you to pass custom rendering and behavior logic for
622
+ /// nodes. Should map node names to constructor functions that
623
+ /// produce a [`NodeView`](#view.NodeView) object implementing the
624
+ /// node's display behavior. The third argument `getPos` is a
625
+ /// function that can be called to get the node's current position,
626
+ /// which can be useful when creating transactions to update it.
624
627
  ///
625
628
  /// `decorations` is an array of node or inline decorations that are
626
629
  /// active around the node. They are automatically drawn in the
@@ -634,8 +637,19 @@ export interface EditorProps {
634
637
  /// on the content. But if you, for example, want to create a nested
635
638
  /// editor with the content, it may make sense to provide it with the
636
639
  /// inner decorations.
640
+ ///
641
+ /// (For backwards compatibility reasons, [mark
642
+ /// views](#view.ViewProps.markViews) can also be included in this
643
+ /// object.)
637
644
  nodeViews?: {[node: string]: NodeViewConstructor}
638
645
 
646
+ /// Pass custom mark rendering functions. Note that these cannot
647
+ /// provide the kind of dynamic behavior that [node
648
+ /// views](#view.NodeView) can—they just provide custom rendering
649
+ /// logic. The third argument indicates whether the mark's content
650
+ /// is inline.
651
+ markViews?: {[mark: string]: MarkViewConstructor}
652
+
639
653
  /// The DOM serializer to use when putting content onto the
640
654
  /// clipboard. If not given, the result of
641
655
  /// [`DOMSerializer.fromSchema`](#model.DOMSerializer^fromSchema)
package/src/viewdesc.ts CHANGED
@@ -565,9 +565,9 @@ class MarkViewDesc extends ViewDesc {
565
565
 
566
566
  static create(parent: ViewDesc, mark: Mark, inline: boolean, view: EditorView) {
567
567
  let custom = view.nodeViews[mark.type.name]
568
- let spec: NodeView = custom && (custom as any)(mark, view, inline)
568
+ let spec: {dom: HTMLElement, contentDOM?: HTMLElement} = custom && (custom as any)(mark, view, inline)
569
569
  if (!spec || !spec.dom)
570
- spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM!(mark, inline))
570
+ spec = DOMSerializer.renderSpec(document, mark.type.spec.toDOM!(mark, inline)) as any
571
571
  return new MarkViewDesc(parent, mark, spec.dom, spec.contentDOM || spec.dom as HTMLElement)
572
572
  }
573
573
 
@@ -631,7 +631,7 @@ export class NodeViewDesc extends ViewDesc {
631
631
  static create(parent: ViewDesc | undefined, node: Node, outerDeco: readonly Decoration[],
632
632
  innerDeco: DecorationSource, view: EditorView, pos: number) {
633
633
  let custom = view.nodeViews[node.type.name], descObj: ViewDesc
634
- let spec = custom && custom(node, view, () => {
634
+ let spec: NodeView | undefined = custom && (custom as any)(node, view, () => {
635
635
  // (This is a function that allows the custom view to find its
636
636
  // own position)
637
637
  if (!descObj) return pos