@codemirror/view 6.1.2 → 6.2.0

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,27 @@
1
+ ## 6.2.0 (2022-08-05)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug where `posAtCoords` would return the wrong results for positions to the right of wrapped lines.
6
+
7
+ ### New features
8
+
9
+ The new `EditorView.setRoot` method can be used when an editor view is moved to a new document or shadow root.
10
+
11
+ ## 6.1.4 (2022-08-04)
12
+
13
+ ### Bug fixes
14
+
15
+ Make selection-restoration on focus more reliable.
16
+
17
+ ## 6.1.3 (2022-08-03)
18
+
19
+ ### Bug fixes
20
+
21
+ Fix a bug where a document that contains only non-printing characters would lead to bogus text measurements (and, from those, to crashing).
22
+
23
+ Make sure differences between estimated and actual block heights don't cause visible scroll glitches.
24
+
1
25
  ## 6.1.2 (2022-07-27)
2
26
 
3
27
  ### 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);
@@ -2481,7 +2483,6 @@ class DocView extends ContentView {
2481
2483
  this.updateDeco();
2482
2484
  this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2483
2485
  }
2484
- get root() { return this.view.root; }
2485
2486
  get editorView() { return this.view; }
2486
2487
  get length() { return this.view.state.doc.length; }
2487
2488
  // Update the document view to a given state. scrollIntoView can be
@@ -2604,7 +2605,7 @@ class DocView extends ContentView {
2604
2605
  this.dom.blur();
2605
2606
  this.dom.focus({ preventScroll: true });
2606
2607
  }
2607
- let rawSel = getSelection(this.root);
2608
+ let rawSel = getSelection(this.view.root);
2608
2609
  if (!rawSel) ;
