@codemirror/view 6.1.1 → 6.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 6.1.4 (2022-08-04)
2
+
3
+ ### Bug fixes
4
+
5
+ Make selection-restoration on focus more reliable.
6
+
7
+ ## 6.1.3 (2022-08-03)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix a bug where a document that contains only non-printing characters would lead to bogus text measurements (and, from those, to crashing).
12
+
13
+ Make sure differences between estimated and actual block heights don't cause visible scroll glitches.
14
+
15
+ ## 6.1.2 (2022-07-27)
16
+
17
+ ### Bug fixes
18
+
19
+ Fix an issue where double tapping enter to confirm IME input and insert a newline on iOS would sometimes insert two newlines.
20
+
21
+ Fix an issue on iOS where a composition could get aborted if the editor scrolled on backspace.
22
+
1
23
  ## 6.1.1 (2022-07-25)
2
24
 
3
25
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1473,15 +1473,17 @@ class LineView extends ContentView {
1473
1473
  return null;
1474
1474
  let totalWidth = 0;
1475
1475
  for (let child of this.children) {
1476
- if (!(child instanceof TextView))
1476
+ if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1477
1477
  return null;
1478
1478
  let rects = clientRectsFor(child.dom);
1479
1479
  if (rects.length != 1)
1480
1480
  return null;
1481
1481
  totalWidth += rects[0].width;
1482
1482
  }
1483
- return { lineHeight: this.dom.getBoundingClientRect().height,
1484
- charWidth: totalWidth / this.length };
1483
+ return !totalWidth ? null : {
1484
+ lineHeight: this.dom.getBoundingClientRect().height,
1485
+ charWidth: totalWidth / this.length
1486
+ };
1485
1487
  }
1486
1488
  coordsAt(pos, side) {
1487
1489
  return coordsInChildren(this, pos, side);
@@ -3356,10 +3358,10 @@ class InputState {
3356
3358
  event.preventDefault();
3357
3359
  else
3358
3360
  handler(view, event);
3359
- });
3361
+ }, handlerOptions[type]);
3360
3362
  this.registeredEvents.push(type);
3361
3363
  }
