@codemirror/view 0.20.5 → 6.0.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,29 @@
1
+ ## 6.0.0 (2022-06-08)
2
+
3
+ ### New features
4
+
5
+ The new static `EditorView.findFromDOM` method can be used to retrieve an editor instance from its DOM structure.
6
+
7
+ Instead of passing a constructed state to the `EditorView` constructor, it is now also possible to inline the configuration options to the state in the view config object.
8
+
9
+ ## 0.20.7 (2022-05-30)
10
+
11
+ ### Bug fixes
12
+
13
+ Fix an issue on Chrome Android where the DOM could fail to display the actual document after backspace.
14
+
15
+ Avoid an issue on Chrome Android where DOM changes were sometimes inappropriately replace by a backspace key effect due to spurious beforeinput events.
16
+
17
+ Fix a problem where the content element's width didn't cover the width of the actual content.
18
+
19
+ Work around a bug in Chrome 102 which caused wheel scrolling of the editor to be interrupted every few lines.
20
+
21
+ ## 0.20.6 (2022-05-20)
22
+
23
+ ### Bug fixes
24
+
25
+ Make sure the editor re-measures itself when its attributes are updated.
26
+
1
27
  ## 0.20.5 (2022-05-18)
2
28
 
3
29
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -754,7 +754,7 @@ function textCoords(text, pos, side) {
754
754
  from--;
755
755
  flatten = 1;
756
756
  } // FIXME this is wrong in RTL text
757
- else {
757
+ else if (to < length) {
758
758
  to++;
759
759
  flatten = -1;
760
760
  }
@@ -763,7 +763,7 @@ function textCoords(text, pos, side) {
763
763
  else {
764
764
  if (side < 0)
765
765
  from--;
766
- else
766
+ else if (to < length)
767
767
  to++;
768
768
  }
769
769
  let rects = textRange(text, from, to).getClientRects();
@@ -1063,14 +1063,16 @@ function attrsEq(a, b) {
1063
1063
  return true;
1064
1064
  }
1065
1065
  function updateAttrs(dom, prev, attrs) {
1066
+ let changed = null;
1066
1067
  if (prev)
1067
1068
  for (let name in prev)
1068
1069
  if (!(attrs && name in attrs))
1069
- dom.removeAttribute(name);
1070
+ dom.removeAttribute(changed = name);
1070
1071
  if (attrs)
1071
1072
  for (let name in attrs)
1072
1073
  if (!(prev && prev[name] == attrs[name]))
1073
- dom.setAttribute(name, attrs[name]);
1074
+ dom.setAttribute(changed = name, attrs[name]);
1075
+ return !!changed;
1074
1076
  }
1075
1077
 
1076
1078
  /**
@@ -1377,6 +1379,7 @@ class LineView extends ContentView {
1377
1379
  transferDOM(other) {
1378
1380
  if (!this.dom)
1379
1381
  return;
1382
+ this.markDirty();
1380
1383
  other.setDOM(this.dom);
1381
1384
  other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1382
1385
  this.prevAttrs = undefined;
@@ -2508,7 +2511,7 @@ class DocView extends ContentView {
2508
2511
  // no relayout is triggered and I cannot imagine how it can
2509
2512
  // recompute the scroll position without a layout)
2510
2513
  this.dom.style.height = this.view.viewState.contentHeight + "px";
2511
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2514
+ this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2512
2515
  // Chrome will sometimes, when DOM mutations occur directly
2513
2516
  // around the selection, get confused and report a different
2514
2517
  // selection from the one it displays (issue #218). This tries
@@ -3266,6 +3269,7 @@ class InputState {
3266
3269
  constructor(view) {
3267
3270
  this.lastKeyCode = 0;
3268
3271
  this.lastKeyTime = 0;
3272
+ this.chromeScrollHack = -1;
3269
3273
  // On iOS, some keys need to have their default behavior happen
3270
3274
  // (after which we retroactively handle them and reset the DOM) to
3271
3275
  // avoid messing up the virtual keyboard state.
@@ -3306,6 +3310,21 @@ class InputState {
3306
3310
  });
3307
3311
  this.registeredEvents.push(type);
3308
3312
  }
3313
+ if (browser.chrome && browser.chrome_version >= 102) {
3314
+ // On Chrome 102, viewport updates somehow stop wheel-based
3315
+ // scrolling. Turning off pointer events during the scroll seems
3316
+ // to avoid the issue.
3317
+ view.scrollDOM.addEventListener("wheel", () => {
3318
+ if (this.chromeScrollHack < 0)
3319
+ view.contentDOM.style.pointerEvents = "none";
3320
+ else
3321
+ window.clearTimeout(this.chromeScrollHack);
3322
+ this.chromeScrollHack = setTimeout(() => {
3323
+ this.chromeScrollHack = -1;
3324
+ view.contentDOM.style.pointerEvents = "";
3325
+ }, 100);
3326
+ }, { passive: true });
3327
+ }
3309
3328
  this.notifiedFocused = view.hasFocus;
3310
3329
  // On Safari adding an input event handler somehow prevents an
3311
3330
  // issue where the composition vanishes when you press enter.
@@ -4747,24 +4766,22 @@ class ViewState {
4747
4766
  this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
4748
4767
  let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
4749
4768
  let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4769
+ this.contentDOMHeight = dom.clientHeight;
4770
+ this.mustMeasureContent = false;
4750
4771
  let result = 0, bias = 0;
4772
+ // Vertical padding
4773
+ let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4774
+ if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4775
+ this.paddingTop = paddingTop;
4776
+ this.paddingBottom = paddingBottom;
4777
+ result |= 8 /* Geometry */ | 2 /* Height */;
4778
+ }
4751
4779
  if (this.editorWidth != view.scrollDOM.clientWidth) {
4752
4780
  if (oracle.lineWrapping)
4753
4781
  measureContent = true;
4754
4782
  this.editorWidth = view.scrollDOM.clientWidth;
4755
4783
  result |= 8 /* Geometry */;
4756
4784
  }
4757
- if (measureContent) {
4758
- this.mustMeasureContent = false;
4759
- this.contentDOMHeight = dom.clientHeight;
4760
- // Vertical padding
4761
- let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4762
- if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4763
- result |= 8 /* Geometry */;
4764
- this.paddingTop = paddingTop;
4765
- this.paddingBottom = paddingBottom;
4766
- }
4767
- }
4768
4785
  // Pixel viewport
