@codemirror/view 6.21.4 → 6.22.1

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,33 @@
1
+ ## 6.22.1 (2023-11-27)
2
+
3
+ ### Bug fixes
4
+
5
+ Call widget `destroy` methods when the entire editor is destroyed or reset.
6
+
7
+ Work around an issue on Safari on macOS Sonoma that made the native cursor visible even when `drawSelection` is enabled.
8
+
9
+ Fix an issue where, on some browsers, the screenreader announced text ended up in the printed document.
10
+
11
+ Fix a bug where a hover tooltip could stick around even though the pointer was no longer on the editor when it was moved out over the tooltip.
12
+
13
+ Fix an issue where hover tooltips could close when moving the mouse onto them due to mouse position rounding issues.
14
+
15
+ ## 6.22.0 (2023-11-03)
16
+
17
+ ### Bug fixes
18
+
19
+ Exceptions raised by update listeners are now routed to the configured exception sink, if any.
20
+
21
+ Fix an issue where passing large scroll margins to `scrollIntoView` would cause the measure loop to fail to terminate.
22
+
23
+ Widgets that are draggable (and allow drag events through in their `ignoreEvent` implementation) can now use the editor's built-in drag/drop behavior.
24
+
25
+ ### New features
26
+
27
+ The new `scrollTo` option to `EditorView` allows an initial scroll position to be provided.
28
+
29
+ The new `EditorView.scrollSnapshot` method returns an effect that can be used to reset to a previous scroll position.
30
+
1
31
  ## 6.21.4 (2023-10-24)
2
32
 
3
33
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -503,7 +503,7 @@ class ContentView {
503
503
  this.markDirty();
504
504
  for (let i = from; i < to; i++) {
505
505
  let child = this.children[i];
506
- if (child.parent == this)
506
+ if (child.parent == this && children.indexOf(child) < 0)
507
507
  child.destroy();
508
508
  }
509
509
  this.children.splice(from, to - from, ...children);
@@ -540,6 +540,8 @@ class ContentView {
540
540
  // number > 0 when after its position.
541
541
  getSide() { return 0; }
542
542
  destroy() {
543
+ for (let child of this.children)
544
+ child.destroy();
543
545
  this.parent = null;
544
546
  }
545
547
  }
@@ -1788,15 +1790,28 @@ const nativeSelectionHidden = state.Facet.define({
1788
1790
  combine: values => values.some(x => x)
1789
1791
  });
1790
1792
  class ScrollTarget {
1791
- constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1793
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5,
1794
+ // This data structure is abused to also store precise scroll
1795
+ // snapshots, instead of a `scrollIntoView` request. When this
1796
+ // flag is `true`, `range` points at a position in the reference
1797
+ // line, `yMargin` holds the difference between the top of that
1798
+ // line and the top of the editor, and `xMargin` holds the
1799
+ // editor's `scrollLeft`.
1800
+ isSnapshot = false) {
1792
1801
  this.range = range;
1793
1802
  this.y = y;
1794
1803
  this.x = x;
1795
1804
  this.yMargin = yMargin;
1796
1805
  this.xMargin = xMargin;
1806
+ this.isSnapshot = isSnapshot;
1797
1807
  }
1798
1808
  map(changes) {
1799
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1809
+ return changes.empty ? this :
1810
+ new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1811
+ }
1812
+ clip(state$1) {
1813
+ return this.range.to <= state$1.doc.length ? this :
1814
+ new ScrollTarget(state.EditorSelection.cursor(state$1.doc.length), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1800
1815
  }
1801
1816
  }
1802
1817
  const scrollIntoView = state.StateEffect.define({ map: (t, ch) => t.map(ch) });
@@ -3090,6 +3105,12 @@ class DocView extends ContentView {
3090
3105
  ];
3091
3106
  }
3092
3107
  scrollIntoView(target) {
3108
+ if (target.isSnapshot) {
3109
+ let ref = this.view.viewState.lineBlockAt(target.range.head);
3110
+ this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
3111
+ this.view.scrollDOM.scrollLeft = target.xMargin;
3112
+ return;
3113
+ }
3093
3114
  let { range } = target;
3094
3115
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
3095
3116
  if (!rect)
@@ -3102,7 +3123,8 @@ class DocView extends ContentView {
3102
3123
  left: rect.left - margins.left, top: rect.top - margins.top,
3103
3124
  right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
3104
3125
  };
3105
- 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);
3126
+ let { offsetWidth, offsetHeight } = this.view.scrollDOM;
3127
+ scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == exports.Direction.LTR);
3106
3128
  }
3107
3129
  }
