prosemirror-view 1.31.7 → 1.32.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,25 @@
1
+ ## 1.32.0 (2023-09-27)
2
+
3
+ ### Bug fixes
4
+
5
+ Work around an issue on Chrome where, when clicking above the right side of an uneditable node, the cursor would be put after the node.
6
+
7
+ Fix a bug that could corrupt the composition when it occurs next to text identical to the composed characters.
8
+
9
+ ### New features
10
+
11
+ The new `EditorView.updateRoot` method can be used to make the editor update its DOM root when it is moved to a new document or shadow tree.
12
+
13
+ ## 1.31.8 (2023-09-12)
14
+
15
+ ### Bug fixes
16
+
17
+ Fix a bug that broke arrow motion near uneditable nodes on Chrome in some situations.
18
+
19
+ Fix an issue that broke backspace at the end of a textblock that ended in a newline on Chrome.
20
+
21
+ Fix a crash in the way inline decorations that ended right after a node end position were drawn.
22
+
1
23
  ## 1.31.7 (2023-07-28)
2
24
 
3
25
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -32,10 +32,6 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+
32
32
 
33
33
  function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
34
34
 
35
- Object.defineProperty(exports, '__esModule', {
36
- value: true
37
- });
38
-
39
35
  var prosemirrorState = require('prosemirror-state');
40
36
 
41
37
  var prosemirrorModel = require('prosemirror-model');
@@ -517,6 +513,8 @@ function _posAtCoords(view, coords) {
517
513
  }
518
514
  }
519
515
 
516
+ var prev;
517
+ if (webkit && offset && node.nodeType == 1 && (prev = node.childNodes[offset - 1]).nodeType == 1 && prev.contentEditable == "false" && prev.getBoundingClientRect().top >= coords.top) offset--;
520
518
  if (node == view.dom && offset == node.childNodes.length - 1 && node.lastChild.nodeType == 1 && coords.top > node.lastChild.getBoundingClientRect().bottom) pos = view.state.doc.content.size;else if (offset == 0 || node.nodeType != 1 || node.childNodes[offset - 1].nodeName != "BR") pos = posFromCaret(view, node, offset, coords);
521
519
  }
522
520
 
@@ -2190,9 +2188,13 @@ var ViewTreeUpdater = function () {
2190
2188
  return true;
2191
2189
  } else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
2192
2190
  this.top.children[this.index] = updated;
2193
- updated.dirty = CONTENT_DIRTY;
2194
- updated.updateChildren(view, pos + 1);
2195
- updated.dirty = NOT_DIRTY;
2191
+
2192
+ if (updated.contentDOM) {
2193
+ updated.dirty = CONTENT_DIRTY;
2194
+ updated.updateChildren(view, pos + 1);
2195
+ updated.dirty = NOT_DIRTY;
2196
+ }
2197
+
2196
2198
  this.changed = true;
2197
2199
  this.index++;
2198
2200
  return true;
@@ -2209,25 +2211,27 @@ var ViewTreeUpdater = function () {
2209
2211
  value: function recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
2210
2212
  if (next.dirty || node.isAtom || !next.children.length || !next.node.content.eq(node.content)) return null;
2211
2213
  var wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
2212
- if (!wrapper.contentDOM) return null;
2213
- wrapper.children = next.children;
2214
- next.children = [];
2215
- next.destroy();
2216
2214
 
2217
- var _iterator = _createForOfIteratorHelper(wrapper.children),
2218
- _step;
2215
+ if (wrapper.contentDOM) {
2216
+ wrapper.children = next.children;
2217
+ next.children = [];
2219
2218
 
2220
- try {
2221
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
2222
- var ch = _step.value;
2223
- ch.parent = wrapper;
2219
+ var _iterator = _createForOfIteratorHelper(wrapper.children),
2220
+ _step;
2221
+
2222
+ try {
2223
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
2224
+ var ch = _step.value;
2225
+ ch.parent = wrapper;
2226
+ }
2227
+ } catch (err) {
2228
+ _iterator.e(err);
2229
+ } finally {
2230
+ _iterator.f();
2224
2231
  }
2225
- } catch (err) {
2226
- _iterator.e(err);
2227
- } finally {
2228
- _iterator.f();
2229
2232
  }
2230
2233
 
2234
+ next.destroy();
2231
2235
  return wrapper;
2232
2236
  }