4769
4786
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
4770
4787
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5564,21 +5581,16 @@ class DOMObserver {
5564
5581
  // composition events that, when interrupted, cause text duplication
5565
5582
  // or other kinds of corruption. This hack makes the editor back off
5566
5583
  // from handling DOM changes for a moment when such a key is
5567
- // detected (via beforeinput or keydown), and then dispatches the
5568
- // key event, throwing away the DOM changes if it gets handled.
5584
+ // detected (via beforeinput or keydown), and then tries to flush
5585
+ // them or, if that has no effect, dispatches the given key.
5569
5586
  delayAndroidKey(key, keyCode) {
5570
5587
  if (!this.delayedAndroidKey)
5571
5588
  requestAnimationFrame(() => {
5572
5589
  let key = this.delayedAndroidKey;
5573
5590
  this.delayedAndroidKey = null;
5574
- let startState = this.view.state;
5575
- this.readSelectionRange();
5576
- if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
5577
- this.processRecords();
5578
- else
5579
- this.flush();
5580
- if (this.view.state == startState)
5581
- this.view.update([]);
5591
+ this.delayedFlush = -1;
5592
+ if (!this.flush())
5593
+ dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5582
5594
  });
5583
5595
  // Since backspace beforeinput is sometimes signalled spuriously,
5584
5596
  // Enter always takes precedence.
@@ -5634,10 +5646,11 @@ class DOMObserver {
5634
5646
  return;
5635
5647
  this.selectionChanged = false;
5636
5648
  let startState = this.view.state;
5637
- this.onChange(from, to, typeOver);
5649
+ let handled = this.onChange(from, to, typeOver);
5638
5650
  // The view wasn't updated
5639
5651
  if (this.view.state == startState)
5640
5652
  this.view.update([]);
5653
+ return handled;
5641
5654
  }
5642
5655
  readMutation(rec) {
5643
5656
  let cView = this.view.docView.nearest(rec.target);
@@ -5720,7 +5733,7 @@ function applyDOMChange(view, start, end, typeOver) {
5720
5733
  if (start > -1) {
5721
5734
  let bounds = view.docView.domBoundsAround(start, end, 0);
5722
5735
  if (!bounds || view.state.readOnly)
5723
- return;
5736
+ return false;
5724
5737
  let { from, to } = bounds;
5725
5738
  let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
5726
5739
  let reader = new DOMReader(selPoints, view.state);
@@ -5760,7 +5773,7 @@ function applyDOMChange(view, start, end, typeOver) {
5760
5773
  newSel = state.EditorSelection.single(anchor, head);
5761
5774
  }
5762
5775
  if (!change && !newSel)
5763
- return;
5776
+ return false;
5764
5777
  // Heuristic to notice typing over a selected character
5765
5778
  if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
5766
5779
  change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
@@ -5776,13 +5789,13 @@ function applyDOMChange(view, start, end, typeOver) {
5776
5789
  };
5777
5790
  // Detect insert-period-on-double-space Mac behavior, and transform
5778
5791
  // it into a regular space insert.
5779
- else if (browser.mac && change && change.from == change.to && change.from == sel.head - 1 &&
5792
+ else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5780
5793
  change.insert.toString() == ".")
5781
5794
  change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
5782
5795
  if (change) {
5783
5796
  let startState = view.state;
5784
5797
  if (browser.ios && view.inputState.flushIOSKey(view))
5785
- return;
5798
+ return true;
5786
5799
  // Android browsers don't fire reasonable key events for enter,
5787
5800
  // backspace, or delete. So this detects changes that look like
5788
5801
  // they're caused by those keys, and reinterprets them as key
@@ -5797,10 +5810,10 @@ function applyDOMChange(view, start, end, typeOver) {
5797
5810
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5798
5811
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5799
5812
  dispatchKey(view.contentDOM, "Delete", 46))))
5800
- return;
5813
+ return true;
5801
5814
  let text = change.insert.toString();
5802
5815
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
5803
- return;
5816
+ return true;
5804
5817
  if (view.inputState.composing >= 0)
5805
5818
  view.inputState.composing++;
5806
5819
  let tr;
@@ -5856,6 +5869,7 @@ function applyDOMChange(view, start, end, typeOver) {
5856
5869
  }
5857
5870
  }
5858
5871
  view.dispatch(tr, { scrollIntoView: true, userEvent });
5872
+ return true;
5859
5873
  }
5860
5874
  else if (newSel && !newSel.main.eq(sel)) {
5861
5875
  let scrollIntoView = false, userEvent = "select";
@@ -5865,6 +5879,10 @@ function applyDOMChange(view, start, end, typeOver) {
5865
5879
  userEvent = view.inputState.lastSelectionOrigin;
5866
5880
  }
5867
5881
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
5882
+ return true;
5883
+ }
5884
+ else {
5885
+ return false;
5868
5886
  }
5869
5887
  }
5870
5888
  function findDiff(a, b, preferredPos, preferredSide) {
@@ -5941,11 +5959,7 @@ class EditorView {
5941
5959
  option, or put `view.dom` into your document after creating a
5942
5960
  view, so that the user can see the editor.
5943
5961
  */
5944
- constructor(
5945
- /**
5946
- Initialization options.
5947
- */
5948
- config = {}) {
5962
+ constructor(config = {}) {
5949
5963
  this.plugins = [];
5950
5964
  this.pluginMap = new Map;
5951
5965
  this.editorAttrs = {};
@@ -5978,12 +5992,12 @@ class EditorView {
5978
5992
  this._dispatch = config.dispatch || ((tr) => this.update([tr]));
5979
5993
  this.dispatch = this.dispatch.bind(this);
5980
5994
  this.root = (config.root || getRoot(config.parent) || document);
5981
- this.viewState = new ViewState(config.state || state.EditorState.create());
5995
+ this.viewState = new ViewState(config.state || state.EditorState.create(config));
5982
5996
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
5983
5997
  for (let plugin of this.plugins)
5984
5998
  plugin.update(this);
5985
5999
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5986
- applyDOMChange(this, from, to, typeOver);
6000
+ return applyDOMChange(this, from, to, typeOver);
5987
6001
  }, event => {
5988
6002
  this.inputState.runScrollHandlers(this, event);
5989
6003
  if (this.observer.intersecting)
@@ -6053,7 +6067,7 @@ class EditorView {
6053
6067
  update(transactions) {
6054
6068
  if (this.updateState != 0 /* Idle */)
6055
6069
  throw new Error("Calls to EditorView.update are not allowed while an update is in progress");
6056
- let redrawn = false, update;
6070
+ let redrawn = false, attrsChanged = false, update;
6057
6071
  let state$1 = this.state;
6058
6072
  for (let tr of transactions) {
6059
6073
  if (tr.startState != state$1)
@@ -6064,6 +6078,7 @@ class EditorView {
6064
6078
  this.viewState.state = state$1;
6065
6079
  return;
6066
6080
  }
6081
+ this.observer.clear();
6067
6082
  // When the phrases change, redraw the editor
6068
6083
  if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
6069
6084
  return this.setState(state$1);
@@ -6091,7 +6106,7 @@ class EditorView {
6091
6106
  redrawn = this.docView.update(update);
6092
6107
  if (this.state.facet(styleModule) != this.styleModules)
6093
6108
  this.mountStyles();
6094
- this.updateAttrs();
6109
+ attrsChanged = this.updateAttrs();
6095
6110
  this.showAnnouncements(transactions);
6096
6111
  this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
6097
6112
  }
@@ -6100,7 +6115,7 @@ class EditorView {
6100
6115
  }
6101
6116
  if (update.startState.facet(theme) != update.state.facet(theme))
6102
6117
  this.viewState.mustMeasureContent = true;
6103
- if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6118
+ if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6104
6119
  this.requestMeasure();
6105
6120
  if (!update.empty)
6106
6121
  for (let listener of this.state.facet(updateListener))
@@ -6280,12 +6295,14 @@ class EditorView {
6280
6295
  if (this.state.readOnly)
6281
6296
  contentAttrs["aria-readonly"] = "true";
6282
6297
  attrsFromFacet(this, contentAttributes, contentAttrs);
6283
- this.observer.ignore(() => {
6284
- updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6285
- updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6298
+ let changed = this.observer.ignore(() => {
6299
+ let changedContent = updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6300
+ let changedEditor = updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6301
+ return changedContent || changedEditor;
6286
6302
  });
6287
6303
  this.editorAttrs = editorAttrs;
6288
6304
  this.contentAttrs = contentAttrs;
6305
+ return changed;
6289
6306
  }
6290
6307
  showAnnouncements(trs) {
6291
6308
  let first = true;
@@ -6650,6 +6667,16 @@ class EditorView {
6650
6667
  static baseTheme(spec) {
6651
6668
  return state.Prec.lowest(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs)));
6652
6669
  }
6670
+ /**
6671
+ Retrieve an editor view instance from the view's DOM
6672
+ representation.
6673
+ */
6674
+ static findFromDOM(dom) {
6675
+ var _a;
6676
+ let content = dom.querySelector(".cm-content");
6677
+ let cView = content && ContentView.get(content) || ContentView.get(dom);
6678
+ return ((_a = cView === null || cView === void 0 ? void 0 : cView.rootView) === null || _a === void 0 ? void 0 : _a.view) || null;
6679
+ }
6653
6680
  }
6654
6681
  /**
6655
6682
  Facet to add a [style
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, TransactionSpec, SelectionRange, Line, StateEffect, Facet } from '@codemirror/state';
2
+ import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, EditorSelection, EditorStateConfig, TransactionSpec, SelectionRange, Line, StateEffect, Facet } from '@codemirror/state';
3
3
  import { StyleModule, StyleSpec } from 'style-mod';
4
4
 
5
5
  declare type Attrs = {
@@ -559,10 +559,16 @@ declare class BidiSpan {
559
559
  get dir(): Direction;
560
560
  }
561
561
 
562
- interface EditorConfig {
562
+ /**
563
+ The type of object given to the [`EditorView`](https://codemirror.net/6/docs/ref/#view.EditorView)
564
+ constructor.
565
+ */
566
+ interface EditorViewConfig extends EditorStateConfig {
563
567
  /**
564
- The view's initial state. Defaults to an extension-less state
565
- with an empty document.
568
+ The view's initial state. If not given, a new state is created
569
+ by passing this configuration object to
570
+ [`EditorState.create`](https://codemirror.net/6/docs/ref/#state.EditorState^create), using its
571
+ `doc`, `selection`, and `extensions` field (if provided).
566
572
  */
567
573
  state?: EditorState;
568
574
  /**
@@ -676,11 +682,7 @@ declare class EditorView {
676
682
  option, or put `view.dom` into your document after creating a
677
683
  view, so that the user can see the editor.
678
684
  */
679
- constructor(
680
- /**
681
- Initialization options.
682
- */
683
- config?: EditorConfig);
685
+ constructor(config?: EditorViewConfig);
684
686
  /**
685
687
  All regular editor state updates should go through this. It
686
688
  takes a transaction or transaction spec and updates the view to
@@ -1131,6 +1133,11 @@ declare class EditorView {
1131
1133
  search match).
1132
1134
  */
1133
1135
  static announce: _codemirror_state.StateEffectType<string>;
1136
+ /**
1137
+ Retrieve an editor view instance from the view's DOM
1138
+ representation.
1139
+ */
1140
+ static findFromDOM(dom: HTMLElement): EditorView | null;
1134
1141
  }
1135
1142
  /**
1136
1143
  Helper type that maps event names to event object types, or the
@@ -1783,4 +1790,4 @@ line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
1783
1790
  */
1784
1791
  declare function highlightActiveLineGutter(): Extension;
1785
1792
 
1786
- export { BidiSpan, BlockInfo, BlockType, Command, DOMEventHandlers, DOMEventMap, Decoration, DecorationSet, Direction, EditorView, GutterMarker, KeyBinding, MatchDecorator, MouseSelectionStyle, Panel, PanelConstructor, PluginSpec, PluginValue, Rect, Tooltip, TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
1793
+ export { BidiSpan, BlockInfo, BlockType, Command, DOMEventHandlers, DOMEventMap, Decoration, DecorationSet, Direction, EditorView, EditorViewConfig, GutterMarker, KeyBinding, MatchDecorator, MouseSelectionStyle, Panel, PanelConstructor, PluginSpec, PluginValue, Rect, Tooltip, TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
package/dist/index.js CHANGED
@@ -750,7 +750,7 @@ function textCoords(text, pos, side) {
750
750
  from--;
751
751
  flatten = 1;
752
752
  } // FIXME this is wrong in RTL text
753
- else {
753
+ else if (to < length) {
754
754
  to++;
755
755
  flatten = -1;
756
756
  }
@@ -759,7 +759,7 @@ function textCoords(text, pos, side) {
759
759
  else {
760
760
  if (side < 0)
761
761
  from--;
762
- else
762
+ else if (to < length)
763
763
  to++;
764
764
  }
765
765
  let rects = textRange(text, from, to).getClientRects();
@@ -1059,14 +1059,16 @@ function attrsEq(a, b) {
1059
1059
  return true;
1060
1060
  }
1061
1061
  function updateAttrs(dom, prev, attrs) {
1062
+ let changed = null;
1062
1063
  if (prev)
1063
1064
  for (let name in prev)
1064
1065
  if (!(attrs && name in attrs))
1065
- dom.removeAttribute(name);
1066
+ dom.removeAttribute(changed = name);
1066
1067
  if (attrs)
1067
1068
  for (let name in attrs)
1068
1069
  if (!(prev && prev[name] == attrs[name]))
1069
- dom.setAttribute(name, attrs[name]);
1070
+ dom.setAttribute(changed = name, attrs[name]);
1071
+ return !!changed;
1070
1072
  }
1071
1073
 
1072
1074
  /**
@@ -1372,6 +1374,7 @@ class LineView extends ContentView {
1372
1374
  transferDOM(other) {
1373
1375
  if (!this.dom)
1374
1376
  return;
1377
+ this.markDirty();
1375
1378
  other.setDOM(this.dom);
1376
1379
  other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1377
1380
  this.prevAttrs = undefined;
@@ -2502,7 +2505,7 @@ class DocView extends ContentView {
2502
2505
  // no relayout is triggered and I cannot imagine how it can
2503
2506
  // recompute the scroll position without a layout)
2504
2507
  this.dom.style.height = this.view.viewState.contentHeight + "px";
2505
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2508
+ this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2506
2509
  // Chrome will sometimes, when DOM mutations occur directly
2507
2510
  // around the selection, get confused and report a different
2508
2511
  // selection from the one it displays (issue #218). This tries
@@ -3260,6 +3263,7 @@ class InputState {
3260
3263
  constructor(view) {
3261
3264
  this.lastKeyCode = 0;
3262
3265
  this.lastKeyTime = 0;
3266
+ this.chromeScrollHack = -1;
3263
3267
  // On iOS, some keys need to have their default behavior happen
3264
3268
  // (after which we retroactively handle them and reset the DOM) to
3265
3269
  // avoid messing up the virtual keyboard state.
@@ -3300,6 +3304,21 @@ class InputState {
3300
3304
  });
3301
3305
  this.registeredEvents.push(type);
3302
3306
  }
3307
+ if (browser.chrome && browser.chrome_version >= 102) {
3308
+ // On Chrome 102, viewport updates somehow stop wheel-based
3309
+ // scrolling. Turning off pointer events during the scroll seems
3310
+ // to avoid the issue.
3311
+ view.scrollDOM.addEventListener("wheel", () => {
3312
+ if (this.chromeScrollHack < 0)
3313
+ view.contentDOM.style.pointerEvents = "none";
3314
+ else
3315
+ window.clearTimeout(this.chromeScrollHack);
3316
+ this.chromeScrollHack = setTimeout(() => {
3317
+ this.chromeScrollHack = -1;
3318
+ view.contentDOM.style.pointerEvents = "";
3319
+ }, 100);
3320
+ }, { passive: true });
3321
+ }
3303
3322
  this.notifiedFocused = view.hasFocus;
3304
3323
  // On Safari adding an input event handler somehow prevents an
3305
3324
  // issue where the composition vanishes when you press enter.
@@ -4740,24 +4759,22 @@ class ViewState {
4740
4759
  this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
4741
4760
  let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
4742
4761
  let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4762
+ this.contentDOMHeight = dom.clientHeight;
4763
+ this.mustMeasureContent = false;
4743
4764
  let result = 0, bias = 0;
4765
+ // Vertical padding
4766
+ let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4767
+ if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4768
+ this.paddingTop = paddingTop;
4769
+ this.paddingBottom = paddingBottom;
4770
+ result |= 8 /* Geometry */ | 2 /* Height */;
4771
+ }
4744
4772
  if (this.editorWidth != view.scrollDOM.clientWidth) {
4745
4773
  if (oracle.lineWrapping)
4746
4774
  measureContent = true;
4747
4775
  this.editorWidth = view.scrollDOM.clientWidth;
4748
4776
  result |= 8 /* Geometry */;
4749
4777
  }
4750
- if (measureContent) {
4751
- this.mustMeasureContent = false;
4752
- this.contentDOMHeight = dom.clientHeight;
4753
- // Vertical padding
4754
- let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4755
- if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4756
- result |= 8 /* Geometry */;
4757
- this.paddingTop = paddingTop;
4758
- this.paddingBottom = paddingBottom;
4759
- }
4760
- }
4761
4778
  // Pixel viewport
4762
4779
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
4763
4780
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5557,21 +5574,16 @@ class DOMObserver {
5557
5574
  // composition events that, when interrupted, cause text duplication
5558
5575
  // or other kinds of corruption. This hack makes the editor back off
5559
5576
  // from handling DOM changes for a moment when such a key is
5560
- // detected (via beforeinput or keydown), and then dispatches the
5561
- // key event, throwing away the DOM changes if it gets handled.
5577
+ // detected (via beforeinput or keydown), and then tries to flush
5578
+ // them or, if that has no effect, dispatches the given key.
5562
5579
  delayAndroidKey(key, keyCode) {
5563
5580
  if (!this.delayedAndroidKey)
5564
5581
  requestAnimationFrame(() => {
5565
5582
  let key = this.delayedAndroidKey;
5566
5583
  this.delayedAndroidKey = null;
5567
- let startState = this.view.state;
5568
- this.readSelectionRange();
5569
- if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
5570
- this.processRecords();
5571
- else
5572
- this.flush();
5573
- if (this.view.state == startState)
5574
- this.view.update([]);
5584
+ this.delayedFlush = -1;
5585
+ if (!this.flush())
5586
+ dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5575
5587
  });
5576
5588
  // Since backspace beforeinput is sometimes signalled spuriously,
5577
5589
  // Enter always takes precedence.
@@ -5627,10 +5639,11 @@ class DOMObserver {
5627
5639
  return;
5628
5640
  this.selectionChanged = false;
5629
5641
  let startState = this.view.state;
5630
- this.onChange(from, to, typeOver);
5642
+ let handled = this.onChange(from, to, typeOver);
5631
5643
  // The view wasn't updated
5632
5644
  if (this.view.state == startState)
5633
5645
  this.view.update([]);
5646
+ return handled;
5634
5647
  }
5635
5648
  readMutation(rec) {
5636
5649
  let cView = this.view.docView.nearest(rec.target);
@@ -5713,7 +5726,7 @@ function applyDOMChange(view, start, end, typeOver) {
5713
5726
  if (start > -1) {
5714
5727
  let bounds = view.docView.domBoundsAround(start, end, 0);
5715
5728
  if (!bounds || view.state.readOnly)
5716
- return;
5729
+ return false;
5717
5730
  let { from, to } = bounds;
5718
5731
  let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
5719
5732
  let reader = new DOMReader(selPoints, view.state);
@@ -5753,7 +5766,7 @@ function applyDOMChange(view, start, end, typeOver) {
5753
5766
  newSel = EditorSelection.single(anchor, head);
5754
5767
  }
5755
5768
  if (!change && !newSel)
5756
- return;
5769
+ return false;
5757
5770
  // Heuristic to notice typing over a selected character
5758
5771
  if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
5759
5772
  change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
@@ -5769,13 +5782,13 @@ function applyDOMChange(view, start, end, typeOver) {
5769
5782
  };
5770
5783
  // Detect insert-period-on-double-space Mac behavior, and transform
5771
5784
  // it into a regular space insert.
5772
- else if (browser.mac && change && change.from == change.to && change.from == sel.head - 1 &&
5785
+ else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5773
5786
  change.insert.toString() == ".")
5774
5787
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
5775
5788
  if (change) {
5776
5789
  let startState = view.state;
5777
5790
  if (browser.ios && view.inputState.flushIOSKey(view))
5778
- return;
5791
+ return true;
5779
5792
  // Android browsers don't fire reasonable key events for enter,
5780
5793
  // backspace, or delete. So this detects changes that look like
5781
5794
  // they're caused by those keys, and reinterprets them as key
@@ -5790,10 +5803,10 @@ function applyDOMChange(view, start, end, typeOver) {
5790
5803
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5791
5804
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5792
5805
  dispatchKey(view.contentDOM, "Delete", 46))))
5793
- return;
5806
+ return true;
5794
5807
  let text = change.insert.toString();
5795
5808
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
5796
- return;
5809
+ return true;
5797
5810
  if (view.inputState.composing >= 0)
5798
5811
  view.inputState.composing++;
5799
5812
  let tr;
@@ -5849,6 +5862,7 @@ function applyDOMChange(view, start, end, typeOver) {
5849
5862
  }
5850
5863
  }
5851
5864
  view.dispatch(tr, { scrollIntoView: true, userEvent });
5865
+ return true;
5852
5866
  }
5853
5867
  else if (newSel && !newSel.main.eq(sel)) {
5854
5868
  let scrollIntoView = false, userEvent = "select";
@@ -5858,6 +5872,10 @@ function applyDOMChange(view, start, end, typeOver) {
5858
5872
  userEvent = view.inputState.lastSelectionOrigin;
5859
5873
  }
5860
5874
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
5875
+ return true;
5876
+ }
5877
+ else {
5878
+ return false;
5861
5879
  }
5862
5880
  }
5863
5881
  function findDiff(a, b, preferredPos, preferredSide) {
@@ -5934,11 +5952,7 @@ class EditorView {
5934
5952
  option, or put `view.dom` into your document after creating a
5935
5953
  view, so that the user can see the editor.
5936
5954
  */
5937
- constructor(
5938
- /**
5939
- Initialization options.
5940
- */
5941
- config = {}) {
5955
+ constructor(config = {}) {
5942
5956
  this.plugins = [];
5943
5957
  this.pluginMap = new Map;
5944
5958
  this.editorAttrs = {};
@@ -5971,12 +5985,12 @@ class EditorView {
5971
5985
  this._dispatch = config.dispatch || ((tr) => this.update([tr]));
5972
5986
  this.dispatch = this.dispatch.bind(this);
5973
5987
  this.root = (config.root || getRoot(config.parent) || document);
5974
- this.viewState = new ViewState(config.state || EditorState.create());
5988
+ this.viewState = new ViewState(config.state || EditorState.create(config));
5975
5989
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
5976
5990
  for (let plugin of this.plugins)
5977
5991
  plugin.update(this);
5978
5992
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5979
- applyDOMChange(this, from, to, typeOver);
5993
+ return applyDOMChange(this, from, to, typeOver);
5980
5994
  }, event => {
5981
5995
  this.inputState.runScrollHandlers(this, event);
5982
5996
  if (this.observer.intersecting)
@@ -6046,7 +6060,7 @@ class EditorView {
6046
6060
  update(transactions) {
6047
6061
  if (this.updateState != 0 /* Idle */)
6048
6062
  throw new Error("Calls to EditorView.update are not allowed while an update is in progress");
6049
- let redrawn = false, update;
6063
+ let redrawn = false, attrsChanged = false, update;
6050
6064
  let state = this.state;
6051
6065
  for (let tr of transactions) {
6052
6066
  if (tr.startState != state)
@@ -6057,6 +6071,7 @@ class EditorView {
6057
6071
  this.viewState.state = state;
6058
6072
  return;
6059
6073
  }
6074
+ this.observer.clear();
6060
6075
  // When the phrases change, redraw the editor
6061
6076
  if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
6062
6077
  return this.setState(state);
@@ -6084,7 +6099,7 @@ class EditorView {
6084
6099
  redrawn = this.docView.update(update);
6085
6100
  if (this.state.facet(styleModule) != this.styleModules)
6086
6101
  this.mountStyles();
6087
- this.updateAttrs();
6102
+ attrsChanged = this.updateAttrs();
6088
6103
  this.showAnnouncements(transactions);
6089
6104
  this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
6090
6105
  }
@@ -6093,7 +6108,7 @@ class EditorView {
6093
6108
  }
6094
6109
  if (update.startState.facet(theme) != update.state.facet(theme))
6095
6110
  this.viewState.mustMeasureContent = true;
6096
- if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6111
+ if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6097
6112
  this.requestMeasure();
6098
6113
  if (!update.empty)
6099
6114
  for (let listener of this.state.facet(updateListener))
@@ -6273,12 +6288,14 @@ class EditorView {
6273
6288
  if (this.state.readOnly)
6274
6289
  contentAttrs["aria-readonly"] = "true";
6275
6290
  attrsFromFacet(this, contentAttributes, contentAttrs);
6276
- this.observer.ignore(() => {
6277
- updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6278
- updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6291
+ let changed = this.observer.ignore(() => {
6292
+ let changedContent = updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6293
+ let changedEditor = updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6294
+ return changedContent || changedEditor;
6279
6295
  });
6280
6296
  this.editorAttrs = editorAttrs;
6281
6297
  this.contentAttrs = contentAttrs;
6298
+ return changed;
6282
6299
  }
6283
6300
  showAnnouncements(trs) {
6284
6301
  let first = true;
@@ -6643,6 +6660,16 @@ class EditorView {
6643
6660
  static baseTheme(spec) {
6644
6661
  return Prec.lowest(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs)));
6645
6662
  }
6663
+ /**
6664
+ Retrieve an editor view instance from the view's DOM
6665
+ representation.
6666
+ */
6667
+ static findFromDOM(dom) {
6668
+ var _a;
6669
+ let content = dom.querySelector(".cm-content");
6670
+ let cView = content && ContentView.get(content) || ContentView.get(dom);
6671
+ return ((_a = cView === null || cView === void 0 ? void 0 : cView.rootView) === null || _a === void 0 ? void 0 : _a.view) || null;
6672
+ }
6646
6673
  }
6647
6674
  /**
6648
6675
  Facet to add a [style
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.20.5",
3
+ "version": "6.0.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -26,7 +26,7 @@
26
26
  "sideEffects": false,
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@codemirror/state": "^0.20.0",
29
+ "@codemirror/state": "^6.0.0",
30
30
  "style-mod": "^4.0.0",
31
31
  "w3c-keyname": "^2.2.4"
32
32
  },