@codemirror/view 0.20.6 → 0.20.7

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
+ ## 0.20.7 (2022-05-30)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue on Chrome Android where the DOM could fail to display the actual document after backspace.
6
+
7
+ Avoid an issue on Chrome Android where DOM changes were sometimes inappropriately replace by a backspace key effect due to spurious beforeinput events.
8
+
9
+ Fix a problem where the content element's width didn't cover the width of the actual content.
10
+
11
+ Work around a bug in Chrome 102 which caused wheel scrolling of the editor to be interrupted every few lines.
12
+
1
13
  ## 0.20.6 (2022-05-20)
2
14
 
3
15
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -754,7 +754,7 @@ function textCoords(text, pos, side) {
754
754
  from--;
755
755
  flatten = 1;
756
756
  } // FIXME this is wrong in RTL text
757
- else {
757
+ else if (to < length) {
758
758
  to++;
759
759
  flatten = -1;
760
760
  }
@@ -763,7 +763,7 @@ function textCoords(text, pos, side) {
763
763
  else {
764
764
  if (side < 0)
765
765
  from--;
766
- else
766
+ else if (to < length)
767
767
  to++;
768
768
  }
769
769
  let rects = textRange(text, from, to).getClientRects();
@@ -1379,6 +1379,7 @@ class LineView extends ContentView {
1379
1379
  transferDOM(other) {
1380
1380
  if (!this.dom)
1381
1381
  return;
1382
+ this.markDirty();
1382
1383
  other.setDOM(this.dom);
1383
1384
  other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1384
1385
  this.prevAttrs = undefined;
@@ -2510,7 +2511,7 @@ class DocView extends ContentView {
2510
2511
  // no relayout is triggered and I cannot imagine how it can
2511
2512
  // recompute the scroll position without a layout)
2512
2513
  this.dom.style.height = this.view.viewState.contentHeight + "px";
2513
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2514
+ this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2514
2515
  // Chrome will sometimes, when DOM mutations occur directly
2515
2516
  // around the selection, get confused and report a different
2516
2517
  // selection from the one it displays (issue #218). This tries
@@ -3268,6 +3269,7 @@ class InputState {
3268
3269
  constructor(view) {
3269
3270
  this.lastKeyCode = 0;
3270
3271
  this.lastKeyTime = 0;
3272
+ this.chromeScrollHack = -1;
3271
3273
  // On iOS, some keys need to have their default behavior happen
3272
3274
  // (after which we retroactively handle them and reset the DOM) to
3273
3275
  // avoid messing up the virtual keyboard state.
@@ -3308,6 +3310,21 @@ class InputState {
3308
3310
  });
3309
3311
  this.registeredEvents.push(type);
3310
3312
  }
3313
+ if (browser.chrome && browser.chrome_version >= 102) {
3314
+ // On Chrome 102, viewport updates somehow stop wheel-based
3315
+ // scrolling. Turning off pointer events during the scroll seems
3316
+ // to avoid the issue.
3317
+ view.scrollDOM.addEventListener("wheel", () => {
3318
+ if (this.chromeScrollHack < 0)
3319
+ view.contentDOM.style.pointerEvents = "none";
3320
+ else
3321
+ window.clearTimeout(this.chromeScrollHack);
3322
+ this.chromeScrollHack = setTimeout(() => {
3323
+ this.chromeScrollHack = -1;
3324
+ view.contentDOM.style.pointerEvents = "";
3325
+ }, 100);
3326
+ }, { passive: true });
3327
+ }
3311
3328
  this.notifiedFocused = view.hasFocus;
3312
3329
  // On Safari adding an input event handler somehow prevents an
3313
3330
  // issue where the composition vanishes when you press enter.
@@ -5564,21 +5581,16 @@ class DOMObserver {
5564
5581
  // composition events that, when interrupted, cause text duplication
5565
5582
  // or other kinds of corruption. This hack makes the editor back off
5566
5583
  // from handling DOM changes for a moment when such a key is
5567
- // detected (via beforeinput or keydown), and then dispatches the
5568
- // key event, throwing away the DOM changes if it gets handled.
5584
+ // detected (via beforeinput or keydown), and then tries to flush
5585
+ // them or, if that has no effect, dispatches the given key.
5569
5586
  delayAndroidKey(key, keyCode) {
5570
5587
  if (!this.delayedAndroidKey)
5571
5588
  requestAnimationFrame(() => {
5572
5589
  let key = this.delayedAndroidKey;
5573
5590
  this.delayedAndroidKey = null;
5574
- let startState = this.view.state;
5575
- this.readSelectionRange();
5576
- if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
5577
- this.processRecords();
5578
- else
5579
- this.flush();
5580
- if (this.view.state == startState)
5581
- this.view.update([]);
5591
+ this.delayedFlush = -1;
5592
+ if (!this.flush())
5593
+ dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5582
5594
  });
5583
5595
  // Since backspace beforeinput is sometimes signalled spuriously,
5584
5596
  // Enter always takes precedence.
@@ -5634,10 +5646,11 @@ class DOMObserver {
5634
5646
  return;
5635
5647
  this.selectionChanged = false;
5636
5648
  let startState = this.view.state;
5637
- this.onChange(from, to, typeOver);
5649
+ let handled = this.onChange(from, to, typeOver);
5638
5650
  // The view wasn't updated
5639
5651
  if (this.view.state == startState)
5640
5652
  this.view.update([]);
5653
+ return handled;
5641
5654
  }
5642
5655
  readMutation(rec) {
5643
5656
  let cView = this.view.docView.nearest(rec.target);
@@ -5720,7 +5733,7 @@ function applyDOMChange(view, start, end, typeOver) {
5720
5733
  if (start > -1) {
5721
5734
  let bounds = view.docView.domBoundsAround(start, end, 0);
5722
5735
  if (!bounds || view.state.readOnly)
5723
- return;
5736
+ return false;
5724
5737
  let { from, to } = bounds;
5725
5738
  let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
5726
5739
  let reader = new DOMReader(selPoints, view.state);
@@ -5760,7 +5773,7 @@ function applyDOMChange(view, start, end, typeOver) {
5760
5773
  newSel = state.EditorSelection.single(anchor, head);
5761
5774
  }
5762
5775
  if (!change && !newSel)
5763
- return;
5776
+ return false;
5764
5777
  // Heuristic to notice typing over a selected character
5765
5778
  if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
5766
5779
  change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
@@ -5776,13 +5789,13 @@ function applyDOMChange(view, start, end, typeOver) {
5776
5789
  };
5777
5790
  // Detect insert-period-on-double-space Mac behavior, and transform
5778
5791
  // it into a regular space insert.
5779
- else if (browser.mac && change && change.from == change.to && change.from == sel.head - 1 &&
5792
+ else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5780
5793
  change.insert.toString() == ".")
5781
5794
  change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
5782
5795
  if (change) {
5783
5796
  let startState = view.state;
5784
5797
  if (browser.ios && view.inputState.flushIOSKey(view))
5785
- return;
5798
+ return true;
5786
5799
  // Android browsers don't fire reasonable key events for enter,
5787
5800
  // backspace, or delete. So this detects changes that look like
5788
5801
  // they're caused by those keys, and reinterprets them as key
@@ -5797,10 +5810,10 @@ function applyDOMChange(view, start, end, typeOver) {
5797
5810
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5798
5811
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5799
5812
  dispatchKey(view.contentDOM, "Delete", 46))))
5800
- return;
5813
+ return true;
5801
5814
  let text = change.insert.toString();
5802
5815
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
5803
- return;
5816
+ return true;
5804
5817
  if (view.inputState.composing >= 0)
5805
5818
  view.inputState.composing++;
5806
5819
  let tr;
@@ -5856,6 +5869,7 @@ function applyDOMChange(view, start, end, typeOver) {
5856
5869
  }
5857
5870
  }
5858
5871
  view.dispatch(tr, { scrollIntoView: true, userEvent });
5872
+ return true;
5859
5873
  }
5860
5874
  else if (newSel && !newSel.main.eq(sel)) {
5861
5875
  let scrollIntoView = false, userEvent = "select";
@@ -5865,6 +5879,10 @@ function applyDOMChange(view, start, end, typeOver) {
5865
5879
  userEvent = view.inputState.lastSelectionOrigin;
5866
5880
  }
5867
5881
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
5882
+ return true;
5883
+ }
5884
+ else {
5885
+ return false;
5868
5886
  }
5869
5887
  }
5870
5888
  function findDiff(a, b, preferredPos, preferredSide) {
@@ -5983,7 +6001,7 @@ class EditorView {
5983
6001
  for (let plugin of this.plugins)
5984
6002
  plugin.update(this);
5985
6003
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5986
- applyDOMChange(this, from, to, typeOver);
6004
+ return applyDOMChange(this, from, to, typeOver);
5987
6005
  }, event => {
5988
6006
  this.inputState.runScrollHandlers(this, event);
5989
6007
  if (this.observer.intersecting)
@@ -6064,6 +6082,7 @@ class EditorView {
6064
6082
  this.viewState.state = state$1;
6065
6083
  return;
6066
6084
  }
6085
+ this.observer.clear();
6067
6086
  // When the phrases change, redraw the editor
6068
6087
  if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
6069
6088
  return this.setState(state$1);
package/dist/index.js CHANGED
@@ -750,7 +750,7 @@ function textCoords(text, pos, side) {
750
750
  from--;
751
751
  flatten = 1;
752
752
  } // FIXME this is wrong in RTL text
753
- else {
753
+ else if (to < length) {
754
754
  to++;
755
755
  flatten = -1;
756
756
  }
@@ -759,7 +759,7 @@ function textCoords(text, pos, side) {
759
759
  else {
760
760
  if (side < 0)
761
761
  from--;
762
- else
762
+ else if (to < length)
763
763
  to++;
764
764
  }
765
765
  let rects = textRange(text, from, to).getClientRects();
@@ -1374,6 +1374,7 @@ class LineView extends ContentView {
1374
1374
  transferDOM(other) {
1375
1375
  if (!this.dom)
1376
1376
  return;
1377
+ this.markDirty();
1377
1378
  other.setDOM(this.dom);
1378
1379
  other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1379
1380
  this.prevAttrs = undefined;
@@ -2504,7 +2505,7 @@ class DocView extends ContentView {
2504
2505
  // no relayout is triggered and I cannot imagine how it can
2505
2506
  // recompute the scroll position without a layout)
2506
2507
  this.dom.style.height = this.view.viewState.contentHeight + "px";
2507
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2508
+ this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2508
2509
  // Chrome will sometimes, when DOM mutations occur directly
2509
2510
  // around the selection, get confused and report a different
2510
2511
  // selection from the one it displays (issue #218). This tries
@@ -3262,6 +3263,7 @@ class InputState {
3262
3263
  constructor(view) {
3263
3264
  this.lastKeyCode = 0;
3264
3265
  this.lastKeyTime = 0;
3266
+ this.chromeScrollHack = -1;
3265
3267
  // On iOS, some keys need to have their default behavior happen
3266
3268
  // (after which we retroactively handle them and reset the DOM) to
3267
3269
  // avoid messing up the virtual keyboard state.
@@ -3302,6 +3304,21 @@ class InputState {
3302
3304
  });
3303
3305
  this.registeredEvents.push(type);
3304
3306
  }
3307
+ if (browser.chrome && browser.chrome_version >= 102) {
3308
+ // On Chrome 102, viewport updates somehow stop wheel-based
3309
+ // scrolling. Turning off pointer events during the scroll seems
3310
+ // to avoid the issue.
3311
+ view.scrollDOM.addEventListener("wheel", () => {
3312
+ if (this.chromeScrollHack < 0)
3313
+ view.contentDOM.style.pointerEvents = "none";
3314
+ else
3315
+ window.clearTimeout(this.chromeScrollHack);
3316
+ this.chromeScrollHack = setTimeout(() => {
3317
+ this.chromeScrollHack = -1;
3318
+ view.contentDOM.style.pointerEvents = "";
3319
+ }, 100);
3320
+ }, { passive: true });
3321
+ }
3305
3322
  this.notifiedFocused = view.hasFocus;
3306
3323
  // On Safari adding an input event handler somehow prevents an
3307
3324
  // issue where the composition vanishes when you press enter.
@@ -5557,21 +5574,16 @@ class DOMObserver {
5557
5574
  // composition events that, when interrupted, cause text duplication
5558
5575
  // or other kinds of corruption. This hack makes the editor back off
5559
5576
  // from handling DOM changes for a moment when such a key is
5560
- // detected (via beforeinput or keydown), and then dispatches the
5561
- // key event, throwing away the DOM changes if it gets handled.
5577
+ // detected (via beforeinput or keydown), and then tries to flush
5578
+ // them or, if that has no effect, dispatches the given key.
5562
5579
  delayAndroidKey(key, keyCode) {
5563
5580
  if (!this.delayedAndroidKey)
5564
5581
  requestAnimationFrame(() => {
5565
5582
  let key = this.delayedAndroidKey;
5566
5583
  this.delayedAndroidKey = null;
5567
- let startState = this.view.state;
5568
- this.readSelectionRange();
5569
- if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
5570
- this.processRecords();
5571
- else
5572
- this.flush();
5573
- if (this.view.state == startState)
5574
- this.view.update([]);
5584
+ this.delayedFlush = -1;
5585
+ if (!this.flush())
5586
+ dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5575
5587
  });
5576
5588
  // Since backspace beforeinput is sometimes signalled spuriously,
5577
5589
  // Enter always takes precedence.
@@ -5627,10 +5639,11 @@ class DOMObserver {
5627
5639
  return;
5628
5640
  this.selectionChanged = false;
5629
5641
  let startState = this.view.state;
5630
- this.onChange(from, to, typeOver);
5642
+ let handled = this.onChange(from, to, typeOver);
5631
5643
  // The view wasn't updated
5632
5644
  if (this.view.state == startState)
5633
5645
  this.view.update([]);
5646
+ return handled;
5634
5647
  }
5635
5648
  readMutation(rec) {
5636
5649
  let cView = this.view.docView.nearest(rec.target);
@@ -5713,7 +5726,7 @@ function applyDOMChange(view, start, end, typeOver) {
5713
5726
  if (start > -1) {
5714
5727
  let bounds = view.docView.domBoundsAround(start, end, 0);
5715
5728
  if (!bounds || view.state.readOnly)
5716
- return;
5729
+ return false;
5717
5730
  let { from, to } = bounds;
5718
5731
  let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
5719
5732
  let reader = new DOMReader(selPoints, view.state);
@@ -5753,7 +5766,7 @@ function applyDOMChange(view, start, end, typeOver) {
5753
5766
  newSel = EditorSelection.single(anchor, head);
5754
5767
  }
5755
5768
  if (!change && !newSel)
5756
- return;
5769
+ return false;
5757
5770
  // Heuristic to notice typing over a selected character
5758
5771
  if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
5759
5772
  change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
@@ -5769,13 +5782,13 @@ function applyDOMChange(view, start, end, typeOver) {
5769
5782
  };
5770
5783
  // Detect insert-period-on-double-space Mac behavior, and transform
5771
5784
  // it into a regular space insert.
5772
- else if (browser.mac && change && change.from == change.to && change.from == sel.head - 1 &&
5785
+ else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5773
5786
  change.insert.toString() == ".")
5774
5787
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
5775
5788
  if (change) {
5776
5789
  let startState = view.state;
5777
5790
  if (browser.ios && view.inputState.flushIOSKey(view))
5778
- return;
5791
+ return true;
5779
5792
  // Android browsers don't fire reasonable key events for enter,
5780
5793
  // backspace, or delete. So this detects changes that look like
5781
5794
  // they're caused by those keys, and reinterprets them as key
@@ -5790,10 +5803,10 @@ function applyDOMChange(view, start, end, typeOver) {
5790
5803
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5791
5804
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5792
5805
  dispatchKey(view.contentDOM, "Delete", 46))))
5793
- return;
5806
+ return true;
5794
5807
  let text = change.insert.toString();
5795
5808
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
5796
- return;
5809
+ return true;
5797
5810
  if (view.inputState.composing >= 0)
5798
5811
  view.inputState.composing++;
5799
5812
  let tr;
@@ -5849,6 +5862,7 @@ function applyDOMChange(view, start, end, typeOver) {
5849
5862
  }
5850
5863
  }
5851
5864
  view.dispatch(tr, { scrollIntoView: true, userEvent });
5865
+ return true;
5852
5866
  }
5853
5867
  else if (newSel && !newSel.main.eq(sel)) {
5854
5868
  let scrollIntoView = false, userEvent = "select";
@@ -5858,6 +5872,10 @@ function applyDOMChange(view, start, end, typeOver) {
5858
5872
  userEvent = view.inputState.lastSelectionOrigin;
5859
5873
  }
5860
5874
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
5875
+ return true;
5876
+ }
5877
+ else {
5878
+ return false;
5861
5879
  }
5862
5880
  }
5863
5881
  function findDiff(a, b, preferredPos, preferredSide) {
@@ -5976,7 +5994,7 @@ class EditorView {
5976
5994
  for (let plugin of this.plugins)
5977
5995
  plugin.update(this);
5978
5996
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5979
- applyDOMChange(this, from, to, typeOver);
5997
+ return applyDOMChange(this, from, to, typeOver);
5980
5998
  }, event => {
5981
5999
  this.inputState.runScrollHandlers(this, event);
5982
6000
  if (this.observer.intersecting)
@@ -6057,6 +6075,7 @@ class EditorView {
6057
6075
  this.viewState.state = state;
6058
6076
  return;
6059
6077
  }
6078
+ this.observer.clear();
6060
6079
  // When the phrases change, redraw the editor
6061
6080
  if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
6062
6081
  return this.setState(state);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.20.6",
3
+ "version": "0.20.7",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",