2233
2237
  }, {
@@ -2364,14 +2368,18 @@ function iterDeco(parent, deco, onWidget, onNode) {
2364
2368
  restNode = null;
2365
2369
 
2366
2370
  for (var parentIndex = 0;;) {
2367
- if (decoIndex < locals.length && locals[decoIndex].to == offset) {
2368
- var widget = locals[decoIndex++],
2369
- widgets = void 0;
2371
+ var widget = void 0,
2372
+ widgets = void 0;
2373
+
2374
+ while (decoIndex < locals.length && locals[decoIndex].to == offset) {
2375
+ var next = locals[decoIndex++];
2370
2376
 
2371
- while (decoIndex < locals.length && locals[decoIndex].to == offset) {
2372
- (widgets || (widgets = [widget])).push(locals[decoIndex++]);
2377
+ if (next.widget) {
2378
+ if (!widget) widget = next;else (widgets || (widgets = [widget])).push(next);
2373
2379
  }
2380
+ }
2374
2381
 
2382
+ if (widget) {
2375
2383
  if (widgets) {
2376
2384
  widgets.sort(compareSide);
2377
2385
 
@@ -2473,6 +2481,7 @@ function findTextInFragment(frag, text, from, to) {
2473
2481
  }
2474
2482
 
2475
2483
  if (pos >= from) {
2484
+ if (pos >= to && str.slice(to - text.length - childStart, to - childStart) == text) return to - text.length;
2476
2485
  var found = childStart < to ? str.lastIndexOf(text, to - childStart - 1) : -1;
2477
2486
  if (found >= 0 && found + text.length + childStart >= from) return childStart + found;
2478
2487
  if (from == to && str.length >= to + text.length - childStart && str.slice(to - childStart, to - childStart + text.length) == text) return to;
@@ -2773,10 +2782,10 @@ function nodeLen(node) {
2773
2782
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
2774
2783
  }
2775
2784
 
2776
- function isIgnorable(dom) {
2785
+ function isIgnorable(dom, dir) {
2777
2786
  if (dom.contentEditable == "false") return true;
2778
2787
  var desc = dom.pmViewDesc;
2779
- return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
2788
+ return desc && desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR");
2780
2789
  }
2781
2790
 
2782
2791
  function skipIgnoredNodes(view, dir) {
@@ -2791,7 +2800,7 @@ function skipIgnoredNodesBefore(view) {
2791
2800
  var moveNode,
2792
2801
  moveOffset,
2793
2802
  force = false;
2794
- if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset])) force = true;
2803
+ if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset], -1)) force = true;
2795
2804
 
2796
2805
  for (;;) {
2797
2806
  if (offset > 0) {
@@ -2800,7 +2809,7 @@ function skipIgnoredNodesBefore(view) {
2800
2809
  } else {
2801
2810
  var before = node.childNodes[offset - 1];
2802
2811
 
2803
- if (isIgnorable(before)) {
2812
+ if (isIgnorable(before, -1)) {
2804
2813
  moveNode = node;
2805
2814
  moveOffset = --offset;
2806
2815
  } else if (before.nodeType == 3) {
@@ -2813,7 +2822,7 @@ function skipIgnoredNodesBefore(view) {
2813
2822
  } else {
2814
2823
  var prev = node.previousSibling;
2815
2824
 
2816
- while (prev && isIgnorable(prev)) {
2825
+ while (prev && isIgnorable(prev, -1)) {
2817
2826
  moveNode = node.parentNode;
2818
2827
  moveOffset = domIndex(prev);
2819
2828
  prev = prev.previousSibling;
@@ -2846,7 +2855,7 @@ function skipIgnoredNodesAfter(view) {
2846
2855
  if (node.nodeType != 1) break;
2847
2856
  var after = node.childNodes[offset];
2848
2857
 
2849
- if (isIgnorable(after)) {
2858
+ if (isIgnorable(after, 1)) {
2850
2859
  moveNode = node;
2851
2860
  moveOffset = ++offset;
2852
2861
  } else break;
@@ -2855,7 +2864,7 @@ function skipIgnoredNodesAfter(view) {
2855
2864
  } else {
2856
2865
  var next = node.nextSibling;
2857
2866
 
2858
- while (next && isIgnorable(next)) {
2867
+ while (next && isIgnorable(next, 1)) {
2859
2868
  moveNode = next.parentNode;
2860
2869
  moveOffset = domIndex(next) + 1;
2861
2870
  next = next.nextSibling;
@@ -2888,8 +2897,10 @@ function textNodeAfter(node, offset) {
2888
2897
  }
2889
2898
 
2890
2899
  while (node && offset < node.childNodes.length) {
2891
- node = node.childNodes[offset];
2892
- if (node.nodeType == 3) return node;
2900
+ var next = node.childNodes[offset];
2901
+ if (next.nodeType == 3) return next;
2902
+ if (next.nodeType == 1 && next.contentEditable == "false") break;
2903
+ node = next;
2893
2904
  offset = 0;
2894
2905
  }
2895
2906
  }
@@ -2901,8 +2912,10 @@ function textNodeBefore(node, offset) {
2901
2912
  }
2902
2913
 
2903
2914
  while (node && offset) {
2904
- node = node.childNodes[offset - 1];
2905
- if (node.nodeType == 3) return node;
2915
+ var next = node.childNodes[offset - 1];
2916
+ if (next.nodeType == 3) return next;
2917
+ if (next.nodeType == 1 && next.contentEditable == "false") break;
2918
+ node = next;
2906
2919
  offset = node.childNodes.length;
2907
2920
  }
2908
2921
  }
@@ -3059,7 +3072,7 @@ function captureKeyDown(view, event) {
3059
3072
  } else if (code == 38 || mac && code == 80 && mods == "c") {
3060
3073
  return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
3061
3074
  } else if (code == 40 || mac && code == 78 && mods == "c") {
3062
- return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view);
3075
+ return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1);
3063
3076
  } else if (mods == (mac ? "m" : "c") && (code == 66 || code == 73 || code == 89 || code == 90)) {
3064
3077
  return true;
3065
3078
  }
@@ -4296,6 +4309,11 @@ var Decoration = function () {
4296
4309
  get: function get() {
4297
4310
  return this.type instanceof InlineType;
4298
4311
  }
4312
+ }, {
4313
+ key: "widget",
4314
+ get: function get() {
4315
+ return this.type instanceof WidgetType;
4316
+ }
4299
4317
  }], [{
4300
4318
  key: "widget",
4301
4319
  value: function widget(pos, toDOM, spec) {
@@ -5928,6 +5946,11 @@ var EditorView = function () {
5928
5946
 
5929
5947
  return cached || document;
5930
5948
  }
5949
+ }, {
5950
+ key: "updateRoot",
5951
+ value: function updateRoot() {
5952
+ this._root = null;
5953
+ }
5931
5954
  }, {
5932
5955
  key: "posAtCoords",
5933
5956
  value: function posAtCoords(coords) {
package/dist/index.d.cts CHANGED
@@ -2,9 +2,9 @@ import { EditorState, Transaction, Selection, Plugin } from 'prosemirror-state';
2
2
  import { Mark, Node, ParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
3
3
  import { Mapping } from 'prosemirror-transform';
4
4
 
5
- declare type DOMNode = InstanceType<typeof window.Node>;
5
+ type DOMNode = InstanceType<typeof window.Node>;
6
6
 
7
- declare type WidgetConstructor = ((view: EditorView, getPos: () => number | undefined) => DOMNode) | DOMNode;
7
+ type WidgetConstructor = ((view: EditorView, getPos: () => number | undefined) => DOMNode) | DOMNode;
8
8
  /**
9
9
  Decoration objects can be provided to the view through the
10
10
  [`decorations` prop](https://prosemirror.net/docs/ref/#view.EditorProps.decorations). They come in
@@ -125,7 +125,7 @@ A set of attributes to add to a decorated node. Most properties
125
125
  simply directly correspond to DOM attributes of the same name,
126
126
  which will be set to the property's value. These are exceptions:
127
127
  */
128
- declare type DecorationAttrs = {
128
+ type DecorationAttrs = {
129
129
  /**
130
130
  When non-null, the target node is wrapped in a DOM element of
131
131
  this type (and the other attributes are applied to this element).
@@ -166,7 +166,8 @@ updates create a new value.
166
166
  declare class DecorationSet implements DecorationSource {
167
167
  /**
168
168
  Create a set of decorations, using the structure of the given
169
- document.
169
+ document. This will consume (modify) the `decorations` array, so
170
+ you must make a copy if you want need to preserve that.
170
171
  */
171
172
  static create(doc: Node, decorations: Decoration[]): DecorationSet;
172
173
  /**
@@ -193,8 +194,9 @@ declare class DecorationSet implements DecorationSource {
193
194
  }): DecorationSet;
194
195
  /**
195
196
  Add the given array of decorations to the ones in the set,
196
- producing a new set. Needs access to the current document to
197
- create the appropriate tree structure.
197
+ producing a new set. Consumes the `decorations` array. Needs
198
+ access to the current document to create the appropriate tree
199
+ structure.
198
200
  */
199
201
  add(doc: Node, decorations: Decoration[]): DecorationSet;
200
202
  private addInner;
@@ -356,7 +358,7 @@ declare class NodeViewDesc extends ViewDesc {
356
358
  parseRule(): ParseRule | null;
357
359
  matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
358
360
  get size(): number;
359
- get border(): 1 | 0;
361
+ get border(): 0 | 1;
360
362
  updateChildren(view: EditorView, pos: number): void;
361
363
  localCompositionInfo(view: EditorView, pos: number): {
362
364
  node: Text;
@@ -490,6 +492,11 @@ declare class EditorView {
490
492
  */
491
493
  get root(): Document | ShadowRoot;
492
494
  /**
495
+ When an existing editor view is moved to a new document or
496
+ shadow tree, call this to make it recompute its root.
497
+ */
498
+ updateRoot(): void;
499
+ /**
493
500
  Given a pair of viewport coordinates, return the document
494
501
  position that corresponds to them. May return null if the given
495
502
  coordinates aren't inside of the editor. When an object is
@@ -604,12 +611,12 @@ declare class EditorView {
604
611
  The type of function [provided](https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews) to
605
612
  create [node views](https://prosemirror.net/docs/ref/#view.NodeView).
606
613
  */
607
- declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
614
+ type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
608
615
  /**
609
616
  The function types [used](https://prosemirror.net/docs/ref/#view.EditorProps.markViews) to create
610
617
  mark views.
611
618
  */
612
- declare type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
619
+ type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
613
620
  dom: HTMLElement;
614
621
  contentDOM?: HTMLElement;
615
622
  };
package/dist/index.d.ts CHANGED
@@ -2,9 +2,9 @@ import { EditorState, Transaction, Selection, Plugin } from 'prosemirror-state';
2
2
  import { Mark, Node, ParseRule, Slice, ResolvedPos, DOMParser, DOMSerializer } from 'prosemirror-model';
3
3
  import { Mapping } from 'prosemirror-transform';
4
4
 
5
- declare type DOMNode = InstanceType<typeof window.Node>;
5
+ type DOMNode = InstanceType<typeof window.Node>;
6
6
 
7
- declare type WidgetConstructor = ((view: EditorView, getPos: () => number | undefined) => DOMNode) | DOMNode;
7
+ type WidgetConstructor = ((view: EditorView, getPos: () => number | undefined) => DOMNode) | DOMNode;
8
8
  /**
9
9
  Decoration objects can be provided to the view through the
10
10
  [`decorations` prop](https://prosemirror.net/docs/ref/#view.EditorProps.decorations). They come in
@@ -125,7 +125,7 @@ A set of attributes to add to a decorated node. Most properties
125
125
  simply directly correspond to DOM attributes of the same name,
126
126
  which will be set to the property's value. These are exceptions:
127
127
  */
128
- declare type DecorationAttrs = {
128
+ type DecorationAttrs = {
129
129
  /**
130
130
  When non-null, the target node is wrapped in a DOM element of
131
131
  this type (and the other attributes are applied to this element).
@@ -166,7 +166,8 @@ updates create a new value.
166
166
  declare class DecorationSet implements DecorationSource {
167
167
  /**
168
168
  Create a set of decorations, using the structure of the given
169
- document.
169
+ document. This will consume (modify) the `decorations` array, so
170
+ you must make a copy if you want need to preserve that.
170
171
  */
171
172
  static create(doc: Node, decorations: Decoration[]): DecorationSet;
172
173
  /**
@@ -193,8 +194,9 @@ declare class DecorationSet implements DecorationSource {
193
194
  }): DecorationSet;
194
195
  /**
195
196
  Add the given array of decorations to the ones in the set,
196
- producing a new set. Needs access to the current document to
197
- create the appropriate tree structure.
197
+ producing a new set. Consumes the `decorations` array. Needs
198
+ access to the current document to create the appropriate tree
199
+ structure.
198
200
  */
199
201
  add(doc: Node, decorations: Decoration[]): DecorationSet;
200
202
  private addInner;
@@ -356,7 +358,7 @@ declare class NodeViewDesc extends ViewDesc {
356
358
  parseRule(): ParseRule | null;
357
359
  matchesNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource): boolean;
358
360
  get size(): number;
359
- get border(): 1 | 0;
361
+ get border(): 0 | 1;
360
362
  updateChildren(view: EditorView, pos: number): void;
361
363
  localCompositionInfo(view: EditorView, pos: number): {
362
364
  node: Text;
@@ -490,6 +492,11 @@ declare class EditorView {
490
492
  */
491
493
  get root(): Document | ShadowRoot;
492
494
  /**
495
+ When an existing editor view is moved to a new document or
496
+ shadow tree, call this to make it recompute its root.
497
+ */
498
+ updateRoot(): void;
499
+ /**
493
500
  Given a pair of viewport coordinates, return the document
494
501
  position that corresponds to them. May return null if the given
495
502
  coordinates aren't inside of the editor. When an object is
@@ -604,12 +611,12 @@ declare class EditorView {
604
611
  The type of function [provided](https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews) to
605
612
  create [node views](https://prosemirror.net/docs/ref/#view.NodeView).
606
613
  */
607
- declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
614
+ type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
608
615
  /**
609
616
  The function types [used](https://prosemirror.net/docs/ref/#view.EditorProps.markViews) to create
610
617
  mark views.
611
618
  */
612
- declare type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
619
+ type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
613
620
  dom: HTMLElement;
614
621
  contentDOM?: HTMLElement;
615
622
  };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { TextSelection, NodeSelection, Selection, AllSelection } from 'prosemirror-state';
1
+ import { TextSelection, NodeSelection, AllSelection, Selection } from 'prosemirror-state';
2
2
  import { DOMSerializer, Fragment, Mark, Slice, DOMParser } from 'prosemirror-model';
3
3
  import { dropPoint } from 'prosemirror-transform';
4
4
 
@@ -426,6 +426,11 @@ function posAtCoords(view, coords) {
426
426
  offset++;
427
427
  }
428
428
  }
429
+ let prev;
430
+ // When clicking above the right side of an uneditable node, Chrome will report a cursor position after that node.
431
+ if (webkit && offset && node.nodeType == 1 && (prev = node.childNodes[offset - 1]).nodeType == 1 &&
432
+ prev.contentEditable == "false" && prev.getBoundingClientRect().top >= coords.top)
433
+ offset--;
429
434
  // Suspiciously specific kludge to work around caret*FromPoint
430
435
  // never returning a position at the end of the document
431
436
  if (node == view.dom && offset == node.childNodes.length - 1 && node.lastChild.nodeType == 1 &&
@@ -1810,9 +1815,11 @@ class ViewTreeUpdater {
1810
1815
  }
1811
1816
  else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
1812
1817
  this.top.children[this.index] = updated;
1813
- updated.dirty = CONTENT_DIRTY;
1814
- updated.updateChildren(view, pos + 1);
1815
- updated.dirty = NOT_DIRTY;
1818
+ if (updated.contentDOM) {
1819
+ updated.dirty = CONTENT_DIRTY;
1820
+ updated.updateChildren(view, pos + 1);
1821
+ updated.dirty = NOT_DIRTY;
1822
+ }
1816
1823
  this.changed = true;
1817
1824
  this.index++;
1818
1825
  return true;
@@ -1829,13 +1836,13 @@ class ViewTreeUpdater {
1829
1836
  !next.node.content.eq(node.content))
1830
1837
  return null;
1831
1838
  let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
1832
- if (!wrapper.contentDOM)
1833
- return null;
1834
- wrapper.children = next.children;
1835
- next.children = [];
1839
+ if (wrapper.contentDOM) {
1840
+ wrapper.children = next.children;
1841
+ next.children = [];
1842
+ for (let ch of wrapper.children)
1843
+ ch.parent = wrapper;
1844
+ }
1836
1845
  next.destroy();
1837
- for (let ch of wrapper.children)
1838
- ch.parent = wrapper;
1839
1846
  return wrapper;
1840
1847
  }
1841
1848
  // Insert the node as a newly created node desc.
@@ -1963,10 +1970,17 @@ function iterDeco(parent, deco, onWidget, onNode) {
1963
1970
  }
1964
1971
  let decoIndex = 0, active = [], restNode = null;
1965
1972
  for (let parentIndex = 0;;) {
1966
- if (decoIndex < locals.length && locals[decoIndex].to == offset) {
1967
- let widget = locals[decoIndex++], widgets;
1968
- while (decoIndex < locals.length && locals[decoIndex].to == offset)
1969
- (widgets || (widgets = [widget])).push(locals[decoIndex++]);
1973
+ let widget, widgets;
1974
+ while (decoIndex < locals.length && locals[decoIndex].to == offset) {
1975
+ let next = locals[decoIndex++];
1976
+ if (next.widget) {
1977
+ if (!widget)
1978
+ widget = next;
1979
+ else
1980
+ (widgets || (widgets = [widget])).push(next);
1981
+ }
1982
+ }
1983
+ if (widget) {
1970
1984
  if (widgets) {
1971
1985
  widgets.sort(compareSide);
1972
1986
  for (let i = 0; i < widgets.length; i++)
@@ -2059,6 +2073,8 @@ function findTextInFragment(frag, text, from, to) {
2059
2073
  str += next.text;
2060
2074
  }
2061
2075
  if (pos >= from) {
2076
+ if (pos >= to && str.slice(to - text.length - childStart, to - childStart) == text)
2077
+ return to - text.length;
2062
2078
  let found = childStart < to ? str.lastIndexOf(text, to - childStart - 1) : -1;
2063
2079
  if (found >= 0 && found + text.length + childStart >= from)
2064
2080
  return childStart + found;
@@ -2355,11 +2371,11 @@ function selectHorizontally(view, dir, mods) {
2355
2371
  function nodeLen(node) {
2356
2372
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
2357
2373
  }
2358
- function isIgnorable(dom) {
2374
+ function isIgnorable(dom, dir) {
2359
2375
  if (dom.contentEditable == "false")
2360
2376
  return true;
2361
2377
  let desc = dom.pmViewDesc;
2362
- return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
2378
+ return desc && desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR");
2363
2379
  }
2364
2380
  function skipIgnoredNodes(view, dir) {
2365
2381
  return dir < 0 ? skipIgnoredNodesBefore(view) : skipIgnoredNodesAfter(view);
@@ -2375,7 +2391,7 @@ function skipIgnoredNodesBefore(view) {
2375
2391
  // Gecko will do odd things when the selection is directly in front
2376
2392
  // of a non-editable node, so in that case, move it into the next
2377
2393
  // node if possible. Issue prosemirror/prosemirror#832.
2378
- if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset]))
2394
+ if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset], -1))
2379
2395
  force = true;
2380
2396
  for (;;) {
2381
2397
  if (offset > 0) {
@@ -2384,7 +2400,7 @@ function skipIgnoredNodesBefore(view) {
2384
2400
  }
2385
2401
  else {
2386
2402
  let before = node.childNodes[offset - 1];
2387
- if (isIgnorable(before)) {
2403
+ if (isIgnorable(before, -1)) {
2388
2404
  moveNode = node;
2389
2405
  moveOffset = --offset;
2390
2406
  }
@@ -2401,7 +2417,7 @@ function skipIgnoredNodesBefore(view) {
2401
2417
  }
2402
2418
  else {
2403
2419
  let prev = node.previousSibling;
2404
- while (prev && isIgnorable(prev)) {
2420
+ while (prev && isIgnorable(prev, -1)) {
2405
2421
  moveNode = node.parentNode;
2406
2422
  moveOffset = domIndex(prev);
2407
2423
  prev = prev.previousSibling;
@@ -2437,7 +2453,7 @@ function skipIgnoredNodesAfter(view) {
2437
2453
  if (node.nodeType != 1)
2438
2454
  break;
2439
2455
  let after = node.childNodes[offset];
2440
- if (isIgnorable(after)) {
2456
+ if (isIgnorable(after, 1)) {
2441
2457
  moveNode = node;
2442
2458
  moveOffset = ++offset;
2443
2459
  }
@@ -2449,7 +2465,7 @@ function skipIgnoredNodesAfter(view) {
2449
2465
  }
2450
2466
  else {
2451
2467
  let next = node.nextSibling;
2452
- while (next && isIgnorable(next)) {
2468
+ while (next && isIgnorable(next, 1)) {
2453
2469
  moveNode = next.parentNode;
2454
2470
  moveOffset = domIndex(next) + 1;
2455
2471
  next = next.nextSibling;
@@ -2480,9 +2496,12 @@ function textNodeAfter(node, offset) {
2480
2496
  node = node.parentNode;
2481
2497
  }
2482
2498
  while (node && offset < node.childNodes.length) {
2483
- node = node.childNodes[offset];
2484
- if (node.nodeType == 3)
2485
- return node;
2499
+ let next = node.childNodes[offset];
2500
+ if (next.nodeType == 3)
2501
+ return next;
2502
+ if (next.nodeType == 1 && next.contentEditable == "false")
2503
+ break;
2504
+ node = next;
2486
2505
  offset = 0;
2487
2506
  }
2488
2507
  }
@@ -2492,9 +2511,12 @@ function textNodeBefore(node, offset) {
2492
2511
  node = node.parentNode;
2493
2512
  }
2494
2513
  while (node && offset) {
2495
- node = node.childNodes[offset - 1];
2496
- if (node.nodeType == 3)
2497
- return node;
2514
+ let next = node.childNodes[offset - 1];
2515
+ if (next.nodeType == 3)
2516
+ return next;
2517
+ if (next.nodeType == 1 && next.contentEditable == "false")
2518
+ break;
2519
+ node = next;
2498
2520
  offset = node.childNodes.length;
2499
2521
  }
2500
2522
  }
@@ -2656,7 +2678,7 @@ function captureKeyDown(view, event) {
2656
2678
  return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
2657
2679
  }
2658
2680
  else if (code == 40 || (mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
2659
- return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view);
2681
+ return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1);
2660
2682
  }
2661
2683
  else if (mods == (mac ? "m" : "c") &&
2662
2684
  (code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
@@ -3814,6 +3836,10 @@ class Decoration {
3814
3836
  @internal
3815
3837
  */
3816
3838
  get inline() { return this.type instanceof InlineType; }
3839
+ /**
3840
+ @internal
3841
+ */
3842
+ get widget() { return this.type instanceof WidgetType; }
3817
3843
  }
3818
3844
  const none = [], noSpec = {};
3819
3845
  /**
@@ -3832,7 +3858,8 @@ class DecorationSet {
3832
3858
  }
3833
3859
  /**
3834
3860
  Create a set of decorations, using the structure of the given
3835
- document.
3861
+ document. This will consume (modify) the `decorations` array, so
3862
+ you must make a copy if you want need to preserve that.
3836
3863
  */
3837
3864
  static create(doc, decorations) {
3838
3865
  return decorations.length ? buildTree(decorations, doc, 0, noSpec) : empty;
@@ -3891,8 +3918,9 @@ class DecorationSet {
3891
3918
  }
3892
3919
  /**
3893
3920
  Add the given array of decorations to the ones in the set,
3894
- producing a new set. Needs access to the current document to
3895
- create the appropriate tree structure.
3921
+ producing a new set. Consumes the `decorations` array. Needs
3922
+ access to the current document to create the appropriate tree
3923
+ structure.
3896
3924
  */
3897
3925
  add(doc, decorations) {
3898
3926
  if (!decorations.length)
@@ -5333,6 +5361,13 @@ class EditorView {
5333
5361
  return cached || document;
5334
5362
  }
5335
5363
  /**
5364
+ When an existing editor view is moved to a new document or
5365
+ shadow tree, call this to make it recompute its root.
5366
+ */
5367
+ updateRoot() {
5368
+ this._root = null;
5369
+ }
5370
+ /**
5336
5371
  Given a pair of viewport coordinates, return the document
5337
5372
  position that corresponds to them. May return null if the given
5338
5373
  coordinates aren't inside of the editor. When an object is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.31.7",
3
+ "version": "1.32.0",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -54,10 +54,10 @@ function nodeLen(node: Node) {
54
54
  return node.nodeType == 3 ? node.nodeValue!.length : node.childNodes.length
55
55
  }
56
56
 
57
- function isIgnorable(dom: Node) {
57
+ function isIgnorable(dom: Node, dir: number) {
58
58
  if ((dom as HTMLElement).contentEditable == "false") return true
59
59
  let desc = dom.pmViewDesc
60
- return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR")
60
+ return desc && desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR")
61
61
  }
62
62
 
63
63
  function skipIgnoredNodes(view: EditorView, dir: number) {
@@ -74,14 +74,14 @@ function skipIgnoredNodesBefore(view: EditorView) {
74
74
  // Gecko will do odd things when the selection is directly in front
75
75
  // of a non-editable node, so in that case, move it into the next
76
76
  // node if possible. Issue prosemirror/prosemirror#832.
77
- if (browser.gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset])) force = true
77
+ if (browser.gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset], -1)) force = true
78
78
  for (;;) {
79
79
  if (offset > 0) {
80
80
  if (node.nodeType != 1) {
81
81
  break
82
82
  } else {
83
83
  let before = node.childNodes[offset - 1]
84
- if (isIgnorable(before)) {
84
+ if (isIgnorable(before, -1)) {
85
85
  moveNode = node
86
86
  moveOffset = --offset
87
87
  } else if (before.nodeType == 3) {
@@ -93,7 +93,7 @@ function skipIgnoredNodesBefore(view: EditorView) {
93
93
  break
94
94
  } else {
95
95
  let prev = node.previousSibling
96
- while (prev && isIgnorable(prev)) {
96
+ while (prev && isIgnorable(prev, -1)) {
97
97
  moveNode = node.parentNode
98
98
  moveOffset = domIndex(prev)
99
99
  prev = prev.previousSibling
@@ -124,7 +124,7 @@ function skipIgnoredNodesAfter(view: EditorView) {
124
124
  if (offset < len) {
125
125
  if (node.nodeType != 1) break
126
126
  let after = node.childNodes[offset]
127
- if (isIgnorable(after)) {
127
+ if (isIgnorable(after, 1)) {
128
128
  moveNode = node
129
129
  moveOffset = ++offset
130
130
  }
@@ -133,7 +133,7 @@ function skipIgnoredNodesAfter(view: EditorView) {
133
133
  break
134
134
  } else {
135
135
  let next = node.nextSibling
136
- while (next && isIgnorable(next)) {
136
+ while (next && isIgnorable(next, 1)) {
137
137
  moveNode = next.parentNode
138
138
  moveOffset = domIndex(next) + 1
139
139
  next = next.nextSibling
@@ -163,8 +163,10 @@ function textNodeAfter(node: Node | null, offset: number): Text | undefined {
163
163
  node = node.parentNode
164
164
  }
165
165
  while (node && offset < node.childNodes.length) {
166
- node = node.childNodes[offset]
167
- if (node.nodeType == 3) return node as Text
166
+ let next = node.childNodes[offset]
167
+ if (next.nodeType == 3) return next as Text
168
+ if (next.nodeType == 1 && (next as HTMLElement).contentEditable == "false") break
169
+ node = next
168
170
  offset = 0
169
171
  }
170
172
  }
@@ -175,8 +177,10 @@ function textNodeBefore(node: Node | null, offset: number): Text | undefined {
175
177
  node = node.parentNode
176
178
  }
177
179
  while (node && offset) {
178
- node = node.childNodes[offset - 1]
179
- if (node.nodeType == 3) return node as Text
180
+ let next = node.childNodes[offset - 1]
181
+ if (next.nodeType == 3) return next as Text
182
+ if (next.nodeType == 1 && (next as HTMLElement).contentEditable == "false") break
183
+ node = next
180
184
  offset = node.childNodes.length
181
185
  }
182
186
  }
@@ -327,7 +331,7 @@ export function captureKeyDown(view: EditorView, event: KeyboardEvent) {
327
331
  } else if (code == 38 || (browser.mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
328
332
  return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1)
329
333
  } else if (code == 40 || (browser.mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
330
- return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view)
334
+ return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1)
331
335
  } else if (mods == (browser.mac ? "m" : "c") &&
332
336
  (code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
333
337
  return true
package/src/decoration.ts CHANGED
@@ -223,6 +223,9 @@ export class Decoration {
223
223
 
224
224
  /// @internal
225
225
  get inline() { return this.type instanceof InlineType }
226
+
227
+ /// @internal
228
+ get widget() { return this.type instanceof WidgetType }
226
229
  }
227
230
 
228
231
  /// A set of attributes to add to a decorated node. Most properties
@@ -278,7 +281,8 @@ export class DecorationSet implements DecorationSource {
278
281
  }
279
282
 
280
283
  /// Create a set of decorations, using the structure of the given
281
- /// document.
284
+ /// document. This will consume (modify) the `decorations` array, so
285
+ /// you must make a copy if you want need to preserve that.
282
286
  static create(doc: Node, decorations: Decoration[]) {
283
287
  return decorations.length ? buildTree(decorations, doc, 0, noSpec) : empty
284
288
  }
@@ -302,7 +306,7 @@ export class DecorationSet implements DecorationSource {
302
306
  result.push(span.copy(span.from + offset, span.to + offset))
303
307
  }
304
308
  for (let i = 0; i < this.children.length; i += 3) {
305
- if (this.children[i] < end && this.children[i + 1] > start) {
309
+ if ((this.children[i] as number) < end && (this.children[i + 1] as number) > start) {
306
310
  let childOff = (this.children[i] as number) + 1
307
311
  ;(this.children[i + 2] as DecorationSet).findInner(start - childOff, end - childOff,
308
312
  result, offset + childOff, predicate)
@@ -340,8 +344,9 @@ export class DecorationSet implements DecorationSource {
340
344
  }
341
345
 
342
346
  /// Add the given array of decorations to the ones in the set,
343
- /// producing a new set. Needs access to the current document to
344
- /// create the appropriate tree structure.
347
+ /// producing a new set. Consumes the `decorations` array. Needs
348
+ /// access to the current document to create the appropriate tree
349
+ /// structure.
345
350
  add(doc: Node, decorations: Decoration[]) {
346
351
  if (!decorations.length) return this
347
352
  if (this == empty) return DecorationSet.create(doc, decorations)
@@ -355,7 +360,7 @@ export class DecorationSet implements DecorationSource {
355
360
  if (!(found = takeSpansForNode(decorations, childNode, baseOffset))) return
356
361
 
357
362
  if (!children) children = this.children.slice()
358
- while (childIndex < children.length && children[childIndex] < childOffset) childIndex += 3
363
+ while (childIndex < children.length && (children[childIndex] as number) < childOffset) childIndex += 3
359
364
  if (children[childIndex] == childOffset)
360
365
  children[childIndex + 2] = (children[childIndex + 2] as DecorationSet).addInner(childNode, found, baseOffset + 1)
361
366
  else
@@ -414,7 +419,7 @@ export class DecorationSet implements DecorationSource {
414
419
  if (node.isLeaf) return DecorationSet.empty
415
420
 
416
421
  let child, local: Decoration[] | undefined
417
- for (let i = 0; i < this.children.length; i += 3) if (this.children[i] >= offset) {
422
+ for (let i = 0; i < this.children.length; i += 3) if ((this.children[i] as number) >= offset) {
418
423
  if (this.children[i] == offset) child = this.children[i + 2] as DecorationSet
419
424
  break
420
425
  }
@@ -576,7 +581,7 @@ function mapChildren(
576
581
  // Find the child nodes that still correspond to a single node,
577
582
  // recursively call mapInner on them and update their positions.
578
583
  let mustRebuild = false
579
- for (let i = 0; i < children.length; i += 3) if (children[i + 1] < 0) { // Touched nodes
584
+ for (let i = 0; i < children.length; i += 3) if ((children[i + 1] as number) < 0) { // Touched nodes
580
585
  if (children[i + 1] == -2) {
581
586
  mustRebuild = true
582
587
  children[i + 1] = -1
@@ -613,7 +618,7 @@ function mapChildren(
613
618
  offset, oldOffset, options)
614
619
  let built = buildTree(decorations, node, 0, options)
615
620
  newLocal = built.local as Decoration[]
616
- for (let i = 0; i < children.length; i += 3) if (children[i + 1] < 0) {
621
+ for (let i = 0; i < children.length; i += 3) if ((children[i + 1] as number) < 0) {
617
622
  children.splice(i, 3)
618
623
  i -= 3
619
624
  }
package/src/domcoords.ts CHANGED
@@ -294,6 +294,11 @@ export function posAtCoords(view: EditorView, coords: {top: number, left: number
294
294
  offset++
295
295
  }
296
296
  }
297
+ let prev
298
+ // When clicking above the right side of an uneditable node, Chrome will report a cursor position after that node.
299
+ if (browser.webkit && offset && node.nodeType == 1 && (prev = node.childNodes[offset - 1]).nodeType == 1 &&
300
+ (prev as HTMLElement).contentEditable == "false" && (prev as HTMLElement).getBoundingClientRect().top >= coords.top)
301
+ offset--
297
302
  // Suspiciously specific kludge to work around caret*FromPoint
298
303
  // never returning a position at the end of the document
299
304
  if (node == view.dom && offset == node.childNodes.length - 1 && node.lastChild!.nodeType == 1 &&
package/src/index.ts CHANGED
@@ -339,6 +339,12 @@ export class EditorView {
339
339
  return cached || document
340
340
  }
341
341
 
342
+ /// When an existing editor view is moved to a new document or
343
+ /// shadow tree, call this to make it recompute its root.
344
+ updateRoot() {
345
+ this._root = null
346
+ }
347
+
342
348
  /// Given a pair of viewport coordinates, return the document
343
349
  /// position that corresponds to them. May return null if the given
344
350
  /// coordinates aren't inside of the editor. When an object is
package/src/viewdesc.ts CHANGED
@@ -387,7 +387,7 @@ export class ViewDesc {
387
387
  brKludge = !!(offset && node.nodeValue![offset - 1] == "\n")
388
388
  // Issue #1128
389
389
  if (brKludge && offset == node.nodeValue!.length) {
390
- for (let scan: DOMNode | null = node, after; scan; scan = scan.parentNode) {
390
+ for (let scan: DOMNode | null = node, after; scan; scan = scan.parentNode) {
391
391
  if (after = scan.nextSibling) {
392
392
  if (after.nodeName == "BR")
393
393
  anchorDOM = headDOM = {node: after.parentNode!, offset: domIndex(after) + 1}
@@ -1244,9 +1244,11 @@ class ViewTreeUpdater {
1244
1244
  return true
1245
1245
  } else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
1246
1246
  this.top.children[this.index] = updated
1247
- updated.dirty = CONTENT_DIRTY
1248
- updated.updateChildren(view, pos + 1)
1249
- updated.dirty = NOT_DIRTY
1247
+ if (updated.contentDOM) {
1248
+ updated.dirty = CONTENT_DIRTY
1249
+ updated.updateChildren(view, pos + 1)
1250
+ updated.dirty = NOT_DIRTY
1251
+ }
1250
1252
  this.changed = true
1251
1253
  this.index++
1252
1254
  return true
@@ -1264,12 +1266,12 @@ class ViewTreeUpdater {
1264
1266
  if (next.dirty || node.isAtom || !next.children.length ||
1265
1267
  !next.node.content.eq(node.content)) return null
1266
1268
  let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos)
1267
- if (!wrapper.contentDOM) return null
1268
- wrapper.children = next.children
1269
- next.children = []
1269
+ if (wrapper.contentDOM) {
1270
+ wrapper.children = next.children
1271
+ next.children = []
1272
+ for (let ch of wrapper.children) ch.parent = wrapper
1273
+ }
1270
1274
  next.destroy()
1271
- for (let ch of wrapper.children) ch.parent = wrapper
1272
-
1273
1275
  return wrapper
1274
1276
  }
1275
1277
 
@@ -1403,10 +1405,15 @@ function iterDeco(
1403
1405
 
1404
1406
  let decoIndex = 0, active = [], restNode = null
1405
1407
  for (let parentIndex = 0;;) {
1406
- if (decoIndex < locals.length && locals[decoIndex].to == offset) {
1407
- let widget = locals[decoIndex++], widgets
1408
- while (decoIndex < locals.length && locals[decoIndex].to == offset)
1409
- (widgets || (widgets = [widget])).push(locals[decoIndex++])
1408
+ let widget, widgets
1409
+ while (decoIndex < locals.length && locals[decoIndex].to == offset) {
1410
+ let next = locals[decoIndex++]
1411
+ if (next.widget) {
1412
+ if (!widget) widget = next
1413
+ else (widgets || (widgets = [widget])).push(next)
1414
+ }
1415
+ }
1416
+ if (widget) {
1410
1417
  if (widgets) {
1411
1418
  widgets.sort(compareSide)
1412
1419
  for (let i = 0; i < widgets.length; i++) onWidget(widgets[i], parentIndex, !!restNode)
@@ -1492,6 +1499,8 @@ function findTextInFragment(frag: Fragment, text: string, from: number, to: numb
1492
1499
  str += next.text
1493
1500
  }
1494
1501
  if (pos >= from) {
1502
+ if (pos >= to && str.slice(to - text.length - childStart, to - childStart) == text)
1503
+ return to - text.length
1495
1504
  let found = childStart < to ? str.lastIndexOf(text, to - childStart - 1) : -1
1496
1505
  if (found >= 0 && found + text.length + childStart >= from)
1497
1506
  return childStart + found