@schukai/monster 4.137.5 → 4.137.6

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/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.137.5"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.137.6"}
@@ -282,7 +282,7 @@ function getTemplate() {
282
282
  <div data-monster-replace="path:content"></div>
283
283
  </div>
284
284
  </div>
285
-
285
+
286
286
  </div>
287
287
  `;
288
288
  }
@@ -56,6 +56,12 @@ const menuStateSymbol = Symbol("sheetMenuState");
56
56
  const dragFillSymbol = Symbol("sheetDragFill");
57
57
  const lastCopySymbol = Symbol("sheetLastCopy");
58
58
  const dragSelectSymbol = Symbol("sheetDragSelect");
59
+ const pendingResizeMoveSymbol = Symbol("sheetPendingResizeMove");
60
+ const resizeMoveFrameSymbol = Symbol("sheetResizeMoveFrame");
61
+ const pendingSelectionMoveSymbol = Symbol("sheetPendingSelectionMove");
62
+ const selectionMoveFrameSymbol = Symbol("sheetSelectionMoveFrame");
63
+ const pendingFillMoveSymbol = Symbol("sheetPendingFillMove");
64
+ const fillMoveFrameSymbol = Symbol("sheetFillMoveFrame");
59
65
 
60
66
  class Sheet extends CustomControl {
61
67
  static get [instanceSymbol]() {
@@ -1020,15 +1026,31 @@ function handleResizeMove(event) {
1020
1026
  const grid = event.currentTarget;
1021
1027
  const sheet = grid.getRootNode().host;
1022
1028
  if (!sheet || !sheet[resizeStateSymbol]) return;
1023
- const state = sheet[resizeStateSymbol];
1029
+ scheduleResizeMove.call(sheet, event.clientX, event.clientY);
1030
+ }
1031
+
1032
+ function scheduleResizeMove(clientX, clientY) {
1033
+ this[pendingResizeMoveSymbol] = { clientX, clientY };
1034
+ if (this[resizeMoveFrameSymbol]) return;
1035
+ this[resizeMoveFrameSymbol] = requestAnimationFrame(() => {
1036
+ this[resizeMoveFrameSymbol] = null;
1037
+ flushResizeMove.call(this);
1038
+ });
1039
+ }
1040
+
1041
+ function flushResizeMove() {
1042
+ const pending = this[pendingResizeMoveSymbol];
1043
+ this[pendingResizeMoveSymbol] = null;
1044
+ if (!pending || !this[resizeStateSymbol]) return;
1045
+ const state = this[resizeStateSymbol];
1024
1046
  if (state.kind === "column") {
1025
- const delta = event.clientX - state.start;
1047
+ const delta = pending.clientX - state.start;
1026
1048
  const size = state.startSize + delta;
1027
- setColumnSize.call(sheet, state.id, size);
1049
+ setColumnSize.call(this, state.id, size);
1028
1050
  } else if (state.kind === "row") {
1029
- const delta = event.clientY - state.start;
1051
+ const delta = pending.clientY - state.start;
1030
1052
  const size = state.startSize + delta;
1031
- setRowSize.call(sheet, state.id, size);
1053
+ setRowSize.call(this, state.id, size);
1032
1054
  }
1033
1055
  }
1034
1056
 
@@ -1043,6 +1065,11 @@ function handleResizeEnd(event) {
1043
1065
  grid.removeEventListener("pointermove", handleResizeMove);
1044
1066
  grid.removeEventListener("pointerup", handleResizeEnd);
1045
1067
  grid.removeEventListener("pointercancel", handleResizeEnd);
1068
+ if (sheet[resizeMoveFrameSymbol]) {
1069
+ cancelAnimationFrame(sheet[resizeMoveFrameSymbol]);
1070
+ sheet[resizeMoveFrameSymbol] = null;
1071
+ }
1072
+ flushResizeMove.call(sheet);
1046
1073
  sheet[resizeStateSymbol] = null;
1047
1074
  }
1048
1075
 
@@ -1050,6 +1077,9 @@ function setColumnSize(columnId, size) {
1050
1077
  const min = getSizeNumber(this.getOption("constraints.minColumnWidth"), 64);
1051
1078
  const max = getSizeNumber(this.getOption("constraints.maxColumnWidth"), 360);
1052
1079
  const next = clamp(size, min, max);
1080
+ if (getColumnWidthNumber.call(this, columnId) === next) {
1081
+ return;
1082
+ }
1053
1083
  this.setOption(`sizes.columns.${columnId}`, next);
1054
1084
  this[skipRenderSymbol] = true;
1055
1085
  if (this.getOption("features.virtualize", false) === true) {
@@ -1067,6 +1097,9 @@ function setRowSize(rowId, size) {
1067
1097
  const min = getSizeNumber(this.getOption("constraints.minRowHeight"), 28);
1068
1098
  const max = getSizeNumber(this.getOption("constraints.maxRowHeight"), 120);
1069
1099
  const next = clamp(size, min, max);
1100
+ if (getRowHeightNumber.call(this, rowId) === next) {
1101
+ return;
1102
+ }
1070
1103
  this.setOption(`sizes.rows.${rowId}`, next);
1071
1104
  this[skipRenderSymbol] = true;
1072
1105
  if (this.getOption("features.virtualize", false) === true) {
@@ -1178,7 +1211,23 @@ function handleSelectionDragMove(event) {
1178
1211
  if (Math.abs(dx) < 3 && Math.abs(dy) < 3) return;
1179
1212
  state.moved = true;
1180
1213
  }
1181
- const cell = getCellFromPoint.call(this, event.clientX, event.clientY);
1214
+ scheduleSelectionDragMove.call(this, event.clientX, event.clientY);
1215
+ }
1216
+
1217
+ function scheduleSelectionDragMove(clientX, clientY) {
1218
+ this[pendingSelectionMoveSymbol] = { clientX, clientY };
1219
+ if (this[selectionMoveFrameSymbol]) return;
1220
+ this[selectionMoveFrameSymbol] = requestAnimationFrame(() => {
1221
+ this[selectionMoveFrameSymbol] = null;
1222
+ flushSelectionDragMove.call(this);
1223
+ });
1224
+ }
1225
+
1226
+ function flushSelectionDragMove() {
1227
+ const pending = this[pendingSelectionMoveSymbol];
1228
+ this[pendingSelectionMoveSymbol] = null;
1229
+ if (!pending || !this[dragSelectSymbol]?.active) return;
1230
+ const cell = getCellFromPoint.call(this, pending.clientX, pending.clientY);
1182
1231
  if (!cell) return;
1183
1232
  const rowId = cell.dataset.rowId;
1184
1233
  const colId = cell.dataset.colId;
@@ -1189,6 +1238,11 @@ function handleSelectionDragMove(event) {
1189
1238
  function handleSelectionDragEnd() {
1190
1239
  const state = this[dragSelectSymbol];
1191
1240
  if (!state) return;
1241
+ if (this[selectionMoveFrameSymbol]) {
1242
+ cancelAnimationFrame(this[selectionMoveFrameSymbol]);
1243
+ this[selectionMoveFrameSymbol] = null;
1244
+ }
1245
+ flushSelectionDragMove.call(this);
1192
1246
  if (state.cleanup) state.cleanup();
1193
1247
  this[dragSelectSymbol] = null;
1194
1248
  this.removeAttribute("data-selecting");
@@ -1805,8 +1859,25 @@ function handleFillMove(event) {
1805
1859
  if (Math.abs(dx) < 3 && Math.abs(dy) < 3) return;
1806
1860
  state.moved = true;
1807
1861
  }
1862
+ scheduleFillMove.call(this, event.clientX, event.clientY);
1863
+ }
1864
+
1865
+ function scheduleFillMove(clientX, clientY) {
1866
+ this[pendingFillMoveSymbol] = { clientX, clientY };
1867
+ if (this[fillMoveFrameSymbol]) return;
1868
+ this[fillMoveFrameSymbol] = requestAnimationFrame(() => {
1869
+ this[fillMoveFrameSymbol] = null;
1870
+ flushFillMove.call(this);
1871
+ });
1872
+ }
1873
+
1874
+ function flushFillMove() {
1875
+ const state = this[dragFillSymbol];
1876
+ const pending = this[pendingFillMoveSymbol];
1877
+ this[pendingFillMoveSymbol] = null;
1878
+ if (!state || !pending) return;
1808
1879
  const data = normalizeValue.call(this);
1809
- const cell = getCellFromPoint.call(this, event.clientX, event.clientY);
1880
+ const cell = getCellFromPoint.call(this, pending.clientX, pending.clientY);
1810
1881
  if (!cell) return;
1811
1882
  const rowId = cell.dataset.rowId;
1812
1883
  const colId = cell.dataset.colId;
@@ -1829,6 +1900,11 @@ function handleFillMove(event) {
1829
1900
  function handleFillEnd() {
1830
1901
  const state = this[dragFillSymbol];
1831
1902
  if (!state) return;
1903
+ if (this[fillMoveFrameSymbol]) {
1904
+ cancelAnimationFrame(this[fillMoveFrameSymbol]);
1905
+ this[fillMoveFrameSymbol] = null;
1906
+ }
1907
+ flushFillMove.call(this);
1832
1908
  if (state.cleanup) state.cleanup();
1833
1909
  this.removeAttribute("data-filling");
1834
1910
  const data = normalizeValue.call(this);
@@ -106,27 +106,28 @@ class Slider extends CustomElement {
106
106
  startPos: 0,
107
107
  autoPlayInterval: null,
108
108
  resizeObserver: null, // Store the observer for later cleanup
109
+ resizeFrame: null,
110
+ pendingResizeSize: null,
111
+ lastResizeSize: null,
112
+ visibleSlides: null,
113
+ dragFrame: null,
114
+ pendingDragEvent: null,
109
115
 
110
116
  eventHandler: {
111
117
  mouseOverPause: null,
112
118
  mouseout: null,
113
119
  touchstart: null,
114
120
  touchend: null,
121
+ thumbnailMoved: null,
115
122
  },
116
123
  };
117
124
 
118
- // Set the CSS custom property for slide width based on visible slides.
119
- const slides = this.shadowRoot.querySelector(
120
- `[${ATTRIBUTE_ROLE}="slider"]`,
121
- );
125
+ initControlReferences.call(this);
122
126
 
127
+ // Set the CSS custom property for slide width based on visible slides.
123
128
  const slidesVisible = getVisibleSlidesFromContainerWidth.call(this);
124
- slides.style.setProperty(
125
- "--monster-slides-width",
126
- `${100 / slidesVisible}%`,
127
- );
129
+ setVisibleSlidesWidth.call(this, slidesVisible);
128
130
 
129
- initControlReferences.call(this);
130
131
  initEventHandler.call(this);
131
132
  initStructure.call(this);
132
133
 
@@ -151,6 +152,16 @@ class Slider extends CustomElement {
151
152
  this[configSymbol].resizeObserver = null;
152
153
  }
153
154
 
155
+ if (this[configSymbol]?.resizeFrame) {
156
+ getWindow().cancelAnimationFrame(this[configSymbol].resizeFrame);
157
+ this[configSymbol].resizeFrame = null;
158
+ }
159
+
160
+ if (this[configSymbol]?.dragFrame) {
161
+ getWindow().cancelAnimationFrame(this[configSymbol].dragFrame);
162
+ this[configSymbol].dragFrame = null;
163
+ }
164
+
154
165
  // Remove autoplay-related event listeners
155
166
  if (this[configSymbol]?.eventHandler) {
156
167
  const { mouseOverPause, mouseout, touchstart, touchend } =
@@ -172,6 +183,13 @@ class Slider extends CustomElement {
172
183
  this.removeEventListener("touchend", touchend);
173
184
  this[configSymbol].eventHandler.touchend = null;
174
185
  }
186
+ if (this[configSymbol].eventHandler.thumbnailMoved) {
187
+ this.removeEventListener(
188
+ "monster-slider-moved",
189
+ this[configSymbol].eventHandler.thumbnailMoved,
190
+ );
191
+ this[configSymbol].eventHandler.thumbnailMoved = null;
192
+ }
175
193
  }
176
194
  }
177
195
 
@@ -327,7 +345,6 @@ function initStructure() {
327
345
  * @description Generates the thumbnail navigation elements.
328
346
  */
329
347
  function initThumbnails() {
330
- const self = this;
331
348
  const thumbnails = this.shadowRoot.querySelector(
332
349
  "[data-monster-role='thumbnails']",
333
350
  );
@@ -350,20 +367,26 @@ function initThumbnails() {
350
367
  });
351
368
 
352
369
  // Listen for move events to update the active thumbnail
353
- this.addEventListener("monster-slider-moved", (e) => {
354
- const index = e.detail.index;
355
- const thumbnail = thumbnails.children[index];
370
+ if (this[configSymbol].eventHandler.thumbnailMoved === null) {
371
+ this[configSymbol].eventHandler.thumbnailMoved = (e) => {
372
+ const index = e.detail.index;
373
+ const thumbnail = thumbnails.children[index];
356
374
 
357
- if (!thumbnail) {
358
- return;
359
- }
375
+ if (!thumbnail) {
376
+ return;
377
+ }
360
378
 
361
- Array.from(thumbnails.children).forEach((thumb) => {
362
- thumb.classList.remove("current");
363
- });
379
+ Array.from(thumbnails.children).forEach((thumb) => {
380
+ thumb.classList.remove("current");
381
+ });
364
382
 
365
- thumbnail.classList.add("current");
366
- });
383
+ thumbnail.classList.add("current");
384
+ };
385
+ this.addEventListener(
386
+ "monster-slider-moved",
387
+ this[configSymbol].eventHandler.thumbnailMoved,
388
+ );
389
+ }
367
390
  }
368
391
 
369
392
  /**
@@ -509,6 +532,23 @@ function getVisibleSlidesFromContainerWidth() {
509
532
  return visibleSlides;
510
533
  }
511
534
 
535
+ /**
536
+ * @private
537
+ * @param {number} slidesVisible
538
+ * @return {boolean}
539
+ */
540
+ function setVisibleSlidesWidth(slidesVisible) {
541
+ if (this[configSymbol].visibleSlides === slidesVisible) {
542
+ return false;
543
+ }
544
+ this[configSymbol].visibleSlides = slidesVisible;
545
+ this[sliderElementSymbol].style.setProperty(
546
+ "--monster-slides-width",
547
+ `${100 / slidesVisible}%`,
548
+ );
549
+ return true;
550
+ }
551
+
512
552
  /**
513
553
  * @private
514
554
  * @description Clones slides to create the "infinite" loop effect for the carousel.
@@ -757,7 +797,7 @@ function initEventHandler() {
757
797
  );
758
798
  }
759
799
 
760
- const initialSize = {
800
+ this[configSymbol].lastResizeSize = {
761
801
  width: this[sliderElementSymbol]?.offsetWidth || 0,
762
802
  height: this[sliderElementSymbol]?.offsetHeight || 0,
763
803
  };
@@ -766,30 +806,7 @@ function initEventHandler() {
766
806
  const resizeObserver = new ResizeObserver((entries) => {
767
807
  for (let entry of entries) {
768
808
  const { width, height } = entry.contentRect;
769
- if (width !== initialSize.width || height !== initialSize.height) {
770
- self.stopAutoPlay();
771
-
772
- // Re-init thumbnails if layout changes
773
- if (this.getOption("features.thumbnails")) {
774
- initThumbnails.call(this);
775
- }
776
-
777
- // Recalculate visible slides and update CSS property
778
- const slidesVisible = getVisibleSlidesFromContainerWidth.call(this);
779
- this[sliderElementSymbol].style.setProperty(
780
- "--monster-slides-width",
781
- `${100 / slidesVisible}%`,
782
- );
783
-
784
- // Move to start without animation
785
- moveTo.call(self, 0, false);
786
- self.startAutoPlay();
787
-
788
- fireCustomEvent(self, "monster-slider-resized", {
789
- width: width,
790
- height: height,
791
- });
792
- }
809
+ scheduleResizeUpdate.call(this, width, height);
793
810
  }
794
811
  });
795
812
 
@@ -799,6 +816,62 @@ function initEventHandler() {
799
816
  return this;
800
817
  }
801
818
 
819
+ function scheduleResizeUpdate(width, height) {
820
+ const lastSize = this[configSymbol].lastResizeSize;
821
+ if (
822
+ this[configSymbol].resizeFrame === null &&
823
+ lastSize &&
824
+ width === lastSize.width &&
825
+ height === lastSize.height
826
+ ) {
827
+ return;
828
+ }
829
+ this[configSymbol].pendingResizeSize = { width, height };
830
+ if (this[configSymbol].resizeFrame !== null) {
831
+ return;
832
+ }
833
+ this[configSymbol].resizeFrame = getWindow().requestAnimationFrame(() => {
834
+ this[configSymbol].resizeFrame = null;
835
+ flushResizeUpdate.call(this);
836
+ });
837
+ }
838
+
839
+ function flushResizeUpdate() {
840
+ const size = this[configSymbol].pendingResizeSize;
841
+ this[configSymbol].pendingResizeSize = null;
842
+ if (!size) {
843
+ return;
844
+ }
845
+
846
+ const lastSize = this[configSymbol].lastResizeSize;
847
+ if (
848
+ lastSize &&
849
+ size.width === lastSize.width &&
850
+ size.height === lastSize.height
851
+ ) {
852
+ return;
853
+ }
854
+ this[configSymbol].lastResizeSize = size;
855
+
856
+ const previousVisibleSlides = this[configSymbol].visibleSlides;
857
+ const slidesVisible = getVisibleSlidesFromContainerWidth.call(this);
858
+ const visibleSlidesChanged = setVisibleSlidesWidth.call(this, slidesVisible);
859
+
860
+ if (
861
+ visibleSlidesChanged &&
862
+ previousVisibleSlides !== null &&
863
+ this.getOption("features.thumbnails")
864
+ ) {
865
+ initThumbnails.call(this);
866
+ }
867
+
868
+ this.stopAutoPlay();
869
+ moveTo.call(this, 0, false);
870
+ this.startAutoPlay();
871
+
872
+ fireCustomEvent(this, "monster-slider-resized", size);
873
+ }
874
+
802
875
  /**
803
876
  * @private
804
877
  * @description Handles the "mousedown" or "touchstart" event to begin dragging.
@@ -819,7 +892,7 @@ function startDragging(e, type) {
819
892
  this[sliderElementSymbol].style.transitionProperty = "none";
820
893
 
821
894
  const callbackMousemove = (x) => {
822
- dragging.call(this, x, type);
895
+ scheduleDragging.call(this, x, type);
823
896
  };
824
897
 
825
898
  const callbackMouseUp = () => {
@@ -830,6 +903,12 @@ function startDragging(e, type) {
830
903
  document.body.removeEventListener(endEvent, callbackMouseUp);
831
904
  document.body.removeEventListener(moveEvent, callbackMousemove);
832
905
 
906
+ if (this[configSymbol].dragFrame !== null) {
907
+ getWindow().cancelAnimationFrame(this[configSymbol].dragFrame);
908
+ this[configSymbol].dragFrame = null;
909
+ }
910
+ flushDragging.call(this, type);
911
+
833
912
  this[configSymbol].isDragging = false;
834
913
  this[configSymbol].startPos = 0;
835
914
  this[sliderElementSymbol].classList.remove("grabbing");
@@ -880,6 +959,26 @@ function dragging(e, type) {
880
959
  `translateX(calc(${this[configSymbol].lastOffset} + ${this[configSymbol].draggingPos}px))`;
881
960
  }
882
961
 
962
+ function scheduleDragging(e, type) {
963
+ this[configSymbol].pendingDragEvent = e;
964
+ if (this[configSymbol].dragFrame !== null) {
965
+ return;
966
+ }
967
+ this[configSymbol].dragFrame = getWindow().requestAnimationFrame(() => {
968
+ this[configSymbol].dragFrame = null;
969
+ flushDragging.call(this, type);
970
+ });
971
+ }
972
+
973
+ function flushDragging(type) {
974
+ const event = this[configSymbol].pendingDragEvent;
975
+ this[configSymbol].pendingDragEvent = null;
976
+ if (!event) {
977
+ return;
978
+ }
979
+ dragging.call(this, event, type);
980
+ }
981
+
883
982
  /**
884
983
  * @private
885
984
  * @description Caches references to key elements in the shadow DOM.
@@ -156,7 +156,7 @@ function getMonsterVersion() {
156
156
  }
157
157
 
158
158
  /** don't touch, replaced by make with package.json version */
159
- monsterVersion = new Version("4.136.7");
159
+ monsterVersion = new Version("4.137.5");
160
160
 
161
161
  return monsterVersion;
162
162
  }
@@ -0,0 +1,126 @@
1
+ import * as chai from "chai";
2
+ import { initJSDOM } from "../../../util/jsdom.mjs";
3
+
4
+ const expect = chai.expect;
5
+
6
+ describe("Sheet", function () {
7
+ let Sheet;
8
+
9
+ before(function (done) {
10
+ initJSDOM()
11
+ .then(() => {
12
+ import("../../../../source/components/form/sheet.mjs")
13
+ .then((m) => {
14
+ Sheet = m.Sheet;
15
+ done();
16
+ })
17
+ .catch((e) => done(e));
18
+ })
19
+ .catch((e) => done(e));
20
+ });
21
+
22
+ afterEach(() => {
23
+ document.getElementById("mocks").innerHTML = "";
24
+ });
25
+
26
+ it("should register monster-sheet", function () {
27
+ expect(document.createElement("monster-sheet")).to.be.instanceof(Sheet);
28
+ });
29
+
30
+ it("should coalesce column resize pointer moves", function (done) {
31
+ const originalRequestAnimationFrame = window.requestAnimationFrame;
32
+ const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
33
+ const originalCancelAnimationFrame = window.cancelAnimationFrame;
34
+ const originalGlobalCancelAnimationFrame = globalThis.cancelAnimationFrame;
35
+ const scheduledCallbacks = [];
36
+
37
+ try {
38
+ window.requestAnimationFrame = (callback) => {
39
+ scheduledCallbacks.push(callback);
40
+ return scheduledCallbacks.length;
41
+ };
42
+ globalThis.requestAnimationFrame = window.requestAnimationFrame;
43
+ window.cancelAnimationFrame = () => {};
44
+ globalThis.cancelAnimationFrame = window.cancelAnimationFrame;
45
+
46
+ const mocks = document.getElementById("mocks");
47
+ const sheet = document.createElement("monster-sheet");
48
+ mocks.appendChild(sheet);
49
+
50
+ setTimeout(() => {
51
+ try {
52
+ const grid = sheet.shadowRoot.querySelector(
53
+ '[data-monster-role="grid"]',
54
+ );
55
+ const handle = sheet.shadowRoot.querySelector(
56
+ '[data-monster-role="column-resize"]',
57
+ );
58
+ const header = handle.parentElement;
59
+ grid.setPointerCapture = () => {};
60
+ grid.releasePointerCapture = () => {};
61
+ Object.defineProperty(header, "getBoundingClientRect", {
62
+ configurable: true,
63
+ value: () => ({ width: 120 }),
64
+ });
65
+
66
+ let resizeEvents = 0;
67
+ sheet.addEventListener("monster-sheet-resize-column", () => {
68
+ resizeEvents++;
69
+ });
70
+
71
+ const down = new Event("pointerdown", { bubbles: true });
72
+ Object.assign(down, {
73
+ button: 0,
74
+ clientX: 100,
75
+ clientY: 0,
76
+ pointerId: 1,
77
+ });
78
+ handle.dispatchEvent(down);
79
+
80
+ const firstMove = new Event("pointermove", { bubbles: true });
81
+ Object.assign(firstMove, {
82
+ clientX: 130,
83
+ clientY: 0,
84
+ pointerId: 1,
85
+ });
86
+ const secondMove = new Event("pointermove", { bubbles: true });
87
+ Object.assign(secondMove, {
88
+ clientX: 150,
89
+ clientY: 0,
90
+ pointerId: 1,
91
+ });
92
+ grid.dispatchEvent(firstMove);
93
+ grid.dispatchEvent(secondMove);
94
+
95
+ expect(scheduledCallbacks.length).to.equal(1);
96
+ scheduledCallbacks.shift()();
97
+ expect(resizeEvents).to.equal(1);
98
+
99
+ const repeatedMove = new Event("pointermove", { bubbles: true });
100
+ Object.assign(repeatedMove, {
101
+ clientX: 150,
102
+ clientY: 0,
103
+ pointerId: 1,
104
+ });
105
+ grid.dispatchEvent(repeatedMove);
106
+ scheduledCallbacks.shift()();
107
+ expect(resizeEvents).to.equal(1);
108
+ done();
109
+ } catch (e) {
110
+ done(e);
111
+ } finally {
112
+ window.requestAnimationFrame = originalRequestAnimationFrame;
113
+ globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
114
+ window.cancelAnimationFrame = originalCancelAnimationFrame;
115
+ globalThis.cancelAnimationFrame = originalGlobalCancelAnimationFrame;
116
+ }
117
+ }, 0);
118
+ } catch (e) {
119
+ window.requestAnimationFrame = originalRequestAnimationFrame;
120
+ globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
121
+ window.cancelAnimationFrame = originalCancelAnimationFrame;
122
+ globalThis.cancelAnimationFrame = originalGlobalCancelAnimationFrame;
123
+ done(e);
124
+ }
125
+ });
126
+ });