3108
3130
  function betweenUneditable(pos) {
@@ -3639,6 +3661,9 @@ class InputState {
3639
3661
  // the mutation events fire shortly after the compositionend event
3640
3662
  this.compositionPendingChange = false;
3641
3663
  this.mouseSelection = null;
3664
+ // When a drag from the editor is active, this points at the range
3665
+ // being dragged.
3666
+ this.draggedContent = null;
3642
3667
  this.handleEvent = this.handleEvent.bind(this);
3643
3668
  this.notifiedFocused = view.hasFocus;
3644
3669
  // On Safari adding an input event handler somehow prevents an
@@ -3755,6 +3780,8 @@ class InputState {
3755
3780
  update(update) {
3756
3781
  if (this.mouseSelection)
3757
3782
  this.mouseSelection.update(update);
3783
+ if (this.draggedContent && update.docChanged)
3784
+ this.draggedContent = this.draggedContent.map(update.changes);
3758
3785
  if (update.transactions.length)
3759
3786
  this.lastKeyCode = this.lastSelectionTime = 0;
3760
3787
  }
@@ -3872,7 +3899,7 @@ class MouseSelection {
3872
3899
  let doc = this.view.contentDOM.ownerDocument;
3873
3900
  doc.removeEventListener("mousemove", this.move);
3874
3901
  doc.removeEventListener("mouseup", this.up);
3875
- this.view.inputState.mouseSelection = null;
3902
+ this.view.inputState.mouseSelection = this.view.inputState.draggedContent = null;
3876
3903
  }
3877
3904
  setScrollSpeed(sx, sy) {
3878
3905
  this.scrollSpeed = { x: sx, y: sy };
@@ -3930,8 +3957,6 @@ class MouseSelection {
3930
3957
  this.mustSelect = false;
3931
3958
  }
3932
3959
  update(update) {
3933
- if (update.docChanged && this.dragging)
3934
- this.dragging = this.dragging.map(update.changes);
3935
3960
  if (this.style.update(update))
3936
3961
  setTimeout(() => this.select(this.lastEvent), 20);
3937
3962
  }
@@ -4159,23 +4184,36 @@ function removeRangeAround(sel, pos) {
4159
4184
  return null;
4160
4185
  }
4161
4186
  handlers.dragstart = (view, event) => {
4162
- let { selection: { main } } = view.state;
4163
- let { mouseSelection } = view.inputState;
4164
- if (mouseSelection)
4165
- mouseSelection.dragging = main;
4187
+ let { selection: { main: range } } = view.state;
4188
+ if (event.target.draggable) {
4189
+ let cView = view.docView.nearest(event.target);
4190
+ if (cView && cView.isWidget) {
4191
+ let from = cView.posAtStart, to = from + cView.length;
4192
+ if (from >= range.to || to <= range.from)
4193
+ range = state.EditorSelection.range(from, to);
4194
+ }
4195
+ }
4196
+ let { inputState } = view;
4197
+ if (inputState.mouseSelection)
4198
+ inputState.mouseSelection.dragging = true;
4199
+ inputState.draggedContent = range;
4166
4200
  if (event.dataTransfer) {
4167
- event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4201
+ event.dataTransfer.setData("Text", view.state.sliceDoc(range.from, range.to));
4168
4202
  event.dataTransfer.effectAllowed = "copyMove";
4169
4203
  }
4170
4204
  return false;
4171
4205
  };
4206
+ handlers.dragend = view => {
4207
+ view.inputState.draggedContent = null;
4208
+ return false;
4209
+ };
4172
4210
  function dropText(view, event, text, direct) {
4173
4211
  if (!text)
4174
4212
  return;
4175
4213
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4176
- let { mouseSelection } = view.inputState;
4177
- let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4178
- { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
4214
+ let { draggedContent } = view.inputState;
4215
+ let del = direct && draggedContent && dragMovesSelection(view, event)
4216
+ ? { from: draggedContent.from, to: draggedContent.to } : null;
4179
4217
  let ins = { from: dropPos, insert: text };
4180
4218
  let changes = view.state.changes(del ? [del, ins] : ins);
4181
4219
  view.focus();
@@ -4184,6 +4222,7 @@ function dropText(view, event, text, direct) {
4184
4222
  selection: { anchor: changes.mapPos(dropPos, -1), head: changes.mapPos(dropPos, 1) },
4185
4223
  userEvent: del ? "move.drop" : "input.drop"
4186
4224
  });
4225
+ view.inputState.draggedContent = null;
4187
4226
  }
4188
4227
  handlers.drop = (view, event) => {
4189
4228
  if (!event.dataTransfer)
@@ -5844,6 +5883,13 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5844
5883
  "&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor": {
5845
5884
  display: "block"
5846
5885
  },
5886
+ ".cm-announced": {
5887
+ position: "fixed",
5888
+ top: "-10000px"
5889
+ },
5890
+ "@media print": {
5891
+ ".cm-announced": { display: "none" }
5892
+ },
5847
5893
  "&light .cm-activeLine": { backgroundColor: "#cceeff44" },
5848
5894
  "&dark .cm-activeLine": { backgroundColor: "#99eeff33" },
5849
5895
  "&light .cm-specialChar": { color: "red" },
@@ -6892,7 +6938,7 @@ class EditorView {
6892
6938
  this.scrollDOM.className = "cm-scroller";
6893
6939
  this.scrollDOM.appendChild(this.contentDOM);
6894
6940
  this.announceDOM = document.createElement("div");
6895
- this.announceDOM.style.cssText = "position: fixed; top: -10000px";
6941
+ this.announceDOM.className = "cm-announced";
6896
6942
  this.announceDOM.setAttribute("aria-live", "polite");
6897
6943
  this.dom = document.createElement("div");
6898
6944
  this.dom.appendChild(this.announceDOM);
@@ -6904,6 +6950,8 @@ class EditorView {
6904
6950
  this.dispatch = this.dispatch.bind(this);
6905
6951
  this._root = (config.root || getRoot(config.parent) || document);
6906
6952
  this.viewState = new ViewState(config.state || state.EditorState.create(config));
6953
+ if (config.scrollTo && config.scrollTo.is(scrollIntoView))
6954
+ this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
6907
6955
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
6908
6956
  for (let plugin of this.plugins)
6909
6957
  plugin.update(this);
@@ -6991,7 +7039,7 @@ class EditorView {
6991
7039
  }
6992
7040
  for (let e of tr.effects)
6993
7041
  if (e.is(scrollIntoView))
6994
- scrollTarget = e.value;
7042
+ scrollTarget = e.value.clip(this.state);
6995
7043
  }
6996
7044
  this.viewState.update(update, scrollTarget);
6997
7045
  this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
@@ -7014,8 +7062,14 @@ class EditorView {
7014
7062
  if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
7015
7063
  this.requestMeasure();
7016
7064
  if (!update.empty)
7017
- for (let listener of this.state.facet(updateListener))
7018
- listener(update);
7065
+ for (let listener of this.state.facet(updateListener)) {
7066
+ try {
7067
+ listener(update);
7068
+ }
7069
+ catch (e) {
7070
+ logException(this.state, e, "update listener");
7071
+ }
7072
+ }
7019
7073
  if (dispatchFocus || domChange)
7020
7074
  Promise.resolve().then(() => {
7021
7075
  if (dispatchFocus && this.state == dispatchFocus.startState)
@@ -7050,6 +7104,7 @@ class EditorView {
7050
7104
  this.pluginMap.clear();
7051
7105
  for (let plugin of this.plugins)
7052
7106
  plugin.update(this);
7107
+ this.docView.destroy();
7053
7108
  this.docView = new DocView(this);
7054
7109
  this.inputState.ensureHandlers(this.plugins);
7055
7110
  this.mountStyles();
@@ -7582,6 +7637,7 @@ class EditorView {
7582
7637
  plugin.destroy(this);
7583
7638
  this.plugins = [];
7584
7639
  this.inputState.destroy();
7640
+ this.docView.destroy();
7585
7641
  this.dom.remove();
7586
7642
  this.observer.destroy();
7587
7643
  if (this.measureScheduled > -1)
@@ -7597,6 +7653,23 @@ class EditorView {
7597
7653
  return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
7598
7654
  }
7599
7655
  /**
7656
+ Return an effect that resets the editor to its current (at the
7657
+ time this method was called) scroll position. Note that this
7658
+ only affects the editor's own scrollable element, not parents.
7659
+ See also
7660
+ [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
7661
+
7662
+ The effect should be used with a document identical to the one
7663
+ it was created for. Failing to do so is not an error, but may
7664
+ not scroll to the expected position. You can
7665
+ [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
7666
+ */
7667
+ scrollSnapshot() {
7668
+ let { scrollTop, scrollLeft } = this.scrollDOM;
7669
+ let ref = this.viewState.scrollAnchorAt(scrollTop);
7670
+ return scrollIntoView.of(new ScrollTarget(state.EditorSelection.cursor(ref.from), "start", "start", ref.top - scrollTop, scrollLeft, true));
7671
+ }
7672
+ /**
7600
7673
  Returns an extension that can be used to add DOM event handlers.
7601
7674
  The value should be an object mapping event names to handler
7602
7675
  functions. For any given event, such functions are ordered by
@@ -8425,8 +8498,10 @@ const themeSpec = {
8425
8498
  "&::selection": { backgroundColor: "transparent !important" }
8426
8499
  }
8427
8500
  };
8428
- if (CanHidePrimary)
8501
+ if (CanHidePrimary) {
8429
8502
  themeSpec[".cm-line"].caretColor = "transparent !important";
8503
+ themeSpec[".cm-content"] = { caretColor: "transparent !important" };
8504
+ }
8430
8505
  const hideNativeSelection = state.Prec.highest(EditorView.theme(themeSpec));
8431
8506
 
8432
8507
  const setDropCursorPos = state.StateEffect.define({
@@ -9483,7 +9558,7 @@ const showHoverTooltipHost = showTooltip.compute([showHoverTooltip], state => {
9483
9558
  return null;
9484
9559
  return {
9485
9560
  pos: Math.min(...tooltips.map(t => t.pos)),
9486
- end: Math.max(...tooltips.filter(t => t.end != null).map(t => t.end)),
9561
+ end: Math.max(...tooltips.map(t => { var _a; return (_a = t.end) !== null && _a !== void 0 ? _a : t.pos; })),
9487
9562
  create: HoverTooltipHost.create,
9488
9563
  above: tooltips[0].above,
9489
9564
  arrow: tooltips.some(t => t.arrow),
@@ -9563,14 +9638,19 @@ class HoverPlugin {
9563
9638
  view.dispatch({ effects: this.setHover.of(open) });
9564
9639
  }
9565
9640
  }
9641
+ get tooltip() {
9642
+ let plugin = this.view.plugin(tooltipPlugin);
9643
+ let index = plugin ? plugin.manager.tooltips.findIndex(t => t.create == HoverTooltipHost.create) : -1;
9644
+ return index > -1 ? plugin.manager.tooltipViews[index] : null;
9645
+ }
9566
9646
  mousemove(event) {
9567
9647
  var _a;
9568
9648
  this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
9569
9649
  if (this.hoverTimeout < 0)
9570
9650
  this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
9571
- let tooltip = this.active;
9572
- if (tooltip && !isInTooltip(this.lastMove.target) || this.pending) {
9573
- let { pos } = tooltip || this.pending, end = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.end) !== null && _a !== void 0 ? _a : pos;
9651
+ let { active, tooltip } = this;
9652
+ if (active && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
9653
+ let { pos } = active || this.pending, end = (_a = active === null || active === void 0 ? void 0 : active.end) !== null && _a !== void 0 ? _a : pos;
9574
9654
  if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
9575
9655
  : !isOverRange(this.view, pos, end, event.clientX, event.clientY))) {
9576
9656
  this.view.dispatch({ effects: this.setHover.of(null) });
@@ -9578,11 +9658,26 @@ class HoverPlugin {
9578
9658
  }
9579
9659
  }
9580
9660
  }
9581
- mouseleave(e) {
9661
+ mouseleave(event) {
9582
9662
  clearTimeout(this.hoverTimeout);
9583
9663
  this.hoverTimeout = -1;
9584
- if (this.active && !isInTooltip(e.relatedTarget))
9585
- this.view.dispatch({ effects: this.setHover.of(null) });
9664
+ let { active } = this;
9665
+ if (active) {
9666
+ let { tooltip } = this;
9667
+ let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget);
9668
+ if (!inTooltip)
9669
+ this.view.dispatch({ effects: this.setHover.of(null) });
9670
+ else
9671
+ this.watchTooltipLeave(tooltip.dom);
9672
+ }
9673
+ }
9674
+ watchTooltipLeave(tooltip) {
9675
+ let watch = (event) => {
9676
+ tooltip.removeEventListener("mouseleave", watch);
9677
+ if (this.active && !this.view.dom.contains(event.relatedTarget))
9678
+ this.view.dispatch({ effects: this.setHover.of(null) });
9679
+ };
9680
+ tooltip.addEventListener("mouseleave", watch);
9586
9681
  }
9587
9682
  destroy() {
9588
9683
  clearTimeout(this.hoverTimeout);
@@ -9590,11 +9685,11 @@ class HoverPlugin {
9590
9685
  this.view.dom.removeEventListener("mousemove", this.mousemove);
9591
9686
  }
9592
9687
  }
9593
- function isInTooltip(elt) {
9594
- for (let cur = elt; cur; cur = cur.parentNode)
9595
- if (cur.nodeType == 1 && cur.classList.contains("cm-tooltip"))
9596
- return true;
9597
- return false;
9688
+ const tooltipMargin = 4;
9689
+ function isInTooltip(tooltip, event) {
9690
+ let rect = tooltip.getBoundingClientRect();
9691
+ return event.clientX >= rect.left - tooltipMargin && event.clientX <= rect.right + tooltipMargin &&
9692
+ event.clientY >= rect.top - tooltipMargin && event.clientY <= rect.bottom + tooltipMargin;
9598
9693
  }
9599
9694
  function isOverRange(view, from, to, x, y, margin) {
9600
9695
  let rect = view.scrollDOM.getBoundingClientRect();
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _codemirror_state from '@codemirror/state';
2
- import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, EditorSelection, EditorStateConfig, TransactionSpec, SelectionRange, Line, StateEffect, Facet } from '@codemirror/state';
2
+ import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, SelectionRange, ChangeDesc, EditorSelection, EditorStateConfig, StateEffect, TransactionSpec, Line, Facet } from '@codemirror/state';
3
3
  import { StyleModule, StyleSpec } from 'style-mod';
4
4
 
5
5
  /**
@@ -361,6 +361,17 @@ apply to the editor, and if it can, perform it as a side effect
361
361
  transaction) and return `true`.
362
362
  */
363
363
  type Command = (target: EditorView) => boolean;
364
+ declare class ScrollTarget {
365
+ readonly range: SelectionRange;
366
+ readonly y: ScrollStrategy;
367
+ readonly x: ScrollStrategy;
368
+ readonly yMargin: number;
369
+ readonly xMargin: number;
370
+ readonly isSnapshot: boolean;
371
+ constructor(range: SelectionRange, y?: ScrollStrategy, x?: ScrollStrategy, yMargin?: number, xMargin?: number, isSnapshot?: boolean);
372
+ map(changes: ChangeDesc): ScrollTarget;
373
+ clip(state: EditorState): ScrollTarget;
374
+ }
364
375
  /**
365
376
  Log or report an unhandled exception in client code. Should
366
377
  probably only be used by extension code that allows client code to
@@ -633,6 +644,13 @@ interface EditorViewConfig extends EditorStateConfig {
633
644
  */
634
645
  root?: Document | ShadowRoot;
635
646
  /**
647
+ Pass an effect created with
648
+ [`EditorView.scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) or
649
+ [`EditorView.scrollSnapshot`](https://codemirror.net/6/docs/ref/#view.EditorView.scrollSnapshot)
650
+ here to set an initial scroll position.
651
+ */
652
+ scrollTo?: StateEffect<any>;
653
+ /**
636
654
  Override the way transactions are
637
655
  [dispatched](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) for this editor view.
638
656
  Your implementation, if provided, should probably call the
@@ -1032,15 +1050,30 @@ declare class EditorView {
1032
1050
  /**
1033
1051
  Extra vertical distance to add when moving something into
1034
1052
  view. Not used with the `"center"` strategy. Defaults to 5.
1053
+ Must be less than the height of the editor.
1035
1054
  */
1036
1055
  yMargin?: number;
1037
1056
  /**
1038
1057
  Extra horizontal distance to add. Not used with the `"center"`
1039
- strategy. Defaults to 5.
1058
+ strategy. Defaults to 5. Must be less than the width of the
1059
+ editor.
1040
1060
  */
1041
1061
  xMargin?: number;
1042
1062
  }): StateEffect<unknown>;
1043
1063
  /**
1064
+ Return an effect that resets the editor to its current (at the
1065
+ time this method was called) scroll position. Note that this
1066
+ only affects the editor's own scrollable element, not parents.
1067
+ See also
1068
+ [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
1069
+
1070
+ The effect should be used with a document identical to the one
1071
+ it was created for. Failing to do so is not an error, but may
1072
+ not scroll to the expected position. You can
1073
+ [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
1074
+ */
1075
+ scrollSnapshot(): StateEffect<ScrollTarget>;
1076
+ /**
1044
1077
  Facet to add a [style
1045
1078
  module](https://github.com/marijnh/style-mod#documentation) to
1046
1079
  an editor view. The view will ensure that the module is
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _codemirror_state from '@codemirror/state';
2
- import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, EditorSelection, EditorStateConfig, TransactionSpec, SelectionRange, Line, StateEffect, Facet } from '@codemirror/state';
2
+ import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, SelectionRange, ChangeDesc, EditorSelection, EditorStateConfig, StateEffect, TransactionSpec, Line, Facet } from '@codemirror/state';
3
3
  import { StyleModule, StyleSpec } from 'style-mod';
4
4
 
5
5
  /**
@@ -361,6 +361,17 @@ apply to the editor, and if it can, perform it as a side effect
361
361
  transaction) and return `true`.
362
362
  */
363
363
  type Command = (target: EditorView) => boolean;
364
+ declare class ScrollTarget {
365
+ readonly range: SelectionRange;
366
+ readonly y: ScrollStrategy;
367
+ readonly x: ScrollStrategy;
368
+ readonly yMargin: number;
369
+ readonly xMargin: number;
370
+ readonly isSnapshot: boolean;
371
+ constructor(range: SelectionRange, y?: ScrollStrategy, x?: ScrollStrategy, yMargin?: number, xMargin?: number, isSnapshot?: boolean);
372
+ map(changes: ChangeDesc): ScrollTarget;
373
+ clip(state: EditorState): ScrollTarget;
374
+ }
364
375
  /**
365
376
  Log or report an unhandled exception in client code. Should
366
377
  probably only be used by extension code that allows client code to
@@ -633,6 +644,13 @@ interface EditorViewConfig extends EditorStateConfig {
633
644
  */
634
645
  root?: Document | ShadowRoot;
635
646
  /**
647
+ Pass an effect created with
648
+ [`EditorView.scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) or
649
+ [`EditorView.scrollSnapshot`](https://codemirror.net/6/docs/ref/#view.EditorView.scrollSnapshot)
650
+ here to set an initial scroll position.
651
+ */
652
+ scrollTo?: StateEffect<any>;
653
+ /**
636
654
  Override the way transactions are
637
655
  [dispatched](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) for this editor view.
638
656
  Your implementation, if provided, should probably call the
@@ -1032,15 +1050,30 @@ declare class EditorView {
1032
1050
  /**
1033
1051
  Extra vertical distance to add when moving something into
1034
1052
  view. Not used with the `"center"` strategy. Defaults to 5.
1053
+ Must be less than the height of the editor.
1035
1054
  */
1036
1055
  yMargin?: number;
1037
1056
  /**
1038
1057
  Extra horizontal distance to add. Not used with the `"center"`
1039
- strategy. Defaults to 5.
1058
+ strategy. Defaults to 5. Must be less than the width of the
1059
+ editor.
1040
1060
  */
1041
1061
  xMargin?: number;
1042
1062
  }): StateEffect<unknown>;
1043
1063
  /**
1064
+ Return an effect that resets the editor to its current (at the
1065
+ time this method was called) scroll position. Note that this
1066
+ only affects the editor's own scrollable element, not parents.
1067
+ See also
1068
+ [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
1069
+
1070
+ The effect should be used with a document identical to the one
1071
+ it was created for. Failing to do so is not an error, but may
1072
+ not scroll to the expected position. You can
1073
+ [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
1074
+ */
1075
+ scrollSnapshot(): StateEffect<ScrollTarget>;
1076
+ /**
1044
1077
  Facet to add a [style
1045
1078
  module](https://github.com/marijnh/style-mod#documentation) to
1046
1079
  an editor view. The view will ensure that the module is
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, findColumn, CharCategory, Annotation, EditorState, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
1
+ import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, EditorSelection, findClusterBreak, findColumn, CharCategory, Annotation, EditorState, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
2
2
  import { StyleModule } from 'style-mod';
3
3
  import { keyName, base, shift } from 'w3c-keyname';
4
4
 
@@ -501,7 +501,7 @@ class ContentView {
501
501
  this.markDirty();
502
502
  for (let i = from; i < to; i++) {
503
503
  let child = this.children[i];
504
- if (child.parent == this)
504
+ if (child.parent == this && children.indexOf(child) < 0)
505
505
  child.destroy();
506
506
  }
507
507
  this.children.splice(from, to - from, ...children);
@@ -538,6 +538,8 @@ class ContentView {
538
538
  // number > 0 when after its position.
539
539
  getSide() { return 0; }
540
540
  destroy() {
541
+ for (let child of this.children)
542
+ child.destroy();
541
543
  this.parent = null;
542
544
  }
543
545
  }
@@ -1785,15 +1787,28 @@ const nativeSelectionHidden = /*@__PURE__*/Facet.define({
1785
1787
  combine: values => values.some(x => x)
1786
1788
  });
1787
1789
  class ScrollTarget {
1788
- constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1790
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5,
1791
+ // This data structure is abused to also store precise scroll
1792
+ // snapshots, instead of a `scrollIntoView` request. When this
1793
+ // flag is `true`, `range` points at a position in the reference
1794
+ // line, `yMargin` holds the difference between the top of that
1795
+ // line and the top of the editor, and `xMargin` holds the
1796
+ // editor's `scrollLeft`.
1797
+ isSnapshot = false) {
1789
1798
  this.range = range;
1790
1799
  this.y = y;
1791
1800
  this.x = x;
1792
1801
  this.yMargin = yMargin;
1793
1802
  this.xMargin = xMargin;
1803
+ this.isSnapshot = isSnapshot;
1794
1804
  }
1795
1805
  map(changes) {
1796
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1806
+ return changes.empty ? this :
1807
+ new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1808
+ }
1809
+ clip(state) {
1810
+ return this.range.to <= state.doc.length ? this :
1811
+ new ScrollTarget(EditorSelection.cursor(state.doc.length), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1797
1812
  }
1798
1813
  }
1799
1814
  const scrollIntoView = /*@__PURE__*/StateEffect.define({ map: (t, ch) => t.map(ch) });
@@ -3086,6 +3101,12 @@ class DocView extends ContentView {
3086
3101
  ];
3087
3102
  }
3088
3103
  scrollIntoView(target) {
3104
+ if (target.isSnapshot) {
3105
+ let ref = this.view.viewState.lineBlockAt(target.range.head);
3106
+ this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
3107
+ this.view.scrollDOM.scrollLeft = target.xMargin;
3108
+ return;
3109
+ }
3089
3110
  let { range } = target;
3090
3111
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
3091
3112
  if (!rect)
@@ -3098,7 +3119,8 @@ class DocView extends ContentView {
3098
3119
  left: rect.left - margins.left, top: rect.top - margins.top,
3099
3120
  right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
3100
3121
  };
3101
- scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == Direction.LTR);
3122
+ let { offsetWidth, offsetHeight } = this.view.scrollDOM;
3123
+ scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == Direction.LTR);
3102
3124
  }
3103
3125
  }
3104
3126
  function betweenUneditable(pos) {
@@ -3635,6 +3657,9 @@ class InputState {
3635
3657
  // the mutation events fire shortly after the compositionend event
3636
3658
  this.compositionPendingChange = false;
3637
3659
  this.mouseSelection = null;
3660
+ // When a drag from the editor is active, this points at the range
3661
+ // being dragged.
3662
+ this.draggedContent = null;
3638
3663
  this.handleEvent = this.handleEvent.bind(this);
3639
3664
  this.notifiedFocused = view.hasFocus;
3640
3665
  // On Safari adding an input event handler somehow prevents an
@@ -3751,6 +3776,8 @@ class InputState {
3751
3776
  update(update) {
3752
3777
  if (this.mouseSelection)
3753
3778
  this.mouseSelection.update(update);
3779
+ if (this.draggedContent && update.docChanged)
3780
+ this.draggedContent = this.draggedContent.map(update.changes);
3754
3781
  if (update.transactions.length)
3755
3782
  this.lastKeyCode = this.lastSelectionTime = 0;
3756
3783
  }
@@ -3868,7 +3895,7 @@ class MouseSelection {
3868
3895
  let doc = this.view.contentDOM.ownerDocument;
3869
3896
  doc.removeEventListener("mousemove", this.move);
3870
3897
  doc.removeEventListener("mouseup", this.up);
3871
- this.view.inputState.mouseSelection = null;
3898
+ this.view.inputState.mouseSelection = this.view.inputState.draggedContent = null;
3872
3899
  }
3873
3900
  setScrollSpeed(sx, sy) {
3874
3901
  this.scrollSpeed = { x: sx, y: sy };
@@ -3926,8 +3953,6 @@ class MouseSelection {
3926
3953
  this.mustSelect = false;
3927
3954
  }
3928
3955
  update(update) {
3929
- if (update.docChanged && this.dragging)
3930
- this.dragging = this.dragging.map(update.changes);
3931
3956
  if (this.style.update(update))
3932
3957
  setTimeout(() => this.select(this.lastEvent), 20);
3933
3958
  }
@@ -4155,23 +4180,36 @@ function removeRangeAround(sel, pos) {
4155
4180
  return null;
4156
4181
  }
4157
4182
  handlers.dragstart = (view, event) => {
4158
- let { selection: { main } } = view.state;
4159
- let { mouseSelection } = view.inputState;
4160
- if (mouseSelection)
4161
- mouseSelection.dragging = main;
4183
+ let { selection: { main: range } } = view.state;
4184
+ if (event.target.draggable) {
4185
+ let cView = view.docView.nearest(event.target);
4186
+ if (cView && cView.isWidget) {
4187
+ let from = cView.posAtStart, to = from + cView.length;
4188
+ if (from >= range.to || to <= range.from)
4189
+ range = EditorSelection.range(from, to);
4190
+ }
4191
+ }
4192
+ let { inputState } = view;
4193
+ if (inputState.mouseSelection)
4194
+ inputState.mouseSelection.dragging = true;
4195
+ inputState.draggedContent = range;
4162
4196
  if (event.dataTransfer) {
4163
- event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4197
+ event.dataTransfer.setData("Text", view.state.sliceDoc(range.from, range.to));
4164
4198
  event.dataTransfer.effectAllowed = "copyMove";
4165
4199
  }
4166
4200
  return false;
4167
4201
  };
4202
+ handlers.dragend = view => {
4203
+ view.inputState.draggedContent = null;
4204
+ return false;
4205
+ };
4168
4206
  function dropText(view, event, text, direct) {
4169
4207
  if (!text)
4170
4208
  return;
4171
4209
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4172
- let { mouseSelection } = view.inputState;
4173
- let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4174
- { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
4210
+ let { draggedContent } = view.inputState;
4211
+ let del = direct && draggedContent && dragMovesSelection(view, event)
4212
+ ? { from: draggedContent.from, to: draggedContent.to } : null;
4175
4213
  let ins = { from: dropPos, insert: text };
4176
4214
  let changes = view.state.changes(del ? [del, ins] : ins);
4177
4215
  view.focus();
@@ -4180,6 +4218,7 @@ function dropText(view, event, text, direct) {
4180
4218
  selection: { anchor: changes.mapPos(dropPos, -1), head: changes.mapPos(dropPos, 1) },
4181
4219
  userEvent: del ? "move.drop" : "input.drop"
4182
4220
  });
4221
+ view.inputState.draggedContent = null;
4183
4222
  }
4184
4223
  handlers.drop = (view, event) => {
4185
4224
  if (!event.dataTransfer)
@@ -5839,6 +5878,13 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5839
5878
  "&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor": {
5840
5879
  display: "block"
5841
5880
  },
5881
+ ".cm-announced": {
5882
+ position: "fixed",
5883
+ top: "-10000px"
5884
+ },
5885
+ "@media print": {
5886
+ ".cm-announced": { display: "none" }
5887
+ },
5842
5888
  "&light .cm-activeLine": { backgroundColor: "#cceeff44" },
5843
5889
  "&dark .cm-activeLine": { backgroundColor: "#99eeff33" },
5844
5890
  "&light .cm-specialChar": { color: "red" },
@@ -6887,7 +6933,7 @@ class EditorView {
6887
6933
  this.scrollDOM.className = "cm-scroller";
6888
6934
  this.scrollDOM.appendChild(this.contentDOM);
6889
6935
  this.announceDOM = document.createElement("div");
6890
- this.announceDOM.style.cssText = "position: fixed; top: -10000px";
6936
+ this.announceDOM.className = "cm-announced";
6891
6937
  this.announceDOM.setAttribute("aria-live", "polite");
6892
6938
  this.dom = document.createElement("div");
6893
6939
  this.dom.appendChild(this.announceDOM);
@@ -6899,6 +6945,8 @@ class EditorView {
6899
6945
  this.dispatch = this.dispatch.bind(this);
6900
6946
  this._root = (config.root || getRoot(config.parent) || document);
6901
6947
  this.viewState = new ViewState(config.state || EditorState.create(config));
6948
+ if (config.scrollTo && config.scrollTo.is(scrollIntoView))
6949
+ this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
6902
6950
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
6903
6951
  for (let plugin of this.plugins)
6904
6952
  plugin.update(this);
@@ -6986,7 +7034,7 @@ class EditorView {
6986
7034
  }
6987
7035
  for (let e of tr.effects)
6988
7036
  if (e.is(scrollIntoView))
6989
- scrollTarget = e.value;
7037
+ scrollTarget = e.value.clip(this.state);
6990
7038
  }
6991
7039
  this.viewState.update(update, scrollTarget);
6992
7040
  this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
@@ -7009,8 +7057,14 @@ class EditorView {
7009
7057
  if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
7010
7058
  this.requestMeasure();
7011
7059
  if (!update.empty)
7012
- for (let listener of this.state.facet(updateListener))
7013
- listener(update);
7060
+ for (let listener of this.state.facet(updateListener)) {
7061
+ try {
7062
+ listener(update);
7063
+ }
7064
+ catch (e) {
7065
+ logException(this.state, e, "update listener");
7066
+ }
7067
+ }
7014
7068
  if (dispatchFocus || domChange)
7015
7069
  Promise.resolve().then(() => {
7016
7070
  if (dispatchFocus && this.state == dispatchFocus.startState)
@@ -7045,6 +7099,7 @@ class EditorView {
7045
7099
  this.pluginMap.clear();
7046
7100
  for (let plugin of this.plugins)
7047
7101
  plugin.update(this);
7102
+ this.docView.destroy();
7048
7103
  this.docView = new DocView(this);
7049
7104
  this.inputState.ensureHandlers(this.plugins);
7050
7105
  this.mountStyles();
@@ -7577,6 +7632,7 @@ class EditorView {
7577
7632
  plugin.destroy(this);
7578
7633
  this.plugins = [];
7579
7634
  this.inputState.destroy();
7635
+ this.docView.destroy();
7580
7636
  this.dom.remove();
7581
7637
  this.observer.destroy();
7582
7638
  if (this.measureScheduled > -1)
@@ -7592,6 +7648,23 @@ class EditorView {
7592
7648
  return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
7593
7649
  }
7594
7650
  /**
7651
+ Return an effect that resets the editor to its current (at the
7652
+ time this method was called) scroll position. Note that this
7653
+ only affects the editor's own scrollable element, not parents.
7654
+ See also
7655
+ [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
7656
+
7657
+ The effect should be used with a document identical to the one
7658
+ it was created for. Failing to do so is not an error, but may
7659
+ not scroll to the expected position. You can
7660
+ [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
7661
+ */
7662
+ scrollSnapshot() {
7663
+ let { scrollTop, scrollLeft } = this.scrollDOM;
7664
+ let ref = this.viewState.scrollAnchorAt(scrollTop);
7665
+ return scrollIntoView.of(new ScrollTarget(EditorSelection.cursor(ref.from), "start", "start", ref.top - scrollTop, scrollLeft, true));
7666
+ }
7667
+ /**
7595
7668
  Returns an extension that can be used to add DOM event handlers.
7596
7669
  The value should be an object mapping event names to handler
7597
7670
  functions. For any given event, such functions are ordered by
@@ -8420,8 +8493,10 @@ const themeSpec = {
8420
8493
  "&::selection": { backgroundColor: "transparent !important" }
8421
8494
  }
8422
8495
  };
8423
- if (CanHidePrimary)
8496
+ if (CanHidePrimary) {
8424
8497
  themeSpec[".cm-line"].caretColor = "transparent !important";
8498
+ themeSpec[".cm-content"] = { caretColor: "transparent !important" };
8499
+ }
8425
8500
  const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec));
8426
8501
 
8427
8502
  const setDropCursorPos = /*@__PURE__*/StateEffect.define({
@@ -9478,7 +9553,7 @@ const showHoverTooltipHost = /*@__PURE__*/showTooltip.compute([showHoverTooltip]
9478
9553
  return null;
9479
9554
  return {
9480
9555
  pos: Math.min(...tooltips.map(t => t.pos)),
9481
- end: Math.max(...tooltips.filter(t => t.end != null).map(t => t.end)),
9556
+ end: Math.max(...tooltips.map(t => { var _a; return (_a = t.end) !== null && _a !== void 0 ? _a : t.pos; })),
9482
9557
  create: HoverTooltipHost.create,
9483
9558
  above: tooltips[0].above,
9484
9559
  arrow: tooltips.some(t => t.arrow),
@@ -9558,14 +9633,19 @@ class HoverPlugin {
9558
9633
  view.dispatch({ effects: this.setHover.of(open) });
9559
9634
  }
9560
9635
  }
9636
+ get tooltip() {
9637
+ let plugin = this.view.plugin(tooltipPlugin);
9638
+ let index = plugin ? plugin.manager.tooltips.findIndex(t => t.create == HoverTooltipHost.create) : -1;
9639
+ return index > -1 ? plugin.manager.tooltipViews[index] : null;
9640
+ }
9561
9641
  mousemove(event) {
9562
9642
  var _a;
9563
9643
  this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
9564
9644
  if (this.hoverTimeout < 0)
9565
9645
  this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
9566
- let tooltip = this.active;
9567
- if (tooltip && !isInTooltip(this.lastMove.target) || this.pending) {
9568
- let { pos } = tooltip || this.pending, end = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.end) !== null && _a !== void 0 ? _a : pos;
9646
+ let { active, tooltip } = this;
9647
+ if (active && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
9648
+ let { pos } = active || this.pending, end = (_a = active === null || active === void 0 ? void 0 : active.end) !== null && _a !== void 0 ? _a : pos;
9569
9649
  if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
9570
9650
  : !isOverRange(this.view, pos, end, event.clientX, event.clientY))) {
9571
9651
  this.view.dispatch({ effects: this.setHover.of(null) });
@@ -9573,11 +9653,26 @@ class HoverPlugin {
9573
9653
  }
9574
9654
  }
9575
9655
  }
9576
- mouseleave(e) {
9656
+ mouseleave(event) {
9577
9657
  clearTimeout(this.hoverTimeout);
9578
9658
  this.hoverTimeout = -1;
9579
- if (this.active && !isInTooltip(e.relatedTarget))
9580
- this.view.dispatch({ effects: this.setHover.of(null) });
9659
+ let { active } = this;
9660
+ if (active) {
9661
+ let { tooltip } = this;
9662
+ let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget);
9663
+ if (!inTooltip)
9664
+ this.view.dispatch({ effects: this.setHover.of(null) });
9665
+ else
9666
+ this.watchTooltipLeave(tooltip.dom);
9667
+ }
9668
+ }
9669
+ watchTooltipLeave(tooltip) {
9670
+ let watch = (event) => {
9671
+ tooltip.removeEventListener("mouseleave", watch);
9672
+ if (this.active && !this.view.dom.contains(event.relatedTarget))
9673
+ this.view.dispatch({ effects: this.setHover.of(null) });
9674
+ };
9675
+ tooltip.addEventListener("mouseleave", watch);
9581
9676
  }
9582
9677
  destroy() {
9583
9678
  clearTimeout(this.hoverTimeout);
@@ -9585,11 +9680,11 @@ class HoverPlugin {
9585
9680
  this.view.dom.removeEventListener("mousemove", this.mousemove);
9586
9681
  }
9587
9682
  }
9588
- function isInTooltip(elt) {
9589
- for (let cur = elt; cur; cur = cur.parentNode)
9590
- if (cur.nodeType == 1 && cur.classList.contains("cm-tooltip"))
9591
- return true;
9592
- return false;
9683
+ const tooltipMargin = 4;
9684
+ function isInTooltip(tooltip, event) {
9685
+ let rect = tooltip.getBoundingClientRect();
9686
+ return event.clientX >= rect.left - tooltipMargin && event.clientX <= rect.right + tooltipMargin &&
9687
+ event.clientY >= rect.top - tooltipMargin && event.clientY <= rect.bottom + tooltipMargin;
9593
9688
  }
9594
9689
  function isOverRange(view, from, to, x, y, margin) {
9595
9690
  let rect = view.scrollDOM.getBoundingClientRect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.21.4",
3
+ "version": "6.22.1",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",