2609
2610
  else if (main.empty) {
2610
2611
  // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
@@ -2647,7 +2648,7 @@ class DocView extends ContentView {
2647
2648
  if (this.compositionDeco.size)
2648
2649
  return;
2649
2650
  let cursor = this.view.state.selection.main;
2650
- let sel = getSelection(this.root);
2651
+ let sel = getSelection(this.view.root);
2651
2652
  if (!sel || !cursor.empty || !cursor.assoc || !sel.modify)
2652
2653
  return;
2653
2654
  let line = LineView.find(this, cursor.head);
@@ -2664,7 +2665,7 @@ class DocView extends ContentView {
2664
2665
  sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
2665
2666
  }
2666
2667
  mayControlSelection() {
2667
- let active = this.root.activeElement;
2668
+ let active = this.view.root.activeElement;
2668
2669
  return active == this.dom ||
2669
2670
  hasSelection(this.dom, this.view.observer.selectionRange) && !(active && this.dom.contains(active));
2670
2671
  }
@@ -3017,7 +3018,7 @@ function upBot(rect, bottom) {
3017
3018
  return bottom > rect.bottom ? { top: rect.top, left: rect.left, right: rect.right, bottom } : rect;
3018
3019
  }
3019
3020
  function domPosAtCoords(parent, x, y) {
3020
- let closest, closestRect, closestX, closestY;
3021
+ let closest, closestRect, closestX, closestY, closestOverlap = false;
3021
3022
  let above, below, aboveRect, belowRect;
3022
3023
  for (let child = parent.firstChild; child; child = child.nextSibling) {
3023
3024
  let rects = clientRectsFor(child);
@@ -3033,6 +3034,7 @@ function domPosAtCoords(parent, x, y) {
3033
3034
  closestRect = rect;
3034
3035
  closestX = dx;
3035
3036
  closestY = dy;
3037
+ closestOverlap = !dx || (dx > 0 ? i < rects.length - 1 : i > 0);
3036
3038
  }
3037
3039
  if (dx == 0) {
3038
3040
  if (y > rect.bottom && (!aboveRect || aboveRect.bottom < rect.bottom)) {
@@ -3065,7 +3067,7 @@ function domPosAtCoords(parent, x, y) {
3065
3067
  let clipX = Math.max(closestRect.left, Math.min(closestRect.right, x));
3066
3068
  if (closest.nodeType == 3)
3067
3069
  return domPosInText(closest, clipX, y);
3068
- if (!closestX && closest.contentEditable == "true")
3070
+ if (closestOverlap && closest.contentEditable != "false")
3069
3071
  return domPosAtCoords(closest, clipX, y);
3070
3072
  let offset = Array.prototype.indexOf.call(parent.childNodes, closest) +
3071
3073
  (x >= (closestRect.left + closestRect.right) / 2 ? 1 : 0);
@@ -3162,7 +3164,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3162
3164
  let range = doc.caretRangeFromPoint(x, y);
3163
3165
  if (range) {
3164
3166
  ({ startContainer: node, startOffset: offset } = range);
3165
- if (browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3167
+ if (!view.contentDOM.contains(node) ||
3168
+ browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3166
3169
  browser.chrome && isSuspiciousChromeCaretResult(node, offset, x))
3167
3170
  node = undefined;
3168
3171
  }
@@ -4675,7 +4678,7 @@ function visiblePixelRange(dom, paddingTop) {
4675
4678
  left = Math.max(left, parentRect.left);
4676
4679
  right = Math.min(right, parentRect.right);
4677
4680
  top = Math.max(top, parentRect.top);
4678
- bottom = Math.min(bottom, parentRect.bottom);
4681
+ bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
4679
4682
  }
4680
4683
  parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
4681
4684
  }
@@ -5376,6 +5379,7 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5376
5379
  ".cm-widgetBuffer": {
5377
5380
  verticalAlign: "text-top",
5378
5381
  height: "1em",
5382
+ width: 0,
5379
5383
  display: "inline"
5380
5384
  },
5381
5385
  ".cm-placeholder": {
@@ -5485,7 +5489,9 @@ class DOMObserver {
5485
5489
  this.flushSoon();
5486
5490
  };
5487
5491
  this.onSelectionChange = this.onSelectionChange.bind(this);
5488
- window.addEventListener("resize", this.onResize = this.onResize.bind(this));
5492
+ this.onResize = this.onResize.bind(this);
5493
+ this.onPrint = this.onPrint.bind(this);
5494
+ this.onScroll = this.onScroll.bind(this);
5489
5495
  if (typeof ResizeObserver == "function") {
5490
5496
  this.resize = new ResizeObserver(() => {
5491
5497
  if (this.view.docView.lastUpdate < Date.now() - 75)
@@ -5493,9 +5499,9 @@ class DOMObserver {
5493
5499
  });
5494
5500
  this.resize.observe(view.scrollDOM);
5495
5501
  }
5496
- window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this));
5502
+ this.win = view.dom.ownerDocument.defaultView;
5503
+ this.addWindowListeners(this.win);
5497
5504
  this.start();
5498
- window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
5499
5505
  if (typeof IntersectionObserver == "function") {
5500
5506
  this.intersection = new IntersectionObserver(entries => {
5501
5507
  if (this.parentCheck < 0)
@@ -5514,7 +5520,6 @@ class DOMObserver {
5514
5520
  }
5515
5521
  this.listenForScroll();
5516
5522
  this.readSelectionRange();
5517
- this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
5518
5523
  }
5519
5524
  onScroll(e) {
5520
5525
  if (this.intersecting)
@@ -5577,10 +5582,11 @@ class DOMObserver {
5577
5582
  // Detect the situation where the browser has, on focus, moved the
5578
5583
  // selection to the start of the content element. Reset it to the
5579
5584
  // position from the editor state.
5580
- if (local && !this.selectionChanged && this.selectionRange.focusNode &&
5585
+ if (local && !this.selectionChanged &&
5581
5586
  view.inputState.lastFocusTime > Date.now() - 200 &&
5582
5587
  view.inputState.lastTouchTime < Date.now() - 300 &&
5583
5588
  atElementStart(this.dom, range)) {
5589
+ this.view.inputState.lastFocusTime = 0;
5584
5590
  view.docView.updateSelection();
5585
5591
  return false;
5586
5592
  }
@@ -5757,6 +5763,25 @@ class DOMObserver {
5757
5763
  return null;
5758
5764
  }
5759
5765
  }
5766
+ setWindow(win) {
5767
+ if (win != this.win) {
5768
+ this.removeWindowListeners(this.win);
5769
+ this.win = win;
5770
+ this.addWindowListeners(this.win);
5771
+ }
5772
+ }
5773
+ addWindowListeners(win) {
5774
+ win.addEventListener("resize", this.onResize);
5775
+ win.addEventListener("beforeprint", this.onPrint);
5776
+ win.addEventListener("scroll", this.onScroll);
5777
+ win.document.addEventListener("selectionchange", this.onSelectionChange);
5778
+ }
5779
+ removeWindowListeners(win) {
5780
+ win.removeEventListener("scroll", this.onScroll);
5781
+ win.removeEventListener("resize", this.onResize);
5782
+ win.removeEventListener("beforeprint", this.onPrint);
5783
+ win.document.removeEventListener("selectionchange", this.onSelectionChange);
5784
+ }
5760
5785
  destroy() {
5761
5786
  var _a, _b, _c;
5762
5787
  this.stop();
@@ -5765,10 +5790,7 @@ class DOMObserver {
5765
5790
  (_c = this.resize) === null || _c === void 0 ? void 0 : _c.disconnect();
5766
5791
  for (let dom of this.scrollTargets)
5767
5792
  dom.removeEventListener("scroll", this.onScroll);
5768
- window.removeEventListener("scroll", this.onScroll);
5769
- window.removeEventListener("resize", this.onResize);
5770
- window.removeEventListener("beforeprint", this.onPrint);
5771
- this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5793
+ this.removeWindowListeners(this.win);
5772
5794
  clearTimeout(this.parentCheck);
5773
5795
  clearTimeout(this.resizeTimeout);
5774
5796
  }
@@ -6076,7 +6098,7 @@ class EditorView {
6076
6098
  this.dom.appendChild(this.scrollDOM);
6077
6099
  this._dispatch = config.dispatch || ((tr) => this.update([tr]));
6078
6100
  this.dispatch = this.dispatch.bind(this);
6079
- this.root = (config.root || getRoot(config.parent) || document);
6101
+ this._root = (config.root || getRoot(config.parent) || document);
6080
6102
  this.viewState = new ViewState(config.state || state.EditorState.create(config));
6081
6103
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
6082
6104
  for (let plugin of this.plugins)
@@ -6137,6 +6159,10 @@ class EditorView {
6137
6159
  composition there.
6138
6160
  */
6139
6161
  get compositionStarted() { return this.inputState.composing >= 0; }
6162
+ /**
6163
+ The document or shadow root that the view lives in.
6164
+ */
6165
+ get root() { return this._root; }
6140
6166
  dispatch(...input) {
6141
6167
  this._dispatch(input.length == 1 && input[0] instanceof state.Transaction ? input[0]
6142
6168
  : this.state.update(...input));
@@ -6284,10 +6310,13 @@ class EditorView {
6284
6310
  if (flush)
6285
6311
  this.observer.forceFlush();
6286
6312
  let updated = null;
6313
+ let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
6314
+ let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
6287
6315
  try {
6288
6316
  for (let i = 0;; i++) {
6289
6317
  this.updateState = 1 /* Measuring */;
6290
6318
  let oldViewport = this.viewport;
6319
+ let refBlock = this.viewState.lineBlockAtHeight(refHeight);
6291
6320
  let changed = this.viewState.measure(this);
6292
6321
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6293
6322
  break;
@@ -6339,6 +6368,13 @@ class EditorView {
6339
6368
  this.viewState.scrollTarget = null;
6340
6369
  scrolled = true;
6341
6370
  }
6371
+ else {
6372
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6373
+ if (diff > 1 || diff < -1) {
6374
+ this.scrollDOM.scrollTop += diff;
6375
+ scrolled = true;
6376
+ }
6377
+ }
6342
6378
  if (redrawn)
6343
6379
  this.docView.updateSelection(true);
6344
6380
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
@@ -6462,7 +6498,7 @@ class EditorView {
6462
6498
  /**
6463
6499
  Find the text line or block widget at the given vertical
6464
6500
  position (which is interpreted as relative to the [top of the
6465
- document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
6501
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
6466
6502
  */
6467
6503
  elementAtHeight(height) {
6468
6504
  this.readMeasured();
@@ -6471,7 +6507,8 @@ class EditorView {
6471
6507
  /**
6472
6508
  Find the line block (see
6473
6509
  [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
6474
- height.
6510
+ height, again interpreted relative to the [top of the
6511
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop).
6475
6512
  */
6476
6513
  lineBlockAtHeight(height) {
6477
6514
  this.readMeasured();
@@ -6678,6 +6715,17 @@ class EditorView {
6678
6715
  });
6679
6716
  }
6680
6717
  /**
6718
+ Update the [root](https://codemirror.net/6/docs/ref/##view.EditorViewConfig.root) in which the editor lives. This is only
6719
+ necessary when moving the editor's existing DOM to a new window or shadow root.
6720
+ */
6721
+ setRoot(root) {
6722
+ if (this._root != root) {
6723
+ this._root = root;
6724
+ this.observer.setWindow((root.nodeType == 9 ? root : root.ownerDocument).defaultView);
6725
+ this.mountStyles();
6726
+ }
6727
+ }
6728
+ /**
6681
6729
  Clean up this editor view, removing its element from the
6682
6730
  document, unregistering event handlers, and notifying
6683
6731
  plugins. The view instance can no longer be used after
package/dist/index.d.ts CHANGED
@@ -647,10 +647,11 @@ declare class EditorView {
647
647
  */
648
648
  get compositionStarted(): boolean;
649
649
  private _dispatch;
650
+ private _root;
650
651
  /**
651
652
  The document or shadow root that the view lives in.
652
653
  */
653
- readonly root: DocumentOrShadowRoot;
654
+ get root(): DocumentOrShadowRoot;
654
655
  /**
655
656
  The DOM element that wraps the entire editor view.
656
657
  */
@@ -752,13 +753,14 @@ declare class EditorView {
752
753
  /**
753
754
  Find the text line or block widget at the given vertical
754
755
  position (which is interpreted as relative to the [top of the
755
- document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
756
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
756
757
  */
757
758
  elementAtHeight(height: number): BlockInfo;
758
759
  /**
759
760
  Find the line block (see
760
761
  [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
761
- height.
762
+ height, again interpreted relative to the [top of the
763
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop).
762
764
  */
763
765
  lineBlockAtHeight(height: number): BlockInfo;
764
766
  /**
@@ -921,6 +923,11 @@ declare class EditorView {
921
923
  */
922
924
  focus(): void;
923
925
  /**
926
+ Update the [root](https://codemirror.net/6/docs/ref/##view.EditorViewConfig.root) in which the editor lives. This is only
927
+ necessary when moving the editor's existing DOM to a new window or shadow root.
928
+ */
929
+ setRoot(root: Document | ShadowRoot): void;
930
+ /**
924
931
  Clean up this editor view, removing its element from the
925
932
  document, unregistering event handlers, and notifying
926
933
  plugins. The view instance can no longer be used after
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);
@@ -2475,7 +2477,6 @@ class DocView extends ContentView {
2475
2477
  this.updateDeco();
2476
2478
  this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2477
2479
  }
2478
- get root() { return this.view.root; }
2479
2480
  get editorView() { return this.view; }
2480
2481
  get length() { return this.view.state.doc.length; }
2481
2482
  // Update the document view to a given state. scrollIntoView can be
@@ -2598,7 +2599,7 @@ class DocView extends ContentView {
2598
2599
  this.dom.blur();
2599
2600
  this.dom.focus({ preventScroll: true });
2600
2601
  }
2601
- let rawSel = getSelection(this.root);
2602
+ let rawSel = getSelection(this.view.root);
2602
2603
  if (!rawSel) ;
2603
2604
  else if (main.empty) {
2604
2605
  // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
@@ -2641,7 +2642,7 @@ class DocView extends ContentView {
2641
2642
  if (this.compositionDeco.size)
2642
2643
  return;
2643
2644
  let cursor = this.view.state.selection.main;
2644
- let sel = getSelection(this.root);
2645
+ let sel = getSelection(this.view.root);
2645
2646
  if (!sel || !cursor.empty || !cursor.assoc || !sel.modify)
2646
2647
  return;
2647
2648
  let line = LineView.find(this, cursor.head);
@@ -2658,7 +2659,7 @@ class DocView extends ContentView {
2658
2659
  sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
2659
2660
  }
2660
2661
  mayControlSelection() {
2661
- let active = this.root.activeElement;
2662
+ let active = this.view.root.activeElement;
2662
2663
  return active == this.dom ||
2663
2664
  hasSelection(this.dom, this.view.observer.selectionRange) && !(active && this.dom.contains(active));
2664
2665
  }
@@ -3011,7 +3012,7 @@ function upBot(rect, bottom) {
3011
3012
  return bottom > rect.bottom ? { top: rect.top, left: rect.left, right: rect.right, bottom } : rect;
3012
3013
  }
3013
3014
  function domPosAtCoords(parent, x, y) {
3014
- let closest, closestRect, closestX, closestY;
3015
+ let closest, closestRect, closestX, closestY, closestOverlap = false;
3015
3016
  let above, below, aboveRect, belowRect;
3016
3017
  for (let child = parent.firstChild; child; child = child.nextSibling) {
3017
3018
  let rects = clientRectsFor(child);
@@ -3027,6 +3028,7 @@ function domPosAtCoords(parent, x, y) {
3027
3028
  closestRect = rect;
3028
3029
  closestX = dx;
3029
3030
  closestY = dy;
3031
+ closestOverlap = !dx || (dx > 0 ? i < rects.length - 1 : i > 0);
3030
3032
  }
3031
3033
  if (dx == 0) {
3032
3034
  if (y > rect.bottom && (!aboveRect || aboveRect.bottom < rect.bottom)) {
@@ -3059,7 +3061,7 @@ function domPosAtCoords(parent, x, y) {
3059
3061
  let clipX = Math.max(closestRect.left, Math.min(closestRect.right, x));
3060
3062
  if (closest.nodeType == 3)
3061
3063
  return domPosInText(closest, clipX, y);
3062
- if (!closestX && closest.contentEditable == "true")
3064
+ if (closestOverlap && closest.contentEditable != "false")
3063
3065
  return domPosAtCoords(closest, clipX, y);
3064
3066
  let offset = Array.prototype.indexOf.call(parent.childNodes, closest) +
3065
3067
  (x >= (closestRect.left + closestRect.right) / 2 ? 1 : 0);
@@ -3156,7 +3158,8 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3156
3158
  let range = doc.caretRangeFromPoint(x, y);
3157
3159
  if (range) {
3158
3160
  ({ startContainer: node, startOffset: offset } = range);
3159
- if (browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3161
+ if (!view.contentDOM.contains(node) ||
3162
+ browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3160
3163
  browser.chrome && isSuspiciousChromeCaretResult(node, offset, x))
3161
3164
  node = undefined;
3162
3165
  }
@@ -4668,7 +4671,7 @@ function visiblePixelRange(dom, paddingTop) {
4668
4671
  left = Math.max(left, parentRect.left);
4669
4672
  right = Math.min(right, parentRect.right);
4670
4673
  top = Math.max(top, parentRect.top);
4671
- bottom = Math.min(bottom, parentRect.bottom);
4674
+ bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
4672
4675
  }
4673
4676
  parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
4674
4677
  }
@@ -5369,6 +5372,7 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5369
5372
  ".cm-widgetBuffer": {
5370
5373
  verticalAlign: "text-top",
5371
5374
  height: "1em",
5375
+ width: 0,
5372
5376
  display: "inline"
5373
5377
  },
5374
5378
  ".cm-placeholder": {
@@ -5478,7 +5482,9 @@ class DOMObserver {
5478
5482
  this.flushSoon();
5479
5483
  };
5480
5484
  this.onSelectionChange = this.onSelectionChange.bind(this);
5481
- window.addEventListener("resize", this.onResize = this.onResize.bind(this));
5485
+ this.onResize = this.onResize.bind(this);
5486
+ this.onPrint = this.onPrint.bind(this);
5487
+ this.onScroll = this.onScroll.bind(this);
5482
5488
  if (typeof ResizeObserver == "function") {
5483
5489
  this.resize = new ResizeObserver(() => {
5484
5490
  if (this.view.docView.lastUpdate < Date.now() - 75)
@@ -5486,9 +5492,9 @@ class DOMObserver {
5486
5492
  });
5487
5493
  this.resize.observe(view.scrollDOM);
5488
5494
  }
5489
- window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this));
5495
+ this.win = view.dom.ownerDocument.defaultView;
5496
+ this.addWindowListeners(this.win);
5490
5497
  this.start();
5491
- window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
5492
5498
  if (typeof IntersectionObserver == "function") {
5493
5499
  this.intersection = new IntersectionObserver(entries => {
5494
5500
  if (this.parentCheck < 0)
@@ -5507,7 +5513,6 @@ class DOMObserver {
5507
5513
  }
5508
5514
  this.listenForScroll();
5509
5515
  this.readSelectionRange();
5510
- this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
5511
5516
  }
5512
5517
  onScroll(e) {
5513
5518
  if (this.intersecting)
@@ -5570,10 +5575,11 @@ class DOMObserver {
5570
5575
  // Detect the situation where the browser has, on focus, moved the
5571
5576
  // selection to the start of the content element. Reset it to the
5572
5577
  // position from the editor state.
5573
- if (local && !this.selectionChanged && this.selectionRange.focusNode &&
5578
+ if (local && !this.selectionChanged &&
5574
5579
  view.inputState.lastFocusTime > Date.now() - 200 &&
5575
5580
  view.inputState.lastTouchTime < Date.now() - 300 &&
5576
5581
  atElementStart(this.dom, range)) {
5582
+ this.view.inputState.lastFocusTime = 0;
5577
5583
  view.docView.updateSelection();
5578
5584
  return false;
5579
5585
  }
@@ -5750,6 +5756,25 @@ class DOMObserver {
5750
5756
  return null;
5751
5757
  }
5752
5758
  }
5759
+ setWindow(win) {
5760
+ if (win != this.win) {
5761
+ this.removeWindowListeners(this.win);
5762
+ this.win = win;
5763
+ this.addWindowListeners(this.win);
5764
+ }
5765
+ }
5766
+ addWindowListeners(win) {
5767
+ win.addEventListener("resize", this.onResize);
5768
+ win.addEventListener("beforeprint", this.onPrint);
5769
+ win.addEventListener("scroll", this.onScroll);
5770
+ win.document.addEventListener("selectionchange", this.onSelectionChange);
5771
+ }
5772
+ removeWindowListeners(win) {
5773
+ win.removeEventListener("scroll", this.onScroll);
5774
+ win.removeEventListener("resize", this.onResize);
5775
+ win.removeEventListener("beforeprint", this.onPrint);
5776
+ win.document.removeEventListener("selectionchange", this.onSelectionChange);
5777
+ }
5753
5778
  destroy() {
5754
5779
  var _a, _b, _c;
5755
5780
  this.stop();
@@ -5758,10 +5783,7 @@ class DOMObserver {
5758
5783
  (_c = this.resize) === null || _c === void 0 ? void 0 : _c.disconnect();
5759
5784
  for (let dom of this.scrollTargets)
5760
5785
  dom.removeEventListener("scroll", this.onScroll);
5761
- window.removeEventListener("scroll", this.onScroll);
5762
- window.removeEventListener("resize", this.onResize);
5763
- window.removeEventListener("beforeprint", this.onPrint);
5764
- this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
5786
+ this.removeWindowListeners(this.win);
5765
5787
  clearTimeout(this.parentCheck);
5766
5788
  clearTimeout(this.resizeTimeout);
5767
5789
  }
@@ -6069,7 +6091,7 @@ class EditorView {
6069
6091
  this.dom.appendChild(this.scrollDOM);
6070
6092
  this._dispatch = config.dispatch || ((tr) => this.update([tr]));
6071
6093
  this.dispatch = this.dispatch.bind(this);
6072
- this.root = (config.root || getRoot(config.parent) || document);
6094
+ this._root = (config.root || getRoot(config.parent) || document);
6073
6095
  this.viewState = new ViewState(config.state || EditorState.create(config));
6074
6096
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
6075
6097
  for (let plugin of this.plugins)
@@ -6130,6 +6152,10 @@ class EditorView {
6130
6152
  composition there.
6131
6153
  */
6132
6154
  get compositionStarted() { return this.inputState.composing >= 0; }
6155
+ /**
6156
+ The document or shadow root that the view lives in.
6157
+ */
6158
+ get root() { return this._root; }
6133
6159
  dispatch(...input) {
6134
6160
  this._dispatch(input.length == 1 && input[0] instanceof Transaction ? input[0]
6135
6161
  : this.state.update(...input));
@@ -6277,10 +6303,13 @@ class EditorView {
6277
6303
  if (flush)
6278
6304
  this.observer.forceFlush();
6279
6305
  let updated = null;
6306
+ let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
6307
+ let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
6280
6308
  try {
6281
6309
  for (let i = 0;; i++) {
6282
6310
  this.updateState = 1 /* Measuring */;
6283
6311
  let oldViewport = this.viewport;
6312
+ let refBlock = this.viewState.lineBlockAtHeight(refHeight);
6284
6313
  let changed = this.viewState.measure(this);
6285
6314
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6286
6315
  break;
@@ -6332,6 +6361,13 @@ class EditorView {
6332
6361
  this.viewState.scrollTarget = null;
6333
6362
  scrolled = true;
6334
6363
  }
6364
+ else {
6365
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6366
+ if (diff > 1 || diff < -1) {
6367
+ this.scrollDOM.scrollTop += diff;
6368
+ scrolled = true;
6369
+ }
6370
+ }
6335
6371
  if (redrawn)
6336
6372
  this.docView.updateSelection(true);
6337
6373
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
@@ -6455,7 +6491,7 @@ class EditorView {
6455
6491
  /**
6456
6492
  Find the text line or block widget at the given vertical
6457
6493
  position (which is interpreted as relative to the [top of the
6458
- document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
6494
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
6459
6495
  */
6460
6496
  elementAtHeight(height) {
6461
6497
  this.readMeasured();
@@ -6464,7 +6500,8 @@ class EditorView {
6464
6500
  /**
6465
6501
  Find the line block (see
6466
6502
  [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
6467
- height.
6503
+ height, again interpreted relative to the [top of the
6504
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop).
6468
6505
  */
6469
6506
  lineBlockAtHeight(height) {
6470
6507
  this.readMeasured();
@@ -6671,6 +6708,17 @@ class EditorView {
6671
6708
  });
6672
6709
  }
6673
6710
  /**
6711
+ Update the [root](https://codemirror.net/6/docs/ref/##view.EditorViewConfig.root) in which the editor lives. This is only
6712
+ necessary when moving the editor's existing DOM to a new window or shadow root.
6713
+ */
6714
+ setRoot(root) {
6715
+ if (this._root != root) {
6716
+ this._root = root;
6717
+ this.observer.setWindow((root.nodeType == 9 ? root : root.ownerDocument).defaultView);
6718
+ this.mountStyles();
6719
+ }
6720
+ }
6721
+ /**
6674
6722
  Clean up this editor view, removing its element from the
6675
6723
  document, unregistering event handlers, and notifying
6676
6724
  plugins. The view instance can no longer be used after
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.1.2",
3
+ "version": "6.2.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",