prosemirror-view 1.31.6 → 1.31.8

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,23 @@
1
+ ## 1.31.8 (2023-09-12)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug that broke arrow motion near uneditable nodes on Chrome in some situations.
6
+
7
+ Fix an issue that broke backspace at the end of a textblock that ended in a newline on Chrome.
8
+
9
+ Fix a crash in the way inline decorations that ended right after a node end position were drawn.
10
+
11
+ ## 1.31.7 (2023-07-28)
12
+
13
+ ### Bug fixes
14
+
15
+ Fix a bug where, when a compositionend event was received before the composition's final changes were processed, the resulting transaction didn't have its composition metadata set.
16
+
17
+ Fix an issue that caused cursor motion to stop at uneditable nodes that aren't widget decorations.
18
+
19
+ When scrolling an element higher than the viewport into view, make sure its top is visible.
20
+
1
21
  ## 1.31.6 (2023-07-14)
2
22
 
3
23
  ### 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');
@@ -216,7 +212,7 @@ function scrollRectIntoView(view, rect, startDOM) {
216
212
  var bounding = atTop ? windowRect(doc) : clientRect(elt);
217
213
  var moveX = 0,
218
214
  moveY = 0;
219
- if (rect.top < bounding.top + getSide(scrollThreshold, "top")) moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"));else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom")) moveY = rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
215
+ if (rect.top < bounding.top + getSide(scrollThreshold, "top")) moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"));else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom")) moveY = rect.bottom - rect.top > bounding.bottom - bounding.top ? rect.top + getSide(scrollMargin, "top") - bounding.top : rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
220
216
  if (rect.left < bounding.left + getSide(scrollThreshold, "left")) moveX = -(bounding.left - rect.left + getSide(scrollMargin, "left"));else if (rect.right > bounding.right - getSide(scrollThreshold, "right")) moveX = rect.right - bounding.right + getSide(scrollMargin, "right");
221
217
 
