@codemirror/view 6.28.4 → 6.28.5

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,11 @@
1
+ ## 6.28.5 (2024-07-17)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug that broke drag scrolling along one axis when the innermost scrollable element around the editor was only scrollable along the other axis.
6
+
7
+ Work around a memory leak in Chrome's EditContext implementation.
8
+
1
9
  ## 6.28.4 (2024-07-03)
2
10
 
3
11
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -210,15 +210,17 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
210
210
  }
211
211
  }
212
212
  }
213
- function scrollableParent(dom) {
214
- let doc = dom.ownerDocument;
213
+ function scrollableParents(dom) {
214
+ let doc = dom.ownerDocument, x, y;
215
215
  for (let cur = dom.parentNode; cur;) {
216
- if (cur == doc.body) {
216
+ if (cur == doc.body || (x && y)) {
217
217
  break;
218
218
  }
219
219
  else if (cur.nodeType == 1) {
220
- if (cur.scrollHeight > cur.clientHeight || cur.scrollWidth > cur.clientWidth)
221
- return cur;
220
+ if (!y && cur.scrollHeight > cur.clientHeight)
221
+ y = cur;
222
+ if (!x && cur.scrollWidth > cur.clientWidth)
223
+ x = cur;
222
224
  cur = cur.assignedSlot || cur.parentNode;
223
225
  }
224
226
  else if (cur.nodeType == 11) {
@@ -228,7 +230,7 @@ function scrollableParent(dom) {
228
230
  break;
229
231
  }
230
232
  }
231
- return null;
233
+ return { x, y };
232
234
  }
233
235
  class DOMSelectionState {
234
236
  constructor() {
@@ -3984,7 +3986,7 @@ class MouseSelection {
3984
3986
  this.scrollSpeed = { x: 0, y: 0 };
3985
3987
  this.scrolling = -1;
3986
3988
  this.lastEvent = startEvent;
3987
- this.scrollParent = scrollableParent(view.contentDOM);
3989
+ this.scrollParents = scrollableParents(view.contentDOM);
3988
3990
  this.atoms = view.state.facet(atomicRanges).map(f => f(view));
3989
3991
  let doc = view.contentDOM.ownerDocument;
3990
3992
  doc.addEventListener("mousemove", this.move = this.move.bind(this));
@@ -4000,24 +4002,26 @@ class MouseSelection {
4000
4002
  this.select(event);
4001
4003
  }
4002
4004
  move(event) {
4003
- var _a;
4004
4005
  if (event.buttons == 0)
4005
4006
  return this.destroy();
4006
4007
  if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10)
4007
4008
  return;
4008
4009
  this.select(this.lastEvent = event);
4009
4010
  let sx = 0, sy = 0;
4010
- let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
4011
- || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
4011
+ let left = 0, top = 0, right = this.view.win.innerWidth, bottom = this.view.win.innerHeight;
4012
+ if (this.scrollParents.x)
4013
+ ({ left, right } = this.scrollParents.x.getBoundingClientRect());
4014
+ if (this.scrollParents.y)
4015
+ ({ top, bottom } = this.scrollParents.y.getBoundingClientRect());
4012
4016
  let margins = getScrollMargins(this.view);
4013
- if (event.clientX - margins.left <= rect.left + dragScrollMargin)
4014
- sx = -dragScrollSpeed(rect.left - event.clientX);
4015
- else if (event.clientX + margins.right >= rect.right - dragScrollMargin)
4016
- sx = dragScrollSpeed(event.clientX - rect.right);
4017
- if (event.clientY - margins.top <= rect.top + dragScrollMargin)
4018
- sy = -dragScrollSpeed(rect.top - event.clientY);
4019
- else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin)
4020
- sy = dragScrollSpeed(event.clientY - rect.bottom);
4017
+ if (event.clientX - margins.left <= left + dragScrollMargin)
4018
+ sx = -dragScrollSpeed(left - event.clientX);
4019
+ else if (event.clientX + margins.right >= right - dragScrollMargin)
4020
+ sx = dragScrollSpeed(event.clientX - right);
4021
+ if (event.clientY - margins.top <= top + dragScrollMargin)
4022
+ sy = -dragScrollSpeed(top - event.clientY);
4023
+ else if (event.clientY + margins.bottom >= bottom - dragScrollMargin)
4024
+ sy = dragScrollSpeed(event.clientY - bottom);
4021
4025
  this.setScrollSpeed(sx, sy);
4022
4026
  }
4023
4027
  up(event) {
@@ -4046,13 +4050,17 @@ class MouseSelection {
4046
4050
  }
4047
4051
  }
4048
4052
  scroll() {
4049
- if (this.scrollParent) {
4050
- this.scrollParent.scrollLeft += this.scrollSpeed.x;
4051
- this.scrollParent.scrollTop += this.scrollSpeed.y;
4053
+ let { x, y } = this.scrollSpeed;
4054
+ if (x && this.scrollParents.x) {
4055
+ this.scrollParents.x.scrollLeft += x;
4056
+ x = 0;
4052
4057
  }
4053
- else {
4054
- this.view.win.scrollBy(this.scrollSpeed.x, this.scrollSpeed.y);
4058
+ if (y && this.scrollParents.y) {
4059
+ this.scrollParents.y.scrollTop += y;
4060
+ y = 0;
4055
4061
  }
4062
+ if (x || y)
4063
+ this.view.win.scrollBy(x, y);
4056
4064
  if (this.dragging === false)
4057
4065
  this.select(this.lastEvent);
4058
4066
  }
@@ -7030,6 +7038,10 @@ class DOMObserver {
7030
7038
  clearTimeout(this.resizeTimeout);
7031
7039
  this.win.cancelAnimationFrame(this.delayedFlush);
7032
7040
  this.win.cancelAnimationFrame(this.flushingAndroidKey);
7041
+ if (this.editContext) {
7042
+ this.view.contentDOM.editContext = null;
7043
+ this.editContext.destroy();
7044
+ }
7033
7045
  }
7034
7046
  }
7035
7047
  function findChild(cView, dom, dir) {
@@ -7089,13 +7101,14 @@ class EditContextManager {
7089
7101
  // that sometimes breaks series of multiple edits made for a single
7090
7102
  // user action on some Android keyboards)
7091
7103
  this.pendingContextChange = null;
7104
+ this.handlers = Object.create(null);
7092
7105
  this.resetRange(view.state);
7093
7106
  let context = this.editContext = new window.EditContext({
7094
7107
  text: view.state.doc.sliceString(this.from, this.to),
7095
7108
  selectionStart: this.toContextPos(Math.max(this.from, Math.min(this.to, view.state.selection.main.anchor))),
7096
7109
  selectionEnd: this.toContextPos(view.state.selection.main.head)
7097
7110
  });
7098
- context.addEventListener("textupdate", e => {
7111
+ this.handlers.textupdate = e => {
7099
7112
  let { anchor } = view.state.selection.main;
7100
7113
  let change = { from: this.toEditorPos(e.updateRangeStart),
7101
7114
  to: this.toEditorPos(e.updateRangeEnd),
@@ -7115,8 +7128,8 @@ class EditContextManager {
7115
7128
  // that the context is in sync with the editor state again.
7116
7129
  if (this.pendingContextChange)
7117
7130
  this.revertPending(view.state);
7118
- });
7119
- context.addEventListener("characterboundsupdate", e => {
7131
+ };
7132
+ this.handlers.characterboundsupdate = e => {
7120
7133
  let rects = [], prev = null;
7121
7134
  for (let i = this.toEditorPos(e.rangeStart), end = this.toEditorPos(e.rangeEnd); i < end; i++) {
7122
7135
  let rect = view.coordsForChar(i);
@@ -7125,8 +7138,8 @@ class EditContextManager {
7125
7138
  rects.push(prev);
7126
7139
  }
7127
7140
  context.updateCharacterBounds(e.rangeStart, rects);
7128
- });
7129
- context.addEventListener("textformatupdate", e => {
7141
+ };
7142
+ this.handlers.textformatupdate = e => {
7130
7143
  let deco = [];
7131
7144
  for (let format of e.getTextFormats()) {
7132
7145
  let lineStyle = format.underlineStyle, thickness = format.underlineThickness;
@@ -7137,17 +7150,19 @@ class EditContextManager {
7137
7150
  }
7138
7151
  }
7139
7152
  view.dispatch({ effects: setEditContextFormatting.of(Decoration.set(deco)) });
7140
- });
7141
- context.addEventListener("compositionstart", () => {
7153
+ };
7154
+ this.handlers.compositionstart = () => {
7142
7155
  if (view.inputState.composing < 0) {
7143
7156
  view.inputState.composing = 0;
7144
7157
  view.inputState.compositionFirstChange = true;
7145
7158
  }
7146
- });
7147
- context.addEventListener("compositionend", () => {
7159
+ };
7160
+ this.handlers.compositionend = () => {
7148
7161
  view.inputState.composing = -1;
7149
7162
  view.inputState.compositionFirstChange = null;
7150
- });
7163
+ };
7164
+ for (let event in this.handlers)
7165
+ context.addEventListener(event, this.handlers[event]);
7151
7166
  this.measureReq = { read: view => {
7152
7167
  this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect());
7153
7168
  let sel = getSelection(view.root);
@@ -7231,6 +7246,10 @@ class EditContextManager {
7231
7246
  }
7232
7247
  toEditorPos(contextPos) { return contextPos + this.from; }
7233
7248
  toContextPos(editorPos) { return editorPos - this.from; }
7249
+ destroy() {
7250
+ for (let event in this.handlers)
7251
+ this.editContext.removeEventListener(event, this.handlers[event]);
7252
+ }
7234
7253
  }
7235
7254
 
7236
7255
  // The editor's update state machine looks something like this:
package/dist/index.js CHANGED
@@ -208,15 +208,17 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
208
208
  }
209
209
  }
210
210
  }
211
- function scrollableParent(dom) {
212
- let doc = dom.ownerDocument;
211
+ function scrollableParents(dom) {
212
+ let doc = dom.ownerDocument, x, y;
213
213
  for (let cur = dom.parentNode; cur;) {
214
- if (cur == doc.body) {
214
+ if (cur == doc.body || (x && y)) {
215
215
  break;
216
216
  }
217
217
  else if (cur.nodeType == 1) {
218
- if (cur.scrollHeight > cur.clientHeight || cur.scrollWidth > cur.clientWidth)
219
- return cur;
218
+ if (!y && cur.scrollHeight > cur.clientHeight)
219
+ y = cur;
220
+ if (!x && cur.scrollWidth > cur.clientWidth)
221
+ x = cur;
220
222
  cur = cur.assignedSlot || cur.parentNode;
221
223
  }
222
224
  else if (cur.nodeType == 11) {
@@ -226,7 +228,7 @@ function scrollableParent(dom) {
226
228
  break;
227
229
  }
228
230
  }
229
- return null;
231
+ return { x, y };
230
232
  }
231
233
  class DOMSelectionState {
232
234
  constructor() {
@@ -3980,7 +3982,7 @@ class MouseSelection {
3980
3982
  this.scrollSpeed = { x: 0, y: 0 };
3981
3983
  this.scrolling = -1;
3982
3984
  this.lastEvent = startEvent;
3983
- this.scrollParent = scrollableParent(view.contentDOM);
3985
+ this.scrollParents = scrollableParents(view.contentDOM);
3984
3986
  this.atoms = view.state.facet(atomicRanges).map(f => f(view));
3985
3987
  let doc = view.contentDOM.ownerDocument;
3986
3988
  doc.addEventListener("mousemove", this.move = this.move.bind(this));
@@ -3996,24 +3998,26 @@ class MouseSelection {
3996
3998
  this.select(event);
3997
3999
  }
3998
4000
  move(event) {
3999
- var _a;
4000
4001
  if (event.buttons == 0)
4001
4002
  return this.destroy();
4002
4003
  if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10)
4003
4004
  return;
4004
4005
  this.select(this.lastEvent = event);
4005
4006
  let sx = 0, sy = 0;
4006
- let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
4007
- || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
4007
+ let left = 0, top = 0, right = this.view.win.innerWidth, bottom = this.view.win.innerHeight;
4008
+ if (this.scrollParents.x)
4009
+ ({ left, right } = this.scrollParents.x.getBoundingClientRect());
4010
+ if (this.scrollParents.y)
4011
+ ({ top, bottom } = this.scrollParents.y.getBoundingClientRect());
4008
4012
  let margins = getScrollMargins(this.view);
4009
- if (event.clientX - margins.left <= rect.left + dragScrollMargin)
4010
- sx = -dragScrollSpeed(rect.left - event.clientX);
4011
- else if (event.clientX + margins.right >= rect.right - dragScrollMargin)
4012
- sx = dragScrollSpeed(event.clientX - rect.right);
4013
- if (event.clientY - margins.top <= rect.top + dragScrollMargin)
4014
- sy = -dragScrollSpeed(rect.top - event.clientY);
4015
- else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin)
4016
- sy = dragScrollSpeed(event.clientY - rect.bottom);
4013
+ if (event.clientX - margins.left <= left + dragScrollMargin)
4014
+ sx = -dragScrollSpeed(left - event.clientX);
4015
+ else if (event.clientX + margins.right >= right - dragScrollMargin)
4016
+ sx = dragScrollSpeed(event.clientX - right);
4017
+ if (event.clientY - margins.top <= top + dragScrollMargin)
4018
+ sy = -dragScrollSpeed(top - event.clientY);
4019
+ else if (event.clientY + margins.bottom >= bottom - dragScrollMargin)
4020
+ sy = dragScrollSpeed(event.clientY - bottom);
4017
4021
  this.setScrollSpeed(sx, sy);
4018
4022
  }
4019
4023
  up(event) {
@@ -4042,13 +4046,17 @@ class MouseSelection {
4042
4046
  }
4043
4047
  }
4044
4048
  scroll() {
4045
- if (this.scrollParent) {
4046
- this.scrollParent.scrollLeft += this.scrollSpeed.x;
4047
- this.scrollParent.scrollTop += this.scrollSpeed.y;
4049
+ let { x, y } = this.scrollSpeed;
4050
+ if (x && this.scrollParents.x) {
4051
+ this.scrollParents.x.scrollLeft += x;
4052
+ x = 0;
4048
4053
  }
4049
- else {
4050
- this.view.win.scrollBy(this.scrollSpeed.x, this.scrollSpeed.y);
4054
+ if (y && this.scrollParents.y) {
4055
+ this.scrollParents.y.scrollTop += y;
4056
+ y = 0;
4051
4057
  }
4058
+ if (x || y)
4059
+ this.view.win.scrollBy(x, y);
4052
4060
  if (this.dragging === false)
4053
4061
  this.select(this.lastEvent);
4054
4062
  }
@@ -7025,6 +7033,10 @@ class DOMObserver {
7025
7033
  clearTimeout(this.resizeTimeout);
7026
7034
  this.win.cancelAnimationFrame(this.delayedFlush);
7027
7035
  this.win.cancelAnimationFrame(this.flushingAndroidKey);
7036
+ if (this.editContext) {
7037
+ this.view.contentDOM.editContext = null;
7038
+ this.editContext.destroy();
7039
+ }
7028
7040
  }
7029
7041
  }
7030
7042
  function findChild(cView, dom, dir) {
@@ -7084,13 +7096,14 @@ class EditContextManager {
7084
7096
  // that sometimes breaks series of multiple edits made for a single
7085
7097
  // user action on some Android keyboards)
7086
7098
  this.pendingContextChange = null;
7099
+ this.handlers = Object.create(null);
7087
7100
  this.resetRange(view.state);
7088
7101
  let context = this.editContext = new window.EditContext({
7089
7102
  text: view.state.doc.sliceString(this.from, this.to),
7090
7103
  selectionStart: this.toContextPos(Math.max(this.from, Math.min(this.to, view.state.selection.main.anchor))),
7091
7104
  selectionEnd: this.toContextPos(view.state.selection.main.head)
7092
7105
  });
7093
- context.addEventListener("textupdate", e => {
7106
+ this.handlers.textupdate = e => {
7094
7107
  let { anchor } = view.state.selection.main;
7095
7108
  let change = { from: this.toEditorPos(e.updateRangeStart),
7096
7109
  to: this.toEditorPos(e.updateRangeEnd),
@@ -7110,8 +7123,8 @@ class EditContextManager {
7110
7123
  // that the context is in sync with the editor state again.
7111
7124
  if (this.pendingContextChange)
7112
7125
  this.revertPending(view.state);
7113
- });
7114
- context.addEventListener("characterboundsupdate", e => {
7126
+ };
7127
+ this.handlers.characterboundsupdate = e => {
7115
7128
  let rects = [], prev = null;
7116
7129
  for (let i = this.toEditorPos(e.rangeStart), end = this.toEditorPos(e.rangeEnd); i < end; i++) {
7117
7130
  let rect = view.coordsForChar(i);
@@ -7120,8 +7133,8 @@ class EditContextManager {
7120
7133
  rects.push(prev);
7121
7134
  }
7122
7135
  context.updateCharacterBounds(e.rangeStart, rects);
7123
- });
7124
- context.addEventListener("textformatupdate", e => {
7136
+ };
7137
+ this.handlers.textformatupdate = e => {
7125
7138
  let deco = [];
7126
7139
  for (let format of e.getTextFormats()) {
7127
7140
  let lineStyle = format.underlineStyle, thickness = format.underlineThickness;
@@ -7132,17 +7145,19 @@ class EditContextManager {
7132
7145
  }
7133
7146
  }
7134
7147
  view.dispatch({ effects: setEditContextFormatting.of(Decoration.set(deco)) });
7135
- });
7136
- context.addEventListener("compositionstart", () => {
7148
+ };
7149
+ this.handlers.compositionstart = () => {
7137
7150
  if (view.inputState.composing < 0) {
7138
7151
  view.inputState.composing = 0;
7139
7152
  view.inputState.compositionFirstChange = true;
7140
7153
  }
7141
- });
7142
- context.addEventListener("compositionend", () => {
7154
+ };
7155
+ this.handlers.compositionend = () => {
7143
7156
  view.inputState.composing = -1;
7144
7157
  view.inputState.compositionFirstChange = null;
7145
- });
7158
+ };
7159
+ for (let event in this.handlers)
7160
+ context.addEventListener(event, this.handlers[event]);
7146
7161
  this.measureReq = { read: view => {
7147
7162
  this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect());
7148
7163
  let sel = getSelection(view.root);
@@ -7226,6 +7241,10 @@ class EditContextManager {
7226
7241
  }
7227
7242
  toEditorPos(contextPos) { return contextPos + this.from; }
7228
7243
  toContextPos(editorPos) { return editorPos - this.from; }
7244
+ destroy() {
7245
+ for (let event in this.handlers)
7246
+ this.editContext.removeEventListener(event, this.handlers[event]);
7247
+ }
7229
7248
  }
7230
7249
 
7231
7250
  // The editor's update state machine looks something like this:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.28.4",
3
+ "version": "6.28.5",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",