@codemirror/view 6.11.1 → 6.11.2

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,15 @@
1
+ ## 6.11.2 (2023-05-13)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug where the `crosshairCursor` extension could, when non-native key events were fired, trigger disruptive and needless view updates.
6
+
7
+ Fix an Android issue where backspacing at the front of a line with widget decorations could replace those decorations with their text content.
8
+
9
+ Respect scroll margins when scrolling the target of drag-selection into view.
10
+
11
+ Validate selection offsets reported by the browser, to work around Safari giving us invalid values in some cases.
12
+
1
13
  ## 6.11.1 (2023-05-09)
2
14
 
3
15
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -222,7 +222,9 @@ class DOMSelectionState {
222
222
  this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
223
223
  }
224
224
  setRange(range) {
225
- this.set(range.anchorNode, range.anchorOffset, range.focusNode, range.focusOffset);
225
+ let { anchorNode, focusNode } = range;
226
+ // Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152)
227
+ this.set(anchorNode, Math.min(range.anchorOffset, anchorNode ? maxOffset(anchorNode) : 0), focusNode, Math.min(range.focusOffset, focusNode ? maxOffset(focusNode) : 0));
226
228
  }
227
229
  set(anchorNode, anchorOffset, focusNode, focusOffset) {
228
230
  this.anchorNode = anchorNode;
@@ -295,6 +297,8 @@ function atElementStart(doc, selection) {
295
297
  let node = selection.focusNode, offset = selection.focusOffset;
296
298
  if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
297
299
  return false;
300
+ // Safari can report bogus offsets (#1152)
301
+ offset = Math.min(offset, maxOffset(node));
298
302
  for (;;) {
299
303
  if (offset) {
300
304
  if (node.nodeType != 1)
@@ -2007,6 +2011,23 @@ const contentAttributes = state.Facet.define();
2007
2011
  const decorations = state.Facet.define();
2008
2012
  const atomicRanges = state.Facet.define();
2009
2013
  const scrollMargins = state.Facet.define();
2014
+ function getScrollMargins(view) {
2015
+ let left = 0, right = 0, top = 0, bottom = 0;
2016
+ for (let source of view.state.facet(scrollMargins)) {
2017
+ let m = source(view);
2018
+ if (m) {
2019
+ if (m.left != null)
2020
+ left = Math.max(left, m.left);
2021
+ if (m.right != null)
2022
+ right = Math.max(right, m.right);
2023
+ if (m.top != null)
2024
+ top = Math.max(top, m.top);
2025
+ if (m.bottom != null)
2026
+ bottom = Math.max(bottom, m.bottom);
2027
+ }
2028
+ }
2029
+ return { left, right, top, bottom };
2030
+ }
2010
2031
  const styleModule = state.Facet.define();
2011
2032
  class ChangedRange {
2012
2033
  constructor(fromA, toA, fromB, toB) {
@@ -2938,22 +2959,10 @@ class DocView extends ContentView {
2938
2959
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2939
2960
  rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2940
2961
  right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2941
- let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2942
- for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
2943
- if (margins) {
2944
- let { left, right, top, bottom } = margins;
2945
- if (left != null)
2946
- mLeft = Math.max(mLeft, left);
2947
- if (right != null)
2948
- mRight = Math.max(mRight, right);
2949
- if (top != null)
2950
- mTop = Math.max(mTop, top);
2951
- if (bottom != null)
2952
- mBottom = Math.max(mBottom, bottom);
2953
- }
2962
+ let margins = getScrollMargins(this.view);
2954
2963
  let targetRect = {
2955
- left: rect.left - mLeft, top: rect.top - mTop,
2956
- right: rect.right + mRight, bottom: rect.bottom + mBottom
2964
+ left: rect.left - margins.left, top: rect.top - margins.top,
2965
+ right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
2957
2966
  };
2958
2967
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == exports.Direction.LTR);
2959
2968
  }
@@ -3733,13 +3742,14 @@ class MouseSelection {
3733
3742
  let sx = 0, sy = 0;
3734
3743
  let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
3735
3744
  || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
3736
- if (event.clientX <= rect.left + dragScrollMargin)
3745
+ let margins = getScrollMargins(this.view);
3746
+ if (event.clientX - margins.left <= rect.left + dragScrollMargin)
3737
3747
  sx = -dragScrollSpeed(rect.left - event.clientX);
3738
- else if (event.clientX >= rect.right - dragScrollMargin)
3748
+ else if (event.clientX + margins.right >= rect.right - dragScrollMargin)
3739
3749
  sx = dragScrollSpeed(event.clientX - rect.right);
3740
- if (event.clientY <= rect.top + dragScrollMargin)
3750
+ if (event.clientY - margins.top <= rect.top + dragScrollMargin)
3741
3751
  sy = -dragScrollSpeed(rect.top - event.clientY);
3742
- else if (event.clientY >= rect.bottom - dragScrollMargin)
3752
+ else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin)
3743
3753
  sy = dragScrollSpeed(event.clientY - rect.bottom);
3744
3754
  this.setScrollSpeed(sx, sy);
3745
3755
  }
@@ -5781,13 +5791,13 @@ class DOMChange {
5781
5791
  function applyDOMChange(view, domChange) {
5782
5792
  let change;
5783
5793
  let { newSel } = domChange, sel = view.state.selection.main;
5794
+ let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
5784
5795
  if (domChange.bounds) {
5785
5796
  let { from, to } = domChange.bounds;
5786
5797
  let preferredPos = sel.from, preferredSide = null;
5787
5798
  // Prefer anchoring to end when Backspace is pressed (or, on
5788
5799
  // Android, when something was deleted)
5789
- if (view.inputState.lastKeyCode === 8 && view.inputState.lastKeyTime > Date.now() - 100 ||
5790
- browser.android && domChange.text.length < to - from) {
5800
+ if (lastKey === 8 || browser.android && domChange.text.length < to - from) {
5791
5801
  preferredPos = sel.to;
5792
5802
  preferredSide = "end";
5793
5803
  }
@@ -5795,7 +5805,7 @@ function applyDOMChange(view, domChange) {
5795
5805
  if (diff) {
5796
5806
  // Chrome inserts two newlines when pressing shift-enter at the
5797
5807
  // end of a line. DomChange drops one of those.
5798
- if (browser.chrome && view.inputState.lastKeyCode == 13 &&
5808
+ if (browser.chrome && lastKey == 13 &&
5799
5809
  diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
5800
5810
  diff.toB--;
5801
5811
  change = { from: from + diff.from, to: from + diff.toA,
@@ -5853,7 +5863,8 @@ function applyDOMChange(view, domChange) {
5853
5863
  ((change.from == sel.from && change.to == sel.to &&
5854
5864
  change.insert.length == 1 && change.insert.lines == 2 &&
5855
5865
  dispatchKey(view.contentDOM, "Enter", 13)) ||
5856
- (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
5866
+ ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
5867
+ lastKey == 8 && change.insert.length < change.to - change.from) &&
5857
5868
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5858
5869
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5859
5870
  dispatchKey(view.contentDOM, "Delete", 46))))
@@ -6251,7 +6262,10 @@ class DOMObserver {
6251
6262
  let key = this.delayedAndroidKey;
6252
6263
  if (key) {
6253
6264
  this.clearDelayedAndroidKey();
6254
- if (!this.flush() && key.force)
6265
+ this.view.inputState.lastKeyCode = key.keyCode;
6266
+ this.view.inputState.lastKeyTime = Date.now();
6267
+ let flushed = this.flush();
6268
+ if (!flushed && key.force)
6255
6269
  dispatchKey(this.dom, key.key, key.keyCode);
6256
6270
  }
6257
6271
  };
@@ -8480,10 +8494,10 @@ function rectangularSelection(options) {
8480
8494
  return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null);
8481
8495
  }
8482
8496
  const keys = {
8483
- Alt: [18, e => e.altKey],
8484
- Control: [17, e => e.ctrlKey],
8485
- Shift: [16, e => e.shiftKey],
8486
- Meta: [91, e => e.metaKey]
8497
+ Alt: [18, e => !!e.altKey],
8498
+ Control: [17, e => !!e.ctrlKey],
8499
+ Shift: [16, e => !!e.shiftKey],
8500
+ Meta: [91, e => !!e.metaKey]
8487
8501
  };
8488
8502
  const showCrosshair = { style: "cursor: crosshair" };
8489
8503
  /**
package/dist/index.js CHANGED
@@ -218,7 +218,9 @@ class DOMSelectionState {
218
218
  this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
219
219
  }
220
220
  setRange(range) {
221
- this.set(range.anchorNode, range.anchorOffset, range.focusNode, range.focusOffset);
221
+ let { anchorNode, focusNode } = range;
222
+ // Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152)
223
+ this.set(anchorNode, Math.min(range.anchorOffset, anchorNode ? maxOffset(anchorNode) : 0), focusNode, Math.min(range.focusOffset, focusNode ? maxOffset(focusNode) : 0));
222
224
  }
223
225
  set(anchorNode, anchorOffset, focusNode, focusOffset) {
224
226
  this.anchorNode = anchorNode;
@@ -291,6 +293,8 @@ function atElementStart(doc, selection) {
291
293
  let node = selection.focusNode, offset = selection.focusOffset;
292
294
  if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
293
295
  return false;
296
+ // Safari can report bogus offsets (#1152)
297
+ offset = Math.min(offset, maxOffset(node));
294
298
  for (;;) {
295
299
  if (offset) {
296
300
  if (node.nodeType != 1)
@@ -2002,6 +2006,23 @@ const contentAttributes = /*@__PURE__*/Facet.define();
2002
2006
  const decorations = /*@__PURE__*/Facet.define();
2003
2007
  const atomicRanges = /*@__PURE__*/Facet.define();
2004
2008
  const scrollMargins = /*@__PURE__*/Facet.define();
2009
+ function getScrollMargins(view) {
2010
+ let left = 0, right = 0, top = 0, bottom = 0;
2011
+ for (let source of view.state.facet(scrollMargins)) {
2012
+ let m = source(view);
2013
+ if (m) {
2014
+ if (m.left != null)
2015
+ left = Math.max(left, m.left);
2016
+ if (m.right != null)
2017
+ right = Math.max(right, m.right);
2018
+ if (m.top != null)
2019
+ top = Math.max(top, m.top);
2020
+ if (m.bottom != null)
2021
+ bottom = Math.max(bottom, m.bottom);
2022
+ }
2023
+ }
2024
+ return { left, right, top, bottom };
2025
+ }
2005
2026
  const styleModule = /*@__PURE__*/Facet.define();
2006
2027
  class ChangedRange {
2007
2028
  constructor(fromA, toA, fromB, toB) {
@@ -2932,22 +2953,10 @@ class DocView extends ContentView {
2932
2953
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
2933
2954
  rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
2934
2955
  right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
2935
- let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
2936
- for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
2937
- if (margins) {
2938
- let { left, right, top, bottom } = margins;
2939
- if (left != null)
2940
- mLeft = Math.max(mLeft, left);
2941
- if (right != null)
2942
- mRight = Math.max(mRight, right);
2943
- if (top != null)
2944
- mTop = Math.max(mTop, top);
2945
- if (bottom != null)
2946
- mBottom = Math.max(mBottom, bottom);
2947
- }
2956
+ let margins = getScrollMargins(this.view);
2948
2957
  let targetRect = {
2949
- left: rect.left - mLeft, top: rect.top - mTop,
2950
- right: rect.right + mRight, bottom: rect.bottom + mBottom
2958
+ left: rect.left - margins.left, top: rect.top - margins.top,
2959
+ right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
2951
2960
  };
2952
2961
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == Direction.LTR);
2953
2962
  }
@@ -3727,13 +3736,14 @@ class MouseSelection {
3727
3736
  let sx = 0, sy = 0;
3728
3737
  let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
3729
3738
  || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
3730
- if (event.clientX <= rect.left + dragScrollMargin)
3739
+ let margins = getScrollMargins(this.view);
3740
+ if (event.clientX - margins.left <= rect.left + dragScrollMargin)
3731
3741
  sx = -dragScrollSpeed(rect.left - event.clientX);
3732
- else if (event.clientX >= rect.right - dragScrollMargin)
3742
+ else if (event.clientX + margins.right >= rect.right - dragScrollMargin)
3733
3743
  sx = dragScrollSpeed(event.clientX - rect.right);
3734
- if (event.clientY <= rect.top + dragScrollMargin)
3744
+ if (event.clientY - margins.top <= rect.top + dragScrollMargin)
3735
3745
  sy = -dragScrollSpeed(rect.top - event.clientY);
3736
- else if (event.clientY >= rect.bottom - dragScrollMargin)
3746
+ else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin)
3737
3747
  sy = dragScrollSpeed(event.clientY - rect.bottom);
3738
3748
  this.setScrollSpeed(sx, sy);
3739
3749
  }
@@ -5774,13 +5784,13 @@ class DOMChange {
5774
5784
  function applyDOMChange(view, domChange) {
5775
5785
  let change;
5776
5786
  let { newSel } = domChange, sel = view.state.selection.main;
5787
+ let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
5777
5788
  if (domChange.bounds) {
5778
5789
  let { from, to } = domChange.bounds;
5779
5790
  let preferredPos = sel.from, preferredSide = null;
5780
5791
  // Prefer anchoring to end when Backspace is pressed (or, on
5781
5792
  // Android, when something was deleted)
5782
- if (view.inputState.lastKeyCode === 8 && view.inputState.lastKeyTime > Date.now() - 100 ||
5783
- browser.android && domChange.text.length < to - from) {
5793
+ if (lastKey === 8 || browser.android && domChange.text.length < to - from) {
5784
5794
  preferredPos = sel.to;
5785
5795
  preferredSide = "end";
5786
5796
  }
@@ -5788,7 +5798,7 @@ function applyDOMChange(view, domChange) {
5788
5798
  if (diff) {
5789
5799
  // Chrome inserts two newlines when pressing shift-enter at the
5790
5800
  // end of a line. DomChange drops one of those.
5791
- if (browser.chrome && view.inputState.lastKeyCode == 13 &&
5801
+ if (browser.chrome && lastKey == 13 &&
5792
5802
  diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
5793
5803
  diff.toB--;
5794
5804
  change = { from: from + diff.from, to: from + diff.toA,
@@ -5846,7 +5856,8 @@ function applyDOMChange(view, domChange) {
5846
5856
  ((change.from == sel.from && change.to == sel.to &&
5847
5857
  change.insert.length == 1 && change.insert.lines == 2 &&
5848
5858
  dispatchKey(view.contentDOM, "Enter", 13)) ||
5849
- (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
5859
+ ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
5860
+ lastKey == 8 && change.insert.length < change.to - change.from) &&
5850
5861
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5851
5862
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5852
5863
  dispatchKey(view.contentDOM, "Delete", 46))))
@@ -6244,7 +6255,10 @@ class DOMObserver {
6244
6255
  let key = this.delayedAndroidKey;
6245
6256
  if (key) {
6246
6257
  this.clearDelayedAndroidKey();
6247
- if (!this.flush() && key.force)
6258
+ this.view.inputState.lastKeyCode = key.keyCode;
6259
+ this.view.inputState.lastKeyTime = Date.now();
6260
+ let flushed = this.flush();
6261
+ if (!flushed && key.force)
6248
6262
  dispatchKey(this.dom, key.key, key.keyCode);
6249
6263
  }
6250
6264
  };
@@ -8473,10 +8487,10 @@ function rectangularSelection(options) {
8473
8487
  return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null);
8474
8488
  }
8475
8489
  const keys = {
8476
- Alt: [18, e => e.altKey],
8477
- Control: [17, e => e.ctrlKey],
8478
- Shift: [16, e => e.shiftKey],
8479
- Meta: [91, e => e.metaKey]
8490
+ Alt: [18, e => !!e.altKey],
8491
+ Control: [17, e => !!e.ctrlKey],
8492
+ Shift: [16, e => !!e.shiftKey],
8493
+ Meta: [91, e => !!e.metaKey]
8480
8494
  };
8481
8495
  const showCrosshair = { style: "cursor: crosshair" };
8482
8496
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.11.1",
3
+ "version": "6.11.2",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",