@codemirror/view 6.7.2 → 6.8.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,23 @@
1
+ ## 6.8.0 (2023-02-07)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a regression that caused clicking on the scrollbar to move the selection.
6
+
7
+ Fix an issue where focus or blur event handlers that dispatched editor transactions could corrupt the mouse selection state.
8
+
9
+ Fix a CSS regression that prevented the drop cursor from being positioned properly.
10
+
11
+ ### New features
12
+
13
+ `WidgetType.updateDOM` is now passed the editor view object.
14
+
15
+ ## 6.7.3 (2023-01-12)
16
+
17
+ ### Bug fixes
18
+
19
+ Fix a bug in `posAtCoords` that could cause incorrect results for positions to the left of a wrapped line.
20
+
1
21
  ## 6.7.2 (2023-01-04)
2
22
 
3
23
  ### Bug fixes
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (C) 2018-2021 by Marijn Haverbeke <marijnh@gmail.com> and others
3
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.cjs CHANGED
@@ -334,11 +334,6 @@ class ContentView {
334
334
  this.dom = null;
335
335
  this.dirty = 2 /* Dirty.Node */;
336
336
  }
337
- get editorView() {
338
- if (!this.parent)
339
- throw new Error("Accessing view in orphan content view");
340
- return this.parent.editorView;
341
- }
342
337
  get overrideDOMText() { return null; }
