@codemirror/view 6.21.4 → 6.22.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,19 @@
1
+ ## 6.22.0 (2023-11-03)
2
+
3
+ ### Bug fixes
4
+
5
+ Exceptions raised by update listeners are now routed to the configured exception sink, if any.
6
+
7
+ Fix an issue where passing large scroll margins to `scrollIntoView` would cause the measure loop to fail to terminate.
8
+
9
+ Widgets that are draggable (and allow drag events through in their `ignoreEvent` implementation) can now use the editor's built-in drag/drop behavior.
10
+
11
+ ### New features
12
+
13
+ The new `scrollTo` option to `EditorView` allows an initial scroll position to be provided.
14
+
15
+ The new `EditorView.scrollSnapshot` method returns an effect that can be used to reset to a previous scroll position.
16
+
1
17
  ## 6.21.4 (2023-10-24)
2
18
 
3
19
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1788,15 +1788,28 @@ const nativeSelectionHidden = state.Facet.define({
1788
1788
  combine: values => values.some(x => x)
1789
1789
  });
1790
1790
  class ScrollTarget {
1791
- constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1791
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5,
1792
+ // This data structure is abused to also store precise scroll
1793
+ // snapshots, instead of a `scrollIntoView` request. When this
1794
+ // flag is `true`, `range` points at a position in the reference
1795
+ // line, `yMargin` holds the difference between the top of that
1796
+ // line and the top of the editor, and `xMargin` holds the
1797
+ // editor's `scrollLeft`.
1798
+ isSnapshot = false) {
1792
1799
  this.range = range;
1793
1800
  this.y = y;
1794
1801
  this.x = x;
1795
1802
  this.yMargin = yMargin;
1796
1803
  this.xMargin = xMargin;
1804
+ this.isSnapshot = isSnapshot;
1797
1805
  }
1798
1806
  map(changes) {
1799
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1807
+ return changes.empty ? this :
1808
+ new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1809
+ }
1810
+ clip(state$1) {
1811
+ return this.range.to <= state$1.doc.length ? this :
1812
+ new ScrollTarget(state.EditorSelection.cursor(state$1.doc.length), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1800
1813
  }
1801
1814
  }
1802
1815
  const scrollIntoView = state.StateEffect.define({ map: (t, ch) => t.map(ch) });
@@ -3090,6 +3103,12 @@ class DocView extends ContentView {
3090
3103
  ];
3091
3104
  }
3092
3105
  scrollIntoView(target) {
3106
+ if (target.isSnapshot) {
3107
+ let ref = this.view.viewState.lineBlockAt(target.range.head);
3108
+ this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
3109
+ this.view.scrollDOM.scrollLeft = target.xMargin;
3110
+ return;
3111
+ }
3093
3112
  let { range } = target;
3094
3113
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
3095
3114
  if (!rect)
@@ -3102,7 +3121,8 @@ class DocView extends ContentView {
3102
3121
  left: rect.left - margins.left, top: rect.top - margins.top,
3103
3122
  right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
3104
3123
  };
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);
3124
+ let { offsetWidth, offsetHeight } = this.view.scrollDOM;
3125
+ 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
3126
  }
3107
3127
  }
