@gridkitjs/dom 0.0.0-stage → 0.1.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/dist/index.js ADDED
@@ -0,0 +1,739 @@
1
+ // src/announcer.ts
2
+ var NO_BREAK_SPACE = String.fromCodePoint(160);
3
+ function createLiveAnnouncer(container = document.body) {
4
+ const region = document.createElement("div");
5
+ region.className = "gridkit-sr-only";
6
+ region.setAttribute("role", "status");
7
+ region.setAttribute("aria-live", "polite");
8
+ container.append(region);
9
+ let last = "";
10
+ let padded = false;
11
+ return {
12
+ announce(text) {
13
+ padded = text === last && !padded;
14
+ last = text;
15
+ region.textContent = padded ? text + NO_BREAK_SPACE : text;
16
+ },
17
+ destroy() {
18
+ region.remove();
19
+ }
20
+ };
21
+ }
22
+
23
+ // src/bodyTargets.ts
24
+ function rowIndexOf(row) {
25
+ const attribute = row.getAttribute("data-gridkit-row-index");
26
+ return attribute === null ? null : Number(attribute);
27
+ }
28
+ function bodyCellFrom(target) {
29
+ if (!(target instanceof Element)) {
30
+ return null;
31
+ }
32
+ const cell = target.closest("td[data-gridkit-column]");
33
+ const row = cell?.parentElement;
34
+ const columnId = cell?.getAttribute("data-gridkit-column");
35
+ const rowIndex = row === null || row === void 0 ? null : rowIndexOf(row);
36
+ if (!(cell instanceof HTMLTableCellElement) || !(row instanceof HTMLTableRowElement) || columnId === null || columnId === void 0 || rowIndex === null) {
37
+ return null;
38
+ }
39
+ return { rowIndex, columnIndex: cell.cellIndex, columnId };
40
+ }
41
+ function groupRowFrom(target) {
42
+ if (!(target instanceof Element)) {
43
+ return null;
44
+ }
45
+ const row = target.closest("tr[data-gridkit-group]");
46
+ const groupId = row?.getAttribute("data-gridkit-group");
47
+ const rowIndex = row === null ? null : rowIndexOf(row);
48
+ if (!(row instanceof HTMLTableRowElement) || groupId === null || groupId === void 0 || rowIndex === null) {
49
+ return null;
50
+ }
51
+ return { rowIndex, groupId };
52
+ }
53
+ function bodyRowFrom(target) {
54
+ if (!(target instanceof Element)) {
55
+ return null;
56
+ }
57
+ const row = target.closest("tr[data-gridkit-row-index]");
58
+ const rowIndex = row === null ? null : rowIndexOf(row);
59
+ if (rowIndex === null) {
60
+ return null;
61
+ }
62
+ const cell = target.closest("td[data-gridkit-column]");
63
+ return {
64
+ rowIndex,
65
+ columnId: cell?.parentElement === row ? cell.getAttribute("data-gridkit-column") : null
66
+ };
67
+ }
68
+
69
+ // src/columnResize.ts
70
+ import { applyColumnResize } from "@gridkitjs/core";
71
+
72
+ // src/pointerGesture.ts
73
+ function isPrimaryPress(press) {
74
+ return press.button === 0 && press.isPrimary;
75
+ }
76
+ function startPointerGesture(element, pointerId, handlers) {
77
+ element.setPointerCapture(pointerId);
78
+ function stop() {
79
+ element.removeEventListener("pointermove", move);
80
+ element.removeEventListener("pointerup", end);
81
+ element.removeEventListener("pointercancel", cancel);
82
+ window.removeEventListener("keydown", onKeyDown);
83
+ if (element.hasPointerCapture(pointerId)) {
84
+ element.releasePointerCapture(pointerId);
85
+ }
86
+ }
87
+ function move(event) {
88
+ handlers.onMove(event);
89
+ }
90
+ function end() {
91
+ stop();
92
+ handlers.onEnd();
93
+ }
94
+ function cancel() {
95
+ stop();
96
+ handlers.onCancel();
97
+ }
98
+ function onKeyDown(event) {
99
+ if (event.key === "Escape") {
100
+ cancel();
101
+ }
102
+ }
103
+ element.addEventListener("pointermove", move);
104
+ element.addEventListener("pointerup", end);
105
+ element.addEventListener("pointercancel", cancel);
106
+ window.addEventListener("keydown", onKeyDown);
107
+ }
108
+
109
+ // src/columnResize.ts
110
+ function startColumnResizeGesture(handle, press, session, handlers) {
111
+ if (!isPrimaryPress(press)) {
112
+ return false;
113
+ }
114
+ press.preventDefault();
115
+ let width = session.startWidth;
116
+ startPointerGesture(handle, press.pointerId, {
117
+ onMove(event) {
118
+ width = applyColumnResize(session, event.clientX);
119
+ handlers.onMove(width);
120
+ },
121
+ onEnd() {
122
+ handlers.onEnd(width === session.startWidth ? null : width);
123
+ },
124
+ onCancel() {
125
+ handlers.onCancel();
126
+ }
127
+ });
128
+ return true;
129
+ }
130
+ function measureColumnContent(table, columnId) {
131
+ const cells = table.querySelectorAll(
132
+ `td[data-gridkit-column="${CSS.escape(columnId)}"]`
133
+ );
134
+ const range = document.createRange();
135
+ let widest = null;
136
+ for (const cell of cells) {
137
+ range.selectNodeContents(cell);
138
+ const style = getComputedStyle(cell);
139
+ const width = range.getBoundingClientRect().width + parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth);
140
+ if (widest === null || width > widest) {
141
+ widest = width;
142
+ }
143
+ }
144
+ return widest;
145
+ }
146
+
147
+ // src/dragReorder.ts
148
+ var DRAG_THRESHOLD = 4;
149
+ var NO_DRAG = {
150
+ draggedId: null,
151
+ dropTarget: null,
152
+ ghostTransform: null
153
+ };
154
+ function startDragReorder(id, grabElement, press, handlers) {
155
+ if (!isPrimaryPress(press)) {
156
+ return false;
157
+ }
158
+ const { pointerId, clientX: startX } = press;
159
+ const grabX = startX - grabElement.getBoundingClientRect().left;
160
+ let dragging = false;
161
+ let target = null;
162
+ startPointerGesture(grabElement, pointerId, {
163
+ onMove(moveEvent) {
164
+ const { clientX, clientY } = moveEvent;
165
+ if (!dragging) {
166
+ if (Math.abs(clientX - startX) < DRAG_THRESHOLD) {
167
+ return;
168
+ }
169
+ dragging = true;
170
+ }
171
+ target = handlers.resolveTarget(clientX, clientY, id);
172
+ handlers.onState({
173
+ draggedId: id,
174
+ dropTarget: target,
175
+ ghostTransform: `translate(${String(clientX - grabX)}px, ${String(clientY)}px)`
176
+ });
177
+ },
178
+ onEnd() {
179
+ const dropped = target;
180
+ handlers.onState(NO_DRAG);
181
+ if (dragging && dropped !== null) {
182
+ handlers.onDrop(dropped, id);
183
+ }
184
+ },
185
+ onCancel() {
186
+ target = null;
187
+ handlers.onState(NO_DRAG);
188
+ }
189
+ });
190
+ return true;
191
+ }
192
+
193
+ // src/dropTargets.ts
194
+ import {
195
+ movesColumn,
196
+ movesGroupBy,
197
+ resolveDropBefore
198
+ } from "@gridkitjs/core";
199
+ function elementAt(x, y, selector) {
200
+ return document.elementFromPoint(x, y)?.closest(selector) ?? null;
201
+ }
202
+ function sideOf(element, x) {
203
+ const rect = element.getBoundingClientRect();
204
+ return x < rect.left + rect.width / 2 ? "before" : "after";
205
+ }
206
+ function resolveGroupByDropTarget(clientX, clientY, movedId, groupBy) {
207
+ const overBar = elementAt(clientX, clientY, "[data-gridkit-group-by-bar]");
208
+ if (overBar === null) {
209
+ return null;
210
+ }
211
+ const overChip = elementAt(clientX, clientY, "[data-gridkit-group-chip]");
212
+ const chipId = overChip?.getAttribute("data-gridkit-group-chip");
213
+ const beforeColumnId = overChip && chipId ? resolveDropBefore(
214
+ groupBy.map((entry) => entry.columnId),
215
+ chipId,
216
+ sideOf(overChip, clientX)
217
+ ) : null;
218
+ return movesGroupBy(groupBy, movedId, beforeColumnId) ? { kind: "group-by", beforeColumnId } : null;
219
+ }
220
+ function resolveColumnDropTarget(clientX, clientY, movedId, { order, groupBy, groupByDraggableIds }) {
221
+ const over = elementAt(clientX, clientY, "th[data-gridkit-column]");
222
+ const overId = over?.getAttribute("data-gridkit-column");
223
+ const beforeId = over && overId ? resolveDropBefore(order, overId, sideOf(over, clientX)) : void 0;
224
+ if (beforeId !== void 0) {
225
+ return movesColumn(order, movedId, beforeId) ? { kind: "column-order", beforeId } : null;
226
+ }
227
+ const alreadyGrouped = groupBy.some((entry) => entry.columnId === movedId);
228
+ return groupByDraggableIds.has(movedId) && !alreadyGrouped ? resolveGroupByDropTarget(clientX, clientY, movedId, groupBy) : null;
229
+ }
230
+
231
+ // src/elementWidth.ts
232
+ function observeElementWidth(element, onWidth) {
233
+ onWidth(element.clientWidth);
234
+ const observer = new ResizeObserver(() => {
235
+ onWidth(element.clientWidth);
236
+ });
237
+ observer.observe(element);
238
+ return () => {
239
+ observer.disconnect();
240
+ };
241
+ }
242
+
243
+ // src/gridFocus.ts
244
+ import {
245
+ clampFocus,
246
+ HEADER_ROW,
247
+ nextFocusForKey
248
+ } from "@gridkitjs/core";
249
+ var FALLBACK_PAGE = 10;
250
+ function cellIn(row, columnIndex) {
251
+ if (row === null) {
252
+ return null;
253
+ }
254
+ return row.cells[Math.min(columnIndex, row.cells.length - 1)] ?? null;
255
+ }
256
+ function findBodyRow(bodyTable, rowIndex) {
257
+ return bodyTable?.tBodies[0]?.querySelector(
258
+ `:scope > tr[data-gridkit-row-index="${String(rowIndex)}"]`
259
+ ) ?? null;
260
+ }
261
+ function measurePageSize(bodyTable) {
262
+ const firstRow = bodyTable?.tBodies[0]?.rows[0];
263
+ const viewport = bodyTable?.parentElement;
264
+ if (firstRow === void 0 || viewport === null || viewport === void 0 || firstRow.offsetHeight === 0) {
265
+ return FALLBACK_PAGE;
266
+ }
267
+ return Math.max(1, Math.floor(viewport.clientHeight / firstRow.offsetHeight));
268
+ }
269
+ function createGridFocus(initialOptions) {
270
+ let options = initialOptions;
271
+ let rows = null;
272
+ const listeners = /* @__PURE__ */ new Set();
273
+ let stored = { rowIndex: HEADER_ROW, columnIndex: 0 };
274
+ let snapshot = {
275
+ stored,
276
+ rowCount: options.rowCount,
277
+ columnCount: options.columnCount,
278
+ focus: clampFocus(stored, options.rowCount, options.columnCount)
279
+ };
280
+ let pendingMove = null;
281
+ function getFocus() {
282
+ if (snapshot.stored !== stored || snapshot.rowCount !== options.rowCount || snapshot.columnCount !== options.columnCount) {
283
+ snapshot = {
284
+ stored,
285
+ rowCount: options.rowCount,
286
+ columnCount: options.columnCount,
287
+ focus: clampFocus(stored, options.rowCount, options.columnCount)
288
+ };
289
+ }
290
+ return snapshot.focus;
291
+ }
292
+ function focusCell(rowIndex, columnIndex) {
293
+ const next = clampFocus(
294
+ { rowIndex, columnIndex },
295
+ options.rowCount,
296
+ options.columnCount
297
+ );
298
+ const current = getFocus();
299
+ if (next.rowIndex === current.rowIndex && next.columnIndex === current.columnIndex) {
300
+ return;
301
+ }
302
+ pendingMove = next;
303
+ stored = next;
304
+ for (const listener of listeners) {
305
+ listener();
306
+ }
307
+ }
308
+ return {
309
+ getFocus,
310
+ subscribe(listener) {
311
+ listeners.add(listener);
312
+ return () => {
313
+ listeners.delete(listener);
314
+ };
315
+ },
316
+ setOptions(next) {
317
+ options = next;
318
+ },
319
+ focusCell,
320
+ handleKey(key, modifiers, pageSize) {
321
+ const next = nextFocusForKey(
322
+ key,
323
+ modifiers,
324
+ getFocus(),
325
+ options.rowCount,
326
+ options.columnCount,
327
+ pageSize,
328
+ options.isSkippableRow
329
+ );
330
+ if (next === null) {
331
+ return false;
332
+ }
333
+ focusCell(next.rowIndex, next.columnIndex);
334
+ return true;
335
+ },
336
+ attach(next) {
337
+ rows = next;
338
+ return () => {
339
+ if (rows === next) {
340
+ rows = null;
341
+ }
342
+ };
343
+ },
344
+ focusDrawnCell(drawn) {
345
+ if (rows === null || pendingMove === null || snapshot.stored !== pendingMove || snapshot.focus !== drawn) {
346
+ return;
347
+ }
348
+ const cell = cellIn(
349
+ drawn.rowIndex === HEADER_ROW ? rows.getHeaderRow() : rows.getBodyRow(drawn.rowIndex),
350
+ drawn.columnIndex
351
+ );
352
+ if (cell !== null) {
353
+ pendingMove = null;
354
+ cell.focus();
355
+ return;
356
+ }
357
+ const range = options.virtualRange;
358
+ if (range != null && drawn.rowIndex !== HEADER_ROW && (drawn.rowIndex < range.startIndex || drawn.rowIndex > range.endIndex)) {
359
+ range.scrollToIndex(drawn.rowIndex);
360
+ return;
361
+ }
362
+ pendingMove = null;
363
+ }
364
+ };
365
+ }
366
+
367
+ // src/infiniteScroll.ts
368
+ function attachInfiniteScroll(root, options) {
369
+ let latest = options;
370
+ let intersecting = false;
371
+ let fired = false;
372
+ let wasLoading = options.isLoadingMore ?? false;
373
+ let observer = null;
374
+ let observed = null;
375
+ function maybeLoadMore() {
376
+ if (!intersecting || !latest.hasMore || (latest.isLoadingMore ?? false) || fired) {
377
+ return;
378
+ }
379
+ fired = true;
380
+ latest.onLoadMore?.();
381
+ }
382
+ function disconnect() {
383
+ observer?.disconnect();
384
+ observer = null;
385
+ observed = null;
386
+ intersecting = false;
387
+ }
388
+ function observe() {
389
+ const sentinel = latest.hasMore ? latest.sentinel : null;
390
+ const rootMargin = latest.rootMargin ?? "200px";
391
+ if (observed !== null && observed.sentinel === sentinel && observed.rootMargin === rootMargin) {
392
+ return;
393
+ }
394
+ disconnect();
395
+ if (sentinel === null) {
396
+ return;
397
+ }
398
+ observer = new IntersectionObserver(
399
+ (entries) => {
400
+ const entry = entries[entries.length - 1];
401
+ if (entry === void 0) {
402
+ return;
403
+ }
404
+ intersecting = entry.isIntersecting;
405
+ maybeLoadMore();
406
+ },
407
+ { root, rootMargin }
408
+ );
409
+ observer.observe(sentinel);
410
+ observed = { sentinel, rootMargin };
411
+ }
412
+ observe();
413
+ return {
414
+ update(next) {
415
+ latest = next;
416
+ const loading = next.isLoadingMore ?? false;
417
+ if (wasLoading && !loading) {
418
+ fired = false;
419
+ maybeLoadMore();
420
+ }
421
+ wasLoading = loading;
422
+ observe();
423
+ },
424
+ detach: disconnect
425
+ };
426
+ }
427
+
428
+ // src/overlay/placeOverlay.ts
429
+ var OPPOSITE_SIDE = {
430
+ top: "bottom",
431
+ bottom: "top",
432
+ left: "right",
433
+ right: "left"
434
+ };
435
+ function isVertical(side) {
436
+ return side === "top" || side === "bottom";
437
+ }
438
+ function roomBeside(side, anchor, viewport) {
439
+ switch (side) {
440
+ case "top":
441
+ return anchor.top;
442
+ case "bottom":
443
+ return viewport.height - (anchor.top + anchor.height);
444
+ case "left":
445
+ return anchor.left;
446
+ case "right":
447
+ return viewport.width - (anchor.left + anchor.width);
448
+ }
449
+ }
450
+ function chooseSide(preferred, needed, anchor, viewport) {
451
+ const opposite = OPPOSITE_SIDE[preferred];
452
+ const room = roomBeside(preferred, anchor, viewport);
453
+ const oppositeRoom = roomBeside(opposite, anchor, viewport);
454
+ if (room >= needed) {
455
+ return preferred;
456
+ }
457
+ return oppositeRoom >= needed || oppositeRoom > room ? opposite : preferred;
458
+ }
459
+ function chooseAlign(preferred, fitsAtStart, fitsAtEnd) {
460
+ if (preferred === "start") {
461
+ return fitsAtStart || !fitsAtEnd ? "start" : "end";
462
+ }
463
+ return fitsAtEnd || !fitsAtStart ? "end" : "start";
464
+ }
465
+ function offsetBeside(side, anchor, panel, gap) {
466
+ switch (side) {
467
+ case "top":
468
+ return anchor.top - gap - panel.height;
469
+ case "bottom":
470
+ return anchor.top + anchor.height + gap;
471
+ case "left":
472
+ return anchor.left - gap - panel.width;
473
+ case "right":
474
+ return anchor.left + anchor.width + gap;
475
+ }
476
+ }
477
+ function clampIntoView(value, limit) {
478
+ return Math.max(0, Math.min(value, limit));
479
+ }
480
+ function placeOverlay(anchor, panel, viewport, options = {}) {
481
+ const [preferredSide, preferredAlign] = (options.placement ?? "bottom-start").split("-");
482
+ const gap = options.gap ?? 0;
483
+ const vertical = isVertical(preferredSide);
484
+ const side = chooseSide(
485
+ preferredSide,
486
+ (vertical ? panel.height : panel.width) + gap,
487
+ anchor,
488
+ viewport
489
+ );
490
+ const anchorStart = vertical ? anchor.left : anchor.top;
491
+ const anchorEnd = anchorStart + (vertical ? anchor.width : anchor.height);
492
+ const extent = vertical ? panel.width : panel.height;
493
+ const room = vertical ? viewport.width : viewport.height;
494
+ const align = chooseAlign(
495
+ preferredAlign,
496
+ anchorStart + extent <= room,
497
+ anchorEnd - extent >= 0
498
+ );
499
+ const along = align === "start" ? anchorStart : anchorEnd - extent;
500
+ const away = offsetBeside(side, anchor, panel, gap);
501
+ return {
502
+ top: clampIntoView(vertical ? away : along, viewport.height - panel.height),
503
+ left: clampIntoView(vertical ? along : away, viewport.width - panel.width),
504
+ placement: `${side}-${align}`
505
+ };
506
+ }
507
+
508
+ // src/overlay/attachOverlay.ts
509
+ var FOCUSABLE = "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]";
510
+ function anchorRect(anchor) {
511
+ if (anchor instanceof Element) {
512
+ const { top, left, width, height } = anchor.getBoundingClientRect();
513
+ return { top, left, width, height };
514
+ }
515
+ return { top: anchor.y, left: anchor.x, width: 0, height: 0 };
516
+ }
517
+ function attachOverlay(anchor, panel, options) {
518
+ const { onDismiss } = options;
519
+ const returnFocusTo = options.returnFocusTo === void 0 ? document.activeElement instanceof HTMLElement ? document.activeElement : null : options.returnFocusTo;
520
+ function belongs(target) {
521
+ return target instanceof Node && (panel.contains(target) || anchor instanceof Element && anchor.contains(target));
522
+ }
523
+ function onKeyDown(event) {
524
+ if (event.key === "Escape") {
525
+ onDismiss("escape");
526
+ }
527
+ }
528
+ function onPointerDown(event) {
529
+ if (!belongs(event.target)) {
530
+ onDismiss("outside");
531
+ }
532
+ }
533
+ function onScroll(event) {
534
+ if (!(event.target instanceof Node && panel.contains(event.target))) {
535
+ onDismiss("scroll");
536
+ }
537
+ }
538
+ function onResize() {
539
+ onDismiss("resize");
540
+ }
541
+ function onFocusOut(event) {
542
+ if (event.relatedTarget !== null && !belongs(event.relatedTarget)) {
543
+ onDismiss("blur");
544
+ }
545
+ }
546
+ function onWindowBlur() {
547
+ onDismiss("blur");
548
+ }
549
+ panel.style.position = "fixed";
550
+ panel.style.top = "0px";
551
+ panel.style.left = "0px";
552
+ const { width, height } = panel.getBoundingClientRect();
553
+ const position = placeOverlay(
554
+ anchorRect(anchor),
555
+ { width, height },
556
+ {
557
+ width: document.documentElement.clientWidth,
558
+ height: document.documentElement.clientHeight
559
+ },
560
+ options
561
+ );
562
+ panel.style.top = `${String(position.top)}px`;
563
+ panel.style.left = `${String(position.left)}px`;
564
+ panel.setAttribute("data-gridkit-placement", position.placement);
565
+ const first = panel.querySelector(FOCUSABLE);
566
+ if (first === null && !panel.hasAttribute("tabindex")) {
567
+ panel.tabIndex = -1;
568
+ }
569
+ (first ?? panel).focus({ preventScroll: true });
570
+ document.addEventListener("keydown", onKeyDown, true);
571
+ document.addEventListener("pointerdown", onPointerDown, true);
572
+ window.addEventListener("scroll", onScroll, { capture: true, passive: true });
573
+ window.addEventListener("resize", onResize);
574
+ window.addEventListener("blur", onWindowBlur);
575
+ panel.addEventListener("focusout", onFocusOut);
576
+ let attached = true;
577
+ return () => {
578
+ if (!attached) {
579
+ return;
580
+ }
581
+ attached = false;
582
+ document.removeEventListener("keydown", onKeyDown, true);
583
+ document.removeEventListener("pointerdown", onPointerDown, true);
584
+ window.removeEventListener("scroll", onScroll, { capture: true });
585
+ window.removeEventListener("resize", onResize);
586
+ window.removeEventListener("blur", onWindowBlur);
587
+ panel.removeEventListener("focusout", onFocusOut);
588
+ const active = document.activeElement;
589
+ if (returnFocusTo !== null && (active === null || active === document.body || panel.contains(active))) {
590
+ returnFocusTo.focus({ preventScroll: true });
591
+ }
592
+ };
593
+ }
594
+
595
+ // src/rowVirtualizer.ts
596
+ import { visibleRange } from "@gridkitjs/core";
597
+ function sameRange(a, b) {
598
+ return a.startIndex === b.startIndex && a.endIndex === b.endIndex && a.offsetBefore === b.offsetBefore && a.offsetAfter === b.offsetAfter;
599
+ }
600
+ function createRowVirtualizer(scrollElement, options) {
601
+ const { estimatedRowHeight, overscan, onRange } = options;
602
+ let heights = [];
603
+ let scrollTop = scrollElement.scrollTop;
604
+ let viewportHeight = scrollElement.clientHeight;
605
+ let range = null;
606
+ let destroyed = false;
607
+ let pendingScroll;
608
+ const elements = /* @__PURE__ */ new Map();
609
+ const indexByElement = /* @__PURE__ */ new Map();
610
+ const refCallbacks = /* @__PURE__ */ new Map();
611
+ function recompute() {
612
+ if (destroyed) {
613
+ return;
614
+ }
615
+ const next = visibleRange(
616
+ heights,
617
+ estimatedRowHeight,
618
+ scrollTop,
619
+ viewportHeight,
620
+ overscan
621
+ );
622
+ if (range !== null && sameRange(range, next)) {
623
+ return;
624
+ }
625
+ range = next;
626
+ onRange(next);
627
+ }
628
+ const rowObserver = new ResizeObserver((entries) => {
629
+ let changed = false;
630
+ for (const entry of entries) {
631
+ const index = indexByElement.get(entry.target);
632
+ if (index === void 0) continue;
633
+ const height = entry.target.getBoundingClientRect().height;
634
+ if (heights[index] !== height) {
635
+ heights[index] = height;
636
+ changed = true;
637
+ }
638
+ }
639
+ if (changed) {
640
+ recompute();
641
+ }
642
+ });
643
+ const viewportObserver = new ResizeObserver(() => {
644
+ viewportHeight = scrollElement.clientHeight;
645
+ recompute();
646
+ });
647
+ viewportObserver.observe(scrollElement);
648
+ function handleScroll() {
649
+ scrollTop = scrollElement.scrollTop;
650
+ recompute();
651
+ }
652
+ scrollElement.addEventListener("scroll", handleScroll, { passive: true });
653
+ function estimateOffsetBefore(index) {
654
+ let total = 0;
655
+ for (let current = 0; current < index; current++) {
656
+ total += heights[current] ?? estimatedRowHeight;
657
+ }
658
+ return total;
659
+ }
660
+ recompute();
661
+ return {
662
+ setRowCount(count) {
663
+ heights = Array.from({ length: count }, (_, index) => heights[index]);
664
+ recompute();
665
+ },
666
+ resetHeights() {
667
+ heights = new Array(heights.length).fill(void 0);
668
+ recompute();
669
+ },
670
+ measureRow(index) {
671
+ let callback = refCallbacks.get(index);
672
+ if (callback === void 0) {
673
+ callback = (element) => {
674
+ const previous = elements.get(index);
675
+ if (previous !== void 0) {
676
+ rowObserver.unobserve(previous);
677
+ indexByElement.delete(previous);
678
+ elements.delete(index);
679
+ }
680
+ if (element === null || destroyed) {
681
+ return;
682
+ }
683
+ elements.set(index, element);
684
+ indexByElement.set(element, index);
685
+ rowObserver.observe(element);
686
+ if (pendingScroll?.index === index) {
687
+ element.scrollIntoView(pendingScroll.options);
688
+ pendingScroll = void 0;
689
+ }
690
+ };
691
+ refCallbacks.set(index, callback);
692
+ }
693
+ return callback;
694
+ },
695
+ scrollToIndex(index, scrollOptions) {
696
+ if (heights.length === 0) {
697
+ return;
698
+ }
699
+ const clamped = Math.max(0, Math.min(index, heights.length - 1));
700
+ const mounted = elements.get(clamped);
701
+ if (mounted !== void 0) {
702
+ mounted.scrollIntoView(scrollOptions);
703
+ return;
704
+ }
705
+ pendingScroll = { index: clamped, options: scrollOptions };
706
+ scrollElement.scrollTop = estimateOffsetBefore(clamped);
707
+ },
708
+ destroy() {
709
+ destroyed = true;
710
+ pendingScroll = void 0;
711
+ scrollElement.removeEventListener("scroll", handleScroll);
712
+ viewportObserver.disconnect();
713
+ rowObserver.disconnect();
714
+ elements.clear();
715
+ indexByElement.clear();
716
+ }
717
+ };
718
+ }
719
+ export {
720
+ NO_DRAG,
721
+ attachInfiniteScroll,
722
+ attachOverlay,
723
+ bodyCellFrom,
724
+ bodyRowFrom,
725
+ createGridFocus,
726
+ createLiveAnnouncer,
727
+ createRowVirtualizer,
728
+ findBodyRow,
729
+ groupRowFrom,
730
+ measureColumnContent,
731
+ measurePageSize,
732
+ observeElementWidth,
733
+ placeOverlay,
734
+ resolveColumnDropTarget,
735
+ resolveGroupByDropTarget,
736
+ startColumnResizeGesture,
737
+ startDragReorder
738
+ };
739
+ //# sourceMappingURL=index.js.map