222
218
  if (moveX || moveY) {
@@ -2190,9 +2186,13 @@ var ViewTreeUpdater = function () {
2190
2186
  return true;
2191
2187
  } else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
2192
2188
  this.top.children[this.index] = updated;
2193
- updated.dirty = CONTENT_DIRTY;
2194
- updated.updateChildren(view, pos + 1);
2195
- updated.dirty = NOT_DIRTY;
2189
+
2190
+ if (updated.contentDOM) {
2191
+ updated.dirty = CONTENT_DIRTY;
2192
+ updated.updateChildren(view, pos + 1);
2193
+ updated.dirty = NOT_DIRTY;
2194
+ }
2195
+
2196
2196
  this.changed = true;
2197
2197
  this.index++;
2198
2198
  return true;
@@ -2209,25 +2209,27 @@ var ViewTreeUpdater = function () {
2209
2209
  value: function recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
2210
2210
  if (next.dirty || node.isAtom || !next.children.length || !next.node.content.eq(node.content)) return null;
2211
2211
  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
2212
 
2217
- var _iterator = _createForOfIteratorHelper(wrapper.children),
2218
- _step;
2213
+ if (wrapper.contentDOM) {
2214
+ wrapper.children = next.children;
2215
+ next.children = [];
2219
2216
 
2220
- try {
2221
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
2222
- var ch = _step.value;
2223
- ch.parent = wrapper;
2217
+ var _iterator = _createForOfIteratorHelper(wrapper.children),
2218
+ _step;
2219
+
2220
+ try {
2221
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
2222
+ var ch = _step.value;
2223
+ ch.parent = wrapper;
2224
+ }
2225
+ } catch (err) {
2226
+ _iterator.e(err);
2227
+ } finally {
2228
+ _iterator.f();
2224
2229
  }
2225
- } catch (err) {
2226
- _iterator.e(err);
2227
- } finally {
2228
- _iterator.f();
2229
2230
  }
2230
2231
 
2232
+ next.destroy();
2231
2233
  return wrapper;
2232
2234
  }
2233
2235
  }, {
@@ -2364,14 +2366,18 @@ function iterDeco(parent, deco, onWidget, onNode) {
2364
2366
  restNode = null;
2365
2367
 
2366
2368
  for (var parentIndex = 0;;) {
2367
- if (decoIndex < locals.length && locals[decoIndex].to == offset) {
2368
- var widget = locals[decoIndex++],
2369
- widgets = void 0;
2369
+ var widget = void 0,
2370
+ widgets = void 0;
2371
+
2372
+ while (decoIndex < locals.length && locals[decoIndex].to == offset) {
2373
+ var next = locals[decoIndex++];
2370
2374
 
2371
- while (decoIndex < locals.length && locals[decoIndex].to == offset) {
2372
- (widgets || (widgets = [widget])).push(locals[decoIndex++]);
2375
+ if (next.widget) {
2376
+ if (!widget) widget = next;else (widgets || (widgets = [widget])).push(next);
2373
2377
  }
2378
+ }
2374
2379
 
2380
+ if (widget) {
2375
2381
  if (widgets) {
2376
2382
  widgets.sort(compareSide);
2377
2383
 
@@ -2773,9 +2779,10 @@ function nodeLen(node) {
2773
2779
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
2774
2780
  }
2775
2781
 
2776
- function isIgnorable(dom) {
2782
+ function isIgnorable(dom, dir) {
2783
+ if (dom.contentEditable == "false") return true;
2777
2784
  var desc = dom.pmViewDesc;
2778
- return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
2785
+ return desc && desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR");
2779
2786
  }
2780
2787
 
2781
2788
  function skipIgnoredNodes(view, dir) {
@@ -2790,7 +2797,7 @@ function skipIgnoredNodesBefore(view) {
2790
2797
  var moveNode,
2791
2798
  moveOffset,
2792
2799
  force = false;
2793
- if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset])) force = true;
2800
+ if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset], -1)) force = true;
2794
2801
 
2795
2802
  for (;;) {
2796
2803
  if (offset > 0) {
@@ -2799,7 +2806,7 @@ function skipIgnoredNodesBefore(view) {
2799
2806
  } else {
2800
2807
  var before = node.childNodes[offset - 1];
2801
2808
 
2802
- if (isIgnorable(before)) {
2809
+ if (isIgnorable(before, -1)) {
2803
2810
  moveNode = node;
2804
2811
  moveOffset = --offset;
2805
2812
  } else if (before.nodeType == 3) {
@@ -2812,7 +2819,7 @@ function skipIgnoredNodesBefore(view) {
2812
2819
  } else {
2813
2820
  var prev = node.previousSibling;
2814
2821
 
2815
- while (prev && isIgnorable(prev)) {
2822
+ while (prev && isIgnorable(prev, -1)) {
2816
2823
  moveNode = node.parentNode;
2817
2824
  moveOffset = domIndex(prev);
2818
2825
  prev = prev.previousSibling;
@@ -2845,7 +2852,7 @@ function skipIgnoredNodesAfter(view) {
2845
2852
  if (node.nodeType != 1) break;
2846
2853
  var after = node.childNodes[offset];
2847
2854
 
2848
- if (isIgnorable(after)) {
2855
+ if (isIgnorable(after, 1)) {
2849
2856
  moveNode = node;
2850
2857
  moveOffset = ++offset;
2851
2858
  } else break;
@@ -2854,7 +2861,7 @@ function skipIgnoredNodesAfter(view) {
2854
2861
  } else {
2855
2862
  var next = node.nextSibling;
2856
2863
 
2857
- while (next && isIgnorable(next)) {
2864
+ while (next && isIgnorable(next, 1)) {
2858
2865
  moveNode = next.parentNode;
2859
2866
  moveOffset = domIndex(next) + 1;
2860
2867
  next = next.nextSibling;
@@ -2880,7 +2887,49 @@ function isBlockNode(dom) {
2880
2887
  return desc && desc.node && desc.node.isBlock;
2881
2888
  }
2882
2889
 
2890
+ function textNodeAfter(node, offset) {
2891
+ while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
2892
+ offset = domIndex(node) + 1;
2893
+ node = node.parentNode;
2894
+ }
2895
+
2896
+ while (node && offset < node.childNodes.length) {
2897
+ var next = node.childNodes[offset];
2898
+ if (next.nodeType == 3) return next;
2899
+ if (next.nodeType == 1 && next.contentEditable == "false") break;
2900
+ node = next;
2901
+ offset = 0;
2902
+ }
2903
+ }
2904
+
2905
+ function textNodeBefore(node, offset) {
2906
+ while (node && !offset && !hasBlockDesc(node)) {
2907
+ offset = domIndex(node);
2908
+ node = node.parentNode;
2909
+ }
2910
+
2911
+ while (node && offset) {
2912
+ var next = node.childNodes[offset - 1];
2913
+ if (next.nodeType == 3) return next;
2914
+ if (next.nodeType == 1 && next.contentEditable == "false") break;
2915
+ node = next;
2916
+ offset = node.childNodes.length;
2917
+ }
2918
+ }
2919
+
2883
2920
  function setSelFocus(view, node, offset) {
2921
+ if (node.nodeType != 3) {
2922
+ var before, after;
2923
+
2924
+ if (after = textNodeAfter(node, offset)) {
2925
+ node = after;
2926
+ offset = 0;
2927
+ } else if (before = textNodeBefore(node, offset)) {
2928
+ node = before;
2929
+ offset = before.nodeValue.length;
2930
+ }
2931
+ }
2932
+
2884
2933
  var sel = view.domSelection();
2885
2934
 
2886
2935
  if (selectionCollapsed(sel)) {
@@ -3020,7 +3069,7 @@ function captureKeyDown(view, event) {
3020
3069
  } else if (code == 38 || mac && code == 80 && mods == "c") {
3021
3070
  return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
3022
3071
  } else if (code == 40 || mac && code == 78 && mods == "c") {
3023
- return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view);
3072
+ return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1);
3024
3073
  } else if (mods == (mac ? "m" : "c") && (code == 66 || code == 73 || code == 89 || code == 90)) {
3025
3074
  return true;
3026
3075
  }
@@ -3349,6 +3398,7 @@ var InputState = _createClass(function InputState() {
3349
3398
  this.compositionNodes = [];
3350
3399
  this.compositionEndedAt = -2e8;
3351
3400
  this.compositionID = 1;
3401
+ this.compositionPendingChanges = 0;
3352
3402
  this.domChangeCount = 0;
3353
3403
  this.eventHandlers = Object.create(null);
3354
3404
  this.hideSelectionGuard = null;
@@ -3812,6 +3862,10 @@ editHandlers.compositionend = function (view, event) {
3812
3862
  if (view.composing) {
3813
3863
  view.input.composing = false;
3814
3864
  view.input.compositionEndedAt = event.timeStamp;
3865
+ view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
3866
+ if (view.input.compositionPendingChanges) Promise.resolve().then(function () {
3867
+ return view.domObserver.flush();
3868
+ });
3815
3869
  view.input.compositionID++;
3816
3870
  scheduleComposeEnd(view, 20);
3817
3871
  }
@@ -4252,6 +4306,11 @@ var Decoration = function () {
4252
4306
  get: function get() {
4253
4307
  return this.type instanceof InlineType;
4254
4308
  }
4309
+ }, {
4310
+ key: "widget",
4311
+ get: function get() {
4312
+ return this.type instanceof WidgetType;
4313
+ }
4255
4314
  }], [{
4256
4315
  key: "widget",
4257
4316
  value: function widget(pos, toDOM, spec) {
@@ -5018,18 +5077,34 @@ var DOMObserver = function () {
5018
5077
  return true;
5019
5078
  }
5020
5079
  }
5080
+ }, {
5081
+ key: "pendingRecords",
5082
+ value: function pendingRecords() {
5083
+ if (this.observer) {
5084
+ var _iterator2 = _createForOfIteratorHelper(this.observer.takeRecords()),
5085
+ _step2;
5086
+
5087
+ try {
5088
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
5089
+ var mut = _step2.value;
5090
+ this.queue.push(mut);
5091
+ }
5092
+ } catch (err) {
5093
+ _iterator2.e(err);
5094
+ } finally {
5095
+ _iterator2.f();
5096
+ }
5097
+ }
5098
+
5099
+ return this.queue;
5100
+ }
5021
5101
  }, {
5022
5102
  key: "flush",
5023
5103
  value: function flush() {
5024
5104
  var view = this.view;
5025
5105
  if (!view.docView || this.flushingSoon > -1) return;
5026
- var mutations = this.observer ? this.observer.takeRecords() : [];
5027
-
5028
- if (this.queue.length) {
5029
- mutations = this.queue.concat(mutations);
5030
- this.queue.length = 0;
5031
- }
5032
-
5106
+ var mutations = this.pendingRecords();
5107
+ if (mutations.length) this.queue = [];
5033
5108
  var sel = view.domSelectionRange();
5034
5109
  var newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
5035
5110
  var from = -1,
@@ -5284,6 +5359,9 @@ function ruleFromNode(dom) {
5284
5359
  var isInline = /^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i;
5285
5360
 
5286
5361
  function readDOMChange(view, from, to, typeOver, addedNodes) {
5362
+ var compositionID = view.input.compositionPendingChanges || (view.composing ? view.input.compositionID : 0);
5363
+ view.input.compositionPendingChanges = 0;
5364
+
5287
5365
  if (from < 0) {
5288
5366
  var origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
5289
5367
  var newSel = selectionFromDOM(view, origin);
@@ -5296,7 +5374,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5296
5374
  var _tr = view.state.tr.setSelection(newSel);
5297
5375
 
5298
5376
  if (origin == "pointer") _tr.setMeta("pointer", true);else if (origin == "key") _tr.scrollIntoView();
5299
- if (view.composing) _tr.setMeta("composition", view.input.compositionID);
5377
+ if (compositionID) _tr.setMeta("composition", compositionID);
5300
5378
  view.dispatch(_tr);
5301
5379
  }
5302
5380
 
@@ -5347,7 +5425,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5347
5425
  if (_sel && !_sel.eq(view.state.selection)) {
5348
5426
  var _tr2 = view.state.tr.setSelection(_sel);
5349
5427
 
5350
- if (view.composing) _tr2.setMeta("composition", view.input.compositionID);
5428
+ if (compositionID) _tr2.setMeta("composition", compositionID);
5351
5429
  view.dispatch(_tr2);
5352
5430
  }
5353
5431
  }
@@ -5451,7 +5529,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
5451
5529
  }
5452
5530
 
5453
5531
  if (storedMarks) tr.ensureMarks(storedMarks);
5454
- if (view.composing) tr.setMeta("composition", view.input.compositionID);
5532
+ if (compositionID) tr.setMeta("composition", compositionID);
5455
5533
  view.dispatch(tr.scrollIntoView());
5456
5534
  }
5457
5535
 
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;
@@ -604,12 +606,12 @@ declare class EditorView {
604
606
  The type of function [provided](https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews) to
605
607
  create [node views](https://prosemirror.net/docs/ref/#view.NodeView).
606
608
  */
607
- declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
609
+ type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
608
610
  /**
609
611
  The function types [used](https://prosemirror.net/docs/ref/#view.EditorProps.markViews) to create
610
612
  mark views.
611
613
  */
612
- declare type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
614
+ type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
613
615
  dom: HTMLElement;
614
616
  contentDOM?: HTMLElement;
615
617
  };
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;
@@ -604,12 +606,12 @@ declare class EditorView {
604
606
  The type of function [provided](https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews) to
605
607
  create [node views](https://prosemirror.net/docs/ref/#view.NodeView).
606
608
  */
607
- declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
609
+ type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
608
610
  /**
609
611
  The function types [used](https://prosemirror.net/docs/ref/#view.EditorProps.markViews) to create
610
612
  mark views.
611
613
  */
612
- declare type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
614
+ type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
613
615
  dom: HTMLElement;
614
616
  contentDOM?: HTMLElement;
615
617
  };
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
 
@@ -163,7 +163,9 @@ function scrollRectIntoView(view, rect, startDOM) {
163
163
  if (rect.top < bounding.top + getSide(scrollThreshold, "top"))
164
164
  moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"));
165
165
  else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom"))
166
- moveY = rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
166
+ moveY = rect.bottom - rect.top > bounding.bottom - bounding.top
167
+ ? rect.top + getSide(scrollMargin, "top") - bounding.top
168
+ : rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
167
169
  if (rect.left < bounding.left + getSide(scrollThreshold, "left"))
168
170
  moveX = -(bounding.left - rect.left + getSide(scrollMargin, "left"));
169
171
  else if (rect.right > bounding.right - getSide(scrollThreshold, "right"))
@@ -1808,9 +1810,11 @@ class ViewTreeUpdater {
1808
1810
  }
1809
1811
  else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
1810
1812
  this.top.children[this.index] = updated;
1811
- updated.dirty = CONTENT_DIRTY;
1812
- updated.updateChildren(view, pos + 1);
1813
- updated.dirty = NOT_DIRTY;
1813
+ if (updated.contentDOM) {
1814
+ updated.dirty = CONTENT_DIRTY;
1815
+ updated.updateChildren(view, pos + 1);
1816
+ updated.dirty = NOT_DIRTY;
1817
+ }
1814
1818
  this.changed = true;
1815
1819
  this.index++;
1816
1820
  return true;
@@ -1827,13 +1831,13 @@ class ViewTreeUpdater {
1827
1831
  !next.node.content.eq(node.content))
1828
1832
  return null;
1829
1833
  let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
1830
- if (!wrapper.contentDOM)
1831
- return null;
1832
- wrapper.children = next.children;
1833
- next.children = [];
1834
+ if (wrapper.contentDOM) {
1835
+ wrapper.children = next.children;
1836
+ next.children = [];
1837
+ for (let ch of wrapper.children)
1838
+ ch.parent = wrapper;
1839
+ }
1834
1840
  next.destroy();
1835
- for (let ch of wrapper.children)
1836
- ch.parent = wrapper;
1837
1841
  return wrapper;
1838
1842
  }
1839
1843
  // Insert the node as a newly created node desc.
@@ -1961,10 +1965,17 @@ function iterDeco(parent, deco, onWidget, onNode) {
1961
1965
  }
1962
1966
  let decoIndex = 0, active = [], restNode = null;
1963
1967
  for (let parentIndex = 0;;) {
1964
- if (decoIndex < locals.length && locals[decoIndex].to == offset) {
1965
- let widget = locals[decoIndex++], widgets;
1966
- while (decoIndex < locals.length && locals[decoIndex].to == offset)
1967
- (widgets || (widgets = [widget])).push(locals[decoIndex++]);
1968
+ let widget, widgets;
1969
+ while (decoIndex < locals.length && locals[decoIndex].to == offset) {
1970
+ let next = locals[decoIndex++];
1971
+ if (next.widget) {
1972
+ if (!widget)
1973
+ widget = next;
1974
+ else
1975
+ (widgets || (widgets = [widget])).push(next);
1976
+ }
1977
+ }
1978
+ if (widget) {
1968
1979
  if (widgets) {
1969
1980
  widgets.sort(compareSide);
1970
1981
  for (let i = 0; i < widgets.length; i++)
@@ -2353,9 +2364,11 @@ function selectHorizontally(view, dir, mods) {
2353
2364
  function nodeLen(node) {
2354
2365
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
2355
2366
  }
2356
- function isIgnorable(dom) {
2367
+ function isIgnorable(dom, dir) {
2368
+ if (dom.contentEditable == "false")
2369
+ return true;
2357
2370
  let desc = dom.pmViewDesc;
2358
- return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
2371
+ return desc && desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR");
2359
2372
  }
2360
2373
  function skipIgnoredNodes(view, dir) {
2361
2374
  return dir < 0 ? skipIgnoredNodesBefore(view) : skipIgnoredNodesAfter(view);
@@ -2371,7 +2384,7 @@ function skipIgnoredNodesBefore(view) {
2371
2384
  // Gecko will do odd things when the selection is directly in front
2372
2385
  // of a non-editable node, so in that case, move it into the next
2373
2386
  // node if possible. Issue prosemirror/prosemirror#832.
2374
- if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset]))
2387
+ if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset], -1))
2375
2388
  force = true;
2376
2389
  for (;;) {
2377
2390
  if (offset > 0) {
@@ -2380,7 +2393,7 @@ function skipIgnoredNodesBefore(view) {
2380
2393
  }
2381
2394
  else {
2382
2395
  let before = node.childNodes[offset - 1];
2383
- if (isIgnorable(before)) {
2396
+ if (isIgnorable(before, -1)) {
2384
2397
  moveNode = node;
2385
2398
  moveOffset = --offset;
2386
2399
  }
@@ -2397,7 +2410,7 @@ function skipIgnoredNodesBefore(view) {
2397
2410
  }
2398
2411
  else {
2399
2412
  let prev = node.previousSibling;
2400
- while (prev && isIgnorable(prev)) {
2413
+ while (prev && isIgnorable(prev, -1)) {
2401
2414
  moveNode = node.parentNode;
2402
2415
  moveOffset = domIndex(prev);
2403
2416
  prev = prev.previousSibling;
@@ -2433,7 +2446,7 @@ function skipIgnoredNodesAfter(view) {
2433
2446
  if (node.nodeType != 1)
2434
2447
  break;
2435
2448
  let after = node.childNodes[offset];
2436
- if (isIgnorable(after)) {
2449
+ if (isIgnorable(after, 1)) {
2437
2450
  moveNode = node;
2438
2451
  moveOffset = ++offset;
2439
2452
  }
@@ -2445,7 +2458,7 @@ function skipIgnoredNodesAfter(view) {
2445
2458
  }
2446
2459
  else {
2447
2460
  let next = node.nextSibling;
2448
- while (next && isIgnorable(next)) {
2461
+ while (next && isIgnorable(next, 1)) {
2449
2462
  moveNode = next.parentNode;
2450
2463
  moveOffset = domIndex(next) + 1;
2451
2464
  next = next.nextSibling;
@@ -2470,7 +2483,48 @@ function isBlockNode(dom) {
2470
2483
  let desc = dom.pmViewDesc;
2471
2484
  return desc && desc.node && desc.node.isBlock;
2472
2485
  }
2486
+ function textNodeAfter(node, offset) {
2487
+ while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
2488
+ offset = domIndex(node) + 1;
2489
+ node = node.parentNode;
2490
+ }
2491
+ while (node && offset < node.childNodes.length) {
2492
+ let next = node.childNodes[offset];
2493
+ if (next.nodeType == 3)
2494
+ return next;
2495
+ if (next.nodeType == 1 && next.contentEditable == "false")
2496
+ break;
2497
+ node = next;
2498
+ offset = 0;
2499
+ }
2500
+ }
2501
+ function textNodeBefore(node, offset) {
2502
+ while (node && !offset && !hasBlockDesc(node)) {
2503
+ offset = domIndex(node);
2504
+ node = node.parentNode;
2505
+ }
2506
+ while (node && offset) {
2507
+ let next = node.childNodes[offset - 1];
2508
+ if (next.nodeType == 3)
2509
+ return next;
2510
+ if (next.nodeType == 1 && next.contentEditable == "false")
2511
+ break;
2512
+ node = next;
2513
+ offset = node.childNodes.length;
2514
+ }
2515
+ }
2473
2516
  function setSelFocus(view, node, offset) {
2517
+ if (node.nodeType != 3) {
2518
+ let before, after;
2519
+ if (after = textNodeAfter(node, offset)) {
2520
+ node = after;
2521
+ offset = 0;
2522
+ }
2523
+ else if (before = textNodeBefore(node, offset)) {
2524
+ node = before;
2525
+ offset = before.nodeValue.length;
2526
+ }
2527
+ }
2474
2528
  let sel = view.domSelection();
2475
2529
  if (selectionCollapsed(sel)) {
2476
2530
  let range = document.createRange();
@@ -2617,7 +2671,7 @@ function captureKeyDown(view, event) {
2617
2671
  return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
2618
2672
  }
2619
2673
  else if (code == 40 || (mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
2620
- return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view);
2674
+ return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1);
2621
2675
  }
2622
2676
  else if (mods == (mac ? "m" : "c") &&
2623
2677
  (code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
@@ -2899,6 +2953,8 @@ class InputState {
2899
2953
  this.compositionNodes = [];
2900
2954
  this.compositionEndedAt = -2e8;
2901
2955
  this.compositionID = 1;
2956
+ // Set to a composition ID when there are pending changes at compositionend
2957
+ this.compositionPendingChanges = 0;
2902
2958
  this.domChangeCount = 0;
2903
2959
  this.eventHandlers = Object.create(null);
2904
2960
  this.hideSelectionGuard = null;
@@ -3328,6 +3384,9 @@ editHandlers.compositionend = (view, event) => {
3328
3384
  if (view.composing) {
3329
3385
  view.input.composing = false;
3330
3386
  view.input.compositionEndedAt = event.timeStamp;
3387
+ view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
3388
+ if (view.input.compositionPendingChanges)
3389
+ Promise.resolve().then(() => view.domObserver.flush());
3331
3390
  view.input.compositionID++;
3332
3391
  scheduleComposeEnd(view, 20);
3333
3392
  }
@@ -3770,6 +3829,10 @@ class Decoration {
3770
3829
  @internal
3771
3830
  */
3772
3831
  get inline() { return this.type instanceof InlineType; }
3832
+ /**
3833
+ @internal
3834
+ */
3835
+ get widget() { return this.type instanceof WidgetType; }
3773
3836
  }
3774
3837
  const none = [], noSpec = {};
3775
3838
  /**
@@ -3788,7 +3851,8 @@ class DecorationSet {
3788
3851
  }
3789
3852
  /**
3790
3853
  Create a set of decorations, using the structure of the given
3791
- document.
3854
+ document. This will consume (modify) the `decorations` array, so
3855
+ you must make a copy if you want need to preserve that.
3792
3856
  */
3793
3857
  static create(doc, decorations) {
3794
3858
  return decorations.length ? buildTree(decorations, doc, 0, noSpec) : empty;
@@ -3847,8 +3911,9 @@ class DecorationSet {
3847
3911
  }
3848
3912
  /**
3849
3913
  Add the given array of decorations to the ones in the set,
3850
- producing a new set. Needs access to the current document to
3851
- create the appropriate tree structure.
3914
+ producing a new set. Consumes the `decorations` array. Needs
3915
+ access to the current document to create the appropriate tree
3916
+ structure.
3852
3917
  */
3853
3918
  add(doc, decorations) {
3854
3919
  if (!decorations.length)
@@ -4432,15 +4497,19 @@ class DOMObserver {
4432
4497
  return true;
4433
4498
  }
4434
4499
  }
4500
+ pendingRecords() {
4501
+ if (this.observer)
4502
+ for (let mut of this.observer.takeRecords())
4503
+ this.queue.push(mut);
4504
+ return this.queue;
4505
+ }
4435
4506
  flush() {
4436
4507
  let { view } = this;
4437
4508
  if (!view.docView || this.flushingSoon > -1)
4438
4509
  return;
4439
- let mutations = this.observer ? this.observer.takeRecords() : [];
4440
- if (this.queue.length) {
4441
- mutations = this.queue.concat(mutations);
4442
- this.queue.length = 0;
4443
- }
4510
+ let mutations = this.pendingRecords();
4511
+ if (mutations.length)
4512
+ this.queue = [];
4444
4513
  let sel = view.domSelectionRange();
4445
4514
  let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
4446
4515
  let from = -1, to = -1, typeOver = false, added = [];
@@ -4661,6 +4730,8 @@ function ruleFromNode(dom) {
4661
4730
  }
4662
4731
  const isInline = /^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i;
4663
4732
  function readDOMChange(view, from, to, typeOver, addedNodes) {
4733
+ let compositionID = view.input.compositionPendingChanges || (view.composing ? view.input.compositionID : 0);
4734
+ view.input.compositionPendingChanges = 0;
4664
4735
  if (from < 0) {
4665
4736
  let origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
4666
4737
  let newSel = selectionFromDOM(view, origin);
@@ -4674,8 +4745,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4674
4745
  tr.setMeta("pointer", true);
4675
4746
  else if (origin == "key")
4676
4747
  tr.scrollIntoView();
4677
- if (view.composing)
4678
- tr.setMeta("composition", view.input.compositionID);
4748
+ if (compositionID)
4749
+ tr.setMeta("composition", compositionID);
4679
4750
  view.dispatch(tr);
4680
4751
  }
4681
4752
  return;
@@ -4716,8 +4787,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4716
4787
  let sel = resolveSelection(view, view.state.doc, parse.sel);
4717
4788
  if (sel && !sel.eq(view.state.selection)) {
4718
4789
  let tr = view.state.tr.setSelection(sel);
4719
- if (view.composing)
4720
- tr.setMeta("composition", view.input.compositionID);
4790
+ if (compositionID)
4791
+ tr.setMeta("composition", compositionID);
4721
4792
  view.dispatch(tr);
4722
4793
  }
4723
4794
  }
@@ -4850,8 +4921,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
4850
4921
  }
4851
4922
  if (storedMarks)
4852
4923
  tr.ensureMarks(storedMarks);
4853
- if (view.composing)
4854
- tr.setMeta("composition", view.input.compositionID);
4924
+ if (compositionID)
4925
+ tr.setMeta("composition", compositionID);
4855
4926
  view.dispatch(tr.scrollIntoView());
4856
4927
  }
4857
4928
  function resolveSelection(view, doc, parsedSel) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-view",
3
- "version": "1.31.6",
3
+ "version": "1.31.8",
4
4
  "description": "ProseMirror's view component",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -1,7 +1,7 @@
1
1
  import {Selection, NodeSelection, TextSelection, AllSelection, EditorState} from "prosemirror-state"
2
2
  import {EditorView} from "./index"
3
3
  import * as browser from "./browser"
4
- import {domIndex, selectionCollapsed} from "./dom"
4
+ import {domIndex, selectionCollapsed, hasBlockDesc} from "./dom"
5
5
  import {selectionToDOM} from "./selection"
6
6
 
7
7
  function moveSelectionBlock(state: EditorState, dir: number) {
@@ -54,9 +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
+ if ((dom as HTMLElement).contentEditable == "false") return true
58
59
  let desc = dom.pmViewDesc
59
- return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR")
60
+ return desc && desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR")
60
61
  }
61
62
 
62
63
  function skipIgnoredNodes(view: EditorView, dir: number) {
@@ -73,14 +74,14 @@ function skipIgnoredNodesBefore(view: EditorView) {
73
74
  // Gecko will do odd things when the selection is directly in front
74
75
  // of a non-editable node, so in that case, move it into the next
75
76
  // node if possible. Issue prosemirror/prosemirror#832.
76
- 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
77
78
  for (;;) {
78
79
  if (offset > 0) {
79
80
  if (node.nodeType != 1) {
80
81
  break
81
82
  } else {
82
83
  let before = node.childNodes[offset - 1]
83
- if (isIgnorable(before)) {
84
+ if (isIgnorable(before, -1)) {
84
85
  moveNode = node
85
86
  moveOffset = --offset
86
87
  } else if (before.nodeType == 3) {
@@ -92,7 +93,7 @@ function skipIgnoredNodesBefore(view: EditorView) {
92
93
  break
93
94
  } else {
94
95
  let prev = node.previousSibling
95
- while (prev && isIgnorable(prev)) {
96
+ while (prev && isIgnorable(prev, -1)) {
96
97
  moveNode = node.parentNode
97
98
  moveOffset = domIndex(prev)
98
99
  prev = prev.previousSibling
@@ -123,7 +124,7 @@ function skipIgnoredNodesAfter(view: EditorView) {
123
124
  if (offset < len) {
124
125
  if (node.nodeType != 1) break
125
126
  let after = node.childNodes[offset]
126
- if (isIgnorable(after)) {
127
+ if (isIgnorable(after, 1)) {
127
128
  moveNode = node
128
129
  moveOffset = ++offset
129
130
  }
@@ -132,7 +133,7 @@ function skipIgnoredNodesAfter(view: EditorView) {
132
133
  break
133
134
  } else {
134
135
  let next = node.nextSibling
135
- while (next && isIgnorable(next)) {
136
+ while (next && isIgnorable(next, 1)) {
136
137
  moveNode = next.parentNode
137
138
  moveOffset = domIndex(next) + 1
138
139
  next = next.nextSibling
@@ -156,7 +157,46 @@ function isBlockNode(dom: Node) {
156
157
  return desc && desc.node && desc.node.isBlock
157
158
  }
158
159
 
160
+ function textNodeAfter(node: Node | null, offset: number): Text | undefined {
161
+ while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
162
+ offset = domIndex(node) + 1
163
+ node = node.parentNode
164
+ }
165
+ while (node && offset < node.childNodes.length) {
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
170
+ offset = 0
171
+ }
172
+ }
173
+
174
+ function textNodeBefore(node: Node | null, offset: number): Text | undefined {
175
+ while (node && !offset && !hasBlockDesc(node)) {
176
+ offset = domIndex(node)
177
+ node = node.parentNode
178
+ }
179
+ while (node && offset) {
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
184
+ offset = node.childNodes.length
185
+ }
186
+ }
187
+
159
188
  function setSelFocus(view: EditorView, node: Node, offset: number) {
189
+ if (node.nodeType != 3) {
190
+ let before, after
191
+ if (after = textNodeAfter(node, offset)) {
192
+ node = after
193
+ offset = 0
194
+ } else if (before = textNodeBefore(node, offset)) {
195
+ node = before
196
+ offset = before.nodeValue!.length
197
+ }
198
+ }
199
+
160
200
  let sel = view.domSelection()
161
201
  if (selectionCollapsed(sel)) {
162
202
  let range = document.createRange()
@@ -291,7 +331,7 @@ export function captureKeyDown(view: EditorView, event: KeyboardEvent) {
291
331
  } else if (code == 38 || (browser.mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
292
332
  return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1)
293
333
  } else if (code == 40 || (browser.mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
294
- return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view)
334
+ return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1)
295
335
  } else if (mods == (browser.mac ? "m" : "c") &&
296
336
  (code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
297
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/dom.ts CHANGED
@@ -74,7 +74,7 @@ export function isOnEdge(node: Node, offset: number, parent: Node) {
74
74
  }
75
75
  }
76
76
 
77
- function hasBlockDesc(dom: Node) {
77
+ export function hasBlockDesc(dom: Node) {
78
78
  let desc
79
79
  for (let cur: Node | null = dom; cur; cur = cur.parentNode) if (desc = cur.pmViewDesc) break
80
80
  return desc && desc.node && desc.node.isBlock && (desc.dom == dom || desc.contentDOM == dom)
package/src/domchange.ts CHANGED
@@ -79,6 +79,9 @@ function ruleFromNode(dom: DOMNode): ParseRule | null {
79
79
  const isInline = /^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i
80
80
 
81
81
  export function readDOMChange(view: EditorView, from: number, to: number, typeOver: boolean, addedNodes: readonly DOMNode[]) {
82
+ let compositionID = view.input.compositionPendingChanges || (view.composing ? view.input.compositionID : 0)
83
+ view.input.compositionPendingChanges = 0
84
+
82
85
  if (from < 0) {
83
86
  let origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null
84
87
  let newSel = selectionFromDOM(view, origin)
@@ -90,7 +93,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
90
93
  let tr = view.state.tr.setSelection(newSel)
91
94
  if (origin == "pointer") tr.setMeta("pointer", true)
92
95
  else if (origin == "key") tr.scrollIntoView()
93
- if (view.composing) tr.setMeta("composition", view.input.compositionID)
96
+ if (compositionID) tr.setMeta("composition", compositionID)
94
97
  view.dispatch(tr)
95
98
  }
96
99
  return
@@ -133,7 +136,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
133
136
  let sel = resolveSelection(view, view.state.doc, parse.sel)
134
137
  if (sel && !sel.eq(view.state.selection)) {
135
138
  let tr = view.state.tr.setSelection(sel)
136
- if (view.composing) tr.setMeta("composition", view.input.compositionID)
139
+ if (compositionID) tr.setMeta("composition", compositionID)
137
140
  view.dispatch(tr)
138
141
  }
139
142
  }
@@ -268,7 +271,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
268
271
  tr.setSelection(sel)
269
272
  }
270
273
  if (storedMarks) tr.ensureMarks(storedMarks)
271
- if (view.composing) tr.setMeta("composition", view.input.compositionID)
274
+ if (compositionID) tr.setMeta("composition", compositionID)
272
275
  view.dispatch(tr.scrollIntoView())
273
276
  }
274
277
 
package/src/domcoords.ts CHANGED
@@ -37,7 +37,9 @@ export function scrollRectIntoView(view: EditorView, rect: Rect, startDOM: Node)
37
37
  if (rect.top < bounding.top + getSide(scrollThreshold, "top"))
38
38
  moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"))
39
39
  else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom"))
40
- moveY = rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom")
40
+ moveY = rect.bottom - rect.top > bounding.bottom - bounding.top
41
+ ? rect.top + getSide(scrollMargin, "top") - bounding.top
42
+ : rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom")
41
43
  if (rect.left < bounding.left + getSide(scrollThreshold, "left"))
42
44
  moveX = -(bounding.left - rect.left + getSide(scrollMargin, "left"))
43
45
  else if (rect.right > bounding.right - getSide(scrollThreshold, "right"))
@@ -157,14 +157,16 @@ export class DOMObserver {
157
157
  }
158
158
  }
159
159
 
160
+ pendingRecords() {
161
+ if (this.observer) for (let mut of this.observer.takeRecords()) this.queue.push(mut)
162
+ return this.queue
163
+ }
164
+
160
165
  flush() {
161
166
  let {view} = this
162
167
  if (!view.docView || this.flushingSoon > -1) return
163
- let mutations = this.observer ? this.observer.takeRecords() : []
164
- if (this.queue.length) {
165
- mutations = this.queue.concat(mutations)
166
- this.queue.length = 0
167
- }
168
+ let mutations = this.pendingRecords()
169
+ if (mutations.length) this.queue = []
168
170
 
169
171
  let sel = view.domSelectionRange()
170
172
  let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel)
package/src/input.ts CHANGED
@@ -34,6 +34,8 @@ export class InputState {
34
34
  compositionNodes: ViewDesc[] = []
35
35
  compositionEndedAt = -2e8
36
36
  compositionID = 1
37
+ // Set to a composition ID when there are pending changes at compositionend
38
+ compositionPendingChanges = 0
37
39
  domChangeCount = 0
38
40
  eventHandlers: {[event: string]: (event: Event) => void} = Object.create(null)
39
41
  hideSelectionGuard: (() => void) | null = null
@@ -487,6 +489,8 @@ editHandlers.compositionend = (view, event) => {
487
489
  if (view.composing) {
488
490
  view.input.composing = false
489
491
  view.input.compositionEndedAt = event.timeStamp
492
+ view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0
493
+ if (view.input.compositionPendingChanges) Promise.resolve().then(() => view.domObserver.flush())
490
494
  view.input.compositionID++
491
495
  scheduleComposeEnd(view, 20)
492
496
  }
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)