@gridkitjs/react 0.0.0-dev-20261106222533

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,4978 @@
1
+ "use client";
2
+
3
+ // src/DataGrid/DataGrid.tsx
4
+ import {
5
+ useEffect as useEffect7,
6
+ useId as useId2,
7
+ useImperativeHandle as useImperativeHandle3,
8
+ useMemo as useMemo5,
9
+ useRef as useRef21,
10
+ useState as useState20
11
+ } from "react";
12
+ import {
13
+ moveColumnBefore,
14
+ resolveColumnHeaderText as resolveColumnHeaderText2,
15
+ resolveMessages,
16
+ resolveRows,
17
+ totalColumnWidth
18
+ } from "@gridkitjs/core";
19
+
20
+ // src/DataGrid/components/GridColgroup.tsx
21
+ import { jsx } from "react/jsx-runtime";
22
+ function GridColgroup({ columns }) {
23
+ return /* @__PURE__ */ jsx("colgroup", { children: columns.map((entry) => /* @__PURE__ */ jsx("col", { style: { width: entry.width } }, entry.id)) });
24
+ }
25
+
26
+ // src/DataGrid/components/GridHeader.tsx
27
+ import { createPortal } from "react-dom";
28
+ import {
29
+ buildKeyShortcuts,
30
+ intentOf,
31
+ isColumnFiltered,
32
+ KEYBOARD_STEP,
33
+ resolveColumnFilterMenu,
34
+ sortDirectionFor,
35
+ sortPriorityFor
36
+ } from "@gridkitjs/core";
37
+
38
+ // src/DataGrid/ariaAttr.ts
39
+ function ariaAttr(condition, key, value) {
40
+ return condition ? { [key]: value } : {};
41
+ }
42
+
43
+ // src/DataGrid/classNames.ts
44
+ function classNames(...values) {
45
+ return values.filter(Boolean).join(" ");
46
+ }
47
+
48
+ // src/DataGrid/icons.tsx
49
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
50
+ function Icon({ children }) {
51
+ return /* @__PURE__ */ jsx2(
52
+ "svg",
53
+ {
54
+ viewBox: "0 0 24 24",
55
+ fill: "none",
56
+ stroke: "currentColor",
57
+ strokeWidth: 2,
58
+ strokeLinecap: "round",
59
+ strokeLinejoin: "round",
60
+ "aria-hidden": "true",
61
+ children
62
+ }
63
+ );
64
+ }
65
+ function ChevronsUpDownIcon() {
66
+ return /* @__PURE__ */ jsxs(Icon, { children: [
67
+ /* @__PURE__ */ jsx2("path", { d: "m7 15 5 5 5-5" }),
68
+ /* @__PURE__ */ jsx2("path", { d: "m7 9 5-5 5 5" })
69
+ ] });
70
+ }
71
+ function ChevronUpIcon() {
72
+ return /* @__PURE__ */ jsx2(Icon, { children: /* @__PURE__ */ jsx2("path", { d: "m18 15-6-6-6 6" }) });
73
+ }
74
+ function GroupIcon() {
75
+ return /* @__PURE__ */ jsxs(Icon, { children: [
76
+ /* @__PURE__ */ jsx2("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }),
77
+ /* @__PURE__ */ jsx2("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }),
78
+ /* @__PURE__ */ jsx2("rect", { x: "8.5", y: "14", width: "7", height: "7", rx: "1" })
79
+ ] });
80
+ }
81
+ function FilterIcon() {
82
+ return /* @__PURE__ */ jsx2(Icon, { children: /* @__PURE__ */ jsx2("path", { d: "M22 3H2l8 9.46V19l4 2v-8.54z" }) });
83
+ }
84
+ function ChevronLeftIcon() {
85
+ return /* @__PURE__ */ jsx2(Icon, { children: /* @__PURE__ */ jsx2("path", { d: "m15 18-6-6 6-6" }) });
86
+ }
87
+ function ChevronRightIcon() {
88
+ return /* @__PURE__ */ jsx2(Icon, { children: /* @__PURE__ */ jsx2("path", { d: "m9 18 6-6-6-6" }) });
89
+ }
90
+ function ChevronsLeftIcon() {
91
+ return /* @__PURE__ */ jsxs(Icon, { children: [
92
+ /* @__PURE__ */ jsx2("path", { d: "m11 17-5-5 5-5" }),
93
+ /* @__PURE__ */ jsx2("path", { d: "m18 17-5-5 5-5" })
94
+ ] });
95
+ }
96
+ function ChevronsRightIcon() {
97
+ return /* @__PURE__ */ jsxs(Icon, { children: [
98
+ /* @__PURE__ */ jsx2("path", { d: "m6 17 5-5-5-5" }),
99
+ /* @__PURE__ */ jsx2("path", { d: "m13 17 5-5-5-5" })
100
+ ] });
101
+ }
102
+
103
+ // src/DataGrid/isOwnEvent.ts
104
+ function isOwnEvent(event) {
105
+ return event.target instanceof Node && event.currentTarget.contains(event.target);
106
+ }
107
+
108
+ // src/DataGrid/useGridNavigation.ts
109
+ import {
110
+ useEffect,
111
+ useLayoutEffect,
112
+ useState,
113
+ useSyncExternalStore
114
+ } from "react";
115
+ import { HEADER_ROW } from "@gridkitjs/core";
116
+ import {
117
+ createGridFocus,
118
+ findBodyRow,
119
+ measurePageSize
120
+ } from "@gridkitjs/dom";
121
+ function tabIndexFor(columnIndex, focusedColumnIndex) {
122
+ return columnIndex === focusedColumnIndex ? 0 : -1;
123
+ }
124
+ function useGridNavigation({
125
+ headerTableRef,
126
+ bodyTableRef,
127
+ rowCount,
128
+ columnCount,
129
+ isSkippableRow,
130
+ virtualRange
131
+ }) {
132
+ const options = {
133
+ rowCount,
134
+ columnCount,
135
+ isSkippableRow,
136
+ virtualRange
137
+ };
138
+ const [gridFocus] = useState(() => createGridFocus(options));
139
+ gridFocus.setOptions(options);
140
+ const focus = useSyncExternalStore(
141
+ gridFocus.subscribe,
142
+ gridFocus.getFocus,
143
+ gridFocus.getFocus
144
+ );
145
+ const [rows] = useState(() => ({
146
+ getHeaderRow: () => headerTableRef.current?.tHead?.rows[0] ?? null,
147
+ getBodyRow: (rowIndex) => findBodyRow(bodyTableRef.current, rowIndex)
148
+ }));
149
+ useLayoutEffect(() => gridFocus.attach(rows), [gridFocus, rows]);
150
+ useEffect(() => {
151
+ gridFocus.focusDrawnCell(focus);
152
+ });
153
+ return {
154
+ focus,
155
+ tabIndexFor: (rowIndex, columnIndex) => tabIndexFor(
156
+ columnIndex,
157
+ rowIndex === focus.rowIndex ? focus.columnIndex : null
158
+ ),
159
+ focusCell: gridFocus.focusCell,
160
+ focusCellNow: (rowIndex, columnIndex) => {
161
+ gridFocus.focusCell(rowIndex, columnIndex);
162
+ gridFocus.focusDrawnCell(gridFocus.getFocus());
163
+ },
164
+ focusTabStop: () => {
165
+ const cells = (focus.rowIndex === HEADER_ROW ? rows.getHeaderRow() : rows.getBodyRow(focus.rowIndex))?.cells;
166
+ cells?.[Math.min(focus.columnIndex, cells.length - 1)]?.focus();
167
+ },
168
+ onKeyDown: (event) => {
169
+ if (gridFocus.handleKey(
170
+ event.key,
171
+ event,
172
+ measurePageSize(bodyTableRef.current)
173
+ )) {
174
+ event.preventDefault();
175
+ }
176
+ }
177
+ };
178
+ }
179
+
180
+ // src/DataGrid/useGridSelection.ts
181
+ import { useMemo } from "react";
182
+ function keyboardSelectIntent(event) {
183
+ return {
184
+ ctrlKey: event.key === " " || event.ctrlKey,
185
+ metaKey: event.metaKey,
186
+ shiftKey: event.shiftKey
187
+ };
188
+ }
189
+ function useGridSelection({
190
+ controller,
191
+ state,
192
+ selectable
193
+ }) {
194
+ const selectedRowIds = useMemo(
195
+ () => new Set(state.rowSelection),
196
+ [state.rowSelection]
197
+ );
198
+ const selectedColumnIds = useMemo(
199
+ () => new Set(state.columnSelection),
200
+ [state.columnSelection]
201
+ );
202
+ return {
203
+ rowMode: selectable?.rows ?? false,
204
+ columnMode: selectable?.columns ?? false,
205
+ cellMode: selectable?.cells ?? false,
206
+ toggleOnClick: selectable?.toggleOnClick ?? false,
207
+ selectedRowIds,
208
+ selectedColumnIds,
209
+ selectedCell: state.cellSelection,
210
+ selectRow: controller.selectRow,
211
+ selectColumn: controller.selectColumn,
212
+ selectCell: controller.selectCell,
213
+ selectAllRows: controller.selectAllRows,
214
+ clear: controller.clearSelection
215
+ };
216
+ }
217
+
218
+ // src/DataGrid/components/GridHeader.tsx
219
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
220
+ function GridHeader({
221
+ columns,
222
+ resize,
223
+ drag,
224
+ controller,
225
+ state,
226
+ sortableColumns,
227
+ groupableColumns,
228
+ groupToggleIconColumns,
229
+ headerMenu,
230
+ panels,
231
+ messages,
232
+ nav,
233
+ selection
234
+ }) {
235
+ const { columnSort, groupBy, filter } = state;
236
+ const beforeId = drag.dropTarget?.kind === "column-order" ? drag.dropTarget.beforeId : void 0;
237
+ const draggedEntry = columns.find((entry) => entry.id === drag.draggedColumnId) ?? null;
238
+ function toggleFilterMenu(columnId) {
239
+ const { panel } = panels;
240
+ if (panel?.kind === "filter" && panel.target.area === "header" && panel.target.columnId === columnId) {
241
+ panels.close();
242
+ } else {
243
+ panels.open("filter", { area: "header", columnId });
244
+ }
245
+ }
246
+ return /* @__PURE__ */ jsxs2("thead", { role: "rowgroup", children: [
247
+ /* @__PURE__ */ jsx3("tr", { className: "grid-header", role: "row", "aria-rowindex": 1, children: columns.map((entry, index) => {
248
+ const { column } = entry;
249
+ const resizing = resize.activeColumnId === entry.id;
250
+ const dragging = drag.draggedColumnId === entry.id;
251
+ const dropBefore = beforeId !== void 0 && beforeId === entry.id;
252
+ const dropAfter = beforeId === null && index === columns.length - 1;
253
+ const selected = selection.selectedColumnIds.has(entry.id);
254
+ const sortable = column.sortable ?? sortableColumns;
255
+ const sortDirection = sortDirectionFor(columnSort, entry.id);
256
+ const sortPriority = sortPriorityFor(columnSort, entry.id);
257
+ const groupable = column.groupable ?? groupableColumns;
258
+ const showGroupToggleIcon = column.groupToggleIcon ?? groupToggleIconColumns;
259
+ const isGrouped = groupBy.some(
260
+ (level) => level.columnId === entry.id
261
+ );
262
+ const filterable = resolveColumnFilterMenu(controller.getOptions(), column) !== null;
263
+ const filtered = filterable && isColumnFiltered(filter, entry.id);
264
+ const shortcuts = buildKeyShortcuts({
265
+ reorderable: entry.reorderable,
266
+ resizable: entry.resizable,
267
+ sortable,
268
+ groupable,
269
+ filterable,
270
+ contextMenu: headerMenu
271
+ });
272
+ return /* @__PURE__ */ jsxs2(
273
+ "th",
274
+ {
275
+ scope: "col",
276
+ role: "columnheader",
277
+ "data-gridkit-column": entry.id,
278
+ "aria-colindex": index + 1,
279
+ tabIndex: nav.tabIndexFor(HEADER_ROW, index),
280
+ ...ariaAttr(
281
+ selection.columnMode !== false,
282
+ "aria-selected",
283
+ selected
284
+ ),
285
+ onFocus: () => {
286
+ nav.focusCell(HEADER_ROW, index);
287
+ },
288
+ onClick: (event) => {
289
+ if (drag.justDragged()) {
290
+ return;
291
+ }
292
+ if (event.target instanceof Element && (event.target.closest(".header-resize-handle") !== null || event.target.closest(".header-sort-toggle") !== null || event.target.closest(".header-group-toggle") !== null || event.target.closest(".header-filter-toggle") !== null)) {
293
+ return;
294
+ }
295
+ selection.selectColumn(entry.id, intentOf(event));
296
+ },
297
+ onKeyDown: (event) => {
298
+ if (!isOwnEvent(event)) return;
299
+ const horizontal = event.key === "ArrowLeft" || event.key === "ArrowRight";
300
+ const direction = event.key === "ArrowLeft" ? -1 : 1;
301
+ if (horizontal && event.ctrlKey && entry.reorderable) {
302
+ event.preventDefault();
303
+ drag.moveByKeyboard(entry, direction);
304
+ return;
305
+ }
306
+ if (horizontal && event.altKey && entry.resizable) {
307
+ event.preventDefault();
308
+ resize.nudge(entry, direction * KEYBOARD_STEP);
309
+ return;
310
+ }
311
+ if (event.key === "Enter" && event.altKey && entry.resizable) {
312
+ event.preventDefault();
313
+ resize.sizeToContent(entry);
314
+ return;
315
+ }
316
+ if (event.key === "ArrowUp" && event.altKey && sortable) {
317
+ event.preventDefault();
318
+ controller.toggleColumnSort(entry.id, {
319
+ stack: event.shiftKey
320
+ });
321
+ return;
322
+ }
323
+ if (event.key === "ArrowDown" && event.altKey && event.shiftKey && filterable) {
324
+ event.preventDefault();
325
+ toggleFilterMenu(entry.id);
326
+ return;
327
+ }
328
+ if (event.key === "ArrowDown" && event.altKey && !event.shiftKey && groupable) {
329
+ event.preventDefault();
330
+ controller.toggleGroupBy(entry.id);
331
+ return;
332
+ }
333
+ if (event.key === " " || event.key === "Enter") {
334
+ event.preventDefault();
335
+ selection.selectColumn(
336
+ entry.id,
337
+ intentOf(keyboardSelectIntent(event))
338
+ );
339
+ return;
340
+ }
341
+ nav.onKeyDown(event);
342
+ },
343
+ className: classNames(
344
+ "header-cell",
345
+ // The grab affordance a header showing either drag
346
+ // destination shares — a column dragged only for grouping
347
+ // (`groupByDraggable` without `reorderable`) grabs the same
348
+ // way one dragged only for reordering does.
349
+ entry.reorderable || entry.groupByDraggable ? "is-reorderable" : "",
350
+ selected ? "is-selected" : "",
351
+ resizing ? "is-resizing" : "",
352
+ dragging ? "is-dragging" : "",
353
+ dropBefore ? "is-drop-before" : "",
354
+ dropAfter ? "is-drop-after" : "",
355
+ column.wrap?.header ? "is-wrapped" : "",
356
+ column.headerClassName ?? ""
357
+ ),
358
+ ...ariaAttr(shortcuts !== "", "aria-keyshortcuts", shortcuts),
359
+ ...ariaAttr(
360
+ filtered,
361
+ "aria-description",
362
+ messages.columnFiltered
363
+ ),
364
+ ...ariaAttr(
365
+ sortable && sortDirection !== null,
366
+ "aria-sort",
367
+ sortDirection === "asc" ? "ascending" : "descending"
368
+ ),
369
+ ...(entry.reorderable || entry.groupByDraggable) && {
370
+ onPointerDown: (event) => {
371
+ drag.startDrag(entry, event);
372
+ }
373
+ },
374
+ children: [
375
+ groupable && showGroupToggleIcon && /*
376
+ * Inline, before the label, rather than absolutely
377
+ * positioned like the sort toggle and resize handle: those
378
+ * two already share the header's right edge, and a third
379
+ * overlay there would need its own carve-out. Sitting in
380
+ * normal flow costs nothing but a few pixels of label width.
381
+ * A pointer affordance only, same as the other two — the
382
+ * keyboard equivalent is Alt+ArrowDown on the header itself.
383
+ */
384
+ /* @__PURE__ */ jsx3(
385
+ "span",
386
+ {
387
+ className: classNames(
388
+ "header-group-toggle",
389
+ isGrouped ? "is-grouped" : ""
390
+ ),
391
+ "aria-hidden": "true",
392
+ tabIndex: -1,
393
+ onPointerDown: (event) => {
394
+ event.stopPropagation();
395
+ },
396
+ onClick: (event) => {
397
+ event.stopPropagation();
398
+ controller.toggleGroupBy(entry.id);
399
+ },
400
+ children: /* @__PURE__ */ jsx3(GroupIcon, {})
401
+ }
402
+ ),
403
+ entry.label,
404
+ sortable && /*
405
+ * A pointer affordance only, and hidden from assistive
406
+ * technology like the resize handle: the keyboard equivalent
407
+ * lives on the header itself, under Alt+ArrowUp (plain) /
408
+ * Alt+Shift+ArrowUp (stacking).
409
+ */
410
+ /* @__PURE__ */ jsxs2(
411
+ "span",
412
+ {
413
+ className: classNames(
414
+ "header-sort-toggle",
415
+ sortDirection !== null ? `is-sorted-${sortDirection}` : ""
416
+ ),
417
+ "aria-hidden": "true",
418
+ tabIndex: -1,
419
+ onPointerDown: (event) => {
420
+ event.stopPropagation();
421
+ },
422
+ onClick: (event) => {
423
+ controller.toggleColumnSort(entry.id, {
424
+ stack: event.shiftKey
425
+ });
426
+ },
427
+ children: [
428
+ sortDirection === null ? /* @__PURE__ */ jsx3(ChevronsUpDownIcon, {}) : /* @__PURE__ */ jsx3(ChevronUpIcon, {}),
429
+ sortPriority !== null && sortPriority > 1 && /* @__PURE__ */ jsx3("span", { className: "header-sort-priority", children: sortPriority })
430
+ ]
431
+ }
432
+ ),
433
+ filterable && /*
434
+ * A pointer affordance only, like the sort toggle: the
435
+ * keyboard opens the menu from the header itself, under
436
+ * Alt+Shift+ArrowDown, and a screen reader hears the
437
+ * filtered state as the header's description.
438
+ */
439
+ /* @__PURE__ */ jsx3(
440
+ "span",
441
+ {
442
+ className: classNames(
443
+ "header-filter-toggle",
444
+ filtered ? "is-filtered" : ""
445
+ ),
446
+ "aria-hidden": "true",
447
+ tabIndex: -1,
448
+ onPointerDown: (event) => {
449
+ event.stopPropagation();
450
+ },
451
+ onClick: () => {
452
+ toggleFilterMenu(entry.id);
453
+ },
454
+ children: /* @__PURE__ */ jsx3(FilterIcon, {})
455
+ }
456
+ ),
457
+ entry.resizable && /*
458
+ * A pointer affordance only, and hidden from assistive
459
+ * technology accordingly: a focusable handle per column would
460
+ * put N tab stops inside a widget that is meant to have one.
461
+ * The keyboard resizes from the header itself, under Alt.
462
+ */
463
+ /* @__PURE__ */ jsx3(
464
+ "span",
465
+ {
466
+ className: "header-resize-handle",
467
+ "aria-hidden": "true",
468
+ tabIndex: -1,
469
+ onPointerDown: (event) => {
470
+ resize.startResize(entry, event);
471
+ },
472
+ onDoubleClick: () => {
473
+ resize.sizeToContent(entry);
474
+ }
475
+ }
476
+ )
477
+ ]
478
+ },
479
+ entry.id
480
+ );
481
+ }) }),
482
+ draggedEntry !== null && drag.ghostTransform !== null && createPortal(
483
+ /* @__PURE__ */ jsx3(
484
+ "div",
485
+ {
486
+ className: "gridkit-data-grid header-cell drag-ghost",
487
+ style: {
488
+ width: draggedEntry.width,
489
+ transform: drag.ghostTransform
490
+ },
491
+ "aria-hidden": "true",
492
+ children: draggedEntry.label
493
+ }
494
+ ),
495
+ document.body
496
+ )
497
+ ] });
498
+ }
499
+
500
+ // src/DataGrid/components/GridBody.tsx
501
+ import {
502
+ useMemo as useMemo2,
503
+ useState as useState2
504
+ } from "react";
505
+ import {
506
+ groupRowId,
507
+ intentOf as intentOf2
508
+ } from "@gridkitjs/core";
509
+ import {
510
+ bodyCellFrom,
511
+ bodyRowFrom,
512
+ groupRowFrom
513
+ } from "@gridkitjs/dom";
514
+
515
+ // src/DataGrid/rowProps.ts
516
+ var NO_ATTRIBUTES = {};
517
+ var NO_ROW_PROPS = {
518
+ className: "",
519
+ attributes: NO_ATTRIBUTES
520
+ };
521
+ function sameAttributes(a, b) {
522
+ const keys = Object.keys(a);
523
+ return keys.length === Object.keys(b).length && keys.every((key) => a[key] === b[key]);
524
+ }
525
+ function createRowPropsResolver() {
526
+ let previous = /* @__PURE__ */ new Map();
527
+ return (rows, getRowProps) => {
528
+ if (getRowProps === void 0) {
529
+ previous = /* @__PURE__ */ new Map();
530
+ return () => NO_ROW_PROPS;
531
+ }
532
+ const resolved = /* @__PURE__ */ new Map();
533
+ const attributesById = /* @__PURE__ */ new Map();
534
+ for (const entry of rows) {
535
+ const { className = "", attributes = NO_ATTRIBUTES } = getRowProps(entry);
536
+ const last = previous.get(entry.rowId);
537
+ const stable = last !== void 0 && sameAttributes(last, attributes) ? last : attributes;
538
+ attributesById.set(entry.rowId, stable);
539
+ resolved.set(entry.rowId, { className, attributes: stable });
540
+ }
541
+ previous = attributesById;
542
+ return (rowId) => resolved.get(rowId) ?? NO_ROW_PROPS;
543
+ };
544
+ }
545
+
546
+ // src/DataGrid/components/GridGroupRow.tsx
547
+ import { memo } from "react";
548
+ import {
549
+ formatGroupValue,
550
+ resolveColumnLabel
551
+ } from "@gridkitjs/core";
552
+
553
+ // src/DataGrid/components/renderAggregate.ts
554
+ import {
555
+ formatAggregateValue
556
+ } from "@gridkitjs/core";
557
+ function renderAggregate(value, spec, column, rows, locale) {
558
+ if (column?.footerTemplate) {
559
+ return column.footerTemplate({ value, rows });
560
+ }
561
+ if (value === null || value === void 0) {
562
+ return "\u2014";
563
+ }
564
+ return formatAggregateValue(value, spec, column, locale);
565
+ }
566
+
567
+ // src/DataGrid/components/GridGroupRow.tsx
568
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
569
+ function GridGroupRowComponent({
570
+ columnCount,
571
+ groupId,
572
+ level,
573
+ groupColumn,
574
+ value,
575
+ expanded,
576
+ count,
577
+ rowIndex,
578
+ datasetIndex,
579
+ posinset,
580
+ setsize,
581
+ focused,
582
+ contextMenu,
583
+ aggregates,
584
+ results,
585
+ definitions,
586
+ locale,
587
+ cellMessages,
588
+ ref
589
+ }) {
590
+ return /* @__PURE__ */ jsx4(
591
+ "tr",
592
+ {
593
+ ref,
594
+ role: "row",
595
+ "aria-rowindex": datasetIndex + 2,
596
+ "aria-level": level + 1,
597
+ "aria-expanded": expanded,
598
+ "aria-setsize": setsize,
599
+ "aria-posinset": posinset,
600
+ "data-gridkit-group": groupId,
601
+ "data-gridkit-row-index": rowIndex,
602
+ className: classNames(
603
+ "grid-group-row",
604
+ `is-group-level-${String(level)}`
605
+ ),
606
+ children: /* @__PURE__ */ jsx4(
607
+ "td",
608
+ {
609
+ role: "gridcell",
610
+ colSpan: columnCount,
611
+ tabIndex: focused ? 0 : -1,
612
+ "aria-keyshortcuts": contextMenu ? "Space Enter Shift+F10" : "Space Enter",
613
+ className: "grid-group-cell",
614
+ children: /* @__PURE__ */ jsxs3(
615
+ "div",
616
+ {
617
+ className: "grid-group-cell-content",
618
+ style: { paddingInlineStart: `${String(level * 1.25 + 0.5)}rem` },
619
+ children: [
620
+ /* @__PURE__ */ jsx4(
621
+ "span",
622
+ {
623
+ className: classNames(
624
+ "group-toggle",
625
+ expanded ? "is-expanded" : "is-collapsed"
626
+ ),
627
+ "aria-hidden": "true"
628
+ }
629
+ ),
630
+ /* @__PURE__ */ jsxs3("span", { className: "group-label", children: [
631
+ resolveColumnLabel(groupColumn),
632
+ ":",
633
+ " ",
634
+ formatGroupValue(value, groupColumn, locale, cellMessages)
635
+ ] }),
636
+ /* @__PURE__ */ jsxs3("span", { className: "group-count", children: [
637
+ "(",
638
+ count,
639
+ ")"
640
+ ] }),
641
+ aggregates.length > 0 && /* @__PURE__ */ jsx4("span", { className: "group-aggregates", children: aggregates.map((spec) => {
642
+ const key = spec.id ?? spec.columnId;
643
+ const aggregateValue = results.get(key);
644
+ const column = definitions.get(spec.columnId);
645
+ const rendered = renderAggregate(
646
+ aggregateValue,
647
+ spec,
648
+ column,
649
+ [],
650
+ locale
651
+ );
652
+ const aggregateLabel = spec.id ?? (column === void 0 ? spec.columnId : resolveColumnLabel(column));
653
+ return /* @__PURE__ */ jsxs3("span", { className: "group-aggregate", children: [
654
+ aggregateLabel,
655
+ ": ",
656
+ rendered
657
+ ] }, key);
658
+ }) })
659
+ ]
660
+ }
661
+ )
662
+ }
663
+ )
664
+ }
665
+ );
666
+ }
667
+ var GridGroupRow = memo(
668
+ GridGroupRowComponent
669
+ );
670
+ var GridGroupRow_default = GridGroupRow;
671
+
672
+ // src/DataGrid/components/GridGroupSummaryRow.tsx
673
+ import { memo as memo2 } from "react";
674
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
675
+ function GridGroupSummaryRowComponent({
676
+ groupId,
677
+ level,
678
+ rowIndex,
679
+ datasetIndex,
680
+ results,
681
+ aggregates,
682
+ columns,
683
+ locale,
684
+ ref
685
+ }) {
686
+ return /* @__PURE__ */ jsx5(
687
+ "tr",
688
+ {
689
+ ref,
690
+ role: "row",
691
+ "aria-rowindex": datasetIndex + 2,
692
+ "data-gridkit-group-summary": groupId,
693
+ "data-gridkit-row-index": rowIndex,
694
+ className: classNames(
695
+ "grid-group-summary-row",
696
+ `is-group-level-${String(level)}`
697
+ ),
698
+ children: columns.map((entry) => {
699
+ const specs = aggregates.filter(
700
+ (candidate) => candidate.columnId === entry.id
701
+ );
702
+ return /* @__PURE__ */ jsxs4(
703
+ "td",
704
+ {
705
+ role: "gridcell",
706
+ "data-gridkit-column": entry.id,
707
+ className: "grid-group-summary-cell",
708
+ style: { textAlign: specs[0]?.alignment ?? entry.alignment },
709
+ children: [
710
+ entry.id === columns[0]?.id && /* @__PURE__ */ jsx5(
711
+ "span",
712
+ {
713
+ className: "grid-group-summary-indent",
714
+ style: { width: `${String(level * 1.25 + 0.5)}rem` },
715
+ "aria-hidden": "true"
716
+ }
717
+ ),
718
+ specs.map(
719
+ (spec) => renderAggregate(
720
+ results.get(spec.id ?? spec.columnId),
721
+ spec,
722
+ entry.column,
723
+ [],
724
+ locale
725
+ )
726
+ ).flatMap(
727
+ (rendered, index) => index === 0 ? [rendered] : [", ", rendered]
728
+ )
729
+ ]
730
+ },
731
+ entry.id
732
+ );
733
+ })
734
+ }
735
+ );
736
+ }
737
+ var GridGroupSummaryRow = memo2(
738
+ GridGroupSummaryRowComponent
739
+ );
740
+ var GridGroupSummaryRow_default = GridGroupSummaryRow;
741
+
742
+ // src/DataGrid/components/GridRow.tsx
743
+ import {
744
+ accessDotted,
745
+ formatCellValue
746
+ } from "@gridkitjs/core";
747
+ import { memo as memo3 } from "react";
748
+ import { jsx as jsx6 } from "react/jsx-runtime";
749
+ function GridRowComponent({
750
+ columns,
751
+ rowId,
752
+ row,
753
+ rowIndex,
754
+ datasetIndex,
755
+ activeColumnId,
756
+ selectedColumnIds,
757
+ selected,
758
+ selectedColumnId,
759
+ focusedColumnIndex,
760
+ rowsSelectable,
761
+ cellsSelectable,
762
+ contextMenu,
763
+ locale,
764
+ cellMessages,
765
+ rowClassName,
766
+ rowAttributes,
767
+ ref
768
+ }) {
769
+ const shortcuts = [
770
+ cellsSelectable ? "Space Enter" : "",
771
+ contextMenu ? "Shift+F10" : ""
772
+ ].filter(Boolean).join(" ");
773
+ return /* @__PURE__ */ jsx6(
774
+ "tr",
775
+ {
776
+ ...rowAttributes,
777
+ ref,
778
+ role: "row",
779
+ "aria-rowindex": datasetIndex + 2,
780
+ "data-gridkit-row-index": rowIndex,
781
+ "data-gridkit-row-id": rowId,
782
+ ...ariaAttr(rowsSelectable, "aria-selected", selected),
783
+ className: classNames(
784
+ "grid-row",
785
+ selected ? "is-selected" : "",
786
+ rowClassName
787
+ ),
788
+ children: columns.map(({ id, column, alignment }, columnIndex) => {
789
+ const value = accessDotted(row, column.field);
790
+ const cellSelected = id === selectedColumnId;
791
+ return /* @__PURE__ */ jsx6(
792
+ "td",
793
+ {
794
+ role: "gridcell",
795
+ "data-gridkit-column": id,
796
+ "aria-colindex": columnIndex + 1,
797
+ tabIndex: tabIndexFor(columnIndex, focusedColumnIndex),
798
+ ...ariaAttr(cellsSelectable, "aria-selected", cellSelected),
799
+ ...ariaAttr(shortcuts !== "", "aria-keyshortcuts", shortcuts),
800
+ className: classNames(
801
+ "grid-cell",
802
+ id === activeColumnId ? "is-resizing" : "",
803
+ cellSelected || selectedColumnIds.has(id) ? "is-selected" : "",
804
+ column.wrap?.cells ? "is-wrapped" : "",
805
+ column.cellClassName ?? ""
806
+ ),
807
+ style: { textAlign: alignment },
808
+ children: column.cellTemplate ? column.cellTemplate({
809
+ value,
810
+ row,
811
+ rowIndex,
812
+ datasetIndex,
813
+ rowId,
814
+ selected
815
+ }) : formatCellValue(value, column, locale, row, cellMessages)
816
+ },
817
+ id
818
+ );
819
+ })
820
+ }
821
+ );
822
+ }
823
+ var GridRow = memo3(GridRowComponent);
824
+ var GridRow_default = GridRow;
825
+
826
+ // src/DataGrid/components/GridBody.tsx
827
+ import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
828
+ function groupAriaMeta(rows) {
829
+ const totals = /* @__PURE__ */ new Map();
830
+ for (const entry of rows) {
831
+ if (!("kind" in entry) || entry.kind !== "group") continue;
832
+ const parentKey = groupRowId(entry.path.slice(0, -1));
833
+ totals.set(parentKey, (totals.get(parentKey) ?? 0) + 1);
834
+ }
835
+ const seen = /* @__PURE__ */ new Map();
836
+ const meta = /* @__PURE__ */ new Map();
837
+ for (const entry of rows) {
838
+ if (!("kind" in entry) || entry.kind !== "group") continue;
839
+ const parentKey = groupRowId(entry.path.slice(0, -1));
840
+ const posinset = (seen.get(parentKey) ?? 0) + 1;
841
+ seen.set(parentKey, posinset);
842
+ meta.set(entry.groupId, {
843
+ posinset,
844
+ setsize: totals.get(parentKey) ?? posinset
845
+ });
846
+ }
847
+ return meta;
848
+ }
849
+ function GridBody({
850
+ columns,
851
+ definitions,
852
+ rows,
853
+ activeColumnId,
854
+ nav,
855
+ selection,
856
+ controller,
857
+ aggregates,
858
+ groupAggregateDisplay,
859
+ virtualRange,
860
+ infiniteScroll,
861
+ sentinelRef,
862
+ locale,
863
+ getRowProps,
864
+ onRowClick,
865
+ onRowDoubleClick,
866
+ messages,
867
+ cellMenu,
868
+ groupMenu
869
+ }) {
870
+ const { selectedCell, rowMode, cellMode, toggleOnClick } = selection;
871
+ const ariaMeta = useMemo2(() => groupAriaMeta(rows), [rows]);
872
+ const visibleRows = virtualRange === null ? rows : rows.slice(virtualRange.startIndex, virtualRange.endIndex + 1);
873
+ const { booleanTrue, booleanFalse, blankGroupValue } = messages;
874
+ const cellMessages = useMemo2(
875
+ () => ({ booleanTrue, booleanFalse, blankGroupValue }),
876
+ [booleanTrue, booleanFalse, blankGroupValue]
877
+ );
878
+ const [resolveRowProps] = useState2(() => createRowPropsResolver());
879
+ const rowPropsOf = resolveRowProps(
880
+ visibleRows.filter(
881
+ (entry) => !("kind" in entry)
882
+ ),
883
+ getRowProps
884
+ );
885
+ function targetOf(target) {
886
+ const position = bodyCellFrom(target);
887
+ const entry = position === null ? void 0 : rows[position.rowIndex];
888
+ if (position === null || entry === void 0 || "kind" in entry) {
889
+ return null;
890
+ }
891
+ return { position, rowId: entry.rowId };
892
+ }
893
+ function select(rowId, columnId, modifiers) {
894
+ if (rowMode !== false) {
895
+ selection.selectRow(rowId, intentOf2(modifiers, { toggleOnClick }));
896
+ }
897
+ if (cellMode !== false) {
898
+ selection.selectCell({ rowId, columnId }, intentOf2(modifiers));
899
+ }
900
+ }
901
+ function rowPointerEventOf(event) {
902
+ const target = bodyRowFrom(event.target);
903
+ const entry = target === null ? void 0 : rows[target.rowIndex];
904
+ if (target === null || entry === void 0 || "kind" in entry) {
905
+ return null;
906
+ }
907
+ return {
908
+ row: entry,
909
+ columnId: target.columnId,
910
+ native: {
911
+ ctrlKey: event.ctrlKey,
912
+ metaKey: event.metaKey,
913
+ shiftKey: event.shiftKey,
914
+ altKey: event.altKey,
915
+ button: event.button
916
+ }
917
+ };
918
+ }
919
+ function reportRowPointer(event, callback) {
920
+ if (callback === void 0) return;
921
+ const pointer = rowPointerEventOf(event);
922
+ if (pointer !== null) {
923
+ callback(pointer);
924
+ }
925
+ }
926
+ return /* @__PURE__ */ jsxs5(
927
+ "tbody",
928
+ {
929
+ role: "rowgroup",
930
+ className: "grid-body",
931
+ onFocus: (event) => {
932
+ const group = groupRowFrom(event.target);
933
+ if (group !== null) {
934
+ nav.focusCell(group.rowIndex, 0);
935
+ return;
936
+ }
937
+ const found = targetOf(event.target);
938
+ if (found === null) return;
939
+ nav.focusCell(found.position.rowIndex, found.position.columnIndex);
940
+ },
941
+ onClick: (event) => {
942
+ const group = groupRowFrom(event.target);
943
+ if (group !== null) {
944
+ controller.toggleGroupExpansion(group.groupId);
945
+ return;
946
+ }
947
+ const found = targetOf(event.target);
948
+ if (found !== null) {
949
+ select(found.rowId, found.position.columnId, event);
950
+ }
951
+ reportRowPointer(event, onRowClick);
952
+ },
953
+ onDoubleClick: (event) => {
954
+ reportRowPointer(event, onRowDoubleClick);
955
+ },
956
+ onKeyDown: (event) => {
957
+ if (!isOwnEvent(event)) return;
958
+ const group = groupRowFrom(event.target);
959
+ if (group !== null) {
960
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
961
+ event.preventDefault();
962
+ return;
963
+ }
964
+ if (event.key === " " || event.key === "Enter") {
965
+ event.preventDefault();
966
+ controller.toggleGroupExpansion(group.groupId);
967
+ return;
968
+ }
969
+ nav.onKeyDown(event);
970
+ return;
971
+ }
972
+ if (event.key === " " || event.key === "Enter") {
973
+ const found = targetOf(event.target);
974
+ if (found === null) return;
975
+ event.preventDefault();
976
+ select(
977
+ found.rowId,
978
+ found.position.columnId,
979
+ keyboardSelectIntent(event)
980
+ );
981
+ return;
982
+ }
983
+ nav.onKeyDown(event);
984
+ },
985
+ children: [
986
+ virtualRange !== null && virtualRange.offsetBefore > 0 && // `aria-hidden` and carrying neither `data-gridkit-column` nor
987
+ // `data-gridkit-group`/`data-gridkit-row-index`, so `bodyCellFrom`/
988
+ // `groupRowFrom` never mistake it for a real row — it exists purely
989
+ // to give the browser's scrollbar the correct total height and thumb
990
+ // position, the same way `GridFooter`'s `<tfoot>` isn't a "row" to
991
+ // any of this grid's own row-counting logic.
992
+ /* @__PURE__ */ jsx7("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx7(
993
+ "td",
994
+ {
995
+ style: {
996
+ height: virtualRange.offsetBefore,
997
+ padding: 0,
998
+ border: "none"
999
+ }
1000
+ }
1001
+ ) }),
1002
+ visibleRows.map((entry) => {
1003
+ if (!("kind" in entry)) {
1004
+ const rowProps = rowPropsOf(entry.rowId);
1005
+ return /* @__PURE__ */ jsx7(
1006
+ GridRow_default,
1007
+ {
1008
+ ref: virtualRange?.measureRow(entry.rowIndex),
1009
+ columns,
1010
+ rowId: entry.rowId,
1011
+ row: entry.row,
1012
+ rowIndex: entry.rowIndex,
1013
+ datasetIndex: entry.datasetIndex,
1014
+ activeColumnId,
1015
+ selectedColumnIds: selection.selectedColumnIds,
1016
+ selected: selection.selectedRowIds.has(entry.rowId),
1017
+ selectedColumnId: selectedCell?.rowId === entry.rowId ? selectedCell.columnId : null,
1018
+ focusedColumnIndex: nav.focus.rowIndex === entry.rowIndex ? nav.focus.columnIndex : null,
1019
+ rowsSelectable: rowMode !== false,
1020
+ cellsSelectable: cellMode !== false,
1021
+ contextMenu: cellMenu,
1022
+ locale,
1023
+ cellMessages,
1024
+ rowClassName: rowProps.className,
1025
+ rowAttributes: rowProps.attributes
1026
+ },
1027
+ entry.rowId
1028
+ );
1029
+ }
1030
+ if (entry.kind === "group-summary") {
1031
+ return /* @__PURE__ */ jsx7(
1032
+ GridGroupSummaryRow_default,
1033
+ {
1034
+ ref: virtualRange?.measureRow(entry.rowIndex),
1035
+ groupId: entry.groupId,
1036
+ level: entry.level,
1037
+ rowIndex: entry.rowIndex,
1038
+ datasetIndex: entry.datasetIndex,
1039
+ results: entry.aggregates,
1040
+ aggregates,
1041
+ columns,
1042
+ locale
1043
+ },
1044
+ `${entry.groupId}-summary`
1045
+ );
1046
+ }
1047
+ return /* @__PURE__ */ jsx7(
1048
+ GridGroupRow_default,
1049
+ {
1050
+ ref: virtualRange?.measureRow(entry.rowIndex),
1051
+ columnCount: columns.length,
1052
+ groupId: entry.groupId,
1053
+ level: entry.level,
1054
+ groupColumn: definitions.get(entry.columnId) ?? { field: entry.columnId },
1055
+ value: entry.value,
1056
+ expanded: entry.expanded,
1057
+ count: entry.count,
1058
+ rowIndex: entry.rowIndex,
1059
+ datasetIndex: entry.datasetIndex,
1060
+ posinset: ariaMeta.get(entry.groupId)?.posinset ?? 1,
1061
+ setsize: ariaMeta.get(entry.groupId)?.setsize ?? 1,
1062
+ aggregates: groupAggregateDisplay === "inline" ? aggregates : [],
1063
+ results: entry.aggregates,
1064
+ definitions,
1065
+ locale,
1066
+ cellMessages,
1067
+ focused: nav.focus.rowIndex === entry.rowIndex,
1068
+ contextMenu: groupMenu
1069
+ },
1070
+ entry.groupId
1071
+ );
1072
+ }),
1073
+ virtualRange !== null && virtualRange.offsetAfter > 0 && /* @__PURE__ */ jsx7("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx7(
1074
+ "td",
1075
+ {
1076
+ style: {
1077
+ height: virtualRange.offsetAfter,
1078
+ padding: 0,
1079
+ border: "none"
1080
+ }
1081
+ }
1082
+ ) }),
1083
+ infiniteScroll?.hasMore && /* @__PURE__ */ jsxs5(Fragment, { children: [
1084
+ infiniteScroll.isLoadingMore === true && /* @__PURE__ */ jsx7("tr", { "aria-hidden": "true", className: "grid-loading-row", children: /* @__PURE__ */ jsx7("td", { colSpan: columns.length, className: "grid-loading-cell", children: infiniteScroll.loadingTemplate ? infiniteScroll.loadingTemplate() : messages.loadingMore }) }),
1085
+ /* @__PURE__ */ jsx7(
1086
+ "tr",
1087
+ {
1088
+ "aria-hidden": "true",
1089
+ ref: sentinelRef,
1090
+ className: "grid-sentinel-row",
1091
+ children: /* @__PURE__ */ jsx7(
1092
+ "td",
1093
+ {
1094
+ colSpan: columns.length,
1095
+ style: { padding: 0, border: "none" }
1096
+ }
1097
+ )
1098
+ }
1099
+ )
1100
+ ] })
1101
+ ]
1102
+ }
1103
+ );
1104
+ }
1105
+
1106
+ // src/DataGrid/components/GridFooter.tsx
1107
+ import { jsx as jsx8 } from "react/jsx-runtime";
1108
+ function GridFooter({
1109
+ columns,
1110
+ aggregates,
1111
+ results,
1112
+ rows,
1113
+ locale,
1114
+ ariaRowIndex
1115
+ }) {
1116
+ return /* @__PURE__ */ jsx8("tfoot", { role: "rowgroup", children: /* @__PURE__ */ jsx8("tr", { className: "grid-footer", role: "row", "aria-rowindex": ariaRowIndex, children: columns.map((entry) => {
1117
+ const specs = aggregates.filter(
1118
+ (candidate) => candidate.columnId === entry.id
1119
+ );
1120
+ return /* @__PURE__ */ jsx8(
1121
+ "td",
1122
+ {
1123
+ role: "gridcell",
1124
+ "data-gridkit-column": entry.id,
1125
+ className: "grid-footer-cell",
1126
+ style: { textAlign: specs[0]?.alignment ?? entry.alignment },
1127
+ children: specs.map(
1128
+ (spec) => renderAggregate(
1129
+ results.get(spec.id ?? spec.columnId),
1130
+ spec,
1131
+ entry.column,
1132
+ rows,
1133
+ locale
1134
+ )
1135
+ ).flatMap(
1136
+ (rendered, index) => index === 0 ? [rendered] : [", ", rendered]
1137
+ )
1138
+ },
1139
+ entry.id
1140
+ );
1141
+ }) }) });
1142
+ }
1143
+
1144
+ // src/DataGrid/components/GridPager.tsx
1145
+ import { paginationWindow } from "@gridkitjs/core";
1146
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1147
+ function PagerNavButton({
1148
+ className,
1149
+ label,
1150
+ disabled,
1151
+ onClick,
1152
+ children
1153
+ }) {
1154
+ return /* @__PURE__ */ jsx9(
1155
+ "button",
1156
+ {
1157
+ type: "button",
1158
+ className: `grid-pager-button grid-pager-nav ${className}`,
1159
+ "aria-label": label,
1160
+ title: label,
1161
+ ...ariaAttr(disabled, "aria-disabled", true),
1162
+ onClick: () => {
1163
+ if (!disabled) onClick();
1164
+ },
1165
+ children
1166
+ }
1167
+ );
1168
+ }
1169
+ function GridPager({ pager, config, messages }) {
1170
+ const { pagination, pageCount, currentPage } = pager;
1171
+ const variant = config?.variant ?? "compact";
1172
+ const sizeOptions = config?.sizeOptions;
1173
+ const atFirst = pagination.pageIndex <= 0;
1174
+ const atLast = pagination.pageIndex >= pageCount - 1;
1175
+ const barStyle = {
1176
+ "--gridkit-pager-digits": String(String(Math.max(1, pageCount)).length)
1177
+ };
1178
+ return /* @__PURE__ */ jsxs6(
1179
+ "div",
1180
+ {
1181
+ className: "gridkit-grid-pager",
1182
+ role: "group",
1183
+ "aria-label": messages.pagerLabel,
1184
+ children: [
1185
+ /* @__PURE__ */ jsxs6("div", { className: "grid-pager-bar", style: barStyle, children: [
1186
+ /* @__PURE__ */ jsx9(
1187
+ PagerNavButton,
1188
+ {
1189
+ className: "grid-pager-first",
1190
+ label: messages.firstPage,
1191
+ disabled: atFirst,
1192
+ onClick: () => {
1193
+ pager.goToPage(0);
1194
+ },
1195
+ children: /* @__PURE__ */ jsx9(ChevronsLeftIcon, {})
1196
+ }
1197
+ ),
1198
+ /* @__PURE__ */ jsx9(
1199
+ PagerNavButton,
1200
+ {
1201
+ className: "grid-pager-previous",
1202
+ label: messages.previousPage,
1203
+ disabled: atFirst,
1204
+ onClick: () => {
1205
+ pager.previousPage();
1206
+ },
1207
+ children: /* @__PURE__ */ jsx9(ChevronLeftIcon, {})
1208
+ }
1209
+ ),
1210
+ variant === "numbered" ? paginationWindow(currentPage, pageCount, {
1211
+ boundaryCount: config?.boundaryCount,
1212
+ siblingCount: config?.siblingCount
1213
+ }).map(
1214
+ (entry, index) => entry === "ellipsis" ? /* @__PURE__ */ jsx9(
1215
+ "span",
1216
+ {
1217
+ className: "grid-pager-ellipsis",
1218
+ "aria-hidden": "true",
1219
+ children: "\u2026"
1220
+ },
1221
+ `ellipsis-${String(index)}`
1222
+ ) : /* @__PURE__ */ jsx9(
1223
+ "button",
1224
+ {
1225
+ type: "button",
1226
+ className: "grid-pager-button grid-pager-page",
1227
+ "aria-label": messages.pageButtonLabel(entry),
1228
+ "aria-current": entry === currentPage ? "page" : void 0,
1229
+ onClick: () => {
1230
+ pager.goToPage(entry - 1);
1231
+ },
1232
+ children: entry
1233
+ },
1234
+ entry
1235
+ )
1236
+ ) : /* @__PURE__ */ jsxs6("span", { className: "grid-pager-status-slot", children: [
1237
+ /* @__PURE__ */ jsx9("span", { className: "grid-pager-status", children: messages.pageStatus(
1238
+ pageCount === 0 ? 0 : currentPage,
1239
+ pageCount
1240
+ ) }),
1241
+ /* @__PURE__ */ jsx9("span", { className: "grid-pager-status-sizer", "aria-hidden": "true", children: messages.pageStatus(pageCount, pageCount) })
1242
+ ] }),
1243
+ /* @__PURE__ */ jsx9(
1244
+ PagerNavButton,
1245
+ {
1246
+ className: "grid-pager-next",
1247
+ label: messages.nextPage,
1248
+ disabled: atLast,
1249
+ onClick: () => {
1250
+ pager.nextPage();
1251
+ },
1252
+ children: /* @__PURE__ */ jsx9(ChevronRightIcon, {})
1253
+ }
1254
+ ),
1255
+ /* @__PURE__ */ jsx9(
1256
+ PagerNavButton,
1257
+ {
1258
+ className: "grid-pager-last",
1259
+ label: messages.lastPage,
1260
+ disabled: atLast,
1261
+ onClick: () => {
1262
+ pager.goToPage(pageCount - 1);
1263
+ },
1264
+ children: /* @__PURE__ */ jsx9(ChevronsRightIcon, {})
1265
+ }
1266
+ )
1267
+ ] }),
1268
+ sizeOptions !== void 0 && sizeOptions.length > 0 && /* @__PURE__ */ jsxs6("label", { className: "grid-pager-page-size", children: [
1269
+ /* @__PURE__ */ jsx9(
1270
+ "select",
1271
+ {
1272
+ value: pagination.pageSize,
1273
+ onChange: (event) => {
1274
+ pager.setPageSize(Number(event.target.value));
1275
+ },
1276
+ children: sizeOptions.map((size) => /* @__PURE__ */ jsx9("option", { value: size, children: size }, size))
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsx9("span", { className: "grid-pager-page-size-label", children: messages.pageSizeLabel })
1280
+ ] })
1281
+ ]
1282
+ }
1283
+ );
1284
+ }
1285
+
1286
+ // src/DataGrid/components/GroupByBar.tsx
1287
+ import { createPortal as createPortal2 } from "react-dom";
1288
+ import {
1289
+ resolveColumnLabel as resolveColumnLabel2,
1290
+ resolveKeyboardDropTarget
1291
+ } from "@gridkitjs/core";
1292
+
1293
+ // src/DataGrid/useGroupByDrag.ts
1294
+ import {
1295
+ resolveGroupByDropTarget
1296
+ } from "@gridkitjs/dom";
1297
+
1298
+ // src/DataGrid/useDragReorder.ts
1299
+ import {
1300
+ useRef,
1301
+ useState as useState3
1302
+ } from "react";
1303
+ import {
1304
+ NO_DRAG,
1305
+ startDragReorder
1306
+ } from "@gridkitjs/dom";
1307
+ function useDragReorder({
1308
+ resolveTarget,
1309
+ onDrop
1310
+ }) {
1311
+ const [drag, setDrag] = useState3(NO_DRAG);
1312
+ const dragged = useRef(false);
1313
+ function justDragged() {
1314
+ const answer = dragged.current;
1315
+ dragged.current = false;
1316
+ return answer;
1317
+ }
1318
+ function startDrag(id, grabElement, event) {
1319
+ startDragReorder(id, grabElement, event, {
1320
+ resolveTarget,
1321
+ onState(state) {
1322
+ if (state.draggedId !== null) {
1323
+ dragged.current = true;
1324
+ }
1325
+ setDrag(state);
1326
+ },
1327
+ onDrop
1328
+ });
1329
+ }
1330
+ return {
1331
+ draggedId: drag.draggedId,
1332
+ dropTarget: drag.dropTarget,
1333
+ ghostTransform: drag.ghostTransform,
1334
+ startDrag,
1335
+ justDragged
1336
+ };
1337
+ }
1338
+
1339
+ // src/DataGrid/useGroupByDrag.ts
1340
+ function useGroupByDrag({
1341
+ groupBy,
1342
+ onDrop
1343
+ }) {
1344
+ const drag = useDragReorder({
1345
+ resolveTarget: (clientX, clientY, movedId) => resolveGroupByDropTarget(clientX, clientY, movedId, groupBy),
1346
+ onDrop: (target, movedId) => {
1347
+ onDrop(movedId, target.beforeColumnId);
1348
+ }
1349
+ });
1350
+ return {
1351
+ draggedColumnId: drag.draggedId,
1352
+ dropTarget: drag.dropTarget,
1353
+ ghostTransform: drag.ghostTransform,
1354
+ startDrag: (columnId, event) => {
1355
+ drag.startDrag(columnId, event.currentTarget, event);
1356
+ },
1357
+ justDragged: drag.justDragged
1358
+ };
1359
+ }
1360
+
1361
+ // src/DataGrid/components/GroupByBar.tsx
1362
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1363
+ function GroupByBar({
1364
+ columns,
1365
+ definitions,
1366
+ groupBy,
1367
+ controller,
1368
+ columnName,
1369
+ headerDragEligible,
1370
+ headerDragBlocked,
1371
+ headerDropTarget,
1372
+ messages
1373
+ }) {
1374
+ const groupByIds = groupBy.map((entry) => entry.columnId);
1375
+ const chipDrag = useGroupByDrag({
1376
+ groupBy,
1377
+ onDrop: (movedId, beforeColumnId) => {
1378
+ controller.moveGroupBy(movedId, beforeColumnId);
1379
+ }
1380
+ });
1381
+ const activeDropTarget = chipDrag.dropTarget ?? headerDropTarget;
1382
+ const isDragTarget = chipDrag.draggedColumnId !== null || headerDragEligible;
1383
+ const showPlaceholder = groupBy.length === 0 && columns.some((entry) => entry.groupByDraggable);
1384
+ function labelFor(columnId) {
1385
+ const definition = definitions.get(columnId);
1386
+ return definition === void 0 ? columnId : resolveColumnLabel2(definition);
1387
+ }
1388
+ function moveByKeyboard(columnId, direction) {
1389
+ const beforeColumnId = resolveKeyboardDropTarget(
1390
+ groupByIds,
1391
+ columnId,
1392
+ direction
1393
+ );
1394
+ if (beforeColumnId === void 0) {
1395
+ return;
1396
+ }
1397
+ controller.moveGroupBy(columnId, beforeColumnId);
1398
+ }
1399
+ return /* @__PURE__ */ jsxs7(
1400
+ "div",
1401
+ {
1402
+ className: classNames(
1403
+ "gridkit-group-by-bar",
1404
+ isDragTarget ? "is-drag-target" : "",
1405
+ headerDragBlocked ? "is-drag-blocked" : ""
1406
+ ),
1407
+ "data-gridkit-group-by-bar": "",
1408
+ role: "group",
1409
+ "aria-label": messages.groupByBarLabel,
1410
+ children: [
1411
+ showPlaceholder && /* @__PURE__ */ jsx10("span", { className: "group-by-bar-placeholder", children: messages.groupByBarPlaceholder }),
1412
+ groupBy.map((entry, index) => {
1413
+ const dropBefore = activeDropTarget?.beforeColumnId === entry.columnId;
1414
+ const dropAfter = activeDropTarget?.beforeColumnId === null && index === groupBy.length - 1;
1415
+ const dragging = chipDrag.draggedColumnId === entry.columnId;
1416
+ return /* @__PURE__ */ jsxs7(
1417
+ "span",
1418
+ {
1419
+ className: classNames(
1420
+ "group-by-chip",
1421
+ dragging ? "is-dragging" : "",
1422
+ dropBefore ? "is-drop-before" : "",
1423
+ dropAfter ? "is-drop-after" : ""
1424
+ ),
1425
+ "data-gridkit-group-chip": entry.columnId,
1426
+ tabIndex: 0,
1427
+ "aria-label": messages.groupByChipLabel(
1428
+ columnName(entry.columnId),
1429
+ index + 1,
1430
+ groupBy.length
1431
+ ),
1432
+ "aria-keyshortcuts": "Control+ArrowLeft Control+ArrowRight",
1433
+ onPointerDown: (event) => {
1434
+ chipDrag.startDrag(entry.columnId, event);
1435
+ },
1436
+ onKeyDown: (event) => {
1437
+ const horizontal = event.key === "ArrowLeft" || event.key === "ArrowRight";
1438
+ if (horizontal && event.ctrlKey) {
1439
+ event.preventDefault();
1440
+ moveByKeyboard(
1441
+ entry.columnId,
1442
+ event.key === "ArrowLeft" ? -1 : 1
1443
+ );
1444
+ }
1445
+ },
1446
+ children: [
1447
+ /* @__PURE__ */ jsx10("span", { className: "group-by-chip-label", children: labelFor(entry.columnId) }),
1448
+ /* @__PURE__ */ jsx10(
1449
+ "button",
1450
+ {
1451
+ type: "button",
1452
+ className: "group-by-chip-remove",
1453
+ "aria-label": messages.removeGroupBy(columnName(entry.columnId)),
1454
+ onPointerDown: (event) => {
1455
+ event.stopPropagation();
1456
+ },
1457
+ onClick: () => {
1458
+ controller.toggleGroupBy(entry.columnId);
1459
+ },
1460
+ children: "\xD7"
1461
+ }
1462
+ )
1463
+ ]
1464
+ },
1465
+ entry.columnId
1466
+ );
1467
+ }),
1468
+ chipDrag.draggedColumnId !== null && chipDrag.ghostTransform !== null && createPortal2(
1469
+ /* @__PURE__ */ jsx10(
1470
+ "span",
1471
+ {
1472
+ className: "group-by-chip drag-ghost",
1473
+ style: { transform: chipDrag.ghostTransform },
1474
+ "aria-hidden": "true",
1475
+ children: labelFor(chipDrag.draggedColumnId)
1476
+ }
1477
+ ),
1478
+ document.body
1479
+ )
1480
+ ]
1481
+ }
1482
+ );
1483
+ }
1484
+
1485
+ // src/DataGrid/announcedCallbacks.ts
1486
+ import {
1487
+ isColumnVisible
1488
+ } from "@gridkitjs/core";
1489
+ function announcedCallbacks({
1490
+ callbacks,
1491
+ announce,
1492
+ messages,
1493
+ columnName,
1494
+ columnOf
1495
+ }) {
1496
+ return {
1497
+ /**
1498
+ * Announced only once the drag ends: the controller reports the
1499
+ * continuous `"move"` phase as well, and a live region given every frame
1500
+ * of a drag says nothing an assistive technology can keep up with.
1501
+ */
1502
+ onColumnResize: (event) => {
1503
+ callbacks.onColumnResize?.(event);
1504
+ if (event.phase === "end") {
1505
+ announce(
1506
+ messages.columnResized(
1507
+ columnName(event.columnId),
1508
+ Math.round(event.width)
1509
+ )
1510
+ );
1511
+ }
1512
+ },
1513
+ /**
1514
+ * Naming a column shown or hidden alone, and counting several — "Show
1515
+ * all" in the column chooser, or `hideColumns` given a list. A change
1516
+ * that shows some and hides others, as restoring a layout can, has no
1517
+ * one thing to say, and says nothing.
1518
+ */
1519
+ onColumnVisibilityChange: (event) => {
1520
+ callbacks.onColumnVisibilityChange?.(event);
1521
+ const { columnIds, visibility } = event;
1522
+ const [columnId] = columnIds;
1523
+ const shownCount = columnIds.filter((id) => {
1524
+ const column = columnOf(id);
1525
+ return column !== void 0 && isColumnVisible(column, visibility);
1526
+ }).length;
1527
+ if (columnId === void 0 || shownCount !== 0 && shownCount !== columnIds.length) {
1528
+ return;
1529
+ }
1530
+ const shown = shownCount > 0;
1531
+ if (columnIds.length > 1) {
1532
+ announce(
1533
+ shown ? messages.columnsShown(columnIds.length) : messages.columnsHidden(columnIds.length)
1534
+ );
1535
+ return;
1536
+ }
1537
+ announce(
1538
+ shown ? messages.columnShown(columnName(columnId)) : messages.columnHidden(columnName(columnId))
1539
+ );
1540
+ },
1541
+ /** A toggle, a stack, or a clear back to "none" each get their own. */
1542
+ onColumnSortChange: (event) => {
1543
+ callbacks.onColumnSortChange?.(event);
1544
+ const entry = event.sort.find(
1545
+ (candidate) => candidate.columnId === event.columnId
1546
+ );
1547
+ if (entry === void 0) {
1548
+ announce(messages.sortCleared(columnName(event.columnId)));
1549
+ return;
1550
+ }
1551
+ announce(
1552
+ messages.columnSorted(
1553
+ columnName(event.columnId),
1554
+ entry.direction,
1555
+ event.sort.indexOf(entry) + 1,
1556
+ event.sort.length
1557
+ )
1558
+ );
1559
+ },
1560
+ onGroupByChange: (event) => {
1561
+ callbacks.onGroupByChange?.(event);
1562
+ const entry = event.groupBy.find(
1563
+ (candidate) => candidate.columnId === event.columnId
1564
+ );
1565
+ if (entry === void 0) {
1566
+ announce(messages.groupingRemoved(columnName(event.columnId)));
1567
+ return;
1568
+ }
1569
+ announce(
1570
+ messages.groupedBy(
1571
+ columnName(event.columnId),
1572
+ event.groupBy.indexOf(entry) + 1,
1573
+ event.groupBy.length
1574
+ )
1575
+ );
1576
+ },
1577
+ /**
1578
+ * `groupId: null` marks `expandAll`/`collapseAll`, which touch every
1579
+ * group in one call rather than one at a time — announced as a single
1580
+ * summary instead of naming a group that doesn't apply.
1581
+ */
1582
+ onGroupExpansionChange: (event) => {
1583
+ callbacks.onGroupExpansionChange?.(event);
1584
+ if (event.groupId === null) {
1585
+ announce(
1586
+ event.expansion.length === 0 ? messages.allGroupsExpanded : messages.allGroupsCollapsed
1587
+ );
1588
+ return;
1589
+ }
1590
+ announce(
1591
+ event.expansion.includes(event.groupId) ? messages.groupCollapsed : messages.groupExpanded
1592
+ );
1593
+ },
1594
+ onPaginationChange: (event) => {
1595
+ callbacks.onPaginationChange?.(event);
1596
+ announce(
1597
+ messages.pageStatus(event.pagination.pageIndex + 1, event.pageCount)
1598
+ );
1599
+ },
1600
+ /**
1601
+ * Unlike rows and columns, a cell selection is always exactly one cell —
1602
+ * there is no multi-cell range — so it never crosses the count's >1
1603
+ * threshold and the focused cell's own `aria-selected` is the only
1604
+ * feedback a screen-reader user would otherwise get. Announced on every
1605
+ * change that lands on a cell instead.
1606
+ */
1607
+ onCellSelectionChange: (event) => {
1608
+ callbacks.onCellSelectionChange?.(event);
1609
+ if (event.selected !== null) {
1610
+ announce(
1611
+ messages.cellSelected(
1612
+ columnName(event.selected.columnId),
1613
+ event.selected.rowIndex + 1
1614
+ )
1615
+ );
1616
+ }
1617
+ },
1618
+ /**
1619
+ * Announced only for a change touching more than one row or column. A
1620
+ * single selection is already carried by the focused member's own
1621
+ * `aria-selected`, and repeating it here would talk over that. Counted
1622
+ * by id, so rows the change touched off screen — on another page, in a
1623
+ * collapsed group — count too.
1624
+ */
1625
+ onSelectionDiff: (event) => {
1626
+ if (event.added.length + event.removed.length > 1) {
1627
+ announce(messages.selectedCount(event.selection.length, event.target));
1628
+ }
1629
+ }
1630
+ };
1631
+ }
1632
+
1633
+ // src/DataGrid/clipboard/useGridCopy.ts
1634
+ import { runCommand } from "@gridkitjs/core";
1635
+ import { isNativeCopy } from "@gridkitjs/dom";
1636
+ function isCopyKey(event) {
1637
+ return (event.ctrlKey || event.metaKey) && !event.altKey && (event.key === "c" || event.key === "C");
1638
+ }
1639
+ function useGridCopy({
1640
+ controller,
1641
+ commands,
1642
+ nav
1643
+ }) {
1644
+ function run(commandId, target) {
1645
+ const command = commands.commands.get(commandId);
1646
+ return command !== void 0 && runCommand(command, commands.contextFor(target));
1647
+ }
1648
+ function focusedCell() {
1649
+ const { rowIndex, columnIndex } = nav.focus;
1650
+ if (rowIndex === HEADER_ROW) {
1651
+ return null;
1652
+ }
1653
+ const { pageRows, columns } = controller.getView();
1654
+ const entry = pageRows[rowIndex];
1655
+ const column = columns[columnIndex];
1656
+ return entry === void 0 || "kind" in entry || column === void 0 ? null : { area: "cell", rowId: entry.rowId, columnId: column.id };
1657
+ }
1658
+ return (event) => {
1659
+ if (!isCopyKey(event) || isNativeCopy(event.target)) {
1660
+ return;
1661
+ }
1662
+ const cell = focusedCell();
1663
+ if (run("copy.selection", { area: "grid" }) || cell !== null && run("copy.cell", cell)) {
1664
+ event.preventDefault();
1665
+ }
1666
+ };
1667
+ }
1668
+
1669
+ // src/DataGrid/commands/useGridCommands.ts
1670
+ import { useMemo as useMemo3 } from "react";
1671
+ import {
1672
+ createCommandContext,
1673
+ resolveCommands,
1674
+ runCommand as runCommand2
1675
+ } from "@gridkitjs/core";
1676
+
1677
+ // src/DataGrid/clipboard/copyToClipboard.ts
1678
+ import { writeClipboard } from "@gridkitjs/dom";
1679
+ function copyToClipboard(content, { messages, announce }) {
1680
+ writeClipboard(content).then(
1681
+ () => {
1682
+ announce(messages.copied(content.count, content.unit));
1683
+ },
1684
+ (error) => {
1685
+ console.error("DataGrid: the clipboard could not be written.", error);
1686
+ announce(messages.copyFailed);
1687
+ }
1688
+ );
1689
+ }
1690
+
1691
+ // src/DataGrid/export/useSaveFile.ts
1692
+ import { useRef as useRef2 } from "react";
1693
+ import { downloadBlob } from "@gridkitjs/dom";
1694
+
1695
+ // src/DataGrid/export/exportApi.ts
1696
+ import {
1697
+ exportFile,
1698
+ resolveExportWriters
1699
+ } from "@gridkitjs/core";
1700
+ function exportBlob({ content, mimeType }) {
1701
+ const part = typeof content === "string" ? content : content.slice();
1702
+ return new Blob([part], { type: mimeType });
1703
+ }
1704
+ function exportApi({
1705
+ contextFor
1706
+ }) {
1707
+ return {
1708
+ exportData: async (format, options) => {
1709
+ const context = contextFor({ area: "grid" });
1710
+ const { export: defaults, exportWriters } = context.options;
1711
+ const file = exportFile(
1712
+ context,
1713
+ format,
1714
+ { ...defaults, ...options },
1715
+ resolveExportWriters(exportWriters)
1716
+ );
1717
+ if (file === null) {
1718
+ throw new Error(
1719
+ "DataGrid: there is no export writer for that format \u2014 register one through `exportWriters`."
1720
+ );
1721
+ }
1722
+ return exportBlob(await file);
1723
+ }
1724
+ };
1725
+ }
1726
+
1727
+ // src/DataGrid/export/useSaveFile.ts
1728
+ function download(file) {
1729
+ downloadBlob(exportBlob(file), file.fileName);
1730
+ }
1731
+ function useSaveFile({
1732
+ messages,
1733
+ announce
1734
+ }) {
1735
+ const writingRef = useRef2(false);
1736
+ return (file) => {
1737
+ if (writingRef.current) {
1738
+ console.error(
1739
+ "DataGrid: an export is still being written, so this one was dropped."
1740
+ );
1741
+ return;
1742
+ }
1743
+ if (!(file instanceof Promise)) {
1744
+ download(file);
1745
+ return;
1746
+ }
1747
+ writingRef.current = true;
1748
+ file.then(download, (error) => {
1749
+ console.error("DataGrid: the export's writer failed.", error);
1750
+ announce(messages.exportFailed);
1751
+ }).finally(() => {
1752
+ writingRef.current = false;
1753
+ });
1754
+ };
1755
+ }
1756
+
1757
+ // src/DataGrid/print/printApi.ts
1758
+ import {
1759
+ exportPrintDocument
1760
+ } from "@gridkitjs/core";
1761
+ import { printDocument, printStyles } from "@gridkitjs/dom";
1762
+ function printGrid(document2, { styles = "" }) {
1763
+ return printDocument({
1764
+ title: document2.title,
1765
+ bodyHtml: document2.bodyHtml,
1766
+ styles: printStyles(document2.page) + styles
1767
+ });
1768
+ }
1769
+ function printApi({
1770
+ contextFor
1771
+ }) {
1772
+ return {
1773
+ print: (options) => {
1774
+ const context = contextFor({ area: "grid" });
1775
+ const merged = { ...context.options.print, ...options };
1776
+ return printGrid(exportPrintDocument(context, merged), merged);
1777
+ }
1778
+ };
1779
+ }
1780
+
1781
+ // src/DataGrid/commands/useGridCommands.ts
1782
+ function useGridCommands({
1783
+ controller,
1784
+ commands: appCommands,
1785
+ onCommand,
1786
+ messages,
1787
+ announce,
1788
+ resize,
1789
+ openPanel
1790
+ }) {
1791
+ const commands = useMemo3(() => resolveCommands(appCommands), [appCommands]);
1792
+ const saveFile = useSaveFile({ messages, announce });
1793
+ const host = {
1794
+ autoFitColumns: (columnIds) => {
1795
+ const { columns } = controller.getView();
1796
+ let fitted = 0;
1797
+ for (const columnId of columnIds) {
1798
+ const entry = columns.find((candidate) => candidate.id === columnId);
1799
+ if (entry !== void 0 && resize.sizeToContent(entry)) {
1800
+ fitted += 1;
1801
+ }
1802
+ }
1803
+ if (fitted > 1) {
1804
+ announce(messages.columnsAutoFitted(fitted));
1805
+ }
1806
+ },
1807
+ openPanel,
1808
+ announce,
1809
+ saveFile,
1810
+ writeClipboard: (content) => {
1811
+ copyToClipboard(content, { messages, announce });
1812
+ },
1813
+ print: (document2, options) => {
1814
+ printGrid(document2, options).catch((error) => {
1815
+ console.error("DataGrid: the print failed.", error);
1816
+ });
1817
+ }
1818
+ };
1819
+ function contextFor(target) {
1820
+ return createCommandContext({ controller, target, messages, host });
1821
+ }
1822
+ function report(commandId, target, source) {
1823
+ onCommand?.({ commandId, target, source });
1824
+ }
1825
+ return {
1826
+ commands,
1827
+ host,
1828
+ contextFor,
1829
+ report,
1830
+ run: (commandId, target, source) => {
1831
+ const command = commands.get(commandId);
1832
+ if (command === void 0) {
1833
+ console.error(`DataGrid: there is no command \`${commandId}\` to run.`);
1834
+ return false;
1835
+ }
1836
+ if (!runCommand2(command, contextFor(target))) {
1837
+ return false;
1838
+ }
1839
+ report(commandId, target, source);
1840
+ return true;
1841
+ }
1842
+ };
1843
+ }
1844
+
1845
+ // src/DataGrid/menu/GridMenu.tsx
1846
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef4, useState as useState4 } from "react";
1847
+ import {
1848
+ attachMenu
1849
+ } from "@gridkitjs/dom";
1850
+
1851
+ // src/DataGrid/overlay/Overlay.tsx
1852
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef3 } from "react";
1853
+ import { createPortal as createPortal3 } from "react-dom";
1854
+ import {
1855
+ attachOverlay
1856
+ } from "@gridkitjs/dom";
1857
+ import { jsx as jsx11 } from "react/jsx-runtime";
1858
+ var ANCHOR_GAP = 4;
1859
+ function attachedFocus(focus) {
1860
+ return typeof focus === "object" && !(focus instanceof HTMLElement) ? focus.current ?? "first" : focus;
1861
+ }
1862
+ function overlayThemeClassName(element) {
1863
+ const scope = element?.closest(".gridkit-light, .gridkit-dark");
1864
+ if (scope === null || scope === void 0) {
1865
+ return "";
1866
+ }
1867
+ return scope.classList.contains("gridkit-dark") ? "gridkit-dark" : "gridkit-light";
1868
+ }
1869
+ function Overlay({
1870
+ anchor,
1871
+ placement,
1872
+ gap,
1873
+ offset,
1874
+ initialFocus,
1875
+ returnFocusTo,
1876
+ ignoreScrollAfterInput,
1877
+ trapFocus,
1878
+ themeClassName,
1879
+ onDismiss,
1880
+ children
1881
+ }) {
1882
+ const panelRef = useRef3(null);
1883
+ const onDismissRef = useRef3(onDismiss);
1884
+ useLayoutEffect2(() => {
1885
+ onDismissRef.current = onDismiss;
1886
+ });
1887
+ useLayoutEffect2(() => {
1888
+ const panel = panelRef.current;
1889
+ if (panel === null) {
1890
+ return void 0;
1891
+ }
1892
+ return attachOverlay(anchor, panel, {
1893
+ placement,
1894
+ gap,
1895
+ offset,
1896
+ initialFocus: attachedFocus(initialFocus),
1897
+ returnFocusTo,
1898
+ ignoreScrollAfterInput,
1899
+ trapFocus,
1900
+ onDismiss: (reason) => {
1901
+ onDismissRef.current(reason);
1902
+ }
1903
+ });
1904
+ }, [
1905
+ anchor,
1906
+ placement,
1907
+ gap,
1908
+ offset,
1909
+ initialFocus,
1910
+ returnFocusTo,
1911
+ ignoreScrollAfterInput,
1912
+ trapFocus
1913
+ ]);
1914
+ return createPortal3(
1915
+ /* @__PURE__ */ jsx11(
1916
+ "div",
1917
+ {
1918
+ ref: panelRef,
1919
+ className: classNames("gridkit-overlay", themeClassName),
1920
+ children
1921
+ }
1922
+ ),
1923
+ document.body
1924
+ );
1925
+ }
1926
+
1927
+ // src/DataGrid/menu/menuIcons.tsx
1928
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1929
+ var BUILT_IN_ICONS = {
1930
+ sortAscending: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1931
+ /* @__PURE__ */ jsx12("path", { d: "m3 8 4-4 4 4" }),
1932
+ /* @__PURE__ */ jsx12("path", { d: "M7 4v16" }),
1933
+ /* @__PURE__ */ jsx12("path", { d: "M11 12h4" }),
1934
+ /* @__PURE__ */ jsx12("path", { d: "M11 16h7" }),
1935
+ /* @__PURE__ */ jsx12("path", { d: "M11 20h10" })
1936
+ ] }),
1937
+ sortDescending: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1938
+ /* @__PURE__ */ jsx12("path", { d: "m3 16 4 4 4-4" }),
1939
+ /* @__PURE__ */ jsx12("path", { d: "M7 20V4" }),
1940
+ /* @__PURE__ */ jsx12("path", { d: "M11 4h10" }),
1941
+ /* @__PURE__ */ jsx12("path", { d: "M11 8h7" }),
1942
+ /* @__PURE__ */ jsx12("path", { d: "M11 12h4" })
1943
+ ] }),
1944
+ group: () => /* @__PURE__ */ jsx12(GroupIcon, {}),
1945
+ hide: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1946
+ /* @__PURE__ */ jsx12("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }),
1947
+ /* @__PURE__ */ jsx12("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }),
1948
+ /* @__PURE__ */ jsx12("path", { d: "M6.61 6.61A13.53 13.53 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }),
1949
+ /* @__PURE__ */ jsx12("path", { d: "m2 2 20 20" })
1950
+ ] }),
1951
+ autoFit: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1952
+ /* @__PURE__ */ jsx12("path", { d: "m18 8 4 4-4 4" }),
1953
+ /* @__PURE__ */ jsx12("path", { d: "M2 12h20" }),
1954
+ /* @__PURE__ */ jsx12("path", { d: "m6 8-4 4 4 4" })
1955
+ ] }),
1956
+ copy: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1957
+ /* @__PURE__ */ jsx12("rect", { x: "8", y: "8", width: "14", height: "14", rx: "2" }),
1958
+ /* @__PURE__ */ jsx12("path", { d: "M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2" })
1959
+ ] }),
1960
+ print: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1961
+ /* @__PURE__ */ jsx12("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
1962
+ /* @__PURE__ */ jsx12("path", { d: "M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6" }),
1963
+ /* @__PURE__ */ jsx12("rect", { x: "6", y: "14", width: "12", height: "8", rx: "1" })
1964
+ ] })
1965
+ };
1966
+ var ICONS = new Map(Object.entries(BUILT_IN_ICONS));
1967
+ function MenuIcon({ name }) {
1968
+ const draw = name === void 0 ? void 0 : ICONS.get(name);
1969
+ return draw === void 0 ? null : draw();
1970
+ }
1971
+ function CheckIcon() {
1972
+ return /* @__PURE__ */ jsx12(Icon, { children: /* @__PURE__ */ jsx12("path", { d: "M20 6 9 17l-5-5" }) });
1973
+ }
1974
+ function ChevronDownIcon() {
1975
+ return /* @__PURE__ */ jsx12(Icon, { children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) });
1976
+ }
1977
+ function ChevronLeftIcon2() {
1978
+ return /* @__PURE__ */ jsx12(Icon, { children: /* @__PURE__ */ jsx12("path", { d: "m15 18-6-6 6-6" }) });
1979
+ }
1980
+ function ChevronRightIcon2() {
1981
+ return /* @__PURE__ */ jsx12(Icon, { children: /* @__PURE__ */ jsx12("path", { d: "m9 18 6-6-6-6" }) });
1982
+ }
1983
+
1984
+ // src/DataGrid/menu/GridMenu.tsx
1985
+ import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1986
+ function itemElement(menu, id) {
1987
+ return Array.from(
1988
+ menu.querySelectorAll("[data-gridkit-menu-item]")
1989
+ ).find((item) => item.getAttribute("data-gridkit-menu-item") === id) ?? null;
1990
+ }
1991
+ function topInset(menu) {
1992
+ const style = getComputedStyle(menu);
1993
+ return parseFloat(style.borderTopWidth) + parseFloat(style.paddingTop);
1994
+ }
1995
+ function MenuItem({
1996
+ entry,
1997
+ expanded
1998
+ }) {
1999
+ const isItem2 = entry.type === "item";
2000
+ const checked = isItem2 ? entry.checked : void 0;
2001
+ const shortcut = isItem2 ? entry.shortcut : void 0;
2002
+ return /* @__PURE__ */ jsxs9(
2003
+ "div",
2004
+ {
2005
+ role: checked === void 0 ? "menuitem" : "menuitemcheckbox",
2006
+ "data-gridkit-menu-item": entry.id,
2007
+ className: "menu-item",
2008
+ ...ariaAttr(isItem2 && entry.disabled, "aria-disabled", true),
2009
+ ...ariaAttr(checked !== void 0, "aria-checked", checked),
2010
+ ...ariaAttr(shortcut !== void 0, "aria-keyshortcuts", shortcut),
2011
+ ...ariaAttr(!isItem2, "aria-haspopup", "menu"),
2012
+ ...ariaAttr(!isItem2, "aria-expanded", expanded),
2013
+ children: [
2014
+ /* @__PURE__ */ jsx13("span", { className: "menu-item-icon", "aria-hidden": "true", children: checked === true ? /* @__PURE__ */ jsx13(CheckIcon, {}) : /* @__PURE__ */ jsx13(MenuIcon, { name: entry.icon }) }),
2015
+ /* @__PURE__ */ jsx13("span", { className: "menu-item-label", children: entry.label }),
2016
+ shortcut !== void 0 && /* @__PURE__ */ jsx13("span", { className: "menu-item-shortcut", "aria-hidden": "true", children: shortcut }),
2017
+ !isItem2 && /* @__PURE__ */ jsx13("span", { className: "menu-submenu-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(ChevronRightIcon2, {}) })
2018
+ ]
2019
+ }
2020
+ );
2021
+ }
2022
+ function GridMenu({
2023
+ entries,
2024
+ label,
2025
+ themeClassName,
2026
+ onRun,
2027
+ onClose,
2028
+ onCloseSubmenu,
2029
+ behaviourRef
2030
+ }) {
2031
+ const menuRef = useRef4(null);
2032
+ const submenuBehaviourRef = useRef4(null);
2033
+ const [submenu, setSubmenu] = useState4(null);
2034
+ const isSubmenu = onCloseSubmenu !== void 0;
2035
+ const latest = useRef4({ entries, submenu, onRun, onClose, onCloseSubmenu });
2036
+ useLayoutEffect3(() => {
2037
+ latest.current = { entries, submenu, onRun, onClose, onCloseSubmenu };
2038
+ });
2039
+ useLayoutEffect3(() => {
2040
+ const menu = menuRef.current;
2041
+ if (menu === null) {
2042
+ return void 0;
2043
+ }
2044
+ const behaviour = attachMenu(menu, {
2045
+ onActivate: (id) => {
2046
+ const item = latest.current.entries.find(
2047
+ (entry) => entry.type === "item" && entry.id === id
2048
+ );
2049
+ if (item !== void 0) {
2050
+ latest.current.onRun(item);
2051
+ }
2052
+ },
2053
+ onOpenSubmenu: (id, { focus }) => {
2054
+ if (latest.current.submenu?.id === id) {
2055
+ if (focus === "first") {
2056
+ submenuBehaviourRef.current?.focusItem(0);
2057
+ }
2058
+ return;
2059
+ }
2060
+ const anchor = itemElement(menu, id);
2061
+ if (anchor !== null) {
2062
+ setSubmenu({ id, anchor, focus, offset: -topInset(menu) });
2063
+ }
2064
+ },
2065
+ onCollapseSubmenu: () => {
2066
+ setSubmenu(null);
2067
+ },
2068
+ onCloseSubmenu: isSubmenu ? () => {
2069
+ latest.current.onCloseSubmenu?.();
2070
+ } : void 0,
2071
+ onClose: () => {
2072
+ latest.current.onClose();
2073
+ }
2074
+ });
2075
+ if (behaviourRef !== void 0) {
2076
+ behaviourRef.current = behaviour;
2077
+ }
2078
+ return () => {
2079
+ if (behaviourRef !== void 0) {
2080
+ behaviourRef.current = null;
2081
+ }
2082
+ behaviour.detach();
2083
+ };
2084
+ }, [isSubmenu, behaviourRef]);
2085
+ function closeSubmenu() {
2086
+ setSubmenu(null);
2087
+ }
2088
+ const openEntry = submenu === null ? void 0 : entries.find(
2089
+ (entry) => entry.type === "submenu" && entry.id === submenu.id
2090
+ );
2091
+ return /* @__PURE__ */ jsxs9(Fragment2, { children: [
2092
+ /* @__PURE__ */ jsx13(
2093
+ "div",
2094
+ {
2095
+ ref: menuRef,
2096
+ role: "menu",
2097
+ "aria-label": label,
2098
+ className: "gridkit-menu",
2099
+ children: entries.map(
2100
+ (entry) => entry.type === "separator" ? /* @__PURE__ */ jsx13("div", { role: "separator", className: "menu-separator" }, entry.key) : /* @__PURE__ */ jsx13(
2101
+ MenuItem,
2102
+ {
2103
+ entry,
2104
+ expanded: entry.id === submenu?.id
2105
+ },
2106
+ entry.id
2107
+ )
2108
+ )
2109
+ }
2110
+ ),
2111
+ submenu !== null && openEntry !== void 0 && /* @__PURE__ */ jsx13(
2112
+ Overlay,
2113
+ {
2114
+ anchor: submenu.anchor,
2115
+ placement: "right-start",
2116
+ offset: submenu.offset,
2117
+ initialFocus: submenu.focus,
2118
+ themeClassName,
2119
+ onDismiss: closeSubmenu,
2120
+ children: /* @__PURE__ */ jsx13(
2121
+ GridMenu,
2122
+ {
2123
+ entries: openEntry.items,
2124
+ label: openEntry.label,
2125
+ themeClassName,
2126
+ onRun,
2127
+ onClose,
2128
+ onCloseSubmenu: closeSubmenu,
2129
+ behaviourRef: submenuBehaviourRef
2130
+ }
2131
+ )
2132
+ },
2133
+ submenu.id
2134
+ )
2135
+ ] });
2136
+ }
2137
+
2138
+ // src/DataGrid/contextMenu/GridContextMenu.tsx
2139
+ import { jsx as jsx14 } from "react/jsx-runtime";
2140
+ function GridContextMenu({
2141
+ contextMenu
2142
+ }) {
2143
+ const { menu, run, close } = contextMenu;
2144
+ if (menu === null) {
2145
+ return null;
2146
+ }
2147
+ return /* @__PURE__ */ jsx14(
2148
+ Overlay,
2149
+ {
2150
+ anchor: menu.anchor,
2151
+ gap: menu.anchor instanceof Element ? ANCHOR_GAP : 0,
2152
+ returnFocusTo: menu.cell ?? void 0,
2153
+ themeClassName: menu.themeClassName,
2154
+ onDismiss: close,
2155
+ children: /* @__PURE__ */ jsx14(
2156
+ GridMenu,
2157
+ {
2158
+ entries: menu.entries,
2159
+ label: menu.label,
2160
+ themeClassName: menu.themeClassName,
2161
+ onRun: run,
2162
+ onClose: close
2163
+ }
2164
+ )
2165
+ },
2166
+ menu.key
2167
+ );
2168
+ }
2169
+
2170
+ // src/DataGrid/contextMenu/useContextMenu.ts
2171
+ import {
2172
+ useEffect as useEffect2,
2173
+ useLayoutEffect as useLayoutEffect4,
2174
+ useRef as useRef5,
2175
+ useState as useState5
2176
+ } from "react";
2177
+ import {
2178
+ contextMenuEntries,
2179
+ hasContextMenu,
2180
+ isSameCell,
2181
+ resolveMenu
2182
+ } from "@gridkitjs/core";
2183
+ import {
2184
+ attachContextMenuTrigger
2185
+ } from "@gridkitjs/dom";
2186
+
2187
+ // src/DataGrid/contextMenu/contextMenuPlaces.ts
2188
+ import {
2189
+ HEADER_ROW as HEADER_ROW2
2190
+ } from "@gridkitjs/core";
2191
+ import {
2192
+ bodyCellFrom as bodyCellFrom2,
2193
+ bodyRowFrom as bodyRowFrom2,
2194
+ findBodyRow as findBodyRow2,
2195
+ groupRowFrom as groupRowFrom2
2196
+ } from "@gridkitjs/dom";
2197
+ function headerCell(parts, columnIndex) {
2198
+ const element = parts.headerTable?.tHead?.rows[0]?.cells[columnIndex];
2199
+ return element === void 0 ? null : { element, rowIndex: HEADER_ROW2, columnIndex };
2200
+ }
2201
+ function bodyCell(parts, rowIndex, columnIndex) {
2202
+ const element = findBodyRow2(parts.bodyTable, rowIndex)?.cells[columnIndex];
2203
+ return element === void 0 ? null : { element, rowIndex, columnIndex };
2204
+ }
2205
+ function viewOf(element, parts) {
2206
+ const root = parts.root?.getBoundingClientRect();
2207
+ if (root === void 0) {
2208
+ return null;
2209
+ }
2210
+ const body = parts.body?.contains(element) === true ? parts.body.getBoundingClientRect() : root;
2211
+ const left = Math.max(root.left, body.left);
2212
+ const top = Math.max(root.top, body.top);
2213
+ return new DOMRect(
2214
+ left,
2215
+ top,
2216
+ Math.max(0, Math.min(root.right, body.right) - left),
2217
+ Math.max(0, Math.min(root.bottom, body.bottom) - top)
2218
+ );
2219
+ }
2220
+ function clamp(value, min, max) {
2221
+ return Math.min(Math.max(value, min), max);
2222
+ }
2223
+ function onCell(target, cell, parts) {
2224
+ if (cell === null) {
2225
+ return null;
2226
+ }
2227
+ const box = cell.element.getBoundingClientRect();
2228
+ const view = viewOf(cell.element, parts);
2229
+ if (view === null || box.right > view.left && box.left < view.right && box.bottom > view.top && box.top < view.bottom) {
2230
+ return { target, cell, anchor: cell.element };
2231
+ }
2232
+ return {
2233
+ target,
2234
+ cell,
2235
+ anchor: {
2236
+ x: clamp(box.left, view.left, view.right),
2237
+ y: clamp(box.bottom, view.top, view.bottom)
2238
+ }
2239
+ };
2240
+ }
2241
+ function onBody(body) {
2242
+ const { left, top } = body.getBoundingClientRect();
2243
+ return { target: { area: "body" }, cell: null, anchor: { x: left, y: top } };
2244
+ }
2245
+ function headerPlaceAt(element, parts) {
2246
+ const cell = element.closest("th[data-gridkit-column]");
2247
+ const columnId = cell?.getAttribute("data-gridkit-column");
2248
+ if (cell === null || cell.parentElement !== parts.headerTable?.tHead?.rows[0] || columnId === null || columnId === void 0) {
2249
+ return null;
2250
+ }
2251
+ return onCell(
2252
+ { area: "header", columnId },
2253
+ headerCell(parts, cell.cellIndex),
2254
+ parts
2255
+ );
2256
+ }
2257
+ function bodyPlaceAt(element, parts) {
2258
+ const { body, pageRows } = parts;
2259
+ if (body === null || !body.contains(element)) {
2260
+ return null;
2261
+ }
2262
+ const group = groupRowFrom2(element);
2263
+ if (group !== null) {
2264
+ const entry = pageRows[group.rowIndex];
2265
+ const cell = bodyCell(parts, group.rowIndex, 0);
2266
+ return entry !== void 0 && "kind" in entry && entry.kind === "group" && cell?.element.contains(element) === true ? onCell(
2267
+ { area: "group", groupId: entry.groupId, columnId: entry.columnId },
2268
+ cell,
2269
+ parts
2270
+ ) : null;
2271
+ }
2272
+ const position = bodyCellFrom2(element);
2273
+ if (position !== null) {
2274
+ const entry = pageRows[position.rowIndex];
2275
+ const cell = bodyCell(parts, position.rowIndex, position.columnIndex);
2276
+ return entry !== void 0 && !("kind" in entry) && cell?.element.contains(element) === true ? onCell(
2277
+ { area: "cell", rowId: entry.rowId, columnId: position.columnId },
2278
+ cell,
2279
+ parts
2280
+ ) : null;
2281
+ }
2282
+ return bodyRowFrom2(element) === null ? onBody(body) : null;
2283
+ }
2284
+ function placeAt(element, parts) {
2285
+ return headerPlaceAt(element, parts) ?? bodyPlaceAt(element, parts);
2286
+ }
2287
+ function placeOf(target, parts) {
2288
+ const columnIndexOf = (columnId) => parts.columns.findIndex((column) => column.id === columnId);
2289
+ switch (target.area) {
2290
+ case "header":
2291
+ return onCell(
2292
+ target,
2293
+ headerCell(parts, columnIndexOf(target.columnId)),
2294
+ parts
2295
+ );
2296
+ case "cell": {
2297
+ const rowIndex = parts.pageRows.findIndex(
2298
+ (entry) => !("kind" in entry) && entry.rowId === target.rowId
2299
+ );
2300
+ return onCell(
2301
+ target,
2302
+ bodyCell(parts, rowIndex, columnIndexOf(target.columnId)),
2303
+ parts
2304
+ );
2305
+ }
2306
+ case "group": {
2307
+ const rowIndex = parts.pageRows.findIndex(
2308
+ (entry) => "kind" in entry && entry.kind === "group" && entry.groupId === target.groupId
2309
+ );
2310
+ return onCell(target, bodyCell(parts, rowIndex, 0), parts);
2311
+ }
2312
+ case "body":
2313
+ return parts.body === null ? null : onBody(parts.body);
2314
+ }
2315
+ }
2316
+
2317
+ // src/DataGrid/contextMenu/useContextMenu.ts
2318
+ function useContextMenu({
2319
+ rootRef,
2320
+ headerTableRef,
2321
+ bodyTableRef,
2322
+ bodyScrollRef,
2323
+ contextMenu,
2324
+ onContextMenuOpenChange,
2325
+ controller,
2326
+ commands,
2327
+ nav,
2328
+ selection,
2329
+ messages,
2330
+ columnName
2331
+ }) {
2332
+ const [menu, setMenu] = useState5(null);
2333
+ const current = useRef5(null);
2334
+ const openings = useRef5(0);
2335
+ const enabled = contextMenu !== void 0 && contextMenu !== false;
2336
+ function parts() {
2337
+ const { columns, pageRows } = controller.getView();
2338
+ return {
2339
+ root: rootRef.current,
2340
+ headerTable: headerTableRef.current,
2341
+ bodyTable: bodyTableRef.current,
2342
+ body: bodyScrollRef.current,
2343
+ columns,
2344
+ pageRows
2345
+ };
2346
+ }
2347
+ function labelOf(target) {
2348
+ switch (target.area) {
2349
+ case "header":
2350
+ return messages.headerMenuLabel(columnName(target.columnId));
2351
+ case "cell":
2352
+ return messages.cellMenuLabel;
2353
+ case "group":
2354
+ return messages.groupMenuLabel;
2355
+ case "body":
2356
+ return messages.bodyMenuLabel;
2357
+ }
2358
+ }
2359
+ function selectTarget(target) {
2360
+ const state = controller.getState();
2361
+ if (target.area === "header") {
2362
+ if (selection.columnMode === false || state.columnSelection.includes(target.columnId)) {
2363
+ return false;
2364
+ }
2365
+ controller.selectColumn(target.columnId, "replace");
2366
+ return true;
2367
+ }
2368
+ if (target.area !== "cell") {
2369
+ return false;
2370
+ }
2371
+ const cell = { rowId: target.rowId, columnId: target.columnId };
2372
+ const selectRow = selection.rowMode !== false && !state.rowSelection.includes(target.rowId);
2373
+ const selectCell = selection.cellMode !== false && !isSameCell(state.cellSelection, cell);
2374
+ if (selectRow) {
2375
+ controller.selectRow(target.rowId, "replace");
2376
+ }
2377
+ if (selectCell) {
2378
+ controller.selectCell(cell, "replace");
2379
+ }
2380
+ return selectRow || selectCell;
2381
+ }
2382
+ function resolveFor(target) {
2383
+ const context = commands.contextFor(target);
2384
+ return resolveMenu(contextMenuEntries(contextMenu, context), {
2385
+ commands: commands.commands,
2386
+ context
2387
+ });
2388
+ }
2389
+ function show(next) {
2390
+ const previous = current.current;
2391
+ if (previous === next) {
2392
+ return;
2393
+ }
2394
+ current.current = next;
2395
+ setMenu(next);
2396
+ if (previous !== null) {
2397
+ onContextMenuOpenChange?.({ open: false, target: previous.target });
2398
+ }
2399
+ if (next !== null) {
2400
+ onContextMenuOpenChange?.({ open: true, target: next.target });
2401
+ }
2402
+ }
2403
+ function openAt({ target, cell, anchor }, point) {
2404
+ let resolved = resolveFor(target);
2405
+ if (resolved.length === 0) {
2406
+ return false;
2407
+ }
2408
+ if (selectTarget(target)) {
2409
+ resolved = resolveFor(target);
2410
+ }
2411
+ if (cell !== null && document.activeElement !== cell.element) {
2412
+ cell.element.focus({ preventScroll: true });
2413
+ nav.focusCellNow(cell.rowIndex, cell.columnIndex);
2414
+ }
2415
+ openings.current += 1;
2416
+ show({
2417
+ key: openings.current,
2418
+ target,
2419
+ entries: resolved,
2420
+ label: labelOf(target),
2421
+ anchor: point ?? anchor,
2422
+ cell: cell?.element ?? null,
2423
+ themeClassName: overlayThemeClassName(rootRef.current)
2424
+ });
2425
+ return true;
2426
+ }
2427
+ function handleRequest({ target, point }) {
2428
+ const place = placeAt(target, parts());
2429
+ return place !== null && openAt(place, point);
2430
+ }
2431
+ const latestRequest = useRef5(handleRequest);
2432
+ useLayoutEffect4(() => {
2433
+ latestRequest.current = handleRequest;
2434
+ });
2435
+ useEffect2(() => {
2436
+ const root = rootRef.current;
2437
+ if (!enabled || root === null) {
2438
+ return void 0;
2439
+ }
2440
+ return attachContextMenuTrigger(root, {
2441
+ onRequest: (request) => latestRequest.current(request)
2442
+ });
2443
+ }, [rootRef, enabled]);
2444
+ const shown = useRef5(null);
2445
+ useLayoutEffect4(() => {
2446
+ const closed = shown.current;
2447
+ shown.current = menu;
2448
+ if (menu === null && closed !== null && closed.cell !== null && !closed.cell.isConnected && document.activeElement === document.body) {
2449
+ nav.focusTabStop();
2450
+ }
2451
+ });
2452
+ function close() {
2453
+ show(null);
2454
+ }
2455
+ return {
2456
+ menu,
2457
+ opens: (area) => hasContextMenu(contextMenu, area),
2458
+ open: (target, anchor) => {
2459
+ const place = placeOf(target, parts());
2460
+ return place !== null && openAt(place, anchor ?? null);
2461
+ },
2462
+ run: (item) => {
2463
+ const open = current.current;
2464
+ if (item.run() && open !== null) {
2465
+ commands.report(item.id, open.target, "contextMenu");
2466
+ }
2467
+ close();
2468
+ },
2469
+ close
2470
+ };
2471
+ }
2472
+
2473
+ // src/DataGrid/panels/GridPanels.tsx
2474
+ import { useRef as useRef13 } from "react";
2475
+
2476
+ // src/DataGrid/columnChooser/ColumnChooser.tsx
2477
+ import { useLayoutEffect as useLayoutEffect5, useRef as useRef6, useState as useState6 } from "react";
2478
+ import { resolveColumnChooser } from "@gridkitjs/core";
2479
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
2480
+ function ColumnChooser({
2481
+ controller,
2482
+ messages
2483
+ }) {
2484
+ const [search, setSearch] = useState6("");
2485
+ const showAllRef = useRef6(null);
2486
+ const { entries, selectAll, toggleAll } = resolveColumnChooser({
2487
+ columns: controller.getView().dataColumns,
2488
+ visibility: controller.getState().columnVisibility,
2489
+ search
2490
+ });
2491
+ useLayoutEffect5(() => {
2492
+ if (showAllRef.current !== null) {
2493
+ showAllRef.current.indeterminate = selectAll === "mixed";
2494
+ }
2495
+ });
2496
+ function setVisible(columnIds, visible) {
2497
+ if (visible) {
2498
+ controller.showColumns(columnIds);
2499
+ } else {
2500
+ controller.hideColumns(columnIds);
2501
+ }
2502
+ }
2503
+ return /* @__PURE__ */ jsxs10(
2504
+ "div",
2505
+ {
2506
+ role: "dialog",
2507
+ "aria-label": messages.columnChooserLabel,
2508
+ className: "gridkit-column-chooser",
2509
+ children: [
2510
+ /* @__PURE__ */ jsx15(
2511
+ "input",
2512
+ {
2513
+ type: "search",
2514
+ className: "column-chooser-search",
2515
+ "aria-label": messages.columnChooserSearchLabel,
2516
+ placeholder: messages.searchPlaceholder,
2517
+ value: search,
2518
+ onChange: (event) => {
2519
+ setSearch(event.currentTarget.value);
2520
+ }
2521
+ }
2522
+ ),
2523
+ entries.length === 0 ? /* @__PURE__ */ jsx15("p", { role: "status", className: "column-chooser-empty", children: messages.noMatchingColumns }) : /* @__PURE__ */ jsxs10(Fragment3, { children: [
2524
+ /* @__PURE__ */ jsxs10("label", { className: "column-chooser-option column-chooser-all", children: [
2525
+ /* @__PURE__ */ jsx15(
2526
+ "input",
2527
+ {
2528
+ ref: showAllRef,
2529
+ type: "checkbox",
2530
+ checked: selectAll === "checked",
2531
+ disabled: toggleAll === null,
2532
+ onChange: () => {
2533
+ if (toggleAll !== null) {
2534
+ setVisible(toggleAll.columnIds, toggleAll.visible);
2535
+ }
2536
+ }
2537
+ }
2538
+ ),
2539
+ messages.showAllColumns
2540
+ ] }),
2541
+ /* @__PURE__ */ jsx15("ul", { className: "column-chooser-list", children: entries.map((entry) => /* @__PURE__ */ jsx15("li", { children: /* @__PURE__ */ jsxs10("label", { className: "column-chooser-option", children: [
2542
+ /* @__PURE__ */ jsx15(
2543
+ "input",
2544
+ {
2545
+ type: "checkbox",
2546
+ checked: entry.visible,
2547
+ disabled: entry.disabled,
2548
+ onChange: () => {
2549
+ setVisible([entry.columnId], !entry.visible);
2550
+ }
2551
+ }
2552
+ ),
2553
+ entry.label
2554
+ ] }) }, entry.columnId)) })
2555
+ ] })
2556
+ ]
2557
+ }
2558
+ );
2559
+ }
2560
+
2561
+ // src/DataGrid/filterMenu/FilterMenu.tsx
2562
+ import { useEffect as useEffect3, useRef as useRef11 } from "react";
2563
+ import {
2564
+ resolveColumnFilterMenu as resolveColumnFilterMenu2,
2565
+ resolveColumnHeaderText,
2566
+ resolveMenu as resolveMenu2
2567
+ } from "@gridkitjs/core";
2568
+
2569
+ // src/DataGrid/filterMenu/ExcelFilterEditor.tsx
2570
+ import {
2571
+ useImperativeHandle as useImperativeHandle2,
2572
+ useLayoutEffect as useLayoutEffect9,
2573
+ useMemo as useMemo4,
2574
+ useReducer,
2575
+ useRef as useRef10,
2576
+ useState as useState10
2577
+ } from "react";
2578
+ import {
2579
+ describeColumnFilter as describeColumnFilter2,
2580
+ draftToFilter as draftToFilter2,
2581
+ filterChecklist,
2582
+ filterEditorReducer as filterEditorReducer2,
2583
+ initialFilterEditorDraft as initialFilterEditorDraft2,
2584
+ selectAllState
2585
+ } from "@gridkitjs/core";
2586
+
2587
+ // src/DataGrid/filterMenu/OperatorMenuButton.tsx
2588
+ import { useLayoutEffect as useLayoutEffect6, useRef as useRef7, useState as useState7 } from "react";
2589
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
2590
+ function gapBeside(button) {
2591
+ const dialog = button.closest('[role="dialog"]') ?? button;
2592
+ return dialog.getBoundingClientRect().right - button.getBoundingClientRect().right + ANCHOR_GAP;
2593
+ }
2594
+ function OperatorMenuButton({
2595
+ menu,
2596
+ active,
2597
+ messages,
2598
+ onChoose,
2599
+ ref
2600
+ }) {
2601
+ const [open, setOpen] = useState7(null);
2602
+ const menuBehaviourRef = useRef7(null);
2603
+ const label = messages.filterOperatorMenu(menu.family);
2604
+ useLayoutEffect6(() => {
2605
+ if (open !== null && open.focusItem > 0) {
2606
+ menuBehaviourRef.current?.focusItem(open.focusItem);
2607
+ }
2608
+ }, [open]);
2609
+ function show(button, onLast = false) {
2610
+ setOpen({
2611
+ anchor: button,
2612
+ themeClassName: overlayThemeClassName(button),
2613
+ gap: gapBeside(button),
2614
+ focusItem: onLast ? menu.operators.length - 1 : 0
2615
+ });
2616
+ }
2617
+ function close() {
2618
+ setOpen(null);
2619
+ }
2620
+ const entries = menu.operators.map((operator) => ({
2621
+ type: "item",
2622
+ id: operator,
2623
+ label: messages.filterOperatorLabel(operator, menu.family),
2624
+ disabled: false,
2625
+ checked: active?.includes(operator),
2626
+ run: () => {
2627
+ close();
2628
+ onChoose(operator);
2629
+ return true;
2630
+ }
2631
+ }));
2632
+ return /* @__PURE__ */ jsxs11(Fragment4, { children: [
2633
+ /* @__PURE__ */ jsxs11(
2634
+ "button",
2635
+ {
2636
+ ref,
2637
+ type: "button",
2638
+ className: "filter-menu-submenu",
2639
+ "aria-haspopup": "menu",
2640
+ "aria-expanded": open !== null,
2641
+ onClick: (event) => {
2642
+ if (open === null) {
2643
+ show(event.currentTarget);
2644
+ } else {
2645
+ close();
2646
+ }
2647
+ },
2648
+ onKeyDown: (event) => {
2649
+ if ((event.key === "ArrowDown" || event.key === "ArrowUp") && open === null) {
2650
+ event.preventDefault();
2651
+ show(event.currentTarget, event.key === "ArrowUp");
2652
+ }
2653
+ },
2654
+ children: [
2655
+ /* @__PURE__ */ jsx16("span", { className: "filter-menu-submenu-icon", "aria-hidden": "true", children: active !== void 0 && /* @__PURE__ */ jsx16(CheckIcon, {}) }),
2656
+ label,
2657
+ /* @__PURE__ */ jsx16("span", { className: "filter-menu-submenu-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx16(ChevronRightIcon2, {}) })
2658
+ ]
2659
+ }
2660
+ ),
2661
+ open !== null && /* @__PURE__ */ jsx16(
2662
+ Overlay,
2663
+ {
2664
+ anchor: open.anchor,
2665
+ placement: "right-start",
2666
+ gap: open.gap,
2667
+ returnFocusTo: open.anchor,
2668
+ themeClassName: open.themeClassName,
2669
+ onDismiss: close,
2670
+ children: /* @__PURE__ */ jsx16(
2671
+ GridMenu,
2672
+ {
2673
+ entries,
2674
+ label,
2675
+ themeClassName: open.themeClassName,
2676
+ behaviourRef: menuBehaviourRef,
2677
+ onRun: (item) => {
2678
+ item.run();
2679
+ },
2680
+ onClose: close
2681
+ }
2682
+ )
2683
+ }
2684
+ )
2685
+ ] });
2686
+ }
2687
+
2688
+ // src/DataGrid/filterMenu/SimpleFilterEditor.tsx
2689
+ import {
2690
+ Fragment as Fragment5,
2691
+ useId,
2692
+ useImperativeHandle,
2693
+ useLayoutEffect as useLayoutEffect7,
2694
+ useRef as useRef8,
2695
+ useState as useState8
2696
+ } from "react";
2697
+ import {
2698
+ conditionRowsFrom,
2699
+ conditionsFromRows,
2700
+ describeColumnFilter,
2701
+ draftToFilter,
2702
+ filterEditorReducer,
2703
+ initialFilterEditorDraft
2704
+ } from "@gridkitjs/core";
2705
+
2706
+ // src/DataGrid/filterMenu/ConditionRow.tsx
2707
+ import {
2708
+ filterOperandArity,
2709
+ parseFilterOperand
2710
+ } from "@gridkitjs/core";
2711
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
2712
+ function inputType(family, type) {
2713
+ switch (family) {
2714
+ case "text":
2715
+ return "text";
2716
+ case "number":
2717
+ return "number";
2718
+ case "date":
2719
+ return type === "date" ? "date" : "datetime-local";
2720
+ }
2721
+ }
2722
+ function OperandField({
2723
+ family,
2724
+ type,
2725
+ text,
2726
+ label,
2727
+ invalidMessageId,
2728
+ fieldRef,
2729
+ messages,
2730
+ onChange
2731
+ }) {
2732
+ const field = {
2733
+ ref: fieldRef,
2734
+ className: "filter-menu-value",
2735
+ "aria-label": label,
2736
+ "aria-invalid": invalidMessageId === void 0 ? void 0 : true,
2737
+ "aria-describedby": invalidMessageId,
2738
+ value: text,
2739
+ onChange: (event) => {
2740
+ onChange(event.currentTarget.value);
2741
+ }
2742
+ };
2743
+ if (family === "boolean") {
2744
+ return /* @__PURE__ */ jsxs12("select", { ...field, children: [
2745
+ /* @__PURE__ */ jsx17("option", { value: "" }),
2746
+ /* @__PURE__ */ jsx17("option", { value: "true", children: messages.booleanTrue }),
2747
+ /* @__PURE__ */ jsx17("option", { value: "false", children: messages.booleanFalse })
2748
+ ] });
2749
+ }
2750
+ return /* @__PURE__ */ jsx17("input", { ...field, type: inputType(family, type) });
2751
+ }
2752
+ function ConditionRow({
2753
+ row,
2754
+ position,
2755
+ menu,
2756
+ type,
2757
+ messages,
2758
+ invalidMessageId,
2759
+ focusRef,
2760
+ onChange,
2761
+ ref
2762
+ }) {
2763
+ const arity = filterOperandArity(row.operator);
2764
+ const operators = menu.operators.includes(row.operator) ? menu.operators : [row.operator, ...menu.operators];
2765
+ const operand = { family: menu.family, type, messages };
2766
+ function messageIdFor(text) {
2767
+ return parseFilterOperand(text, type) === void 0 ? invalidMessageId : void 0;
2768
+ }
2769
+ return /* @__PURE__ */ jsxs12(
2770
+ "div",
2771
+ {
2772
+ ref,
2773
+ role: "group",
2774
+ "aria-label": messages.filterConditionLabel(position),
2775
+ className: "filter-menu-condition",
2776
+ children: [
2777
+ /* @__PURE__ */ jsx17(
2778
+ "select",
2779
+ {
2780
+ ref: arity === 0 ? focusRef : void 0,
2781
+ className: "filter-menu-operator",
2782
+ "aria-label": messages.filterOperatorFieldLabel,
2783
+ value: row.operator,
2784
+ onChange: (event) => {
2785
+ const operator = operators[event.currentTarget.selectedIndex];
2786
+ if (operator !== void 0) {
2787
+ onChange({ ...row, operator });
2788
+ }
2789
+ },
2790
+ children: operators.map((operator) => /* @__PURE__ */ jsx17("option", { value: operator, children: messages.filterOperatorLabel(operator, menu.family) }, operator))
2791
+ }
2792
+ ),
2793
+ arity > 0 && /* @__PURE__ */ jsx17(
2794
+ OperandField,
2795
+ {
2796
+ ...operand,
2797
+ text: row.text,
2798
+ label: arity === 2 ? messages.filterFromLabel : messages.filterValueLabel,
2799
+ invalidMessageId: messageIdFor(row.text),
2800
+ fieldRef: focusRef,
2801
+ onChange: (text) => {
2802
+ onChange({ ...row, text });
2803
+ }
2804
+ }
2805
+ ),
2806
+ arity === 2 && /* @__PURE__ */ jsx17(
2807
+ OperandField,
2808
+ {
2809
+ ...operand,
2810
+ text: row.textTo,
2811
+ label: messages.filterToLabel,
2812
+ invalidMessageId: messageIdFor(row.textTo),
2813
+ fieldRef: void 0,
2814
+ onChange: (textTo) => {
2815
+ onChange({ ...row, textTo });
2816
+ }
2817
+ }
2818
+ )
2819
+ ]
2820
+ }
2821
+ );
2822
+ }
2823
+
2824
+ // src/DataGrid/filterMenu/SimpleFilterEditor.tsx
2825
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
2826
+ var COMBINATORS = ["and", "or"];
2827
+ var NO_ROWS = [];
2828
+ var NO_VALUES = {
2829
+ options: [],
2830
+ total: 0,
2831
+ truncated: false,
2832
+ blanks: 0
2833
+ };
2834
+ function holdsUnreadable(row) {
2835
+ return Array.from(row?.querySelectorAll("input") ?? []).some(
2836
+ (input) => input.validity.badInput
2837
+ );
2838
+ }
2839
+ function CombinatorChoice({
2840
+ name,
2841
+ combinator,
2842
+ messages,
2843
+ onChange
2844
+ }) {
2845
+ return /* @__PURE__ */ jsx18(
2846
+ "div",
2847
+ {
2848
+ role: "radiogroup",
2849
+ "aria-label": messages.filterCombinatorLabel,
2850
+ className: "filter-menu-combinator",
2851
+ children: COMBINATORS.map((choice) => /* @__PURE__ */ jsxs13("label", { className: "filter-menu-combinator-option", children: [
2852
+ /* @__PURE__ */ jsx18(
2853
+ "input",
2854
+ {
2855
+ type: "radio",
2856
+ name,
2857
+ checked: choice === combinator,
2858
+ onChange: () => {
2859
+ onChange(choice);
2860
+ }
2861
+ }
2862
+ ),
2863
+ choice === "and" ? messages.filterAnd : messages.filterOr
2864
+ ] }, choice))
2865
+ }
2866
+ );
2867
+ }
2868
+ function SimpleFilterEditor({
2869
+ column,
2870
+ menu,
2871
+ controller,
2872
+ messages,
2873
+ initialFocusRef,
2874
+ applyFilter,
2875
+ operator,
2876
+ ref
2877
+ }) {
2878
+ const type = column.column.type;
2879
+ const [opened] = useState8(() => {
2880
+ const shape = describeColumnFilter(controller.getState().filter, column.id);
2881
+ const rows = conditionRowsFrom(shape, menu.operators, type);
2882
+ const draft2 = {
2883
+ // Its typed values kept, as picking the operator in the row keeps them.
2884
+ rows: operator === void 0 ? rows : rows.map(
2885
+ (row, index) => index === 0 ? { ...row, operator } : row
2886
+ ),
2887
+ combinator: shape.kind === "conditions" ? shape.combinator : "and"
2888
+ };
2889
+ return {
2890
+ custom: shape.kind === "custom" || shape.kind === "values" && operator === void 0,
2891
+ draft: draft2
2892
+ };
2893
+ });
2894
+ const [draft, setDraft] = useState8(opened.draft);
2895
+ const [invalid, setInvalid] = useState8(NO_ROWS);
2896
+ const rowElements = useRef8([]);
2897
+ const invalidMessageId = useId();
2898
+ const combinatorName = useId();
2899
+ function update(next) {
2900
+ setDraft(next);
2901
+ setInvalid(NO_ROWS);
2902
+ }
2903
+ function applyConditions(conditions) {
2904
+ const applied = filterEditorReducer(
2905
+ initialFilterEditorDraft({ kind: "none" }, NO_VALUES),
2906
+ { type: "setConditions", conditions, combinator: draft.combinator }
2907
+ );
2908
+ applyFilter(
2909
+ draftToFilter(applied, column.id, controller.getState().filter)
2910
+ );
2911
+ }
2912
+ useImperativeHandle(ref, () => ({
2913
+ apply: () => {
2914
+ const parsed = conditionsFromRows(draft.rows, column.id, type);
2915
+ const unreadable = draft.rows.map(
2916
+ (_, index) => holdsUnreadable(rowElements.current[index])
2917
+ );
2918
+ const marked = draft.rows.flatMap(
2919
+ (_, index) => parsed.invalid.includes(index) || unreadable[index] === true ? [index] : []
2920
+ );
2921
+ if (marked.length === 0) {
2922
+ applyConditions(parsed.conditions);
2923
+ return true;
2924
+ }
2925
+ if (draft === opened.draft && !unreadable.includes(true)) {
2926
+ return true;
2927
+ }
2928
+ setInvalid(marked);
2929
+ return false;
2930
+ }
2931
+ }));
2932
+ useLayoutEffect7(() => {
2933
+ const [first] = invalid;
2934
+ if (first !== void 0) {
2935
+ rowElements.current[first]?.querySelector('[aria-invalid="true"]')?.focus();
2936
+ }
2937
+ }, [invalid]);
2938
+ return /* @__PURE__ */ jsxs13("div", { className: "filter-menu-conditions", children: [
2939
+ opened.custom && /* @__PURE__ */ jsx18("p", { className: "filter-menu-custom", children: messages.customFilter }),
2940
+ draft.rows.map((row, index) => (
2941
+ // A row is its place: the first condition, or the second.
2942
+ /* @__PURE__ */ jsxs13(Fragment5, { children: [
2943
+ index > 0 && /* @__PURE__ */ jsx18(
2944
+ CombinatorChoice,
2945
+ {
2946
+ name: combinatorName,
2947
+ combinator: draft.combinator,
2948
+ messages,
2949
+ onChange: (combinator) => {
2950
+ update({ ...draft, combinator });
2951
+ }
2952
+ }
2953
+ ),
2954
+ /* @__PURE__ */ jsx18(
2955
+ ConditionRow,
2956
+ {
2957
+ ref: (element) => {
2958
+ rowElements.current[index] = element;
2959
+ },
2960
+ row,
2961
+ position: index + 1,
2962
+ menu,
2963
+ type,
2964
+ messages,
2965
+ invalidMessageId: invalid.includes(index) ? invalidMessageId : void 0,
2966
+ focusRef: index === 0 ? initialFocusRef : void 0,
2967
+ onChange: (next) => {
2968
+ update({
2969
+ ...draft,
2970
+ rows: draft.rows.map(
2971
+ (current, at) => at === index ? next : current
2972
+ )
2973
+ });
2974
+ }
2975
+ }
2976
+ )
2977
+ ] }, index)
2978
+ )),
2979
+ invalid.length > 0 && /* @__PURE__ */ jsx18("p", { id: invalidMessageId, className: "filter-menu-invalid", children: messages.filterValueInvalid(menu.family) })
2980
+ ] });
2981
+ }
2982
+
2983
+ // src/DataGrid/filterMenu/ValueChecklist.tsx
2984
+ import { useLayoutEffect as useLayoutEffect8, useRef as useRef9, useState as useState9 } from "react";
2985
+ import {
2986
+ attachChecklist,
2987
+ createRowVirtualizer
2988
+ } from "@gridkitjs/dom";
2989
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2990
+ var ESTIMATED_ITEM_HEIGHT = 28;
2991
+ var OVERSCAN = 4;
2992
+ var NO_RANGE = {
2993
+ startIndex: 0,
2994
+ endIndex: -1,
2995
+ offsetBefore: 0,
2996
+ offsetAfter: 0
2997
+ };
2998
+ function ValueChecklist({
2999
+ items,
3000
+ showCounts,
3001
+ label,
3002
+ onToggle
3003
+ }) {
3004
+ const listRef = useRef9(null);
3005
+ const checklistRef = useRef9(null);
3006
+ const [virtualizer, setVirtualizer] = useState9(null);
3007
+ const [range, setRange] = useState9(NO_RANGE);
3008
+ const latest = useRef9({ items, onToggle });
3009
+ useLayoutEffect8(() => {
3010
+ latest.current = { items, onToggle };
3011
+ });
3012
+ useLayoutEffect8(() => {
3013
+ const list = listRef.current;
3014
+ if (list === null) {
3015
+ return void 0;
3016
+ }
3017
+ const rows = createRowVirtualizer(list, {
3018
+ estimatedRowHeight: ESTIMATED_ITEM_HEIGHT,
3019
+ overscan: OVERSCAN,
3020
+ onRange: setRange
3021
+ });
3022
+ const checklist = attachChecklist(list, {
3023
+ onToggle: (index) => {
3024
+ const item = latest.current.items[index];
3025
+ if (item !== void 0) {
3026
+ latest.current.onToggle(item);
3027
+ }
3028
+ },
3029
+ scrollToIndex: (index) => {
3030
+ rows.scrollToIndex(index, { block: "nearest" });
3031
+ }
3032
+ });
3033
+ checklistRef.current = checklist;
3034
+ setVirtualizer(rows);
3035
+ return () => {
3036
+ checklistRef.current = null;
3037
+ checklist.detach();
3038
+ rows.destroy();
3039
+ };
3040
+ }, []);
3041
+ useLayoutEffect8(() => {
3042
+ virtualizer?.setRowCount(items.length);
3043
+ checklistRef.current?.setCount(items.length);
3044
+ }, [virtualizer, items.length]);
3045
+ const shown = items.slice(range.startIndex, range.endIndex + 1);
3046
+ return /* @__PURE__ */ jsxs14(
3047
+ "div",
3048
+ {
3049
+ ref: listRef,
3050
+ role: "listbox",
3051
+ "aria-multiselectable": "true",
3052
+ "aria-label": label,
3053
+ className: "filter-menu-checklist",
3054
+ children: [
3055
+ /* @__PURE__ */ jsx19("div", { role: "presentation", style: { height: range.offsetBefore } }),
3056
+ shown.map((item, offset) => {
3057
+ const index = range.startIndex + offset;
3058
+ return /* @__PURE__ */ jsxs14(
3059
+ "div",
3060
+ {
3061
+ ref: virtualizer?.measureRow(index),
3062
+ role: "option",
3063
+ "data-gridkit-checklist-item": "",
3064
+ "data-gridkit-checklist-index": index,
3065
+ "aria-checked": item.checked,
3066
+ "aria-setsize": items.length,
3067
+ "aria-posinset": index + 1,
3068
+ className: "filter-menu-option",
3069
+ children: [
3070
+ /* @__PURE__ */ jsx19("span", { className: "filter-menu-option-check", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(CheckIcon, {}) }),
3071
+ /* @__PURE__ */ jsx19("span", { className: "filter-menu-option-label", children: item.label }),
3072
+ showCounts && /* @__PURE__ */ jsxs14(Fragment6, { children: [
3073
+ " ",
3074
+ /* @__PURE__ */ jsx19("span", { className: "filter-menu-option-count", children: String(item.count) })
3075
+ ] })
3076
+ ]
3077
+ },
3078
+ item.key
3079
+ );
3080
+ }),
3081
+ /* @__PURE__ */ jsx19("div", { role: "presentation", style: { height: range.offsetAfter } })
3082
+ ]
3083
+ }
3084
+ );
3085
+ }
3086
+
3087
+ // src/DataGrid/filterMenu/ExcelFilterEditor.tsx
3088
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
3089
+ function toggleOf(item) {
3090
+ return item.kind === "blanks" ? { type: "setIncludeBlanks", includeBlanks: !item.checked } : { type: "toggleValue", value: item.value };
3091
+ }
3092
+ function ExcelFilterEditor({
3093
+ column,
3094
+ menu,
3095
+ controller,
3096
+ messages,
3097
+ initialFocusRef,
3098
+ applyFilter,
3099
+ columnValues,
3100
+ ref
3101
+ }) {
3102
+ const [opened] = useState10(() => {
3103
+ const shape2 = describeColumnFilter2(controller.getState().filter, column.id);
3104
+ return {
3105
+ shape: shape2,
3106
+ draft: initialFilterEditorDraft2(shape2, columnValues(column.id))
3107
+ };
3108
+ });
3109
+ const [draft, dispatch] = useReducer(filterEditorReducer2, opened.draft);
3110
+ const [operator, setOperator] = useState10(null);
3111
+ const conditions = useRef10(null);
3112
+ const conditionsFocus = useRef10(null);
3113
+ const operatorButton = useRef10(null);
3114
+ const switched = useRef10(false);
3115
+ const selectAllRef = useRef10(null);
3116
+ const selectAll = useMemo4(() => selectAllState(draft), [draft]);
3117
+ const checklist = useMemo4(
3118
+ () => filterChecklist(draft, {
3119
+ valueLimit: menu.valueLimit,
3120
+ blanksLabel: messages.filterBlanks
3121
+ }),
3122
+ [draft, menu.valueLimit, messages.filterBlanks]
3123
+ );
3124
+ useLayoutEffect9(() => {
3125
+ if (selectAllRef.current !== null) {
3126
+ selectAllRef.current.indeterminate = selectAll === "mixed";
3127
+ }
3128
+ });
3129
+ useLayoutEffect9(() => {
3130
+ if (switched.current) {
3131
+ switched.current = false;
3132
+ (operator === null ? operatorButton.current : conditionsFocus.current)?.focus();
3133
+ }
3134
+ }, [operator]);
3135
+ function switchTo(chosen) {
3136
+ switched.current = true;
3137
+ setOperator(chosen);
3138
+ }
3139
+ useImperativeHandle2(ref, () => ({
3140
+ apply: () => {
3141
+ if (operator !== null) {
3142
+ return conditions.current?.apply() ?? true;
3143
+ }
3144
+ const picked = draft.checked !== opened.draft.checked || draft.includeBlanks !== opened.draft.includeBlanks;
3145
+ if (picked) {
3146
+ applyFilter(
3147
+ draftToFilter2(draft, column.id, controller.getState().filter)
3148
+ );
3149
+ }
3150
+ return true;
3151
+ }
3152
+ }));
3153
+ if (operator !== null) {
3154
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
3155
+ /* @__PURE__ */ jsxs15(
3156
+ "button",
3157
+ {
3158
+ type: "button",
3159
+ className: "filter-menu-back",
3160
+ onClick: () => {
3161
+ switchTo(null);
3162
+ },
3163
+ children: [
3164
+ /* @__PURE__ */ jsx20("span", { className: "filter-menu-back-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx20(ChevronLeftIcon2, {}) }),
3165
+ messages.filterBackToValues
3166
+ ]
3167
+ }
3168
+ ),
3169
+ /* @__PURE__ */ jsx20(
3170
+ SimpleFilterEditor,
3171
+ {
3172
+ ref: conditions,
3173
+ column,
3174
+ menu,
3175
+ controller,
3176
+ messages,
3177
+ initialFocusRef: (element) => {
3178
+ conditionsFocus.current = element;
3179
+ },
3180
+ applyFilter,
3181
+ operator
3182
+ }
3183
+ )
3184
+ ] });
3185
+ }
3186
+ const { shape } = opened;
3187
+ return /* @__PURE__ */ jsxs15("div", { className: "filter-menu-values", children: [
3188
+ menu.operators.length > 0 && /* @__PURE__ */ jsx20(
3189
+ OperatorMenuButton,
3190
+ {
3191
+ ref: operatorButton,
3192
+ menu,
3193
+ active: shape.kind === "conditions" ? shape.conditions.map((condition) => condition.operator) : void 0,
3194
+ messages,
3195
+ onChoose: switchTo
3196
+ }
3197
+ ),
3198
+ (shape.kind === "conditions" || shape.kind === "custom") && /* @__PURE__ */ jsx20("p", { className: "filter-menu-custom", children: shape.kind === "conditions" ? messages.filterByCondition : messages.customFilter }),
3199
+ /* @__PURE__ */ jsx20(
3200
+ "input",
3201
+ {
3202
+ ref: initialFocusRef,
3203
+ type: "search",
3204
+ className: "filter-menu-search",
3205
+ "aria-label": messages.filterSearchLabel,
3206
+ placeholder: messages.searchPlaceholder,
3207
+ value: draft.search,
3208
+ onChange: (event) => {
3209
+ dispatch({ type: "search", search: event.currentTarget.value });
3210
+ }
3211
+ }
3212
+ ),
3213
+ /* @__PURE__ */ jsxs15("label", { className: "filter-menu-select-all", children: [
3214
+ /* @__PURE__ */ jsx20(
3215
+ "input",
3216
+ {
3217
+ ref: selectAllRef,
3218
+ type: "checkbox",
3219
+ checked: selectAll === "checked",
3220
+ disabled: !checklist.needsSearch && checklist.items.length === 0,
3221
+ onChange: () => {
3222
+ dispatch({ type: "toggleAll" });
3223
+ }
3224
+ }
3225
+ ),
3226
+ messages.filterSelectAll
3227
+ ] }),
3228
+ checklist.needsSearch ? /* @__PURE__ */ jsx20("p", { className: "filter-menu-search-first", children: messages.filterSearchFirst(checklist.total) }) : /* @__PURE__ */ jsx20(
3229
+ ValueChecklist,
3230
+ {
3231
+ items: checklist.items,
3232
+ showCounts: menu.showCounts,
3233
+ label: messages.filterValuesLabel,
3234
+ onToggle: (item) => {
3235
+ dispatch(toggleOf(item));
3236
+ }
3237
+ }
3238
+ ),
3239
+ checklist.hiddenCount > 0 && /* @__PURE__ */ jsx20("p", { role: "status", className: "filter-menu-more", children: messages.filterMoreValues(checklist.hiddenCount) })
3240
+ ] });
3241
+ }
3242
+
3243
+ // src/DataGrid/filterMenu/FilterMenu.tsx
3244
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
3245
+ var SORT_COMMANDS = [
3246
+ "sort.ascending",
3247
+ "sort.descending",
3248
+ "sort.clear"
3249
+ ];
3250
+ function isItem(entry) {
3251
+ return entry.type === "item";
3252
+ }
3253
+ function FilterMenu({
3254
+ target,
3255
+ controller,
3256
+ commands,
3257
+ messages,
3258
+ close,
3259
+ initialFocusRef,
3260
+ applyFilter,
3261
+ columnValues
3262
+ }) {
3263
+ const editor = useRef11(null);
3264
+ const column = target.area === "header" ? controller.getView().dataColumns.find((entry) => entry.id === target.columnId) : void 0;
3265
+ const menu = column === void 0 ? null : resolveColumnFilterMenu2(controller.getOptions(), column.column);
3266
+ useEffect3(() => {
3267
+ if (menu === null) {
3268
+ close();
3269
+ }
3270
+ });
3271
+ if (column === void 0 || menu === null) {
3272
+ return null;
3273
+ }
3274
+ const resolveOptions = {
3275
+ commands: commands.commands,
3276
+ context: commands.contextFor(target)
3277
+ };
3278
+ const sortItems = resolveMenu2(SORT_COMMANDS, resolveOptions).filter(isItem);
3279
+ const [clearItem] = resolveMenu2(["filter.clear"], resolveOptions).filter(
3280
+ isItem
3281
+ );
3282
+ function run(item) {
3283
+ commands.run(item.id, target, "filterMenu");
3284
+ close();
3285
+ }
3286
+ const editorProps = {
3287
+ ref: editor,
3288
+ column,
3289
+ menu,
3290
+ controller,
3291
+ messages,
3292
+ initialFocusRef,
3293
+ applyFilter
3294
+ };
3295
+ function apply(event) {
3296
+ event.preventDefault();
3297
+ if (editor.current?.apply() ?? true) {
3298
+ close();
3299
+ }
3300
+ }
3301
+ return /* @__PURE__ */ jsx21(
3302
+ "div",
3303
+ {
3304
+ role: "dialog",
3305
+ "aria-label": messages.filterMenuLabel(
3306
+ resolveColumnHeaderText(column.column)
3307
+ ),
3308
+ className: "gridkit-filter-menu",
3309
+ children: /* @__PURE__ */ jsxs16(
3310
+ "form",
3311
+ {
3312
+ className: "filter-menu-form",
3313
+ noValidate: true,
3314
+ onSubmit: apply,
3315
+ onKeyDown: (event) => {
3316
+ if (event.key === "Enter" && event.target instanceof Element && event.target.closest("select, [role=listbox]") !== null) {
3317
+ event.preventDefault();
3318
+ event.currentTarget.requestSubmit();
3319
+ }
3320
+ },
3321
+ children: [
3322
+ sortItems.length > 0 && /* @__PURE__ */ jsx21("div", { className: "filter-menu-sort", children: sortItems.map((item) => /* @__PURE__ */ jsxs16(
3323
+ "button",
3324
+ {
3325
+ type: "button",
3326
+ className: "filter-menu-sort-item",
3327
+ disabled: item.disabled,
3328
+ ...ariaAttr(
3329
+ item.checked !== void 0,
3330
+ "aria-pressed",
3331
+ item.checked
3332
+ ),
3333
+ onClick: () => {
3334
+ run(item);
3335
+ },
3336
+ children: [
3337
+ /* @__PURE__ */ jsx21("span", { className: "filter-menu-sort-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx21(MenuIcon, { name: item.icon }) }),
3338
+ item.label
3339
+ ]
3340
+ },
3341
+ item.id
3342
+ )) }),
3343
+ menu.ui === "excel" ? /* @__PURE__ */ jsx21(ExcelFilterEditor, { ...editorProps, columnValues }) : /* @__PURE__ */ jsx21(SimpleFilterEditor, { ...editorProps }),
3344
+ /* @__PURE__ */ jsxs16("div", { className: "filter-menu-actions", children: [
3345
+ clearItem !== void 0 && /* @__PURE__ */ jsx21(
3346
+ "button",
3347
+ {
3348
+ type: "button",
3349
+ className: "filter-menu-clear",
3350
+ disabled: clearItem.disabled,
3351
+ onClick: () => {
3352
+ run(clearItem);
3353
+ },
3354
+ children: clearItem.label
3355
+ }
3356
+ ),
3357
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "filter-menu-cancel", onClick: close, children: messages.cancelFilter }),
3358
+ /* @__PURE__ */ jsx21("button", { type: "submit", className: "filter-menu-apply", children: messages.applyFilter })
3359
+ ] })
3360
+ ]
3361
+ }
3362
+ )
3363
+ }
3364
+ );
3365
+ }
3366
+
3367
+ // src/DataGrid/filterMenu/useApplyFilter.ts
3368
+ import { useEffect as useEffect4, useRef as useRef12 } from "react";
3369
+ function useApplyFilter(controller, messages, announce) {
3370
+ const unannounced = useRef12(null);
3371
+ useEffect4(() => {
3372
+ if (unannounced.current === controller.getState().filter) {
3373
+ unannounced.current = null;
3374
+ announce(
3375
+ messages.filterResults(
3376
+ controller.getView().rows.length,
3377
+ controller.getOptions().dataSource?.length ?? 0
3378
+ )
3379
+ );
3380
+ }
3381
+ });
3382
+ return (filter) => {
3383
+ unannounced.current = filter;
3384
+ controller.setFilter(filter);
3385
+ };
3386
+ }
3387
+
3388
+ // src/DataGrid/filterMenu/useFilterValueCache.ts
3389
+ import { useState as useState11 } from "react";
3390
+ import {
3391
+ createColumnValueOptionsCache
3392
+ } from "@gridkitjs/core";
3393
+ var NO_ROWS2 = [];
3394
+ function useFilterValueCache(controller, messages) {
3395
+ const [cache] = useState11(createColumnValueOptionsCache);
3396
+ return (columnId) => {
3397
+ const { dataSource = NO_ROWS2, locale } = controller.getOptions();
3398
+ return cache.get(
3399
+ dataSource,
3400
+ controller.getView().dataColumns,
3401
+ controller.getState().filter,
3402
+ columnId,
3403
+ locale === void 0 ? { messages } : { locale, messages }
3404
+ );
3405
+ };
3406
+ }
3407
+
3408
+ // src/DataGrid/panels/GridPanels.tsx
3409
+ import { jsx as jsx22 } from "react/jsx-runtime";
3410
+ var panelKinds = {
3411
+ columnChooser: { component: ColumnChooser, trapFocus: false },
3412
+ filter: { component: FilterMenu, trapFocus: true }
3413
+ };
3414
+ function GridPanels({
3415
+ panels,
3416
+ controller,
3417
+ commands,
3418
+ messages
3419
+ }) {
3420
+ const initialFocus = useRef13(null);
3421
+ const applyFilter = useApplyFilter(
3422
+ controller,
3423
+ messages,
3424
+ commands.host.announce
3425
+ );
3426
+ const columnValues = useFilterValueCache(controller, messages);
3427
+ const { panel, close } = panels;
3428
+ if (panel === null) {
3429
+ return null;
3430
+ }
3431
+ const { component: Panel, trapFocus } = panelKinds[panel.kind];
3432
+ return /* @__PURE__ */ jsx22(
3433
+ Overlay,
3434
+ {
3435
+ anchor: panel.anchor,
3436
+ placement: panel.placement,
3437
+ gap: panel.anchor instanceof Element ? ANCHOR_GAP : 0,
3438
+ initialFocus,
3439
+ returnFocusTo: panel.returnFocusTo,
3440
+ ignoreScrollAfterInput: true,
3441
+ trapFocus,
3442
+ themeClassName: panel.themeClassName,
3443
+ onDismiss: close,
3444
+ children: /* @__PURE__ */ jsx22(
3445
+ Panel,
3446
+ {
3447
+ target: panel.target,
3448
+ controller,
3449
+ commands,
3450
+ messages,
3451
+ close,
3452
+ initialFocusRef: (element) => {
3453
+ initialFocus.current = element;
3454
+ },
3455
+ applyFilter,
3456
+ columnValues
3457
+ }
3458
+ )
3459
+ },
3460
+ panel.key
3461
+ );
3462
+ }
3463
+
3464
+ // src/DataGrid/panels/panelApi.ts
3465
+ import { resolveColumnFilterMenu as resolveColumnFilterMenu3 } from "@gridkitjs/core";
3466
+ function panelApi(panels, controller) {
3467
+ return {
3468
+ openColumnChooser: () => {
3469
+ panels.open("columnChooser", { area: "grid" });
3470
+ },
3471
+ openFilterMenu: (columnId) => {
3472
+ const shown = controller.getView().columns.find((entry) => entry.id === columnId);
3473
+ if (shown === void 0 || resolveColumnFilterMenu3(controller.getOptions(), shown.column) === null) {
3474
+ return false;
3475
+ }
3476
+ panels.open("filter", { area: "header", columnId });
3477
+ return true;
3478
+ },
3479
+ closeFilterMenu: () => {
3480
+ if (panels.panel?.kind === "filter") {
3481
+ panels.close();
3482
+ }
3483
+ }
3484
+ };
3485
+ }
3486
+
3487
+ // src/DataGrid/panels/useGridPanels.ts
3488
+ import { useLayoutEffect as useLayoutEffect10, useRef as useRef14, useState as useState12 } from "react";
3489
+ function toolbarItem(root, itemId) {
3490
+ const items = root.querySelectorAll(
3491
+ ":scope > .gridkit-toolbar > [data-gridkit-toolbar-item]"
3492
+ );
3493
+ return Array.from(items).find(
3494
+ (item) => item.getAttribute("data-gridkit-toolbar-item") === itemId
3495
+ ) ?? // In "More" while the toolbar is narrow: the button it is chosen from.
3496
+ Array.from(items).find(
3497
+ (item) => item.hasAttribute("data-gridkit-toolbar-more")
3498
+ ) ?? null;
3499
+ }
3500
+ function headerCell2(headerTable, columnId) {
3501
+ const cells = headerTable?.tHead?.rows[0]?.cells;
3502
+ return cells === void 0 ? null : Array.from(cells).find(
3503
+ (cell) => cell.getAttribute("data-gridkit-column") === columnId
3504
+ ) ?? null;
3505
+ }
3506
+ function useGridPanels({
3507
+ rootRef,
3508
+ headerTableRef,
3509
+ nav
3510
+ }) {
3511
+ const [panel, setPanel] = useState12(null);
3512
+ const openings = useRef14(0);
3513
+ function placeFor(root, target) {
3514
+ const element = target.area === "toolbar" ? toolbarItem(root, target.itemId) : target.area === "header" ? headerCell2(headerTableRef.current, target.columnId) : null;
3515
+ if (element !== null) {
3516
+ return {
3517
+ anchor: element,
3518
+ // The toolbar's own button sits at its far end.
3519
+ placement: target.area === "toolbar" ? "bottom-end" : "bottom-start",
3520
+ returnFocusTo: element
3521
+ };
3522
+ }
3523
+ const { right, top } = root.getBoundingClientRect();
3524
+ return {
3525
+ anchor: { x: right, y: top },
3526
+ placement: "bottom-end",
3527
+ returnFocusTo: void 0
3528
+ };
3529
+ }
3530
+ const shown = useRef14(null);
3531
+ useLayoutEffect10(() => {
3532
+ const closed = shown.current;
3533
+ shown.current = panel;
3534
+ if (panel === null && closed?.returnFocusTo?.isConnected === false && document.activeElement === document.body) {
3535
+ nav.focusTabStop();
3536
+ }
3537
+ });
3538
+ return {
3539
+ panel,
3540
+ open: (kind, target) => {
3541
+ const root = rootRef.current;
3542
+ if (root === null) {
3543
+ return;
3544
+ }
3545
+ openings.current += 1;
3546
+ setPanel({
3547
+ key: openings.current,
3548
+ kind,
3549
+ target,
3550
+ ...placeFor(root, target),
3551
+ themeClassName: overlayThemeClassName(root)
3552
+ });
3553
+ },
3554
+ close: () => {
3555
+ setPanel(null);
3556
+ }
3557
+ };
3558
+ }
3559
+
3560
+ // src/DataGrid/toolbar/GridToolbar.tsx
3561
+ import { useLayoutEffect as useLayoutEffect13, useRef as useRef18 } from "react";
3562
+ import {
3563
+ resolveToolbar,
3564
+ splitToolbarItems,
3565
+ toolbarEntries
3566
+ } from "@gridkitjs/core";
3567
+ import { attachToolbar } from "@gridkitjs/dom";
3568
+
3569
+ // src/DataGrid/toolbar/ToolbarMenuButton.tsx
3570
+ import { useLayoutEffect as useLayoutEffect11, useRef as useRef15, useState as useState13 } from "react";
3571
+ import { Fragment as Fragment8, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
3572
+ function ToolbarMenuButton({
3573
+ itemKey,
3574
+ label,
3575
+ icon,
3576
+ entries,
3577
+ more = false,
3578
+ onMenuClose
3579
+ }) {
3580
+ const [menu, setMenu] = useState13(null);
3581
+ const menuBehaviourRef = useRef15(null);
3582
+ useLayoutEffect11(() => {
3583
+ if (menu !== null && menu.focusItem > 0) {
3584
+ menuBehaviourRef.current?.focusItem(menu.focusItem);
3585
+ }
3586
+ }, [menu]);
3587
+ function open(button, onLast = false) {
3588
+ setMenu({
3589
+ anchor: button,
3590
+ themeClassName: overlayThemeClassName(button),
3591
+ focusItem: onLast ? entries.filter((entry) => entry.type !== "separator").length - 1 : 0
3592
+ });
3593
+ }
3594
+ function close() {
3595
+ setMenu(null);
3596
+ onMenuClose();
3597
+ }
3598
+ return /* @__PURE__ */ jsxs17(Fragment8, { children: [
3599
+ /* @__PURE__ */ jsxs17(
3600
+ "button",
3601
+ {
3602
+ type: "button",
3603
+ className: classNames("toolbar-button", more && "toolbar-more"),
3604
+ "data-gridkit-toolbar-item": itemKey,
3605
+ ...ariaAttr(more, "data-gridkit-toolbar-more", ""),
3606
+ "aria-haspopup": "menu",
3607
+ "aria-expanded": menu !== null,
3608
+ onClick: (event) => {
3609
+ if (menu === null) {
3610
+ open(event.currentTarget);
3611
+ } else {
3612
+ close();
3613
+ }
3614
+ },
3615
+ onKeyDown: (event) => {
3616
+ if ((event.key === "ArrowDown" || event.key === "ArrowUp") && menu === null) {
3617
+ event.preventDefault();
3618
+ open(event.currentTarget, event.key === "ArrowUp");
3619
+ }
3620
+ },
3621
+ children: [
3622
+ icon !== void 0 && /* @__PURE__ */ jsx23("span", { className: "toolbar-button-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx23(MenuIcon, { name: icon }) }),
3623
+ label,
3624
+ /* @__PURE__ */ jsx23("span", { className: "toolbar-button-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx23(ChevronDownIcon, {}) })
3625
+ ]
3626
+ }
3627
+ ),
3628
+ menu !== null && /* @__PURE__ */ jsx23(
3629
+ Overlay,
3630
+ {
3631
+ anchor: menu.anchor,
3632
+ placement: "bottom-start",
3633
+ gap: ANCHOR_GAP,
3634
+ returnFocusTo: menu.anchor,
3635
+ themeClassName: menu.themeClassName,
3636
+ onDismiss: close,
3637
+ children: /* @__PURE__ */ jsx23(
3638
+ GridMenu,
3639
+ {
3640
+ entries,
3641
+ label,
3642
+ themeClassName: menu.themeClassName,
3643
+ behaviourRef: menuBehaviourRef,
3644
+ onRun: (item) => {
3645
+ item.run();
3646
+ close();
3647
+ },
3648
+ onClose: close
3649
+ }
3650
+ )
3651
+ }
3652
+ )
3653
+ ] });
3654
+ }
3655
+
3656
+ // src/DataGrid/toolbar/ToolbarSearch.tsx
3657
+ import { useEffect as useEffect5, useRef as useRef16, useState as useState14 } from "react";
3658
+ import {
3659
+ searchTextOf,
3660
+ setSearchText
3661
+ } from "@gridkitjs/core";
3662
+ import { jsx as jsx24 } from "react/jsx-runtime";
3663
+ var SEARCH_DELAY_MS = 250;
3664
+ function ToolbarSearch({
3665
+ itemKey,
3666
+ controller,
3667
+ messages,
3668
+ announce
3669
+ }) {
3670
+ const applied = searchTextOf(controller.getState().filter);
3671
+ const [draft, setDraft] = useState14(null);
3672
+ const timer = useRef16(void 0);
3673
+ const unannounced = useRef16(null);
3674
+ const text = draft !== null && draft.over === applied ? draft.text : applied;
3675
+ function apply(next, over) {
3676
+ clearTimeout(timer.current);
3677
+ setDraft(null);
3678
+ const filter = controller.getState().filter;
3679
+ if (searchTextOf(filter) !== over) {
3680
+ return;
3681
+ }
3682
+ const searched = setSearchText(filter, next);
3683
+ if (searched !== filter) {
3684
+ unannounced.current = next;
3685
+ controller.setFilter(searched);
3686
+ }
3687
+ }
3688
+ useEffect5(() => {
3689
+ if (unannounced.current === applied) {
3690
+ unannounced.current = null;
3691
+ announce(messages.searchResults(controller.getView().rows.length));
3692
+ }
3693
+ });
3694
+ useEffect5(
3695
+ () => () => {
3696
+ clearTimeout(timer.current);
3697
+ },
3698
+ []
3699
+ );
3700
+ return /* @__PURE__ */ jsx24(
3701
+ "input",
3702
+ {
3703
+ type: "search",
3704
+ className: "toolbar-search",
3705
+ "data-gridkit-toolbar-item": itemKey,
3706
+ "aria-label": messages.searchLabel,
3707
+ placeholder: messages.searchPlaceholder,
3708
+ value: text,
3709
+ onChange: (event) => {
3710
+ const typed = event.target.value;
3711
+ setDraft({ text: typed, over: applied });
3712
+ clearTimeout(timer.current);
3713
+ timer.current = setTimeout(() => {
3714
+ apply(typed, applied);
3715
+ }, SEARCH_DELAY_MS);
3716
+ },
3717
+ onKeyDown: (event) => {
3718
+ if (event.key === "Enter") {
3719
+ apply(text, applied);
3720
+ } else if (event.key === "Escape" && text !== "") {
3721
+ event.preventDefault();
3722
+ apply("", applied);
3723
+ }
3724
+ }
3725
+ }
3726
+ );
3727
+ }
3728
+
3729
+ // src/DataGrid/toolbar/useToolbarOverflow.ts
3730
+ import { useLayoutEffect as useLayoutEffect12, useRef as useRef17, useState as useState15 } from "react";
3731
+ import { observeToolbarOverflow } from "@gridkitjs/dom";
3732
+ function useToolbarOverflow(toolbarRef, items) {
3733
+ const [fit, setFit] = useState15(items.length);
3734
+ const overflow = useRef17(null);
3735
+ const measuredLayout = useRef17("");
3736
+ const layout = JSON.stringify(
3737
+ items.map((item) => [
3738
+ item.type,
3739
+ item.key,
3740
+ "label" in item ? item.label : null
3741
+ ])
3742
+ );
3743
+ useLayoutEffect12(() => {
3744
+ const toolbar = toolbarRef.current;
3745
+ if (toolbar === null || layout === measuredLayout.current) {
3746
+ return;
3747
+ }
3748
+ measuredLayout.current = layout;
3749
+ const options = { items, onChange: setFit };
3750
+ if (overflow.current === null) {
3751
+ overflow.current = observeToolbarOverflow(toolbar, options);
3752
+ } else {
3753
+ overflow.current.update(options);
3754
+ }
3755
+ }, [toolbarRef, layout, items]);
3756
+ useLayoutEffect12(
3757
+ () => () => {
3758
+ overflow.current?.detach();
3759
+ overflow.current = null;
3760
+ measuredLayout.current = "";
3761
+ },
3762
+ []
3763
+ );
3764
+ return fit;
3765
+ }
3766
+
3767
+ // src/DataGrid/toolbar/GridToolbar.tsx
3768
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
3769
+ function reporting(run, report) {
3770
+ return () => {
3771
+ const ran = run();
3772
+ if (ran) {
3773
+ report();
3774
+ }
3775
+ return ran;
3776
+ };
3777
+ }
3778
+ function reportingEntries(entries, report) {
3779
+ return entries.map((entry) => {
3780
+ switch (entry.type) {
3781
+ case "item":
3782
+ return {
3783
+ ...entry,
3784
+ run: reporting(entry.run, () => {
3785
+ report(entry.id);
3786
+ })
3787
+ };
3788
+ case "submenu":
3789
+ return { ...entry, items: reportingEntries(entry.items, report) };
3790
+ default:
3791
+ return entry;
3792
+ }
3793
+ });
3794
+ }
3795
+ function reportingItem(item, report) {
3796
+ switch (item.type) {
3797
+ case "button":
3798
+ return {
3799
+ ...item,
3800
+ run: reporting(item.run, () => {
3801
+ report(item.id, item.id);
3802
+ })
3803
+ };
3804
+ case "menu":
3805
+ return {
3806
+ ...item,
3807
+ items: reportingEntries(item.items, (commandId) => {
3808
+ report(commandId, item.id);
3809
+ })
3810
+ };
3811
+ default:
3812
+ return item;
3813
+ }
3814
+ }
3815
+ function GridToolbar(props) {
3816
+ const entries = toolbarEntries(props.toolbar);
3817
+ if (entries.length === 0) {
3818
+ return null;
3819
+ }
3820
+ const label = (typeof props.toolbar === "object" ? props.toolbar.label : void 0) ?? props.messages.toolbarLabel;
3821
+ return /* @__PURE__ */ jsx25(Toolbar, { ...props, entries, label });
3822
+ }
3823
+ function Toolbar({
3824
+ entries,
3825
+ label,
3826
+ renderItem,
3827
+ gridId,
3828
+ controller,
3829
+ commands,
3830
+ messages,
3831
+ announce
3832
+ }) {
3833
+ const toolbarRef = useRef18(null);
3834
+ const menuClosed = useRef18(false);
3835
+ const items = resolveToolbar(entries, {
3836
+ commands: commands.commands,
3837
+ contextFor: commands.contextFor
3838
+ }).map(
3839
+ (item) => reportingItem(item, (commandId, itemId) => {
3840
+ commands.report(commandId, { area: "toolbar", itemId }, "toolbar");
3841
+ })
3842
+ );
3843
+ const fit = useToolbarOverflow(toolbarRef, items);
3844
+ const { shown, overflow } = splitToolbarItems(items, fit);
3845
+ useLayoutEffect13(() => {
3846
+ const toolbar = toolbarRef.current;
3847
+ return toolbar === null ? void 0 : attachToolbar(toolbar);
3848
+ }, []);
3849
+ useLayoutEffect13(() => {
3850
+ if (!menuClosed.current) {
3851
+ return;
3852
+ }
3853
+ menuClosed.current = false;
3854
+ const toolbar = toolbarRef.current;
3855
+ if (toolbar !== null && document.activeElement === document.body) {
3856
+ (toolbar.querySelector('[tabindex="0"]') ?? toolbar.querySelector("button, input"))?.focus();
3857
+ }
3858
+ });
3859
+ function onMenuClose() {
3860
+ menuClosed.current = true;
3861
+ }
3862
+ function draw(item) {
3863
+ switch (item.type) {
3864
+ case "button":
3865
+ return /* @__PURE__ */ jsxs18(
3866
+ "button",
3867
+ {
3868
+ type: "button",
3869
+ className: "toolbar-button",
3870
+ "data-gridkit-toolbar-item": item.key,
3871
+ ...ariaAttr(item.disabled, "aria-disabled", true),
3872
+ ...ariaAttr(
3873
+ item.pressed !== void 0,
3874
+ "aria-pressed",
3875
+ item.pressed
3876
+ ),
3877
+ ...ariaAttr(
3878
+ item.shortcut !== void 0,
3879
+ "aria-keyshortcuts",
3880
+ item.shortcut
3881
+ ),
3882
+ onClick: () => {
3883
+ item.run();
3884
+ },
3885
+ children: [
3886
+ item.icon !== void 0 && /* @__PURE__ */ jsx25("span", { className: "toolbar-button-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx25(MenuIcon, { name: item.icon }) }),
3887
+ item.label
3888
+ ]
3889
+ },
3890
+ item.key
3891
+ );
3892
+ case "menu":
3893
+ return /* @__PURE__ */ jsx25(
3894
+ ToolbarMenuButton,
3895
+ {
3896
+ itemKey: item.key,
3897
+ label: item.label,
3898
+ icon: item.icon,
3899
+ entries: item.items,
3900
+ onMenuClose
3901
+ },
3902
+ item.key
3903
+ );
3904
+ case "search":
3905
+ return /* @__PURE__ */ jsx25(
3906
+ ToolbarSearch,
3907
+ {
3908
+ itemKey: item.key,
3909
+ controller,
3910
+ messages,
3911
+ announce
3912
+ },
3913
+ item.key
3914
+ );
3915
+ case "separator":
3916
+ return /* @__PURE__ */ jsx25(
3917
+ "div",
3918
+ {
3919
+ role: "separator",
3920
+ "aria-orientation": "vertical",
3921
+ className: "toolbar-separator",
3922
+ "data-gridkit-toolbar-item": item.key
3923
+ },
3924
+ item.key
3925
+ );
3926
+ case "spacer":
3927
+ return /* @__PURE__ */ jsx25(
3928
+ "div",
3929
+ {
3930
+ className: "toolbar-spacer",
3931
+ "data-gridkit-toolbar-item": item.key
3932
+ },
3933
+ item.key
3934
+ );
3935
+ case "custom":
3936
+ return /* @__PURE__ */ jsx25(
3937
+ "div",
3938
+ {
3939
+ className: "toolbar-custom",
3940
+ "data-gridkit-toolbar-item": item.key,
3941
+ children: renderItem?.(item.id)
3942
+ },
3943
+ item.key
3944
+ );
3945
+ }
3946
+ }
3947
+ return /* @__PURE__ */ jsxs18(
3948
+ "div",
3949
+ {
3950
+ ref: toolbarRef,
3951
+ role: "toolbar",
3952
+ "aria-label": label,
3953
+ "aria-controls": gridId,
3954
+ className: "gridkit-toolbar",
3955
+ children: [
3956
+ shown.map(draw),
3957
+ overflow.length > 0 && /* @__PURE__ */ jsx25(
3958
+ ToolbarMenuButton,
3959
+ {
3960
+ itemKey: "more",
3961
+ label: messages.moreToolbarItems,
3962
+ entries: overflow,
3963
+ more: true,
3964
+ onMenuClose
3965
+ }
3966
+ )
3967
+ ]
3968
+ }
3969
+ );
3970
+ }
3971
+
3972
+ // src/DataGrid/useColumnDrag.ts
3973
+ import {
3974
+ resolveKeyboardDropTarget as resolveKeyboardDropTarget2
3975
+ } from "@gridkitjs/core";
3976
+ import { resolveColumnDropTarget } from "@gridkitjs/dom";
3977
+ function useColumnDrag({
3978
+ order,
3979
+ groupBy,
3980
+ groupByDraggableIds,
3981
+ onDrop
3982
+ }) {
3983
+ const drag = useDragReorder({
3984
+ resolveTarget: (clientX, clientY, movedId) => resolveColumnDropTarget(clientX, clientY, movedId, {
3985
+ order,
3986
+ groupBy,
3987
+ groupByDraggableIds
3988
+ }),
3989
+ onDrop
3990
+ });
3991
+ function startDrag(entry, event) {
3992
+ drag.startDrag(entry.id, event.currentTarget, event);
3993
+ }
3994
+ function moveByKeyboard(entry, direction) {
3995
+ const beforeId = resolveKeyboardDropTarget2(order, entry.id, direction);
3996
+ if (beforeId === void 0) {
3997
+ return;
3998
+ }
3999
+ onDrop({ kind: "column-order", beforeId }, entry.id);
4000
+ }
4001
+ return {
4002
+ draggedColumnId: drag.draggedId,
4003
+ dropTarget: drag.dropTarget,
4004
+ ghostTransform: drag.ghostTransform,
4005
+ startDrag,
4006
+ moveByKeyboard,
4007
+ justDragged: drag.justDragged
4008
+ };
4009
+ }
4010
+
4011
+ // src/DataGrid/useColumnResize.ts
4012
+ import {
4013
+ useState as useState16
4014
+ } from "react";
4015
+ import {
4016
+ beginColumnResize,
4017
+ clampColumnWidth,
4018
+ resolveColumnConstraints,
4019
+ sizeColumnToContent
4020
+ } from "@gridkitjs/core";
4021
+ import { measureColumnContent, startColumnResizeGesture } from "@gridkitjs/dom";
4022
+ function useColumnResize({
4023
+ tableRef,
4024
+ controller,
4025
+ columnSizeDefaults
4026
+ }) {
4027
+ const [activeColumnId, setActiveColumnId] = useState16(null);
4028
+ function startResize(entry, event) {
4029
+ event.stopPropagation();
4030
+ const session = beginColumnResize(
4031
+ entry.column,
4032
+ entry.width,
4033
+ event.clientX,
4034
+ columnSizeDefaults
4035
+ );
4036
+ const base = controller.getState().columnSizing;
4037
+ const started = startColumnResizeGesture(
4038
+ event.currentTarget,
4039
+ event,
4040
+ session,
4041
+ {
4042
+ onMove(width) {
4043
+ controller.resizeColumn(session.columnId, width, "move");
4044
+ },
4045
+ onEnd(width) {
4046
+ setActiveColumnId(null);
4047
+ if (width !== null) {
4048
+ controller.resizeColumn(session.columnId, width, "end");
4049
+ }
4050
+ },
4051
+ /**
4052
+ * Escape puts back only the dragged column's pre-drag width, merged
4053
+ * into whatever the sizing state is *now* — not the `base` snapshot
4054
+ * taken at drag-start — so a different column resized via keyboard
4055
+ * while this drag was still open keeps its change instead of being
4056
+ * clobbered by a stale restore.
4057
+ */
4058
+ onCancel() {
4059
+ setActiveColumnId(null);
4060
+ controller.cancelColumnResize(
4061
+ session.columnId,
4062
+ base,
4063
+ session.startWidth
4064
+ );
4065
+ }
4066
+ }
4067
+ );
4068
+ if (started) {
4069
+ setActiveColumnId(session.columnId);
4070
+ }
4071
+ }
4072
+ function sizeToContent(entry) {
4073
+ const table = tableRef.current;
4074
+ if (table === null) {
4075
+ return false;
4076
+ }
4077
+ const measured = measureColumnContent(table, entry.id);
4078
+ if (measured === null) {
4079
+ return false;
4080
+ }
4081
+ controller.resizeColumn(
4082
+ entry.id,
4083
+ sizeColumnToContent(
4084
+ measured,
4085
+ resolveColumnConstraints(entry.column, columnSizeDefaults)
4086
+ ),
4087
+ "end"
4088
+ );
4089
+ return true;
4090
+ }
4091
+ function nudge(entry, delta) {
4092
+ controller.resizeColumn(
4093
+ entry.id,
4094
+ clampColumnWidth(
4095
+ entry.width + delta,
4096
+ resolveColumnConstraints(entry.column, columnSizeDefaults)
4097
+ ),
4098
+ "end"
4099
+ );
4100
+ }
4101
+ return { activeColumnId, startResize, sizeToContent, nudge };
4102
+ }
4103
+
4104
+ // src/DataGrid/useElementWidth.ts
4105
+ import { useLayoutEffect as useLayoutEffect14, useState as useState17 } from "react";
4106
+ import { observeElementWidth } from "@gridkitjs/dom";
4107
+ function useElementWidth(ref, enabled) {
4108
+ const [width, setWidth] = useState17(null);
4109
+ useLayoutEffect14(() => {
4110
+ const element = ref.current;
4111
+ if (element === null || !enabled) {
4112
+ return void 0;
4113
+ }
4114
+ return observeElementWidth(element, setWidth);
4115
+ }, [ref, enabled]);
4116
+ return width;
4117
+ }
4118
+
4119
+ // src/DataGrid/useGridController.ts
4120
+ import { useState as useState18, useSyncExternalStore as useSyncExternalStore2 } from "react";
4121
+ import {
4122
+ createGridController
4123
+ } from "@gridkitjs/core";
4124
+ function useGridController(options) {
4125
+ const [controller] = useState18(
4126
+ () => createGridController(options)
4127
+ );
4128
+ controller.setOptions(options);
4129
+ const state = useSyncExternalStore2(
4130
+ controller.subscribe,
4131
+ controller.getState,
4132
+ controller.getState
4133
+ );
4134
+ return { controller, state, view: controller.getView() };
4135
+ }
4136
+
4137
+ // src/DataGrid/useInfiniteScroll.ts
4138
+ import { useEffect as useEffect6, useRef as useRef19 } from "react";
4139
+ import { attachInfiniteScroll } from "@gridkitjs/dom";
4140
+ function useInfiniteScroll({
4141
+ rootRef,
4142
+ sentinelRef,
4143
+ config
4144
+ }) {
4145
+ const scroll = useRef19(null);
4146
+ useEffect6(() => {
4147
+ const root = rootRef.current;
4148
+ if (root === null) {
4149
+ return void 0;
4150
+ }
4151
+ const attached = attachInfiniteScroll(root, {
4152
+ sentinel: null,
4153
+ hasMore: false
4154
+ });
4155
+ scroll.current = attached;
4156
+ return () => {
4157
+ attached.detach();
4158
+ scroll.current = null;
4159
+ };
4160
+ }, [rootRef]);
4161
+ useEffect6(() => {
4162
+ scroll.current?.update({
4163
+ sentinel: sentinelRef.current,
4164
+ hasMore: config?.hasMore ?? false,
4165
+ onLoadMore: config?.onLoadMore,
4166
+ isLoadingMore: config?.isLoadingMore,
4167
+ rootMargin: config?.rootMargin
4168
+ });
4169
+ });
4170
+ }
4171
+
4172
+ // src/DataGrid/useLiveAnnouncer.ts
4173
+ import { useCallback, useLayoutEffect as useLayoutEffect15, useRef as useRef20 } from "react";
4174
+ import { createLiveAnnouncer } from "@gridkitjs/dom";
4175
+ function useLiveAnnouncer(containerRef) {
4176
+ const announcer = useRef20(null);
4177
+ useLayoutEffect15(() => {
4178
+ const container = containerRef.current;
4179
+ if (container === null) {
4180
+ return void 0;
4181
+ }
4182
+ const created = createLiveAnnouncer(container);
4183
+ announcer.current = created;
4184
+ return () => {
4185
+ created.destroy();
4186
+ announcer.current = null;
4187
+ };
4188
+ }, [containerRef]);
4189
+ return useCallback((text) => {
4190
+ announcer.current?.announce(text);
4191
+ }, []);
4192
+ }
4193
+
4194
+ // src/DataGrid/useRowVirtualizer.ts
4195
+ import { useLayoutEffect as useLayoutEffect16, useState as useState19 } from "react";
4196
+ import { createRowVirtualizer as createRowVirtualizer2 } from "@gridkitjs/dom";
4197
+ var EMPTY_RANGE = {
4198
+ startIndex: 0,
4199
+ endIndex: -1,
4200
+ offsetBefore: 0,
4201
+ offsetAfter: 0
4202
+ };
4203
+ function ignoreRow() {
4204
+ return () => void 0;
4205
+ }
4206
+ function useRowVirtualizer({
4207
+ enabled,
4208
+ rows,
4209
+ bodyScrollRef,
4210
+ estimatedRowHeight,
4211
+ overscan
4212
+ }) {
4213
+ const [virtualizer, setVirtualizer] = useState19(null);
4214
+ const [range, setRange] = useState19(EMPTY_RANGE);
4215
+ useLayoutEffect16(() => {
4216
+ const element = bodyScrollRef.current;
4217
+ if (!enabled || element === null) {
4218
+ return void 0;
4219
+ }
4220
+ const created = createRowVirtualizer2(element, {
4221
+ estimatedRowHeight,
4222
+ overscan,
4223
+ onRange: setRange
4224
+ });
4225
+ setVirtualizer(created);
4226
+ return () => {
4227
+ created.destroy();
4228
+ };
4229
+ }, [enabled, bodyScrollRef, estimatedRowHeight, overscan]);
4230
+ useLayoutEffect16(() => {
4231
+ virtualizer?.setRowCount(rows.length);
4232
+ virtualizer?.resetHeights();
4233
+ }, [virtualizer, rows]);
4234
+ return {
4235
+ startIndex: range.startIndex,
4236
+ endIndex: range.endIndex,
4237
+ offsetBefore: range.offsetBefore,
4238
+ offsetAfter: range.offsetAfter,
4239
+ measureRow: virtualizer?.measureRow ?? ignoreRow,
4240
+ scrollToIndex: (index, options) => {
4241
+ virtualizer?.scrollToIndex(index, options);
4242
+ }
4243
+ };
4244
+ }
4245
+
4246
+ // src/DataGrid/DataGrid.tsx
4247
+ import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
4248
+ var NO_AGGREGATES = [];
4249
+ function paginationReader(controller) {
4250
+ let last;
4251
+ return () => {
4252
+ const { pageIndex } = controller.getView();
4253
+ const { pageSize } = controller.getState().pagination;
4254
+ if (last?.pageIndex !== pageIndex || last.pageSize !== pageSize) {
4255
+ last = { pageIndex, pageSize };
4256
+ }
4257
+ return last;
4258
+ };
4259
+ }
4260
+ function selectedRowsReader(controller) {
4261
+ let last;
4262
+ return () => {
4263
+ const { rows } = controller.getView();
4264
+ const selection = controller.getState().rowSelection;
4265
+ if (last?.rows !== rows || last.selection !== selection) {
4266
+ const rowsById = new Map(rows.map((entry) => [entry.rowId, entry]));
4267
+ last = { rows, selection, selected: resolveRows(rowsById, selection) };
4268
+ }
4269
+ return last.selected;
4270
+ };
4271
+ }
4272
+ function DataGrid(props) {
4273
+ const {
4274
+ borders,
4275
+ hoverable,
4276
+ selectable,
4277
+ resizeMode = "fit",
4278
+ columnSizeDefaults,
4279
+ sortableColumns = false,
4280
+ groupableColumns = false,
4281
+ groupToggleIconColumns = true,
4282
+ groupByBarVisibility = "auto",
4283
+ paginated = false,
4284
+ pager,
4285
+ infiniteScroll,
4286
+ aggregates = NO_AGGREGATES,
4287
+ groupAggregateDisplay = "inline",
4288
+ height,
4289
+ virtualized = false,
4290
+ overscan = 4,
4291
+ estimatedRowHeight = 40,
4292
+ label,
4293
+ labelledBy,
4294
+ locale,
4295
+ getRowProps,
4296
+ onRowClick,
4297
+ onRowDoubleClick,
4298
+ messages: messageOverrides,
4299
+ ref
4300
+ } = props;
4301
+ const hoverRows = hoverable?.rows ?? true;
4302
+ const hoverColumns = hoverable?.columns ?? true;
4303
+ const hoverCells = hoverable?.cells ?? true;
4304
+ const viewportRef = useRef21(null);
4305
+ const headerTableRef = useRef21(null);
4306
+ const bodyTableRef = useRef21(null);
4307
+ const bodyScrollRef = useRef21(null);
4308
+ const sentinelRef = useRef21(null);
4309
+ const viewportWidth = useElementWidth(viewportRef, resizeMode === "fit");
4310
+ const subscribersRef = useRef21(/* @__PURE__ */ new Set());
4311
+ const gridId = useId2();
4312
+ const announce = useLiveAnnouncer(viewportRef);
4313
+ const messages = useMemo5(
4314
+ () => resolveMessages(messageOverrides, locale),
4315
+ [messageOverrides, locale]
4316
+ );
4317
+ const { controller, state, view } = useGridController({
4318
+ ...props,
4319
+ viewportWidth,
4320
+ ...announcedCallbacks({
4321
+ callbacks: props,
4322
+ announce,
4323
+ messages,
4324
+ columnName,
4325
+ columnOf
4326
+ })
4327
+ });
4328
+ const [readPagination] = useState20(() => paginationReader(controller));
4329
+ const [readSelectedRows] = useState20(() => selectedRowsReader(controller));
4330
+ const virtualizer = useRowVirtualizer({
4331
+ enabled: virtualized,
4332
+ rows: view.pageRows,
4333
+ bodyScrollRef,
4334
+ estimatedRowHeight,
4335
+ overscan
4336
+ });
4337
+ useEffect7(() => {
4338
+ if (virtualized && height === void 0) {
4339
+ console.error(
4340
+ "DataGrid: `virtualized` has no effect without `height` \u2014 a body with no bounded height has no viewport to window rows against."
4341
+ );
4342
+ }
4343
+ }, [virtualized, height]);
4344
+ const hasInfiniteScroll = infiniteScroll !== void 0;
4345
+ useEffect7(() => {
4346
+ if (paginated && hasInfiniteScroll) {
4347
+ console.error(
4348
+ "DataGrid: `paginated` and `infiniteScroll` are mutually exclusive \u2014 only `paginated` takes effect while both are set."
4349
+ );
4350
+ }
4351
+ }, [paginated, hasInfiniteScroll]);
4352
+ useInfiniteScroll({
4353
+ rootRef: bodyScrollRef,
4354
+ sentinelRef,
4355
+ config: infiniteScroll
4356
+ });
4357
+ function columnOf(columnId) {
4358
+ return view.dataColumns.find((candidate) => candidate.id === columnId)?.column;
4359
+ }
4360
+ function columnName(columnId) {
4361
+ const column = columnOf(columnId);
4362
+ return column === void 0 ? columnId : resolveColumnHeaderText2(column);
4363
+ }
4364
+ const resize = useColumnResize({
4365
+ // `@gridkitjs/dom`'s `measureColumnContent` only ever matches
4366
+ // `td[data-gridkit-column]` (never `th`), so the header table has nothing
4367
+ // for it to find — the body table alone was already the effective scope
4368
+ // before this split.
4369
+ tableRef: bodyTableRef,
4370
+ controller,
4371
+ columnSizeDefaults
4372
+ });
4373
+ const nav = useGridNavigation({
4374
+ headerTableRef,
4375
+ bodyTableRef,
4376
+ // `null` when virtualization is off, in which case behavior is
4377
+ // byte-for-byte unchanged from before this feature existed.
4378
+ virtualRange: virtualized ? virtualizer : null,
4379
+ // Group headers are addressable rows too — the whole point of the flat
4380
+ // `DisplayRow[]` shape is that they share one position space with data
4381
+ // rows, so navigation counts them the same way. Page-relative
4382
+ // (`view.pageRows`, not `view.displayRows`): arrow keys operate on what's
4383
+ // actually in the DOM, which is the current page — a different "page"
4384
+ // concept than `useGridNavigation`'s own Page Up/Down viewport scrolling.
4385
+ rowCount: view.pageRows.length,
4386
+ columnCount: view.columns.length,
4387
+ // A group's own summary row (`groupAggregateDisplay: "row"`) occupies a
4388
+ // real slot in `rowCount` above — its DOM position has to line up with
4389
+ // everything else — but is presentational, never a tab stop: arrow-key
4390
+ // vertical movement steps over it rather than landing there.
4391
+ isSkippableRow: (rowIndex) => {
4392
+ const entry = view.pageRows[rowIndex];
4393
+ return entry !== void 0 && "kind" in entry && entry.kind === "group-summary";
4394
+ }
4395
+ });
4396
+ const panels = useGridPanels({ rootRef: viewportRef, headerTableRef, nav });
4397
+ const commands = useGridCommands({
4398
+ controller,
4399
+ commands: props.commands,
4400
+ onCommand: props.onCommand,
4401
+ messages,
4402
+ announce,
4403
+ resize,
4404
+ openPanel: panels.open
4405
+ });
4406
+ const handleCopyKey = useGridCopy({ controller, commands, nav });
4407
+ const orderedIds = useMemo5(
4408
+ () => view.dataColumns.map((entry) => entry.id),
4409
+ [view.dataColumns]
4410
+ );
4411
+ const displayedIds = useMemo5(
4412
+ () => view.columns.map((entry) => entry.id),
4413
+ [view.columns]
4414
+ );
4415
+ const definitions = useMemo5(
4416
+ () => new Map(view.dataColumns.map((entry) => [entry.id, entry.column])),
4417
+ [view.dataColumns]
4418
+ );
4419
+ const groupByDraggableIds = useMemo5(
4420
+ () => new Set(
4421
+ view.columns.filter((entry) => entry.groupByDraggable).map((entry) => entry.id)
4422
+ ),
4423
+ [view.columns]
4424
+ );
4425
+ function handleDrop(target, movedId) {
4426
+ if (target.kind === "group-by") {
4427
+ controller.moveGroupBy(movedId, target.beforeColumnId);
4428
+ return;
4429
+ }
4430
+ const next = moveColumnBefore(orderedIds, movedId, target.beforeId);
4431
+ if (next === orderedIds) {
4432
+ return;
4433
+ }
4434
+ controller.moveColumn(movedId, target.beforeId);
4435
+ const shown = next.filter((id) => displayedIds.includes(id));
4436
+ const position = shown.indexOf(movedId);
4437
+ announce(
4438
+ messages.columnMoved(columnName(movedId), position + 1, shown.length)
4439
+ );
4440
+ nav.focusCell(HEADER_ROW, position);
4441
+ }
4442
+ const drag = useColumnDrag({
4443
+ order: displayedIds,
4444
+ groupBy: state.groupBy,
4445
+ groupByDraggableIds,
4446
+ onDrop: handleDrop
4447
+ });
4448
+ const draggedColumnAlreadyGrouped = drag.draggedColumnId !== null && state.groupBy.some((entry) => entry.columnId === drag.draggedColumnId);
4449
+ const dragEligibleForGroupBy = drag.draggedColumnId !== null && groupByDraggableIds.has(drag.draggedColumnId) && !draggedColumnAlreadyGrouped;
4450
+ const showGroupByBar = groupByBarVisibility === "always" || groupByBarVisibility === "auto" && (state.groupBy.length > 0 || dragEligibleForGroupBy);
4451
+ const headerGroupByDropTarget = drag.dropTarget?.kind === "group-by" ? drag.dropTarget : null;
4452
+ const pagerContext = {
4453
+ pagination: state.pagination,
4454
+ currentPage: Math.min(state.pagination.pageIndex + 1, view.pageCount),
4455
+ pageCount: view.pageCount,
4456
+ pageSizeOptions: pager?.sizeOptions,
4457
+ goToPage: controller.goToPage,
4458
+ nextPage: controller.nextPage,
4459
+ previousPage: controller.previousPage,
4460
+ setPageSize: controller.setPageSize
4461
+ };
4462
+ const selection = useGridSelection({ controller, state, selectable });
4463
+ const multiselectable = selection.rowMode === "multiple" || selection.columnMode === "multiple";
4464
+ const contextMenu = useContextMenu({
4465
+ rootRef: viewportRef,
4466
+ headerTableRef,
4467
+ bodyTableRef,
4468
+ bodyScrollRef,
4469
+ contextMenu: props.contextMenu,
4470
+ onContextMenuOpenChange: props.onContextMenuOpenChange,
4471
+ controller,
4472
+ commands,
4473
+ nav,
4474
+ selection,
4475
+ messages,
4476
+ columnName
4477
+ });
4478
+ useEffect7(() => {
4479
+ for (const listener of subscribersRef.current) {
4480
+ listener();
4481
+ }
4482
+ }, [view, state, nav.focus]);
4483
+ useImperativeHandle3(ref, () => ({
4484
+ get element() {
4485
+ return viewportRef.current;
4486
+ },
4487
+ get table() {
4488
+ return bodyTableRef.current;
4489
+ },
4490
+ getRows: () => controller.getView().rows,
4491
+ // Every group header's own `aggregates` field is populated here — the
4492
+ // whole reason `getDisplayRows()` is where a consumer reads a specific
4493
+ // group's computed results from.
4494
+ getDisplayRows: () => controller.getView().displayRows,
4495
+ getPageRows: () => controller.getView().pageRows,
4496
+ getColumns: () => controller.getView().columns,
4497
+ getDataColumns: () => controller.getView().dataColumns,
4498
+ getColumnSizing: () => controller.getState().columnSizing,
4499
+ getColumnOrder: () => controller.getState().columnOrder,
4500
+ getColumnVisibility: () => controller.getState().columnVisibility,
4501
+ getColumnSort: () => controller.getState().columnSort,
4502
+ getFilter: () => controller.getState().filter,
4503
+ getGroupBy: () => controller.getState().groupBy,
4504
+ getGroupExpansion: () => controller.getState().groupExpansion,
4505
+ getPagination: readPagination,
4506
+ getPageCount: () => controller.getView().pageCount,
4507
+ getAggregates: () => controller.getView().grandTotal,
4508
+ getRowSelection: () => controller.getState().rowSelection,
4509
+ getSelectedRows: readSelectedRows,
4510
+ getColumnSelection: () => controller.getState().columnSelection,
4511
+ getCellSelection: () => controller.getState().cellSelection,
4512
+ getFocusedCell: () => nav.focus,
4513
+ getState: controller.getState,
4514
+ restoreState: controller.restoreState,
4515
+ focusCell: nav.focusCell,
4516
+ clearSelection: controller.clearSelection,
4517
+ selectAllRows: controller.selectAllRows,
4518
+ selectRows: controller.selectRows,
4519
+ expandAllGroups: controller.expandAllGroups,
4520
+ collapseAllGroups: controller.collapseAllGroups,
4521
+ goToPage: controller.goToPage,
4522
+ nextPage: controller.nextPage,
4523
+ previousPage: controller.previousPage,
4524
+ setPageSize: controller.setPageSize,
4525
+ showColumns: controller.showColumns,
4526
+ hideColumns: controller.hideColumns,
4527
+ setFilter: controller.setFilter,
4528
+ setColumnSort: controller.setColumnSort,
4529
+ setGroupBy: controller.setGroupBy,
4530
+ setGroupExpansion: controller.setGroupExpansion,
4531
+ setPagination: controller.setPagination,
4532
+ setColumnSizing: controller.setColumnSizing,
4533
+ setColumnOrder: controller.setColumnOrder,
4534
+ setColumnVisibility: controller.setColumnVisibility,
4535
+ setRowSelection: controller.setRowSelection,
4536
+ setColumnSelection: controller.setColumnSelection,
4537
+ setCellSelection: controller.setCellSelection,
4538
+ autoFitColumn: (columnId) => {
4539
+ commands.host.autoFitColumns([columnId]);
4540
+ },
4541
+ autoFitColumns: (columnIds) => {
4542
+ commands.host.autoFitColumns(
4543
+ columnIds ?? controller.getView().columns.map((entry) => entry.id)
4544
+ );
4545
+ },
4546
+ openContextMenu: contextMenu.open,
4547
+ closeContextMenu: contextMenu.close,
4548
+ ...exportApi(commands),
4549
+ ...printApi(commands),
4550
+ ...panelApi(panels, controller),
4551
+ runCommand: (commandId, target = { area: "grid" }) => commands.run(commandId, target, "api"),
4552
+ scrollToRow: (rowId, options) => {
4553
+ const index = controller.getView().pageRows.findIndex(
4554
+ (entry) => !("kind" in entry) && entry.rowId === rowId
4555
+ );
4556
+ if (index === -1) return;
4557
+ if (virtualized) {
4558
+ virtualizer.scrollToIndex(index, options);
4559
+ return;
4560
+ }
4561
+ bodyTableRef.current?.tBodies[0]?.rows[index]?.scrollIntoView(options);
4562
+ },
4563
+ scrollToColumn: (columnId, options) => {
4564
+ const cells = viewportRef.current?.querySelectorAll(
4565
+ "[data-gridkit-column]"
4566
+ );
4567
+ const cell = cells ? Array.from(cells).find(
4568
+ (entry) => entry.getAttribute("data-gridkit-column") === columnId
4569
+ ) : void 0;
4570
+ cell?.scrollIntoView(options);
4571
+ },
4572
+ subscribe: (listener) => {
4573
+ subscribersRef.current.add(listener);
4574
+ return () => {
4575
+ subscribersRef.current.delete(listener);
4576
+ };
4577
+ }
4578
+ }));
4579
+ const gridTableClassName = classNames(
4580
+ "gridkit-data-grid",
4581
+ borders ? `borders-${borders}` : "",
4582
+ // Hover is on by default and selection off, so one set of classes
4583
+ // turns styling off and the other turns it on. The polarity differs
4584
+ // because the defaults do.
4585
+ hoverRows ? "" : "no-hover-rows",
4586
+ hoverColumns ? "" : "no-hover-columns",
4587
+ hoverCells ? "" : "no-hover-cells",
4588
+ selection.rowMode === false ? "" : "selectable-rows",
4589
+ selection.columnMode === false ? "" : "selectable-columns",
4590
+ selection.cellMode === false ? "" : "selectable-cells"
4591
+ );
4592
+ const hasFooter = aggregates.length > 0;
4593
+ return /* @__PURE__ */ jsxs19("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
4594
+ /* @__PURE__ */ jsx26(
4595
+ GridToolbar,
4596
+ {
4597
+ toolbar: props.toolbar,
4598
+ renderItem: props.renderToolbarItem,
4599
+ gridId,
4600
+ controller,
4601
+ commands,
4602
+ messages,
4603
+ announce
4604
+ }
4605
+ ),
4606
+ showGroupByBar && /* @__PURE__ */ jsx26(
4607
+ GroupByBar,
4608
+ {
4609
+ columns: view.columns,
4610
+ definitions,
4611
+ groupBy: state.groupBy,
4612
+ controller,
4613
+ columnName,
4614
+ headerDragEligible: dragEligibleForGroupBy,
4615
+ headerDragBlocked: draggedColumnAlreadyGrouped,
4616
+ headerDropTarget: headerGroupByDropTarget,
4617
+ messages
4618
+ }
4619
+ ),
4620
+ /* @__PURE__ */ jsxs19(
4621
+ "div",
4622
+ {
4623
+ id: gridId,
4624
+ className: "gridkit-data-grid-tables",
4625
+ role: state.groupBy.length > 0 ? "treegrid" : "grid",
4626
+ "aria-rowcount": view.displayRows.length + (hasFooter ? 2 : 1),
4627
+ "aria-colcount": view.columns.length,
4628
+ ...ariaAttr(multiselectable, "aria-multiselectable", true),
4629
+ ...ariaAttr(labelledBy !== void 0, "aria-labelledby", labelledBy),
4630
+ ...ariaAttr(
4631
+ labelledBy === void 0 && label !== void 0,
4632
+ "aria-label",
4633
+ label
4634
+ ),
4635
+ onKeyDown: (event) => {
4636
+ if (!isOwnEvent(event)) return;
4637
+ handleCopyKey(event);
4638
+ if (event.key === "Escape") {
4639
+ if (drag.draggedColumnId !== null || resize.activeColumnId !== null)
4640
+ return;
4641
+ selection.clear();
4642
+ return;
4643
+ }
4644
+ if ((event.ctrlKey || event.metaKey) && (event.key === "a" || event.key === "A")) {
4645
+ if (selection.rowMode !== "multiple") return;
4646
+ event.preventDefault();
4647
+ selection.selectAllRows();
4648
+ }
4649
+ },
4650
+ children: [
4651
+ /* @__PURE__ */ jsxs19(
4652
+ "table",
4653
+ {
4654
+ ref: headerTableRef,
4655
+ role: "presentation",
4656
+ style: { width: totalColumnWidth(view.columns) },
4657
+ className: classNames(gridTableClassName, "gridkit-data-grid-header"),
4658
+ children: [
4659
+ /* @__PURE__ */ jsx26(GridColgroup, { columns: view.columns }),
4660
+ /* @__PURE__ */ jsx26(
4661
+ GridHeader,
4662
+ {
4663
+ columns: view.columns,
4664
+ resize,
4665
+ drag,
4666
+ controller,
4667
+ state,
4668
+ sortableColumns,
4669
+ groupableColumns,
4670
+ groupToggleIconColumns,
4671
+ headerMenu: contextMenu.opens("header"),
4672
+ panels,
4673
+ messages,
4674
+ nav,
4675
+ selection
4676
+ }
4677
+ )
4678
+ ]
4679
+ }
4680
+ ),
4681
+ /* @__PURE__ */ jsx26(
4682
+ "div",
4683
+ {
4684
+ className: "gridkit-data-grid-body",
4685
+ ref: bodyScrollRef,
4686
+ style: height === void 0 ? void 0 : {
4687
+ "--gridkit-body-height": typeof height === "number" ? `${String(height)}px` : height
4688
+ },
4689
+ children: /* @__PURE__ */ jsxs19(
4690
+ "table",
4691
+ {
4692
+ ref: bodyTableRef,
4693
+ role: "presentation",
4694
+ style: { width: totalColumnWidth(view.columns) },
4695
+ className: gridTableClassName,
4696
+ children: [
4697
+ /* @__PURE__ */ jsx26(GridColgroup, { columns: view.columns }),
4698
+ /* @__PURE__ */ jsx26(
4699
+ GridBody,
4700
+ {
4701
+ columns: view.columns,
4702
+ definitions,
4703
+ rows: view.pageRows,
4704
+ activeColumnId: resize.activeColumnId,
4705
+ nav,
4706
+ selection,
4707
+ controller,
4708
+ aggregates,
4709
+ groupAggregateDisplay,
4710
+ virtualRange: virtualized ? virtualizer : null,
4711
+ infiniteScroll,
4712
+ sentinelRef,
4713
+ locale,
4714
+ getRowProps,
4715
+ onRowClick,
4716
+ onRowDoubleClick,
4717
+ messages,
4718
+ cellMenu: contextMenu.opens("cell"),
4719
+ groupMenu: contextMenu.opens("group")
4720
+ }
4721
+ )
4722
+ ]
4723
+ }
4724
+ )
4725
+ }
4726
+ ),
4727
+ hasFooter && /* @__PURE__ */ jsxs19(
4728
+ "table",
4729
+ {
4730
+ role: "presentation",
4731
+ style: { width: totalColumnWidth(view.columns) },
4732
+ className: classNames(
4733
+ gridTableClassName,
4734
+ "gridkit-data-grid-footer"
4735
+ ),
4736
+ children: [
4737
+ /* @__PURE__ */ jsx26(GridColgroup, { columns: view.columns }),
4738
+ /* @__PURE__ */ jsx26(
4739
+ GridFooter,
4740
+ {
4741
+ columns: view.columns,
4742
+ aggregates,
4743
+ results: view.grandTotal,
4744
+ rows: view.rows.map((entry) => entry.row),
4745
+ locale,
4746
+ ariaRowIndex: view.displayRows.length + 2
4747
+ }
4748
+ )
4749
+ ]
4750
+ }
4751
+ )
4752
+ ]
4753
+ }
4754
+ ),
4755
+ /* @__PURE__ */ jsx26(GridContextMenu, { contextMenu }),
4756
+ /* @__PURE__ */ jsx26(
4757
+ GridPanels,
4758
+ {
4759
+ panels,
4760
+ controller,
4761
+ commands,
4762
+ messages
4763
+ }
4764
+ ),
4765
+ paginated && (pager?.template ? pager.template(pagerContext) : /* @__PURE__ */ jsx26(GridPager, { pager: pagerContext, config: pager, messages }))
4766
+ ] });
4767
+ }
4768
+
4769
+ // src/index.ts
4770
+ import { serializeGridState } from "@gridkitjs/core";
4771
+
4772
+ // src/DataGrid/hooks/usePaginationState.ts
4773
+ import { useCallback as useCallback3 } from "react";
4774
+
4775
+ // src/DataGrid/hooks/useGridSnapshot.ts
4776
+ import { useCallback as useCallback2, useSyncExternalStore as useSyncExternalStore3 } from "react";
4777
+ function useGridSnapshot(gridRef, getSnapshot, fallback) {
4778
+ const subscribe = useCallback2(
4779
+ (listener) => {
4780
+ const api = gridRef.current;
4781
+ if (api === null) {
4782
+ return () => {
4783
+ };
4784
+ }
4785
+ return api.subscribe(listener);
4786
+ },
4787
+ [gridRef]
4788
+ );
4789
+ return useSyncExternalStore3(
4790
+ subscribe,
4791
+ () => {
4792
+ const api = gridRef.current;
4793
+ return api === null ? fallback : getSnapshot(api);
4794
+ },
4795
+ () => fallback
4796
+ );
4797
+ }
4798
+
4799
+ // src/DataGrid/hooks/usePaginationState.ts
4800
+ var FALLBACK_PAGINATION = { pageIndex: 0, pageSize: 0 };
4801
+ function usePaginationState(gridRef) {
4802
+ const pagination = useGridSnapshot(
4803
+ gridRef,
4804
+ useCallback3((api) => api.getPagination(), []),
4805
+ FALLBACK_PAGINATION
4806
+ );
4807
+ const pageCount = useGridSnapshot(
4808
+ gridRef,
4809
+ useCallback3((api) => api.getPageCount(), []),
4810
+ 0
4811
+ );
4812
+ return {
4813
+ pagination,
4814
+ pageCount,
4815
+ goToPage: (pageIndex) => {
4816
+ gridRef.current?.goToPage(pageIndex);
4817
+ },
4818
+ nextPage: () => {
4819
+ gridRef.current?.nextPage();
4820
+ },
4821
+ previousPage: () => {
4822
+ gridRef.current?.previousPage();
4823
+ },
4824
+ setPageSize: (pageSize) => {
4825
+ gridRef.current?.setPageSize(pageSize);
4826
+ }
4827
+ };
4828
+ }
4829
+
4830
+ // src/DataGrid/hooks/useGroupByState.ts
4831
+ import { useCallback as useCallback4 } from "react";
4832
+ var FALLBACK_GROUP_BY = [];
4833
+ var FALLBACK_EXPANSION = [];
4834
+ function useGroupByState(gridRef) {
4835
+ const groupBy = useGridSnapshot(
4836
+ gridRef,
4837
+ useCallback4((api) => api.getGroupBy(), []),
4838
+ FALLBACK_GROUP_BY
4839
+ );
4840
+ const groupExpansion = useGridSnapshot(
4841
+ gridRef,
4842
+ useCallback4((api) => api.getGroupExpansion(), []),
4843
+ FALLBACK_EXPANSION
4844
+ );
4845
+ return {
4846
+ groupBy,
4847
+ groupExpansion,
4848
+ expandAllGroups: () => {
4849
+ gridRef.current?.expandAllGroups();
4850
+ },
4851
+ collapseAllGroups: () => {
4852
+ gridRef.current?.collapseAllGroups();
4853
+ }
4854
+ };
4855
+ }
4856
+
4857
+ // src/DataGrid/hooks/useColumnSortState.ts
4858
+ import { useCallback as useCallback5 } from "react";
4859
+ var FALLBACK_SORT = [];
4860
+ function useColumnSortState(gridRef) {
4861
+ return useGridSnapshot(
4862
+ gridRef,
4863
+ useCallback5((api) => api.getColumnSort(), []),
4864
+ FALLBACK_SORT
4865
+ );
4866
+ }
4867
+
4868
+ // src/DataGrid/hooks/useSelectionState.ts
4869
+ import { useCallback as useCallback6 } from "react";
4870
+ var FALLBACK_SELECTION = [];
4871
+ var FALLBACK_CELL_SELECTION = null;
4872
+ function useSelectionState(gridRef) {
4873
+ const rowSelection = useGridSnapshot(
4874
+ gridRef,
4875
+ useCallback6((api) => api.getRowSelection(), []),
4876
+ FALLBACK_SELECTION
4877
+ );
4878
+ const columnSelection = useGridSnapshot(
4879
+ gridRef,
4880
+ useCallback6((api) => api.getColumnSelection(), []),
4881
+ FALLBACK_SELECTION
4882
+ );
4883
+ const cellSelection = useGridSnapshot(
4884
+ gridRef,
4885
+ useCallback6((api) => api.getCellSelection(), []),
4886
+ FALLBACK_CELL_SELECTION
4887
+ );
4888
+ return {
4889
+ rowSelection,
4890
+ columnSelection,
4891
+ cellSelection,
4892
+ clearSelection: () => {
4893
+ gridRef.current?.clearSelection();
4894
+ },
4895
+ selectAllRows: () => {
4896
+ gridRef.current?.selectAllRows();
4897
+ }
4898
+ };
4899
+ }
4900
+
4901
+ // src/DataGrid/hooks/useAggregateState.ts
4902
+ import { useCallback as useCallback7 } from "react";
4903
+ var FALLBACK_AGGREGATES = /* @__PURE__ */ new Map();
4904
+ function useAggregateState(gridRef) {
4905
+ return useGridSnapshot(
4906
+ gridRef,
4907
+ useCallback7((api) => api.getAggregates(), []),
4908
+ FALLBACK_AGGREGATES
4909
+ );
4910
+ }
4911
+
4912
+ // src/DataGrid/hooks/useColumnSizingState.ts
4913
+ import { useCallback as useCallback8 } from "react";
4914
+ var FALLBACK_SIZING = {};
4915
+ function useColumnSizingState(gridRef) {
4916
+ return useGridSnapshot(
4917
+ gridRef,
4918
+ useCallback8((api) => api.getColumnSizing(), []),
4919
+ FALLBACK_SIZING
4920
+ );
4921
+ }
4922
+
4923
+ // src/DataGrid/hooks/useColumnOrderState.ts
4924
+ import { useCallback as useCallback9 } from "react";
4925
+ var FALLBACK_ORDER = [];
4926
+ function useColumnOrderState(gridRef) {
4927
+ return useGridSnapshot(
4928
+ gridRef,
4929
+ useCallback9((api) => api.getColumnOrder(), []),
4930
+ FALLBACK_ORDER
4931
+ );
4932
+ }
4933
+
4934
+ // src/DataGrid/hooks/useColumnVisibilityState.ts
4935
+ import { useCallback as useCallback10 } from "react";
4936
+ var FALLBACK_VISIBILITY = {};
4937
+ function useColumnVisibilityState(gridRef) {
4938
+ return useGridSnapshot(
4939
+ gridRef,
4940
+ useCallback10((api) => api.getColumnVisibility(), []),
4941
+ FALLBACK_VISIBILITY
4942
+ );
4943
+ }
4944
+
4945
+ // src/DataGrid/hooks/useFilterState.ts
4946
+ import { useCallback as useCallback11, useState as useState21 } from "react";
4947
+ function useFilterState(gridRef) {
4948
+ const [fallback] = useState21(() => []);
4949
+ const filter = useGridSnapshot(
4950
+ gridRef,
4951
+ useCallback11((api) => api.getFilter(), []),
4952
+ fallback
4953
+ );
4954
+ return {
4955
+ filter,
4956
+ setFilter: (next) => {
4957
+ gridRef.current?.setFilter(next);
4958
+ }
4959
+ };
4960
+ }
4961
+
4962
+ // src/index.ts
4963
+ import { excelFilter } from "@gridkitjs/core";
4964
+ export {
4965
+ DataGrid,
4966
+ excelFilter,
4967
+ serializeGridState,
4968
+ useAggregateState,
4969
+ useColumnOrderState,
4970
+ useColumnSizingState,
4971
+ useColumnSortState,
4972
+ useColumnVisibilityState,
4973
+ useFilterState,
4974
+ useGroupByState,
4975
+ usePaginationState,
4976
+ useSelectionState
4977
+ };
4978
+ //# sourceMappingURL=index.js.map