3108
3128
  function betweenUneditable(pos) {
@@ -3639,6 +3659,9 @@ class InputState {
3639
3659
  // the mutation events fire shortly after the compositionend event
3640
3660
  this.compositionPendingChange = false;
3641
3661
  this.mouseSelection = null;
3662
+ // When a drag from the editor is active, this points at the range
3663
+ // being dragged.
3664
+ this.draggedContent = null;
3642
3665
  this.handleEvent = this.handleEvent.bind(this);
3643
3666
  this.notifiedFocused = view.hasFocus;
3644
3667
  // On Safari adding an input event handler somehow prevents an
@@ -3755,6 +3778,8 @@ class InputState {
3755
3778
  update(update) {
3756
3779
  if (this.mouseSelection)
3757
3780
  this.mouseSelection.update(update);
3781
+ if (this.draggedContent && update.docChanged)
3782
+ this.draggedContent = this.draggedContent.map(update.changes);
3758
3783
  if (update.transactions.length)
3759
3784
  this.lastKeyCode = this.lastSelectionTime = 0;
3760
3785
  }
@@ -3872,7 +3897,7 @@ class MouseSelection {
3872
3897
  let doc = this.view.contentDOM.ownerDocument;
3873
3898
  doc.removeEventListener("mousemove", this.move);
3874
3899
  doc.removeEventListener("mouseup", this.up);
3875
- this.view.inputState.mouseSelection = null;
3900
+ this.view.inputState.mouseSelection = this.view.inputState.draggedContent = null;
3876
3901
  }
3877
3902
  setScrollSpeed(sx, sy) {
3878
3903
  this.scrollSpeed = { x: sx, y: sy };
@@ -3930,8 +3955,6 @@ class MouseSelection {
3930
3955
  this.mustSelect = false;
3931
3956
  }
3932
3957
  update(update) {
3933
- if (update.docChanged && this.dragging)
3934
- this.dragging = this.dragging.map(update.changes);
3935
3958
  if (this.style.update(update))
3936
3959
  setTimeout(() => this.select(this.lastEvent), 20);
3937
3960
  }
@@ -4159,23 +4182,36 @@ function removeRangeAround(sel, pos) {
4159
4182
  return null;
4160
4183
  }
4161
4184
  handlers.dragstart = (view, event) => {
4162
- let { selection: { main } } = view.state;
4163
- let { mouseSelection } = view.inputState;
4164
- if (mouseSelection)
4165
- mouseSelection.dragging = main;
4185
+ let { selection: { main: range } } = view.state;
4186
+ if (event.target.draggable) {
4187
+ let cView = view.docView.nearest(event.target);
4188
+ if (cView && cView.isWidget) {
4189
+ let from = cView.posAtStart, to = from + cView.length;
4190
+ if (from >= range.to || to <= range.from)
4191
+ range = state.EditorSelection.range(from, to);
4192
+ }
4193
+ }
4194
+ let { inputState } = view;
4195
+ if (inputState.mouseSelection)
4196
+ inputState.mouseSelection.dragging = true;
4197
+ inputState.draggedContent = range;
4166
4198
  if (event.dataTransfer) {
4167
- event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4199
+ event.dataTransfer.setData("Text", view.state.sliceDoc(range.from, range.to));
4168
4200
  event.dataTransfer.effectAllowed = "copyMove";
4169
4201
  }
4170
4202
  return false;
4171
4203
  };
4204
+ handlers.dragend = view => {
4205
+ view.inputState.draggedContent = null;
4206
+ return false;
4207
+ };
4172
4208
  function dropText(view, event, text, direct) {
4173
4209
  if (!text)
4174
4210
  return;
4175
4211
  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;
4212
+ let { draggedContent } = view.inputState;
4213
+ let del = direct && draggedContent && dragMovesSelection(view, event)
4214
+ ? { from: draggedContent.from, to: draggedContent.to } : null;
4179
4215
  let ins = { from: dropPos, insert: text };
4180
4216
  let changes = view.state.changes(del ? [del, ins] : ins);
4181
4217
  view.focus();
@@ -4184,6 +4220,7 @@ function dropText(view, event, text, direct) {
4184
4220
  selection: { anchor: changes.mapPos(dropPos, -1), head: changes.mapPos(dropPos, 1) },
4185
4221
  userEvent: del ? "move.drop" : "input.drop"
4186
4222
  });
4223
+ view.inputState.draggedContent = null;
4187
4224
  }
4188
4225
  handlers.drop = (view, event) => {
4189
4226
  if (!event.dataTransfer)
@@ -6904,6 +6941,8 @@ class EditorView {
6904
6941
  this.dispatch = this.dispatch.bind(this);
6905
6942
  this._root = (config.root || getRoot(config.parent) || document);
6906
6943
  this.viewState = new ViewState(config.state || state.EditorState.create(config));
6944
+ if (config.scrollTo && config.scrollTo.is(scrollIntoView))
6945
+ this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
6907
6946
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
6908
6947
  for (let plugin of this.plugins)
6909
6948
  plugin.update(this);
@@ -6991,7 +7030,7 @@ class EditorView {
6991
7030
  }
6992
7031
  for (let e of tr.effects)
6993
7032
  if (e.is(scrollIntoView))
6994
- scrollTarget = e.value;
7033
+ scrollTarget = e.value.clip(this.state);
6995
7034
  }
6996
7035
  this.viewState.update(update, scrollTarget);
6997
7036
  this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
@@ -7014,8 +7053,14 @@ class EditorView {
7014
7053
  if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
7015
7054
  this.requestMeasure();
7016
7055
  if (!update.empty)
7017
- for (let listener of this.state.facet(updateListener))
7018
- listener(update);
7056
+ for (let listener of this.state.facet(updateListener)) {
7057
+ try {
7058
+ listener(update);
7059
+ }
7060
+ catch (e) {
7061
+ logException(this.state, e, "update listener");
7062
+ }
7063
+ }
7019
7064
  if (dispatchFocus || domChange)
7020
7065
  Promise.resolve().then(() => {
7021
7066
  if (dispatchFocus && this.state == dispatchFocus.startState)
@@ -7597,6 +7642,23 @@ class EditorView {
7597
7642
  return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
7598
7643
  }
7599
7644
  /**
7645
+ Return an effect that resets the editor to its current (at the
7646
+ time this method was called) scroll position. Note that this
7647
+ only affects the editor's own scrollable element, not parents.
7648
+ See also
7649
+ [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
7650
+
7651
+ The effect should be used with a document identical to the one
7652
+ it was created for. Failing to do so is not an error, but may
7653
+ not scroll to the expected position. You can
7654
+ [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
7655
+ */
7656
+ scrollSnapshot() {
7657
+ let { scrollTop, scrollLeft } = this.scrollDOM;
7658
+ let ref = this.viewState.scrollAnchorAt(scrollTop);
7659
+ return scrollIntoView.of(new ScrollTarget(state.EditorSelection.cursor(ref.from), "start", "start", ref.top - scrollTop, scrollLeft, true));
7660
+ }
7661
+ /**
7600
7662
  Returns an extension that can be used to add DOM event handlers.
7601
7663
  The value should be an object mapping event names to handler
7602
7664
  functions. For any given event, such functions are ordered by
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
 
@@ -1785,15 +1785,28 @@ const nativeSelectionHidden = /*@__PURE__*/Facet.define({
1785
1785
  combine: values => values.some(x => x)
1786
1786
  });
1787
1787
  class ScrollTarget {
1788
- constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1788
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5,
1789
+ // This data structure is abused to also store precise scroll
1790
+ // snapshots, instead of a `scrollIntoView` request. When this
1791
+ // flag is `true`, `range` points at a position in the reference
1792
+ // line, `yMargin` holds the difference between the top of that
1793
+ // line and the top of the editor, and `xMargin` holds the
1794
+ // editor's `scrollLeft`.
1795
+ isSnapshot = false) {
1789
1796
  this.range = range;
1790
1797
  this.y = y;
1791
1798
  this.x = x;
1792
1799
  this.yMargin = yMargin;
1793
1800
  this.xMargin = xMargin;
1801
+ this.isSnapshot = isSnapshot;
1794
1802
  }
1795
1803
  map(changes) {
1796
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1804
+ return changes.empty ? this :
1805
+ new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1806
+ }
1807
+ clip(state) {
1808
+ return this.range.to <= state.doc.length ? this :
1809
+ new ScrollTarget(EditorSelection.cursor(state.doc.length), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot);
1797
1810
  }
1798
1811
  }
1799
1812
  const scrollIntoView = /*@__PURE__*/StateEffect.define({ map: (t, ch) => t.map(ch) });
@@ -3086,6 +3099,12 @@ class DocView extends ContentView {
3086
3099
  ];
3087
3100
  }
3088
3101
  scrollIntoView(target) {
3102
+ if (target.isSnapshot) {
3103
+ let ref = this.view.viewState.lineBlockAt(target.range.head);
3104
+ this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
3105
+ this.view.scrollDOM.scrollLeft = target.xMargin;
3106
+ return;
3107
+ }
3089
3108
  let { range } = target;
3090
3109
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
3091
3110
  if (!rect)
@@ -3098,7 +3117,8 @@ class DocView extends ContentView {
3098
3117
  left: rect.left - margins.left, top: rect.top - margins.top,
3099
3118
  right: rect.right + margins.right, bottom: rect.bottom + margins.bottom
3100
3119
  };
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);
3120
+ let { offsetWidth, offsetHeight } = this.view.scrollDOM;
3121
+ 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
3122
  }
3103
3123
  }
3104
3124
  function betweenUneditable(pos) {
@@ -3635,6 +3655,9 @@ class InputState {
3635
3655
  // the mutation events fire shortly after the compositionend event
3636
3656
  this.compositionPendingChange = false;
3637
3657
  this.mouseSelection = null;
3658
+ // When a drag from the editor is active, this points at the range
3659
+ // being dragged.
3660
+ this.draggedContent = null;
3638
3661
  this.handleEvent = this.handleEvent.bind(this);
3639
3662
  this.notifiedFocused = view.hasFocus;
3640
3663
  // On Safari adding an input event handler somehow prevents an
@@ -3751,6 +3774,8 @@ class InputState {
3751
3774
  update(update) {
3752
3775
  if (this.mouseSelection)
3753
3776
  this.mouseSelection.update(update);
3777
+ if (this.draggedContent && update.docChanged)
3778
+ this.draggedContent = this.draggedContent.map(update.changes);
3754
3779
  if (update.transactions.length)
3755
3780
  this.lastKeyCode = this.lastSelectionTime = 0;
3756
3781
  }
@@ -3868,7 +3893,7 @@ class MouseSelection {
3868
3893
  let doc = this.view.contentDOM.ownerDocument;
3869
3894
  doc.removeEventListener("mousemove", this.move);
3870
3895
  doc.removeEventListener("mouseup", this.up);
3871
- this.view.inputState.mouseSelection = null;
3896
+ this.view.inputState.mouseSelection = this.view.inputState.draggedContent = null;
3872
3897
  }
3873
3898
  setScrollSpeed(sx, sy) {
3874
3899
  this.scrollSpeed = { x: sx, y: sy };
@@ -3926,8 +3951,6 @@ class MouseSelection {
3926
3951
  this.mustSelect = false;
3927
3952
  }
3928
3953
  update(update) {
3929
- if (update.docChanged && this.dragging)
3930
- this.dragging = this.dragging.map(update.changes);
3931
3954
  if (this.style.update(update))
3932
3955
  setTimeout(() => this.select(this.lastEvent), 20);
3933
3956
  }
@@ -4155,23 +4178,36 @@ function removeRangeAround(sel, pos) {
4155
4178
  return null;
4156
4179
  }
4157
4180
  handlers.dragstart = (view, event) => {
4158
- let { selection: { main } } = view.state;
4159
- let { mouseSelection } = view.inputState;
4160
- if (mouseSelection)
4161
- mouseSelection.dragging = main;
4181
+ let { selection: { main: range } } = view.state;
4182
+ if (event.target.draggable) {
4183
+ let cView = view.docView.nearest(event.target);
4184
+ if (cView && cView.isWidget) {
4185
+ let from = cView.posAtStart, to = from + cView.length;
4186
+ if (from >= range.to || to <= range.from)
4187
+ range = EditorSelection.range(from, to);
4188
+ }
4189
+ }
4190
+ let { inputState } = view;
4191
+ if (inputState.mouseSelection)
4192
+ inputState.mouseSelection.dragging = true;
4193
+ inputState.draggedContent = range;
4162
4194
  if (event.dataTransfer) {
4163
- event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4195
+ event.dataTransfer.setData("Text", view.state.sliceDoc(range.from, range.to));
4164
4196
  event.dataTransfer.effectAllowed = "copyMove";
4165
4197
  }
4166
4198
  return false;
4167
4199
  };
4200
+ handlers.dragend = view => {
4201
+ view.inputState.draggedContent = null;
4202
+ return false;
4203
+ };
4168
4204
  function dropText(view, event, text, direct) {
4169
4205
  if (!text)
4170
4206
  return;
4171
4207
  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;
4208
+ let { draggedContent } = view.inputState;
4209
+ let del = direct && draggedContent && dragMovesSelection(view, event)
4210
+ ? { from: draggedContent.from, to: draggedContent.to } : null;
4175
4211
  let ins = { from: dropPos, insert: text };
4176
4212
  let changes = view.state.changes(del ? [del, ins] : ins);
4177
4213
  view.focus();
@@ -4180,6 +4216,7 @@ function dropText(view, event, text, direct) {
4180
4216
  selection: { anchor: changes.mapPos(dropPos, -1), head: changes.mapPos(dropPos, 1) },
4181
4217
  userEvent: del ? "move.drop" : "input.drop"
4182
4218
  });
4219
+ view.inputState.draggedContent = null;
4183
4220
  }
4184
4221
  handlers.drop = (view, event) => {
4185
4222
  if (!event.dataTransfer)
@@ -6899,6 +6936,8 @@ class EditorView {
6899
6936
  this.dispatch = this.dispatch.bind(this);
6900
6937
  this._root = (config.root || getRoot(config.parent) || document);
6901
6938
  this.viewState = new ViewState(config.state || EditorState.create(config));
6939
+ if (config.scrollTo && config.scrollTo.is(scrollIntoView))
6940
+ this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
6902
6941
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
6903
6942
  for (let plugin of this.plugins)
6904
6943
  plugin.update(this);
@@ -6986,7 +7025,7 @@ class EditorView {
6986
7025
  }
6987
7026
  for (let e of tr.effects)
6988
7027
  if (e.is(scrollIntoView))
6989
- scrollTarget = e.value;
7028
+ scrollTarget = e.value.clip(this.state);
6990
7029
  }
6991
7030
  this.viewState.update(update, scrollTarget);
6992
7031
  this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
@@ -7009,8 +7048,14 @@ class EditorView {
7009
7048
  if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
7010
7049
  this.requestMeasure();
7011
7050
  if (!update.empty)
7012
- for (let listener of this.state.facet(updateListener))
7013
- listener(update);
7051
+ for (let listener of this.state.facet(updateListener)) {
7052
+ try {
7053
+ listener(update);
7054
+ }
7055
+ catch (e) {
7056
+ logException(this.state, e, "update listener");
7057
+ }
7058
+ }
7014
7059
  if (dispatchFocus || domChange)
7015
7060
  Promise.resolve().then(() => {
7016
7061
  if (dispatchFocus && this.state == dispatchFocus.startState)
@@ -7592,6 +7637,23 @@ class EditorView {
7592
7637
  return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
7593
7638
  }
7594
7639
  /**
7640
+ Return an effect that resets the editor to its current (at the
7641
+ time this method was called) scroll position. Note that this
7642
+ only affects the editor's own scrollable element, not parents.
7643
+ See also
7644
+ [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
7645
+
7646
+ The effect should be used with a document identical to the one
7647
+ it was created for. Failing to do so is not an error, but may
7648
+ not scroll to the expected position. You can
7649
+ [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
7650
+ */
7651
+ scrollSnapshot() {
7652
+ let { scrollTop, scrollLeft } = this.scrollDOM;
7653
+ let ref = this.viewState.scrollAnchorAt(scrollTop);
7654
+ return scrollIntoView.of(new ScrollTarget(EditorSelection.cursor(ref.from), "start", "start", ref.top - scrollTop, scrollLeft, true));
7655
+ }
7656
+ /**
7595
7657
  Returns an extension that can be used to add DOM event handlers.
7596
7658
  The value should be an object mapping event names to handler
7597
7659
  functions. For any given event, such functions are ordered by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.21.4",
3
+ "version": "6.22.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",