343
338
  get posAtStart() {
344
339
  return this.parent ? this.parent.posBefore(this) : 0;
@@ -362,7 +357,7 @@ class ContentView {
362
357
  // (side > 0) or directly on (when the browser supports it) the
363
358
  // given position.
364
359
  coordsAt(_pos, _side) { return null; }
365
- sync(track) {
360
+ sync(view, track) {
366
361
  if (this.dirty & 2 /* Dirty.Node */) {
367
362
  let parent = this.dom;
368
363
  let prev = null, next;
@@ -373,7 +368,7 @@ class ContentView {
373
368
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
374
369
  child.reuseDOM(next);
375
370
  }
376
- child.sync(track);
371
+ child.sync(view, track);
377
372
  child.dirty = 0 /* Dirty.Not */;
378
373
  }
379
374
  next = prev ? prev.nextSibling : parent.firstChild;
@@ -397,7 +392,7 @@ class ContentView {
397
392
  else if (this.dirty & 1 /* Dirty.Child */) {
398
393
  for (let child of this.children)
399
394
  if (child.dirty) {
400
- child.sync(track);
395
+ child.sync(view, track);
401
396
  child.dirty = 0 /* Dirty.Not */;
402
397
  }
403
398
  }
@@ -691,7 +686,7 @@ class TextView extends ContentView {
691
686
  createDOM(textDOM) {
692
687
  this.setDOM(textDOM || document.createTextNode(this.text));
693
688
  }
694
- sync(track) {
689
+ sync(view, track) {
695
690
  if (!this.dom)
696
691
  this.createDOM();
697
692
  if (this.dom.nodeValue != this.text) {
@@ -752,12 +747,12 @@ class MarkView extends ContentView {
752
747
  this.dirty |= 4 /* Dirty.Attrs */ | 2 /* Dirty.Node */;
753
748
  }
754
749
  }
755
- sync(track) {
750
+ sync(view, track) {
756
751
  if (!this.dom)
757
752
  this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
758
753
  else if (this.dirty & 4 /* Dirty.Attrs */)
759
754
  this.setAttrs(this.dom);
760
- super.sync(track);
755
+ super.sync(view, track);
761
756
  }
762
757
  merge(from, to, source, _hasStart, openStart, openEnd) {
763
758
  if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
@@ -841,12 +836,12 @@ class WidgetView extends ContentView {
841
836
  this.length -= from;
842
837
  return result;
843
838
  }
844
- sync() {
845
- if (!this.dom || !this.widget.updateDOM(this.dom)) {
839
+ sync(view) {
840
+ if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
846
841
  if (this.dom && this.prevWidget)
847
842
  this.prevWidget.destroy(this.dom);
848
843
  this.prevWidget = null;
849
- this.setDOM(this.widget.toDOM(this.editorView));
844
+ this.setDOM(this.widget.toDOM(view));
850
845
  this.dom.contentEditable = "false";
851
846
  }
852
847
  }
@@ -879,7 +874,7 @@ class WidgetView extends ContentView {
879
874
  let top = this;
880
875
  while (top.parent)
881
876
  top = top.parent;
882
- let view = top.editorView, text = view && view.state.doc, start = this.posAtStart;
877
+ let { view } = top, text = view && view.state.doc, start = this.posAtStart;
883
878
  return text ? text.slice(start, start + this.length) : state.Text.empty;
884
879
  }
885
880
  domAtPos(pos) {
@@ -1171,7 +1166,7 @@ class WidgetType {
1171
1166
  couldn't (in which case the widget will be redrawn). The default
1172
1167
  implementation just returns false.
1173
1168
  */
1174
- updateDOM(dom) { return false; }
1169
+ updateDOM(dom, view) { return false; }
1175
1170
  /**
1176
1171
  @internal
1177
1172
  */
@@ -1487,7 +1482,7 @@ class LineView extends ContentView {
1487
1482
  this.dirty |= 4 /* Dirty.Attrs */ | 2 /* Dirty.Node */;
1488
1483
  }
1489
1484
  }
1490
- sync(track) {
1485
+ sync(view, track) {
1491
1486
  var _a;
1492
1487
  if (!this.dom) {
1493
1488
  this.setDOM(document.createElement("div"));
@@ -1504,7 +1499,7 @@ class LineView extends ContentView {
1504
1499
  this.dom.classList.add("cm-line");
1505
1500
  this.prevAttrs = undefined;
1506
1501
  }
1507
- super.sync(track);
1502
+ super.sync(view, track);
1508
1503
  let last = this.dom.lastChild;
1509
1504
  while (last && ContentView.get(last) instanceof MarkView)
1510
1505
  last = last.lastChild;
@@ -1579,12 +1574,12 @@ class BlockWidgetView extends ContentView {
1579
1574
  return end;
1580
1575
  }
1581
1576
  get children() { return noChildren; }
1582
- sync() {
1583
- if (!this.dom || !this.widget.updateDOM(this.dom)) {
1577
+ sync(view) {
1578
+ if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
1584
1579
  if (this.dom && this.prevWidget)
1585
1580
  this.prevWidget.destroy(this.dom);
1586
1581
  this.prevWidget = null;
1587
- this.setDOM(this.widget.toDOM(this.editorView));
1582
+ this.setDOM(this.widget.toDOM(view));
1588
1583
  this.dom.contentEditable = "false";
1589
1584
  }
1590
1585
  }
@@ -2539,7 +2534,6 @@ class DocView extends ContentView {
2539
2534
  this.updateDeco();
2540
2535
  this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2541
2536
  }
2542
- get editorView() { return this.view; }
2543
2537
  get length() { return this.view.state.doc.length; }
2544
2538
  // Update the document view to a given state. scrollIntoView can be
2545
2539
  // used as a hint to compute a new viewport that includes that
@@ -2599,7 +2593,7 @@ class DocView extends ContentView {
2599
2593
  // selection from the one it displays (issue #218). This tries
2600
2594
  // to detect that situation.
2601
2595
  let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2602
- this.sync(track);
2596
+ this.sync(this.view, track);
2603
2597
  this.dirty = 0 /* Dirty.Not */;
2604
2598
  if (track && (track.written || observer.selectionRange.focusNode != track.node))
2605
2599
  this.forceSelection = true;
@@ -3102,7 +3096,8 @@ function domPosAtCoords(parent, x, y) {
3102
3096
  closestRect = rect;
3103
3097
  closestX = dx;
3104
3098
  closestY = dy;
3105
- closestOverlap = !dx || (dx > 0 ? i < rects.length - 1 : i > 0);
3099
+ let side = dy ? (y < rect.top ? -1 : 1) : dx ? (x < rect.left ? -1 : 1) : 0;
3100
+ closestOverlap = !side || (side > 0 ? i < rects.length - 1 : i > 0);
3106
3101
  }
3107
3102
  if (dx == 0) {
3108
3103
  if (y > rect.bottom && (!aboveRect || aboveRect.bottom < rect.bottom)) {
@@ -3434,7 +3429,7 @@ class InputState {
3434
3429
  this.registeredEvents.push(type);
3435
3430
  }
3436
3431
  view.scrollDOM.addEventListener("mousedown", (event) => {
3437
- if (event.target == view.scrollDOM)
3432
+ if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
3438
3433
  handleEvent(handlers.mousedown, event);
3439
3434
  });
3440
3435
  if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
@@ -3612,11 +3607,13 @@ class MouseSelection {
3612
3607
  this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
3613
3608
  this.dragMove = dragMovesSelection(view, startEvent);
3614
3609
  this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
3610
+ }
3611
+ start(event) {
3615
3612
  // When clicking outside of the selection, immediately apply the
3616
3613
  // effect of starting the selection
3617
3614
  if (this.dragging === false) {
3618
- startEvent.preventDefault();
3619
- this.select(startEvent);
3615
+ event.preventDefault();
3616
+ this.select(event);
3620
3617
  }
3621
3618
  }
3622
3619
  move(event) {
@@ -3808,9 +3805,11 @@ handlers.mousedown = (view, event) => {
3808
3805
  style = basicMouseSelection(view, event);
3809
3806
  if (style) {
3810
3807
  let mustFocus = view.root.activeElement != view.contentDOM;
3808
+ view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3811
3809
  if (mustFocus)
3812
3810
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
3813
- view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3811
+ if (view.inputState.mouseSelection)
3812
+ view.inputState.mouseSelection.start(event);
3814
3813
  }
3815
3814
  };
3816
3815
  function rangeForClick(view, pos, bias, type) {
@@ -5421,6 +5420,9 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5421
5420
  "&dark .cm-cursor": {
5422
5421
  borderLeftColor: "#444"
5423
5422
  },
5423
+ ".cm-dropCursor": {
5424
+ position: "absolute"
5425
+ },
5424
5426
  "&.cm-focused .cm-cursor": {
5425
5427
  display: "block"
5426
5428
  },
package/dist/index.d.ts CHANGED
@@ -154,7 +154,7 @@ declare abstract class WidgetType {
154
154
  couldn't (in which case the widget will be redrawn). The default
155
155
  implementation just returns false.
156
156
  */
157
- updateDOM(dom: HTMLElement): boolean;
157
+ updateDOM(dom: HTMLElement, view: EditorView): boolean;
158
158
  /**
159
159
  The estimated height this widget will have, to be used when
160
160
  estimating the height of content that hasn't been drawn. May
package/dist/index.js CHANGED
@@ -330,11 +330,6 @@ class ContentView {
330
330
  this.dom = null;
331
331
  this.dirty = 2 /* Dirty.Node */;
332
332
  }
333
- get editorView() {
334
- if (!this.parent)
335
- throw new Error("Accessing view in orphan content view");
336
- return this.parent.editorView;
337
- }
338
333
  get overrideDOMText() { return null; }
339
334
  get posAtStart() {
340
335
  return this.parent ? this.parent.posBefore(this) : 0;
@@ -358,7 +353,7 @@ class ContentView {
358
353
  // (side > 0) or directly on (when the browser supports it) the
359
354
  // given position.
360
355
  coordsAt(_pos, _side) { return null; }
361
- sync(track) {
356
+ sync(view, track) {
362
357
  if (this.dirty & 2 /* Dirty.Node */) {
363
358
  let parent = this.dom;
364
359
  let prev = null, next;
@@ -369,7 +364,7 @@ class ContentView {
369
364
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
370
365
  child.reuseDOM(next);
371
366
  }
372
- child.sync(track);
367
+ child.sync(view, track);
373
368
  child.dirty = 0 /* Dirty.Not */;
374
369
  }
375
370
  next = prev ? prev.nextSibling : parent.firstChild;
@@ -393,7 +388,7 @@ class ContentView {
393
388
  else if (this.dirty & 1 /* Dirty.Child */) {
394
389
  for (let child of this.children)
395
390
  if (child.dirty) {
396
- child.sync(track);
391
+ child.sync(view, track);
397
392
  child.dirty = 0 /* Dirty.Not */;
398
393
  }
399
394
  }
@@ -687,7 +682,7 @@ class TextView extends ContentView {
687
682
  createDOM(textDOM) {
688
683
  this.setDOM(textDOM || document.createTextNode(this.text));
689
684
  }
690
- sync(track) {
685
+ sync(view, track) {
691
686
  if (!this.dom)
692
687
  this.createDOM();
693
688
  if (this.dom.nodeValue != this.text) {
@@ -748,12 +743,12 @@ class MarkView extends ContentView {
748
743
  this.dirty |= 4 /* Dirty.Attrs */ | 2 /* Dirty.Node */;
749
744
  }
750
745
  }
751
- sync(track) {
746
+ sync(view, track) {
752
747
  if (!this.dom)
753
748
  this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
754
749
  else if (this.dirty & 4 /* Dirty.Attrs */)
755
750
  this.setAttrs(this.dom);
756
- super.sync(track);
751
+ super.sync(view, track);
757
752
  }
758
753
  merge(from, to, source, _hasStart, openStart, openEnd) {
759
754
  if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
@@ -837,12 +832,12 @@ class WidgetView extends ContentView {
837
832
  this.length -= from;
838
833
  return result;
839
834
  }
840
- sync() {
841
- if (!this.dom || !this.widget.updateDOM(this.dom)) {
835
+ sync(view) {
836
+ if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
842
837
  if (this.dom && this.prevWidget)
843
838
  this.prevWidget.destroy(this.dom);
844
839
  this.prevWidget = null;
845
- this.setDOM(this.widget.toDOM(this.editorView));
840
+ this.setDOM(this.widget.toDOM(view));
846
841
  this.dom.contentEditable = "false";
847
842
  }
848
843
  }
@@ -875,7 +870,7 @@ class WidgetView extends ContentView {
875
870
  let top = this;
876
871
  while (top.parent)
877
872
  top = top.parent;
878
- let view = top.editorView, text = view && view.state.doc, start = this.posAtStart;
873
+ let { view } = top, text = view && view.state.doc, start = this.posAtStart;
879
874
  return text ? text.slice(start, start + this.length) : Text.empty;
880
875
  }
881
876
  domAtPos(pos) {
@@ -1167,7 +1162,7 @@ class WidgetType {
1167
1162
  couldn't (in which case the widget will be redrawn). The default
1168
1163
  implementation just returns false.
1169
1164
  */
1170
- updateDOM(dom) { return false; }
1165
+ updateDOM(dom, view) { return false; }
1171
1166
  /**
1172
1167
  @internal
1173
1168
  */
@@ -1482,7 +1477,7 @@ class LineView extends ContentView {
1482
1477
  this.dirty |= 4 /* Dirty.Attrs */ | 2 /* Dirty.Node */;
1483
1478
  }
1484
1479
  }
1485
- sync(track) {
1480
+ sync(view, track) {
1486
1481
  var _a;
1487
1482
  if (!this.dom) {
1488
1483
  this.setDOM(document.createElement("div"));
@@ -1499,7 +1494,7 @@ class LineView extends ContentView {
1499
1494
  this.dom.classList.add("cm-line");
1500
1495
  this.prevAttrs = undefined;
1501
1496
  }
1502
- super.sync(track);
1497
+ super.sync(view, track);
1503
1498
  let last = this.dom.lastChild;
1504
1499
  while (last && ContentView.get(last) instanceof MarkView)
1505
1500
  last = last.lastChild;
@@ -1574,12 +1569,12 @@ class BlockWidgetView extends ContentView {
1574
1569
  return end;
1575
1570
  }
1576
1571
  get children() { return noChildren; }
1577
- sync() {
1578
- if (!this.dom || !this.widget.updateDOM(this.dom)) {
1572
+ sync(view) {
1573
+ if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
1579
1574
  if (this.dom && this.prevWidget)
1580
1575
  this.prevWidget.destroy(this.dom);
1581
1576
  this.prevWidget = null;
1582
- this.setDOM(this.widget.toDOM(this.editorView));
1577
+ this.setDOM(this.widget.toDOM(view));
1583
1578
  this.dom.contentEditable = "false";
1584
1579
  }
1585
1580
  }
@@ -2533,7 +2528,6 @@ class DocView extends ContentView {
2533
2528
  this.updateDeco();
2534
2529
  this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2535
2530
  }
2536
- get editorView() { return this.view; }
2537
2531
  get length() { return this.view.state.doc.length; }
2538
2532
  // Update the document view to a given state. scrollIntoView can be
2539
2533
  // used as a hint to compute a new viewport that includes that
@@ -2593,7 +2587,7 @@ class DocView extends ContentView {
2593
2587
  // selection from the one it displays (issue #218). This tries
2594
2588
  // to detect that situation.
2595
2589
  let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2596
- this.sync(track);
2590
+ this.sync(this.view, track);
2597
2591
  this.dirty = 0 /* Dirty.Not */;
2598
2592
  if (track && (track.written || observer.selectionRange.focusNode != track.node))
2599
2593
  this.forceSelection = true;
@@ -3096,7 +3090,8 @@ function domPosAtCoords(parent, x, y) {
3096
3090
  closestRect = rect;
3097
3091
  closestX = dx;
3098
3092
  closestY = dy;
3099
- closestOverlap = !dx || (dx > 0 ? i < rects.length - 1 : i > 0);
3093
+ let side = dy ? (y < rect.top ? -1 : 1) : dx ? (x < rect.left ? -1 : 1) : 0;
3094
+ closestOverlap = !side || (side > 0 ? i < rects.length - 1 : i > 0);
3100
3095
  }
3101
3096
  if (dx == 0) {
3102
3097
  if (y > rect.bottom && (!aboveRect || aboveRect.bottom < rect.bottom)) {
@@ -3428,7 +3423,7 @@ class InputState {
3428
3423
  this.registeredEvents.push(type);
3429
3424
  }
3430
3425
  view.scrollDOM.addEventListener("mousedown", (event) => {
3431
- if (event.target == view.scrollDOM)
3426
+ if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
3432
3427
  handleEvent(handlers.mousedown, event);
3433
3428
  });
3434
3429
  if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
@@ -3606,11 +3601,13 @@ class MouseSelection {
3606
3601
  this.multiple = view.state.facet(EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
3607
3602
  this.dragMove = dragMovesSelection(view, startEvent);
3608
3603
  this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
3604
+ }
3605
+ start(event) {
3609
3606
  // When clicking outside of the selection, immediately apply the
3610
3607
  // effect of starting the selection
3611
3608
  if (this.dragging === false) {
3612
- startEvent.preventDefault();
3613
- this.select(startEvent);
3609
+ event.preventDefault();
3610
+ this.select(event);
3614
3611
  }
3615
3612
  }
3616
3613
  move(event) {
@@ -3802,9 +3799,11 @@ handlers.mousedown = (view, event) => {
3802
3799
  style = basicMouseSelection(view, event);
3803
3800
  if (style) {
3804
3801
  let mustFocus = view.root.activeElement != view.contentDOM;
3802
+ view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3805
3803
  if (mustFocus)
3806
3804
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
3807
- view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3805
+ if (view.inputState.mouseSelection)
3806
+ view.inputState.mouseSelection.start(event);
3808
3807
  }
3809
3808
  };
3810
3809
  function rangeForClick(view, pos, bias, type) {
@@ -5414,6 +5413,9 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5414
5413
  "&dark .cm-cursor": {
5415
5414
  borderLeftColor: "#444"
5416
5415
  },
5416
+ ".cm-dropCursor": {
5417
+ position: "absolute"
5418
+ },
5417
5419
  "&.cm-focused .cm-cursor": {
5418
5420
  display: "block"
5419
5421
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.7.2",
3
+ "version": "6.8.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "author": {
14
14
  "name": "Marijn Haverbeke",
15
- "email": "marijnh@gmail.com",
15
+ "email": "marijn@haverbeke.berlin",
16
16
  "url": "http://marijnhaverbeke.nl"
17
17
  },
18
18
  "type": "module",