@codemirror/view 6.2.3 → 6.2.5

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,21 @@
1
+ ## 6.2.5 (2022-09-24)
2
+
3
+ ### Bug fixes
4
+
5
+ Don't override double/triple tap behavior on touch screen devices, so that the mobile selection menu pops up properly.
6
+
7
+ Fix an issue where updating the selection could crash on Safari when the editor was hidden.
8
+
9
+ ## 6.2.4 (2022-09-16)
10
+
11
+ ### Bug fixes
12
+
13
+ Highlight the active line even when there is a selection. Prevent the active line background from obscuring the selection backdrop.
14
+
15
+ Fix an issue where elements with negative margins would confuse the editor's scrolling-into-view logic.
16
+
17
+ Fix scrolling to a specific position in an editor that has not been in view yet.
18
+
1
19
  ## 6.2.3 (2022-09-08)
2
20
 
3
21
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -100,7 +100,7 @@ function windowRect(win) {
100
100
  top: 0, bottom: win.innerHeight };
101
101
  }
102
102
  function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
103
- let doc = dom.ownerDocument, win = doc.defaultView;
103
+ let doc = dom.ownerDocument, win = doc.defaultView || window;
104
104
  for (let cur = dom; cur;) {
105
105
  if (cur.nodeType == 1) { // Element
106
106
  let bounding, top = cur == doc.body;
@@ -109,7 +109,7 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
109
109
  }
110
110
  else {
111
111
  if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) {
112
- cur = cur.parentNode;
112
+ cur = cur.assignedSlot || cur.parentNode;
113
113
  continue;
114
114
  }
115
115
  let rect = cur.getBoundingClientRect();
@@ -160,24 +160,28 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
160
160
  win.scrollBy(moveX, moveY);
161
161
  }
162
162
  else {
163
+ let movedX = 0, movedY = 0;
163
164
  if (moveY) {
164
165
  let start = cur.scrollTop;
165
166
  cur.scrollTop += moveY;
166
- moveY = cur.scrollTop - start;
167
+ movedY = cur.scrollTop - start;
167
168
  }
168
169
  if (moveX) {
169
170
  let start = cur.scrollLeft;
170
171
  cur.scrollLeft += moveX;
171
- moveX = cur.scrollLeft - start;
172
+ movedX = cur.scrollLeft - start;
172
173
  }
173
- rect = { left: rect.left - moveX, top: rect.top - moveY,
174
- right: rect.right - moveX, bottom: rect.bottom - moveY };
174
+ rect = { left: rect.left - movedX, top: rect.top - movedY,
175
+ right: rect.right - movedX, bottom: rect.bottom - movedY };
176
+ if (movedX && Math.abs(movedX - moveX) < 1)
177
+ x = "nearest";
178
+ if (movedY && Math.abs(movedY - moveY) < 1)
179
+ y = "nearest";
175
180
  }
176
181
  }
177
182
  if (top)
178
183
  break;
179
184
  cur = cur.assignedSlot || cur.parentNode;
180
- x = y = "nearest";
181
185
  }