3362
- if (browser.chrome && browser.chrome_version >= 102) {
3364
+ if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3363
3365
  // On Chrome 102, viewport updates somehow stop wheel-based
3364
3366
  // scrolling. Turning off pointer events during the scroll seems
3365
3367
  // to avoid the issue.
@@ -3481,7 +3483,7 @@ class InputState {
3481
3483
  // compositionend and keydown events are sometimes emitted in the
3482
3484
  // wrong order. The key event should still be ignored, even when
3483
3485
  // it happens after the compositionend event.
3484
- if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
3486
+ if (browser.safari && !browser.ios && Date.now() - this.compositionEndedAt < 100) {
3485
3487
  this.compositionEndedAt = 0;
3486
3488
  return true;
3487
3489
  }
@@ -3609,6 +3611,7 @@ function eventBelongsToEditor(view, event) {
3609
3611
  return true;
3610
3612
  }
3611
3613
  const handlers = Object.create(null);
3614
+ const handlerOptions = Object.create(null);
3612
3615
  // This is very crude, but unfortunately both these browsers _pretend_
3613
3616
  // that they have a clipboard API—all the objects and methods are
3614
3617
  // there, they just don't work, and they are hard to test.
@@ -3672,6 +3675,7 @@ handlers.touchstart = (view, e) => {
3672
3675
  handlers.touchmove = view => {
3673
3676
  view.inputState.setSelectionOrigin("select.pointer");
3674
3677
  };
3678
+ handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
3675
3679
  handlers.mousedown = (view, event) => {
3676
3680
  view.observer.flush();
3677
3681
  if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
@@ -4673,7 +4677,7 @@ function visiblePixelRange(dom, paddingTop) {
4673
4677
  left = Math.max(left, parentRect.left);
4674
4678
  right = Math.min(right, parentRect.right);
4675
4679
  top = Math.max(top, parentRect.top);
4676
- bottom = Math.min(bottom, parentRect.bottom);
4680
+ bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
4677
4681
  }
4678
4682
  parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
4679
4683
  }
@@ -5575,10 +5579,11 @@ class DOMObserver {
5575
5579
  // Detect the situation where the browser has, on focus, moved the
5576
5580
  // selection to the start of the content element. Reset it to the
5577
5581
  // position from the editor state.
5578
- if (local && !this.selectionChanged && this.selectionRange.focusNode &&
5582
+ if (local && !this.selectionChanged &&
5579
5583
  view.inputState.lastFocusTime > Date.now() - 200 &&
5580
5584
  view.inputState.lastTouchTime < Date.now() - 300 &&
5581
5585
  atElementStart(this.dom, range)) {
5586
+ this.view.inputState.lastFocusTime = 0;
5582
5587
  view.docView.updateSelection();
5583
5588
  return false;
5584
5589
  }
@@ -5687,8 +5692,8 @@ class DOMObserver {
5687
5692
  if (this.delayedFlush >= 0) {
5688
5693
  window.clearTimeout(this.delayedFlush);
5689
5694
  this.delayedFlush = -1;
5690
- this.flush();
5691
5695
  }
5696
+ this.flush();
5692
5697
  }
5693
5698
  processRecords() {
5694
5699
  let records = this.queue;
@@ -5726,7 +5731,6 @@ class DOMObserver {
5726
5731
  let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
5727
5732
  if (from < 0 && !newSel)
5728
5733
  return;
5729
- this.view.inputState.lastFocusTime = 0;
5730
5734
  this.selectionChanged = false;
5731
5735
  let startState = this.view.state;
5732
5736
  let handled = this.onChange(from, to, typeOver);
@@ -6280,12 +6284,15 @@ class EditorView {
6280
6284
  cancelAnimationFrame(this.measureScheduled);
6281
6285
  this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
6282
6286
  if (flush)
6283
- this.observer.flush();
6287
+ this.observer.forceFlush();
6284
6288
  let updated = null;
6289
+ let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
6290
+ let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
6285
6291
  try {
6286
6292
  for (let i = 0;; i++) {
6287
6293
  this.updateState = 1 /* Measuring */;
6288
6294
  let oldViewport = this.viewport;
6295
+ let refBlock = this.viewState.lineBlockAtHeight(refHeight);
6289
6296
  let changed = this.viewState.measure(this);
6290
6297
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6291
6298
  break;
@@ -6337,6 +6344,13 @@ class EditorView {
6337
6344
  this.viewState.scrollTarget = null;
6338
6345
  scrolled = true;
6339
6346
  }
6347
+ else {
6348
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6349
+ if (diff > 1 || diff < -1) {
6350
+ this.scrollDOM.scrollTop += diff;
6351
+ scrolled = true;
6352
+ }
6353
+ }
6340
6354
  if (redrawn)
6341
6355
  this.docView.updateSelection(true);
6342
6356
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
package/dist/index.js CHANGED
@@ -1468,15 +1468,17 @@ class LineView extends ContentView {
1468
1468
  return null;
1469
1469
  let totalWidth = 0;
1470
1470
  for (let child of this.children) {
1471
- if (!(child instanceof TextView))
1471
+ if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1472
1472
  return null;
1473
1473
  let rects = clientRectsFor(child.dom);
1474
1474
  if (rects.length != 1)
1475
1475
  return null;
1476
1476
  totalWidth += rects[0].width;
1477
1477
  }
1478
- return { lineHeight: this.dom.getBoundingClientRect().height,
1479
- charWidth: totalWidth / this.length };
1478
+ return !totalWidth ? null : {
1479
+ lineHeight: this.dom.getBoundingClientRect().height,
1480
+ charWidth: totalWidth / this.length
1481
+ };
1480
1482
  }
1481
1483
  coordsAt(pos, side) {
1482
1484
  return coordsInChildren(this, pos, side);
@@ -3350,10 +3352,10 @@ class InputState {
3350
3352
  event.preventDefault();
3351
3353
  else
3352
3354
  handler(view, event);
3353
- });
3355
+ }, handlerOptions[type]);
3354
3356
  this.registeredEvents.push(type);
3355
3357
  }
3356
- if (browser.chrome && browser.chrome_version >= 102) {
3358
+ if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3357
3359
  // On Chrome 102, viewport updates somehow stop wheel-based
3358
3360
  // scrolling. Turning off pointer events during the scroll seems
3359
3361
  // to avoid the issue.
@@ -3475,7 +3477,7 @@ class InputState {
3475
3477
  // compositionend and keydown events are sometimes emitted in the
3476
3478
  // wrong order. The key event should still be ignored, even when
3477
3479
  // it happens after the compositionend event.
3478
- if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
3480
+ if (browser.safari && !browser.ios && Date.now() - this.compositionEndedAt < 100) {
3479
3481
  this.compositionEndedAt = 0;
3480
3482
  return true;
3481
3483
  }
@@ -3603,6 +3605,7 @@ function eventBelongsToEditor(view, event) {
3603
3605
  return true;
3604
3606
  }
3605
3607
  const handlers = /*@__PURE__*/Object.create(null);
3608
+ const handlerOptions = /*@__PURE__*/Object.create(null);
3606
3609
  // This is very crude, but unfortunately both these browsers _pretend_
3607
3610
  // that they have a clipboard API—all the objects and methods are
3608
3611
  // there, they just don't work, and they are hard to test.
@@ -3666,6 +3669,7 @@ handlers.touchstart = (view, e) => {
3666
3669
  handlers.touchmove = view => {
3667
3670
  view.inputState.setSelectionOrigin("select.pointer");
3668
3671
  };
3672
+ handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
3669
3673
  handlers.mousedown = (view, event) => {
3670
3674
  view.observer.flush();
3671
3675
  if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
@@ -4666,7 +4670,7 @@ function visiblePixelRange(dom, paddingTop) {
4666
4670
  left = Math.max(left, parentRect.left);
4667
4671
  right = Math.min(right, parentRect.right);
4668
4672
  top = Math.max(top, parentRect.top);
4669
- bottom = Math.min(bottom, parentRect.bottom);
4673
+ bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
4670
4674
  }
4671
4675
  parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
4672
4676
  }
@@ -5568,10 +5572,11 @@ class DOMObserver {
5568
5572
  // Detect the situation where the browser has, on focus, moved the
5569
5573
  // selection to the start of the content element. Reset it to the
5570
5574
  // position from the editor state.
5571
- if (local && !this.selectionChanged && this.selectionRange.focusNode &&
5575
+ if (local && !this.selectionChanged &&
5572
5576
  view.inputState.lastFocusTime > Date.now() - 200 &&
5573
5577
  view.inputState.lastTouchTime < Date.now() - 300 &&
5574
5578
  atElementStart(this.dom, range)) {
5579
+ this.view.inputState.lastFocusTime = 0;
5575
5580
  view.docView.updateSelection();
5576
5581
  return false;
5577
5582
  }
@@ -5680,8 +5685,8 @@ class DOMObserver {
5680
5685
  if (this.delayedFlush >= 0) {
5681
5686
  window.clearTimeout(this.delayedFlush);
5682
5687
  this.delayedFlush = -1;
5683
- this.flush();
5684
5688
  }
5689
+ this.flush();
5685
5690
  }
5686
5691
  processRecords() {
5687
5692
  let records = this.queue;
@@ -5719,7 +5724,6 @@ class DOMObserver {
5719
5724
  let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
5720
5725
  if (from < 0 && !newSel)
5721
5726
  return;
5722
- this.view.inputState.lastFocusTime = 0;
5723
5727
  this.selectionChanged = false;
5724
5728
  let startState = this.view.state;
5725
5729
  let handled = this.onChange(from, to, typeOver);
@@ -6273,12 +6277,15 @@ class EditorView {
6273
6277
  cancelAnimationFrame(this.measureScheduled);
6274
6278
  this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
6275
6279
  if (flush)
6276
- this.observer.flush();
6280
+ this.observer.forceFlush();
6277
6281
  let updated = null;
6282
+ let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
6283
+ let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
6278
6284
  try {
6279
6285
  for (let i = 0;; i++) {
6280
6286
  this.updateState = 1 /* Measuring */;
6281
6287
  let oldViewport = this.viewport;
6288
+ let refBlock = this.viewState.lineBlockAtHeight(refHeight);
6282
6289
  let changed = this.viewState.measure(this);
6283
6290
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6284
6291
  break;
@@ -6330,6 +6337,13 @@ class EditorView {
6330
6337
  this.viewState.scrollTarget = null;
6331
6338
  scrolled = true;
6332
6339
  }
6340
+ else {
6341
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6342
+ if (diff > 1 || diff < -1) {
6343
+ this.scrollDOM.scrollTop += diff;
6344
+ scrolled = true;
6345
+ }
6346
+ }
6333
6347
  if (redrawn)
6334
6348
  this.docView.updateSelection(true);
6335
6349
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.1.1",
3
+ "version": "6.1.4",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",