182
186
  else if (cur.nodeType == 11) { // A shadow root
183
187
  cur = cur.host;
@@ -346,7 +350,7 @@ class ContentView {
346
350
  if (child.dirty) {
347
351
  if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
348
352
  let contentView = ContentView.get(next);
349
- if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
353
+ if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
350
354
  child.reuseDOM(next);
351
355
  }
352
356
  child.sync(track);
@@ -504,6 +508,7 @@ class ContentView {
504
508
  return false;
505
509
  }
506
510
  become(other) { return false; }
511
+ canReuseDOM(other) { return other.constructor == this.constructor; }
507
512
  // When this is a zero-length view with a side, this should return a
508
513
  // number <= 0 to indicate it is before its position, or a
509
514
  // number > 0 when after its position.
@@ -907,6 +912,7 @@ class CompositionView extends WidgetView {
907
912
  (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
908
913
  }
909
914
  get isEditable() { return true; }
915
+ canReuseDOM() { return true; }
910
916
  }
911
917
  // Uses the old structure of a chunk of content view frozen for
912
918
  // composition to try and find a reasonable DOM location for the given
@@ -2635,7 +2641,13 @@ class DocView extends ContentView {
2635
2641
  // (one where the focus is before the anchor), but not all
2636
2642
  // browsers support it yet.
2637
2643
  rawSel.collapse(anchor.node, anchor.offset);
2638
- rawSel.extend(head.node, head.offset);
2644
+ // Safari will ignore the call above when the editor is
2645
+ // hidden, and then raise an error on the call to extend
2646
+ // (#940).
2647
+ try {
2648
+ rawSel.extend(head.node, head.offset);
2649
+ }
2650
+ catch (_) { }
2639
2651
  }
2640
2652
  else {
2641
2653
  // Primitive (IE) way
@@ -3688,7 +3700,7 @@ handlers.touchmove = view => {
3688
3700
  handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
3689
3701
  handlers.mousedown = (view, event) => {
3690
3702
  view.observer.flush();
3691
- if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
3703
+ if (view.inputState.lastTouchTime > Date.now() - 2000)
3692
3704
  return; // Ignore touch interaction
3693
3705
  let style = null;
3694
3706
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -4659,10 +4671,10 @@ class DecorationComparator {
4659
4671
 
4660
4672
  function visiblePixelRange(dom, paddingTop) {
4661
4673
  let rect = dom.getBoundingClientRect();
4662
- let left = Math.max(0, rect.left), right = Math.min(innerWidth, rect.right);
4663
- let top = Math.max(0, rect.top), bottom = Math.min(innerHeight, rect.bottom);
4664
- let body = dom.ownerDocument.body;
4665
- for (let parent = dom.parentNode; parent && parent != body;) {
4674
+ let doc = dom.ownerDocument, win = doc.defaultView || window;
4675
+ let left = Math.max(0, rect.left), right = Math.min(win.innerWidth, rect.right);
4676
+ let top = Math.max(0, rect.top), bottom = Math.min(win.innerHeight, rect.bottom);
4677
+ for (let parent = dom.parentNode; parent && parent != doc.body;) {
4666
4678
  if (parent.nodeType == 1) {
4667
4679
  let elt = parent;
4668
4680
  let style = window.getComputedStyle(elt);
@@ -4857,7 +4869,7 @@ class ViewState {
4857
4869
  if (inView)
4858
4870
  measureContent = true;
4859
4871
  }
4860
- if (!this.inView)
4872
+ if (!this.inView && !this.scrollTarget)
4861
4873
  return 0;
4862
4874
  let contentWidth = dom.clientWidth;
4863
4875
  if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
@@ -5301,8 +5313,8 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5301
5313
  "&.cm-focused .cm-cursor": {
5302
5314
  display: "block"
5303
5315
  },
5304
- "&light .cm-activeLine": { backgroundColor: "#f3f9ff" },
5305
- "&dark .cm-activeLine": { backgroundColor: "#223039" },
5316
+ "&light .cm-activeLine": { backgroundColor: "#cceeff44" },
5317
+ "&dark .cm-activeLine": { backgroundColor: "#99eeff33" },
5306
5318
  "&light .cm-specialChar": { color: "red" },
5307
5319
  "&dark .cm-specialChar": { color: "#f78" },
5308
5320
  ".cm-gutters": {
@@ -6175,7 +6187,7 @@ class EditorView {
6175
6187
  /**
6176
6188
  @internal
6177
6189
  */
6178
- get win() { return this.dom.ownerDocument.defaultView; }
6190
+ get win() { return this.dom.ownerDocument.defaultView || window; }
6179
6191
  dispatch(...input) {
6180
6192
  this._dispatch(input.length == 1 && input[0] instanceof state.Transaction ? input[0]
6181
6193
  : this.state.update(...input));
@@ -6734,7 +6746,7 @@ class EditorView {
6734
6746
  setRoot(root) {
6735
6747
  if (this._root != root) {
6736
6748
  this._root = root;
6737
- this.observer.setWindow((root.nodeType == 9 ? root : root.ownerDocument).defaultView);
6749
+ this.observer.setWindow((root.nodeType == 9 ? root : root.ownerDocument).defaultView || window);
6738
6750
  this.mountStyles();
6739
6751
  }
6740
6752
  }
@@ -7539,9 +7551,15 @@ class MatchDecorator {
7539
7551
  if (decorate) {
7540
7552
  this.addMatch = (match, view, from, add) => decorate(add, from, from + match[0].length, match, view);
7541
7553
  }
7554
+ else if (typeof decoration == "function") {
7555
+ this.addMatch = (match, view, from, add) => {
7556
+ let deco = decoration(match, view, from);
7557
+ if (deco)
7558
+ add(from, from + match[0].length, deco);
7559
+ };
7560
+ }
7542
7561
  else if (decoration) {
7543
- let getDeco = typeof decoration == "function" ? decoration : () => decoration;
7544
- this.addMatch = (match, view, from, add) => add(from, from + match[0].length, getDeco(match, view, from));
7562
+ this.addMatch = (match, _view, from, add) => add(from, from + match[0].length, decoration);
7545
7563
  }
7546
7564
  else {
7547
7565
  throw new RangeError("Either 'decorate' or 'decoration' should be provided to MatchDecorator");
@@ -7808,8 +7826,6 @@ const activeLineHighlighter = ViewPlugin.fromClass(class {
7808
7826
  getDeco(view) {
7809
7827
  let lastLineStart = -1, deco = [];
7810
7828
  for (let r of view.state.selection.ranges) {
7811
- if (!r.empty)
7812
- return Decoration.none;
7813
7829
  let line = view.lineBlockAt(r.head);
7814
7830
  if (line.from > lastLineStart) {
7815
7831
  deco.push(lineDeco.range(line.from));
@@ -8027,8 +8043,9 @@ Creates an extension that configures tooltip behavior.
8027
8043
  function tooltips(config = {}) {
8028
8044
  return tooltipConfig.of(config);
8029
8045
  }
8030
- function windowSpace() {
8031
- return { top: 0, left: 0, bottom: innerHeight, right: innerWidth };
8046
+ function windowSpace(view) {
8047
+ let { win } = view;
8048
+ return { top: 0, left: 0, bottom: win.innerHeight, right: win.innerWidth };
8032
8049
  }
8033
8050
  const tooltipConfig = state.Facet.define({
8034
8051
  combine: values => {
@@ -9123,14 +9140,13 @@ const activeLineGutterMarker = new class extends GutterMarker {
9123
9140
  };
9124
9141
  const activeLineGutterHighlighter = gutterLineClass.compute(["selection"], state$1 => {
9125
9142
  let marks = [], last = -1;
9126
- for (let range of state$1.selection.ranges)
9127
- if (range.empty) {
9128
- let linePos = state$1.doc.lineAt(range.head).from;
9129
- if (linePos > last) {
9130
- last = linePos;
9131
- marks.push(activeLineGutterMarker.range(linePos));
9132
- }
9143
+ for (let range of state$1.selection.ranges) {
9144
+ let linePos = state$1.doc.lineAt(range.head).from;
9145
+ if (linePos > last) {
9146
+ last = linePos;
9147
+ marks.push(activeLineGutterMarker.range(linePos));
9133
9148
  }
9149
+ }
9134
9150
  return state.RangeSet.of(marks);
9135
9151
  });
9136
9152
  /**
package/dist/index.d.ts CHANGED
@@ -1386,7 +1386,7 @@ declare class MatchDecorator {
1386
1386
  The decoration to apply to matches, either directly or as a
1387
1387
  function of the match.
1388
1388
  */
1389
- decoration?: Decoration | ((match: RegExpExecArray, view: EditorView, pos: number) => Decoration);
1389
+ decoration?: Decoration | ((match: RegExpExecArray, view: EditorView, pos: number) => Decoration | null);
1390
1390
  /**
1391
1391
  Customize the way decorations are added for matches. This
1392
1392
  function, when given, will be called for matches and should
package/dist/index.js CHANGED
@@ -96,7 +96,7 @@ function windowRect(win) {
96
96
  top: 0, bottom: win.innerHeight };
97
97
  }
98
98
  function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
99
- let doc = dom.ownerDocument, win = doc.defaultView;
99
+ let doc = dom.ownerDocument, win = doc.defaultView || window;
100
100
  for (let cur = dom; cur;) {
101
101
  if (cur.nodeType == 1) { // Element
102
102
  let bounding, top = cur == doc.body;
@@ -105,7 +105,7 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
105
105
  }
106
106
  else {
107
107
  if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) {
108
- cur = cur.parentNode;
108
+ cur = cur.assignedSlot || cur.parentNode;
109
109
  continue;
110
110
  }
111
111
  let rect = cur.getBoundingClientRect();
@@ -156,24 +156,28 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
156
156
  win.scrollBy(moveX, moveY);
157
157
  }
158
158
  else {
159
+ let movedX = 0, movedY = 0;
159
160
  if (moveY) {
160
161
  let start = cur.scrollTop;
161
162
  cur.scrollTop += moveY;
162
- moveY = cur.scrollTop - start;
163
+ movedY = cur.scrollTop - start;
163
164
  }
164
165
  if (moveX) {
165
166
  let start = cur.scrollLeft;
166
167
  cur.scrollLeft += moveX;
167
- moveX = cur.scrollLeft - start;
168
+ movedX = cur.scrollLeft - start;
168
169
  }
169
- rect = { left: rect.left - moveX, top: rect.top - moveY,
170
- right: rect.right - moveX, bottom: rect.bottom - moveY };
170
+ rect = { left: rect.left - movedX, top: rect.top - movedY,
171
+ right: rect.right - movedX, bottom: rect.bottom - movedY };
172
+ if (movedX && Math.abs(movedX - moveX) < 1)
173
+ x = "nearest";
174
+ if (movedY && Math.abs(movedY - moveY) < 1)
175
+ y = "nearest";
171
176
  }
172
177
  }
173
178
  if (top)
174
179
  break;
175
180
  cur = cur.assignedSlot || cur.parentNode;
176
- x = y = "nearest";
177
181
  }
178
182
  else if (cur.nodeType == 11) { // A shadow root
179
183
  cur = cur.host;
@@ -342,7 +346,7 @@ class ContentView {
342
346
  if (child.dirty) {
343
347
  if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
344
348
  let contentView = ContentView.get(next);
345
- if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
349
+ if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
346
350
  child.reuseDOM(next);
347
351
  }
348
352
  child.sync(track);
@@ -500,6 +504,7 @@ class ContentView {
500
504
  return false;
501
505
  }
502
506
  become(other) { return false; }
507
+ canReuseDOM(other) { return other.constructor == this.constructor; }
503
508
  // When this is a zero-length view with a side, this should return a
504
509
  // number <= 0 to indicate it is before its position, or a
505
510
  // number > 0 when after its position.
@@ -903,6 +908,7 @@ class CompositionView extends WidgetView {
903
908
  (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
904
909
  }
905
910
  get isEditable() { return true; }
911
+ canReuseDOM() { return true; }
906
912
  }
907
913
  // Uses the old structure of a chunk of content view frozen for
908
914
  // composition to try and find a reasonable DOM location for the given
@@ -2629,7 +2635,13 @@ class DocView extends ContentView {
2629
2635
  // (one where the focus is before the anchor), but not all
2630
2636
  // browsers support it yet.
2631
2637
  rawSel.collapse(anchor.node, anchor.offset);
2632
- rawSel.extend(head.node, head.offset);
2638
+ // Safari will ignore the call above when the editor is
2639
+ // hidden, and then raise an error on the call to extend
2640
+ // (#940).
2641
+ try {
2642
+ rawSel.extend(head.node, head.offset);
2643
+ }
2644
+ catch (_) { }
2633
2645
  }
2634
2646
  else {
2635
2647
  // Primitive (IE) way
@@ -3682,7 +3694,7 @@ handlers.touchmove = view => {
3682
3694
  handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
3683
3695
  handlers.mousedown = (view, event) => {
3684
3696
  view.observer.flush();
3685
- if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
3697
+ if (view.inputState.lastTouchTime > Date.now() - 2000)
3686
3698
  return; // Ignore touch interaction
3687
3699
  let style = null;
3688
3700
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -4652,10 +4664,10 @@ class DecorationComparator {
4652
4664
 
4653
4665
  function visiblePixelRange(dom, paddingTop) {
4654
4666
  let rect = dom.getBoundingClientRect();
4655
- let left = Math.max(0, rect.left), right = Math.min(innerWidth, rect.right);
4656
- let top = Math.max(0, rect.top), bottom = Math.min(innerHeight, rect.bottom);
4657
- let body = dom.ownerDocument.body;
4658
- for (let parent = dom.parentNode; parent && parent != body;) {
4667
+ let doc = dom.ownerDocument, win = doc.defaultView || window;
4668
+ let left = Math.max(0, rect.left), right = Math.min(win.innerWidth, rect.right);
4669
+ let top = Math.max(0, rect.top), bottom = Math.min(win.innerHeight, rect.bottom);
4670
+ for (let parent = dom.parentNode; parent && parent != doc.body;) {
4659
4671
  if (parent.nodeType == 1) {
4660
4672
  let elt = parent;
4661
4673
  let style = window.getComputedStyle(elt);
@@ -4850,7 +4862,7 @@ class ViewState {
4850
4862
  if (inView)
4851
4863
  measureContent = true;
4852
4864
  }
4853
- if (!this.inView)
4865
+ if (!this.inView && !this.scrollTarget)
4854
4866
  return 0;
4855
4867
  let contentWidth = dom.clientWidth;
4856
4868
  if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
@@ -5294,8 +5306,8 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5294
5306
  "&.cm-focused .cm-cursor": {
5295
5307
  display: "block"
5296
5308
  },
5297
- "&light .cm-activeLine": { backgroundColor: "#f3f9ff" },
5298
- "&dark .cm-activeLine": { backgroundColor: "#223039" },
5309
+ "&light .cm-activeLine": { backgroundColor: "#cceeff44" },
5310
+ "&dark .cm-activeLine": { backgroundColor: "#99eeff33" },
5299
5311
  "&light .cm-specialChar": { color: "red" },
5300
5312
  "&dark .cm-specialChar": { color: "#f78" },
5301
5313
  ".cm-gutters": {
@@ -6168,7 +6180,7 @@ class EditorView {
6168
6180
  /**
6169
6181
  @internal
6170
6182
  */
6171
- get win() { return this.dom.ownerDocument.defaultView; }
6183
+ get win() { return this.dom.ownerDocument.defaultView || window; }
6172
6184
  dispatch(...input) {
6173
6185
  this._dispatch(input.length == 1 && input[0] instanceof Transaction ? input[0]
6174
6186
  : this.state.update(...input));
@@ -6727,7 +6739,7 @@ class EditorView {
6727
6739
  setRoot(root) {
6728
6740
  if (this._root != root) {
6729
6741
  this._root = root;
6730
- this.observer.setWindow((root.nodeType == 9 ? root : root.ownerDocument).defaultView);
6742
+ this.observer.setWindow((root.nodeType == 9 ? root : root.ownerDocument).defaultView || window);
6731
6743
  this.mountStyles();
6732
6744
  }
6733
6745
  }
@@ -7532,9 +7544,15 @@ class MatchDecorator {
7532
7544
  if (decorate) {
7533
7545
  this.addMatch = (match, view, from, add) => decorate(add, from, from + match[0].length, match, view);
7534
7546
  }
7547
+ else if (typeof decoration == "function") {
7548
+ this.addMatch = (match, view, from, add) => {
7549
+ let deco = decoration(match, view, from);
7550
+ if (deco)
7551
+ add(from, from + match[0].length, deco);
7552
+ };
7553
+ }
7535
7554
  else if (decoration) {
7536
- let getDeco = typeof decoration == "function" ? decoration : () => decoration;
7537
- this.addMatch = (match, view, from, add) => add(from, from + match[0].length, getDeco(match, view, from));
7555
+ this.addMatch = (match, _view, from, add) => add(from, from + match[0].length, decoration);
7538
7556
  }
7539
7557
  else {
7540
7558
  throw new RangeError("Either 'decorate' or 'decoration' should be provided to MatchDecorator");
@@ -7801,8 +7819,6 @@ const activeLineHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
7801
7819
  getDeco(view) {
7802
7820
  let lastLineStart = -1, deco = [];
7803
7821
  for (let r of view.state.selection.ranges) {
7804
- if (!r.empty)
7805
- return Decoration.none;
7806
7822
  let line = view.lineBlockAt(r.head);
7807
7823
  if (line.from > lastLineStart) {
7808
7824
  deco.push(lineDeco.range(line.from));
@@ -8020,8 +8036,9 @@ Creates an extension that configures tooltip behavior.
8020
8036
  function tooltips(config = {}) {
8021
8037
  return tooltipConfig.of(config);
8022
8038
  }
8023
- function windowSpace() {
8024
- return { top: 0, left: 0, bottom: innerHeight, right: innerWidth };
8039
+ function windowSpace(view) {
8040
+ let { win } = view;
8041
+ return { top: 0, left: 0, bottom: win.innerHeight, right: win.innerWidth };
8025
8042
  }
8026
8043
  const tooltipConfig = /*@__PURE__*/Facet.define({
8027
8044
  combine: values => {
@@ -9116,14 +9133,13 @@ const activeLineGutterMarker = /*@__PURE__*/new class extends GutterMarker {
9116
9133
  };
9117
9134
  const activeLineGutterHighlighter = /*@__PURE__*/gutterLineClass.compute(["selection"], state => {
9118
9135
  let marks = [], last = -1;
9119
- for (let range of state.selection.ranges)
9120
- if (range.empty) {
9121
- let linePos = state.doc.lineAt(range.head).from;
9122
- if (linePos > last) {
9123
- last = linePos;
9124
- marks.push(activeLineGutterMarker.range(linePos));
9125
- }
9136
+ for (let range of state.selection.ranges) {
9137
+ let linePos = state.doc.lineAt(range.head).from;
9138
+ if (linePos > last) {
9139
+ last = linePos;
9140
+ marks.push(activeLineGutterMarker.range(linePos));
9126
9141
  }
9142
+ }
9127
9143
  return RangeSet.of(marks);
9128
9144
  });
9129
9145
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.2.3",
3
+ "version": "6.2.5",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",