@loykin/gridkit 0.0.1-beta.1

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.cjs ADDED
@@ -0,0 +1,2485 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var reactTable = require('@tanstack/react-table');
6
+ var zustand = require('zustand');
7
+ var lucideReact = require('lucide-react');
8
+ var button = require('@base-ui/react/button');
9
+ var classVarianceAuthority = require('class-variance-authority');
10
+ var clsx = require('clsx');
11
+ var tailwindMerge = require('tailwind-merge');
12
+ var select = require('@base-ui/react/select');
13
+ var reactVirtual = require('@tanstack/react-virtual');
14
+ var menu = require('@base-ui/react/menu');
15
+ var input = require('@base-ui/react/input');
16
+ var checkbox = require('@base-ui/react/checkbox');
17
+ var popover = require('@base-ui/react/popover');
18
+ var core = require('@dnd-kit/core');
19
+ var sortable = require('@dnd-kit/sortable');
20
+ var utilities = require('@dnd-kit/utilities');
21
+
22
+ function _interopNamespace(e) {
23
+ if (e && e.__esModule) return e;
24
+ var n = Object.create(null);
25
+ if (e) {
26
+ Object.keys(e).forEach(function (k) {
27
+ if (k !== 'default') {
28
+ var d = Object.getOwnPropertyDescriptor(e, k);
29
+ Object.defineProperty(n, k, d.get ? d : {
30
+ enumerable: true,
31
+ get: function () { return e[k]; }
32
+ });
33
+ }
34
+ });
35
+ }
36
+ n.default = e;
37
+ return Object.freeze(n);
38
+ }
39
+
40
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
41
+
42
+ // src/DataGrid.tsx
43
+ function IndeterminateCheckbox({ checked, indeterminate, onChange }) {
44
+ const ref = React.useRef(null);
45
+ React.useEffect(() => {
46
+ if (ref.current) {
47
+ ref.current.indeterminate = indeterminate;
48
+ }
49
+ }, [indeterminate]);
50
+ return /* @__PURE__ */ jsxRuntime.jsx(
51
+ "input",
52
+ {
53
+ ref,
54
+ type: "checkbox",
55
+ checked,
56
+ onChange: (e) => onChange(e.target.checked),
57
+ className: "size-4 cursor-pointer rounded border-input accent-primary"
58
+ }
59
+ );
60
+ }
61
+ function createCheckboxColumn(checkboxConfig) {
62
+ const { getRowId, selectedIds, onSelectAll, onSelectOne } = checkboxConfig;
63
+ return {
64
+ id: "__select__",
65
+ size: 40,
66
+ enableResizing: false,
67
+ enableSorting: false,
68
+ enableColumnFilter: false,
69
+ header: ({ table }) => {
70
+ const rows = table.getRowModel().rows;
71
+ const allSelected = rows.length > 0 && rows.every((r) => selectedIds.has(getRowId(r.original)));
72
+ const someSelected = !allSelected && rows.some((r) => selectedIds.has(getRowId(r.original)));
73
+ return /* @__PURE__ */ jsxRuntime.jsx(
74
+ IndeterminateCheckbox,
75
+ {
76
+ checked: allSelected,
77
+ indeterminate: someSelected,
78
+ onChange: (checked) => onSelectAll(rows, checked)
79
+ }
80
+ );
81
+ },
82
+ cell: ({ row }) => {
83
+ const id = getRowId(row.original);
84
+ return /* @__PURE__ */ jsxRuntime.jsx(
85
+ "input",
86
+ {
87
+ type: "checkbox",
88
+ checked: selectedIds.has(id),
89
+ onChange: (e) => onSelectOne(id, e.target.checked),
90
+ onClick: (e) => e.stopPropagation(),
91
+ className: "size-4 cursor-pointer rounded border-input accent-primary"
92
+ }
93
+ );
94
+ }
95
+ };
96
+ }
97
+ var DEFAULT_STATE = {
98
+ pagination: { pageIndex: 0, pageSize: 20 },
99
+ searchTerm: ""
100
+ };
101
+ var useTableStore = zustand.create((set, get) => ({
102
+ tables: {},
103
+ register: (key, initial) => {
104
+ if (get().tables[key]) return;
105
+ set((s) => ({
106
+ tables: {
107
+ ...s.tables,
108
+ [key]: { ...DEFAULT_STATE, ...initial }
109
+ }
110
+ }));
111
+ },
112
+ update: (key, partial) => set((s) => ({
113
+ tables: {
114
+ ...s.tables,
115
+ [key]: { ...s.tables[key] ?? DEFAULT_STATE, ...partial }
116
+ }
117
+ })),
118
+ reset: (key) => set((s) => ({
119
+ tables: { ...s.tables, [key]: { ...DEFAULT_STATE } }
120
+ }))
121
+ }));
122
+ function getDataStoreCoreRowModel() {
123
+ return (table) => {
124
+ const rowCache = /* @__PURE__ */ new Map();
125
+ return reactTable.memo(
126
+ () => {
127
+ const store = table.options.dataStore;
128
+ return [store?.getVersion() ?? -1];
129
+ },
130
+ () => {
131
+ const store = table.options.dataStore;
132
+ const data = store.getSnapshot();
133
+ const rowModel = { rows: [], flatRows: [], rowsById: {} };
134
+ const seenIds = /* @__PURE__ */ new Set();
135
+ for (let i = 0; i < data.length; i++) {
136
+ const item = data[i];
137
+ const id = table._getRowId(item, i);
138
+ seenIds.add(id);
139
+ let row = rowCache.get(id);
140
+ if (row) {
141
+ if (row.original !== item) {
142
+ row.original = item;
143
+ row._valuesCache = {};
144
+ }
145
+ } else {
146
+ row = reactTable.createRow(table, id, item, i, 0);
147
+ rowCache.set(id, row);
148
+ }
149
+ rowModel.rows.push(row);
150
+ rowModel.flatRows.push(row);
151
+ rowModel.rowsById[id] = row;
152
+ }
153
+ for (const id of rowCache.keys()) {
154
+ if (!seenIds.has(id)) rowCache.delete(id);
155
+ }
156
+ return rowModel;
157
+ },
158
+ reactTable.getMemoOptions(
159
+ table.options,
160
+ "debugTable",
161
+ "getRowModel",
162
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
+ () => table._autoResetPageIndex?.()
164
+ )
165
+ );
166
+ };
167
+ }
168
+ var DataStoreFeature = {
169
+ createTable: (table) => {
170
+ const store = table.options.dataStore;
171
+ table._dataStore = store;
172
+ table.applyTransaction = (tx) => {
173
+ if (!store) {
174
+ console.warn("[GridKit] applyTransaction called without dataStore");
175
+ return;
176
+ }
177
+ store.applyTransaction(tx);
178
+ };
179
+ table.getRowNodeById = (id) => store?.get(id);
180
+ }
181
+ };
182
+
183
+ // src/core/engine/extensions/ColumnFlexFeature.ts
184
+ var ColumnFlexFeature = {
185
+ getDefaultColumnDef: () => ({ minSize: 60 }),
186
+ createTable: (table) => {
187
+ table.getFlexColumnSizing = (containerWidth) => {
188
+ const columns = table.getAllLeafColumns();
189
+ const sizing = {};
190
+ const flexCols = columns.filter((col) => col.columnDef.meta?.flex != null);
191
+ if (flexCols.length === 0) return sizing;
192
+ const fixedWidth = columns.filter((col) => col.columnDef.meta?.flex == null).reduce((sum, col) => sum + col.getSize(), 0);
193
+ const totalFlex = flexCols.reduce((sum, col) => sum + col.columnDef.meta.flex, 0);
194
+ const available = Math.max(0, containerWidth - fixedWidth);
195
+ let distributed = 0;
196
+ flexCols.forEach((col, i) => {
197
+ const flex = col.columnDef.meta.flex;
198
+ const minW = col.columnDef.meta?.minWidth ?? 60;
199
+ const isLast = i === flexCols.length - 1;
200
+ const w = isLast ? Math.max(minW, available - distributed) : Math.max(minW, Math.floor(flex / totalFlex * available));
201
+ sizing[col.id] = w;
202
+ distributed += w;
203
+ });
204
+ return sizing;
205
+ };
206
+ }
207
+ };
208
+
209
+ // src/core/engine/extensions/ColumnFilterExtension.ts
210
+ var ColumnFilterExtension = {};
211
+
212
+ // src/core/engine/extensions/RowActionsFeature.ts
213
+ var RowActionsFeature = {};
214
+
215
+ // src/core/engine/gridKitFeatures.ts
216
+ var gridKitFeatures = [
217
+ DataStoreFeature,
218
+ ColumnFlexFeature,
219
+ ColumnFilterExtension,
220
+ RowActionsFeature
221
+ ];
222
+
223
+ // src/core/hooks/useDataGridCore.ts
224
+ var _noopSubscribe = (_listener) => () => {
225
+ };
226
+ var _noopGetVersion = () => 0;
227
+ var defaultGlobalFilterFn = (row, columnId, value) => String(row.getValue(columnId) ?? "").toLowerCase().includes(value.toLowerCase());
228
+ var multiSelectFilterFn = (row, columnId, value) => value.includes(String(row.getValue(columnId) ?? ""));
229
+ multiSelectFilterFn.autoRemove = (val) => !val || val.length === 0;
230
+ var betweenFilterFn = (row, columnId, value) => {
231
+ const raw = row.getValue(columnId);
232
+ const [minStr, maxStr] = value;
233
+ const min = minStr !== "" ? Number(minStr) : -Infinity;
234
+ const max = maxStr !== "" ? Number(maxStr) : Infinity;
235
+ return raw >= min && raw <= max;
236
+ };
237
+ betweenFilterFn.autoRemove = (val) => !val || val[0] === "" && val[1] === "";
238
+ function useDataGridCore({
239
+ data = [],
240
+ dataStore,
241
+ columns,
242
+ enableSorting = true,
243
+ initialSorting,
244
+ onSortingChange,
245
+ manualSorting = false,
246
+ columnFilters: externalColumnFilters,
247
+ globalFilter: externalGlobalFilter,
248
+ onGlobalFilterChange,
249
+ searchableColumns,
250
+ enableColumnResizing = true,
251
+ enableColumnFilters = false,
252
+ visibilityState,
253
+ initialPinning,
254
+ tableKey,
255
+ syncState = false,
256
+ enablePagination = true,
257
+ paginationConfig,
258
+ totalCount,
259
+ onPageChange,
260
+ onTableReady,
261
+ onColumnSizingChange,
262
+ enableExpanding = false,
263
+ getSubRows,
264
+ getRowId,
265
+ sizing,
266
+ setSizing
267
+ }) {
268
+ const { register, update, tables } = useTableStore();
269
+ const persisted = tableKey ? tables[tableKey] : void 0;
270
+ const [expanded, setExpanded] = React.useState({});
271
+ const [sorting, setSorting] = React.useState(initialSorting ?? []);
272
+ const [columnPinning] = React.useState(() => {
273
+ const fromMeta = { left: [], right: [] };
274
+ for (const col of columns) {
275
+ const pin = col.meta?.pin;
276
+ const id = col.accessorKey ?? col.id;
277
+ if (!pin || !id) continue;
278
+ if (pin === "left") fromMeta.left.push(id);
279
+ else fromMeta.right.push(id);
280
+ }
281
+ return {
282
+ left: [...fromMeta.left ?? [], ...initialPinning?.left ?? []],
283
+ right: [...fromMeta.right ?? [], ...initialPinning?.right ?? []]
284
+ };
285
+ });
286
+ const [internalFilters, setInternalFilters] = React.useState([]);
287
+ const [internalGlobal, setInternalGlobal] = React.useState(persisted?.searchTerm ?? "");
288
+ const [columnVisibility, setColumnVisibility] = React.useState(visibilityState ?? {});
289
+ const [pagination, setPagination] = React.useState({
290
+ pageIndex: persisted?.pagination.pageIndex ?? paginationConfig?.initialPageIndex ?? 0,
291
+ pageSize: persisted?.pagination.pageSize ?? paginationConfig?.pageSize ?? 20
292
+ });
293
+ const tableReadyCalled = React.useRef(false);
294
+ React.useEffect(() => {
295
+ if (tableKey && syncState) {
296
+ register(tableKey, {
297
+ pagination: {
298
+ pageIndex: paginationConfig?.initialPageIndex ?? 0,
299
+ pageSize: paginationConfig?.pageSize ?? 20
300
+ }
301
+ });
302
+ }
303
+ }, [tableKey, syncState, register, paginationConfig]);
304
+ const effectiveGlobalFilter = externalGlobalFilter ?? internalGlobal;
305
+ const effectiveColumnFilters = externalColumnFilters ?? internalFilters;
306
+ const searchableFilterFn = searchableColumns?.length ? (row, _, value) => {
307
+ const search = String(value).toLowerCase();
308
+ return searchableColumns.some(
309
+ (colId) => String(row.getValue(colId) ?? "").toLowerCase().includes(search)
310
+ );
311
+ } : void 0;
312
+ const enrichedColumns = columns.map((col) => {
313
+ if (col.meta?.filterType === "number" && !col.filterFn) {
314
+ return { ...col, filterFn: betweenFilterFn };
315
+ }
316
+ if (col.meta?.filterType === "multi-select" && !col.filterFn) {
317
+ return { ...col, filterFn: multiSelectFilterFn };
318
+ }
319
+ return col;
320
+ });
321
+ React.useSyncExternalStore(
322
+ dataStore ? dataStore.subscribe : _noopSubscribe,
323
+ dataStore ? dataStore.getVersion : _noopGetVersion
324
+ );
325
+ const table = reactTable.useReactTable({
326
+ // When dataStore is active, data is irrelevant — getDataStoreCoreRowModel
327
+ // reads directly from the store. Pass empty array to satisfy the type.
328
+ data: dataStore ? [] : data,
329
+ columns: enrichedColumns,
330
+ getRowId,
331
+ _features: [...gridKitFeatures],
332
+ dataStore,
333
+ state: {
334
+ sorting,
335
+ columnFilters: effectiveColumnFilters,
336
+ globalFilter: effectiveGlobalFilter,
337
+ columnVisibility,
338
+ columnSizing: sizing,
339
+ columnPinning,
340
+ ...enablePagination ? { pagination } : {},
341
+ ...enableExpanding ? { expanded } : {}
342
+ },
343
+ manualSorting,
344
+ manualPagination: totalCount !== void 0,
345
+ pageCount: totalCount !== void 0 && pagination.pageSize > 0 ? Math.ceil(totalCount / pagination.pageSize) : void 0,
346
+ onSortingChange: (updater) => {
347
+ setSorting((prev) => {
348
+ const next = typeof updater === "function" ? updater(prev) : updater;
349
+ onSortingChange?.(next);
350
+ return next;
351
+ });
352
+ },
353
+ onColumnFiltersChange: externalColumnFilters ? void 0 : (updater) => {
354
+ setInternalFilters((prev) => typeof updater === "function" ? updater(prev) : updater);
355
+ },
356
+ onGlobalFilterChange: externalGlobalFilter ? void 0 : (updater) => {
357
+ const next = typeof updater === "function" ? updater(internalGlobal) : updater;
358
+ setInternalGlobal(next);
359
+ onGlobalFilterChange?.(next);
360
+ },
361
+ onColumnVisibilityChange: (updater) => {
362
+ setColumnVisibility((prev) => typeof updater === "function" ? updater(prev) : updater);
363
+ },
364
+ onColumnSizingChange: (updater) => {
365
+ setSizing((prev) => {
366
+ const next = typeof updater === "function" ? updater(prev) : updater;
367
+ onColumnSizingChange?.(next);
368
+ return next;
369
+ });
370
+ },
371
+ onPaginationChange: enablePagination ? (updater) => {
372
+ setPagination((prev) => {
373
+ const next = typeof updater === "function" ? updater(prev) : updater;
374
+ if (tableKey && syncState) update(tableKey, { pagination: next });
375
+ onPageChange?.(next.pageIndex, next.pageSize);
376
+ return next;
377
+ });
378
+ } : void 0,
379
+ onExpandedChange: enableExpanding ? setExpanded : void 0,
380
+ getSubRows,
381
+ autoResetExpanded: false,
382
+ // Explicitly derive canExpand from source data so depth-N rows show the
383
+ // toggle button even before their children have been loaded into subRows
384
+ // by the filtered/sorted row models.
385
+ getRowCanExpand: getSubRows ? (row) => {
386
+ const subs = getSubRows(row.original, row.index);
387
+ return Array.isArray(subs) && subs.length > 0;
388
+ } : void 0,
389
+ // Phase 3: row-caching model when DataStore is present; stock model otherwise
390
+ getCoreRowModel: dataStore ? getDataStoreCoreRowModel() : reactTable.getCoreRowModel(),
391
+ getExpandedRowModel: enableExpanding ? reactTable.getExpandedRowModel() : void 0,
392
+ getSortedRowModel: manualSorting ? void 0 : reactTable.getSortedRowModel(),
393
+ getFilteredRowModel: reactTable.getFilteredRowModel(),
394
+ getPaginationRowModel: enablePagination ? reactTable.getPaginationRowModel() : void 0,
395
+ globalFilterFn: searchableFilterFn ?? defaultGlobalFilterFn,
396
+ enableColumnResizing,
397
+ columnResizeMode: "onChange",
398
+ enableSorting,
399
+ enableColumnFilters
400
+ });
401
+ const handleGlobalFilterChange = React.useCallback(
402
+ (value) => {
403
+ table.setGlobalFilter(value);
404
+ if (externalGlobalFilter === void 0) {
405
+ setInternalGlobal(value);
406
+ onGlobalFilterChange?.(value);
407
+ }
408
+ if (tableKey && syncState) update(tableKey, { searchTerm: value });
409
+ },
410
+ [table, externalGlobalFilter, onGlobalFilterChange, tableKey, syncState, update]
411
+ );
412
+ React.useEffect(() => {
413
+ if (!tableReadyCalled.current && onTableReady) {
414
+ tableReadyCalled.current = true;
415
+ onTableReady(table);
416
+ }
417
+ }, [table, onTableReady]);
418
+ return {
419
+ table,
420
+ pagination,
421
+ globalFilter: effectiveGlobalFilter,
422
+ handleGlobalFilterChange
423
+ };
424
+ }
425
+ var VIRTUAL_THRESHOLD = 100;
426
+ function useColumnSizing({
427
+ columns,
428
+ containerRef,
429
+ mode
430
+ }) {
431
+ const userResized = React.useRef(/* @__PURE__ */ new Set());
432
+ const lastComputed = React.useRef({});
433
+ const lastContainerWidth = React.useRef(0);
434
+ const lastFlexFixedWidth = React.useRef(null);
435
+ const hasSized = React.useRef(false);
436
+ const [state, setState] = React.useState({ sizing: {}, isSized: false });
437
+ const setSizing = React.useCallback(
438
+ (updater) => {
439
+ setState((prev) => ({
440
+ ...prev,
441
+ sizing: typeof updater === "function" ? updater(prev.sizing) : updater
442
+ }));
443
+ },
444
+ []
445
+ );
446
+ const columnsRef = React.useRef(columns);
447
+ columnsRef.current = columns;
448
+ const modeRef = React.useRef(mode);
449
+ modeRef.current = mode;
450
+ const sizingRef = React.useRef(state.sizing);
451
+ sizingRef.current = state.sizing;
452
+ const measure = React.useCallback(() => {
453
+ const container = containerRef.current;
454
+ if (!container) return;
455
+ const containerWidth = container.clientWidth;
456
+ if (containerWidth === 0) return;
457
+ const prevContainerWidth = lastContainerWidth.current;
458
+ lastContainerWidth.current = containerWidth;
459
+ const cols = columnsRef.current;
460
+ const currentSizing = sizingRef.current;
461
+ const m = modeRef.current;
462
+ for (const [colId, currentSize] of Object.entries(currentSizing)) {
463
+ const computed = lastComputed.current[colId];
464
+ if (computed !== void 0 && computed !== currentSize) {
465
+ userResized.current.add(colId);
466
+ }
467
+ }
468
+ const newSizing = {};
469
+ if (m === "auto") {
470
+ const cells = container.querySelectorAll("[data-col-id]");
471
+ cells.forEach((cell) => {
472
+ const colId = cell.dataset.colId;
473
+ if (!colId || userResized.current.has(colId)) return;
474
+ const w = cell.scrollWidth;
475
+ const current = newSizing[colId] ?? currentSizing[colId] ?? 0;
476
+ if (w > current) newSizing[colId] = w;
477
+ });
478
+ }
479
+ const getColId = (col) => col.id ?? col.accessorKey;
480
+ const flexCols = cols.filter((col) => col.meta?.flex != null);
481
+ if (flexCols.length > 0) {
482
+ const containerWidthChanged = Math.abs(containerWidth - prevContainerWidth) > 1;
483
+ const anyUserResized = flexCols.some((col) => userResized.current.has(getColId(col)));
484
+ const freeCols = flexCols.filter((col) => !userResized.current.has(getColId(col)));
485
+ const totalFlex = freeCols.reduce((sum, col) => sum + col.meta.flex, 0);
486
+ const fixedWidth = cols.filter((col) => col.meta?.flex == null).reduce((sum, col) => {
487
+ const colId = getColId(col);
488
+ const colDefSize = typeof col.size === "number" ? col.size : 150;
489
+ return sum + (newSizing[colId] ?? currentSizing[colId] ?? colDefSize);
490
+ }, 0);
491
+ const userResizedFlexWidth = flexCols.filter((col) => userResized.current.has(getColId(col))).reduce((sum, col) => sum + (currentSizing[getColId(col)] ?? 0), 0);
492
+ const fixedWidthChanged = lastFlexFixedWidth.current !== null && lastFlexFixedWidth.current !== fixedWidth;
493
+ if (anyUserResized && !containerWidthChanged) ; else if (!containerWidthChanged && hasSized.current && !fixedWidthChanged) ; else {
494
+ lastFlexFixedWidth.current = fixedWidth;
495
+ const availableWidth = Math.max(0, containerWidth - fixedWidth - userResizedFlexWidth);
496
+ let distributed = 0;
497
+ for (let i = 0; i < freeCols.length; i++) {
498
+ const col = freeCols[i];
499
+ const colId = getColId(col);
500
+ if (!colId) continue;
501
+ const flex = col.meta.flex;
502
+ const minW = col.meta?.minWidth ?? 60;
503
+ const isLast = i === freeCols.length - 1;
504
+ const w = isLast ? Math.max(minW, availableWidth - distributed) : Math.max(minW, Math.floor(flex / totalFlex * availableWidth));
505
+ newSizing[colId] = w;
506
+ distributed += w;
507
+ }
508
+ }
509
+ }
510
+ const sizingChanged = Object.keys(newSizing).length > 0 && Object.entries(newSizing).some(([id, w]) => currentSizing[id] !== w);
511
+ const firstTime = !hasSized.current;
512
+ if (sizingChanged || firstTime) {
513
+ if (sizingChanged) Object.assign(lastComputed.current, newSizing);
514
+ if (firstTime) hasSized.current = true;
515
+ setState((prev) => ({
516
+ sizing: sizingChanged ? { ...prev.sizing, ...newSizing } : prev.sizing,
517
+ isSized: true
518
+ }));
519
+ }
520
+ }, [containerRef]);
521
+ const roTimerRef = React.useRef(null);
522
+ React.useEffect(() => {
523
+ const el = containerRef.current;
524
+ if (!el) return;
525
+ const ro = new ResizeObserver((entries) => {
526
+ const entry = entries[0];
527
+ if (!entry) return;
528
+ const width = entry.contentRect.width;
529
+ if (width === 0 || Math.abs(width - lastContainerWidth.current) < 1) return;
530
+ if (roTimerRef.current) clearTimeout(roTimerRef.current);
531
+ roTimerRef.current = setTimeout(measure, 150);
532
+ });
533
+ ro.observe(el);
534
+ return () => {
535
+ ro.disconnect();
536
+ if (roTimerRef.current) clearTimeout(roTimerRef.current);
537
+ };
538
+ }, [measure, containerRef]);
539
+ const resetSizing = React.useCallback(() => {
540
+ userResized.current.clear();
541
+ lastComputed.current = {};
542
+ lastContainerWidth.current = 0;
543
+ lastFlexFixedWidth.current = null;
544
+ hasSized.current = false;
545
+ setState({ sizing: {}, isSized: false });
546
+ measure();
547
+ }, [measure]);
548
+ return { sizing: state.sizing, isSized: state.isSized, setSizing, resetSizing, measure };
549
+ }
550
+
551
+ // src/core/hooks/useDataGridBase.ts
552
+ function useDataGridBase(options) {
553
+ const {
554
+ data = [],
555
+ dataStore,
556
+ columns,
557
+ enableSorting = true,
558
+ initialSorting,
559
+ onSortingChange,
560
+ manualSorting,
561
+ columnFilters,
562
+ globalFilter,
563
+ onGlobalFilterChange,
564
+ searchableColumns,
565
+ enableColumnResizing = true,
566
+ enableColumnFilters = false,
567
+ visibilityState,
568
+ initialPinning,
569
+ columnSizingMode = "flex",
570
+ checkboxConfig,
571
+ enableExpanding,
572
+ getSubRows,
573
+ getRowId,
574
+ tableKey,
575
+ syncState,
576
+ enablePagination = true,
577
+ paginationConfig,
578
+ totalCount,
579
+ onPageChange,
580
+ onTableReady,
581
+ onColumnSizingChange,
582
+ bordered
583
+ } = options;
584
+ const wrapperRef = React.useRef(null);
585
+ const containerRef = React.useRef(null);
586
+ const columnsWithCheckbox = React.useMemo(() => {
587
+ if (!checkboxConfig) return columns;
588
+ return [createCheckboxColumn(checkboxConfig), ...columns];
589
+ }, [columns, checkboxConfig]);
590
+ const { sizing, isSized, setSizing, measure } = useColumnSizing({
591
+ columns,
592
+ containerRef,
593
+ mode: columnSizingMode
594
+ });
595
+ const { table } = useDataGridCore({
596
+ data,
597
+ dataStore,
598
+ columns: columnsWithCheckbox,
599
+ enableSorting,
600
+ initialSorting,
601
+ onSortingChange,
602
+ manualSorting,
603
+ columnFilters,
604
+ globalFilter,
605
+ onGlobalFilterChange,
606
+ searchableColumns,
607
+ enableColumnResizing,
608
+ enableColumnFilters,
609
+ visibilityState,
610
+ initialPinning,
611
+ tableKey,
612
+ syncState,
613
+ enablePagination,
614
+ paginationConfig,
615
+ totalCount,
616
+ onPageChange,
617
+ onTableReady,
618
+ onColumnSizingChange,
619
+ enableExpanding,
620
+ getSubRows,
621
+ getRowId,
622
+ sizing,
623
+ setSizing
624
+ });
625
+ const rows = table.getRowModel().rows;
626
+ return { wrapperRef, containerRef, table, rows, isSized, bordered, measure };
627
+ }
628
+ function cn(...inputs) {
629
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
630
+ }
631
+ var buttonVariants = classVarianceAuthority.cva(
632
+ "group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding text-xs font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
633
+ {
634
+ variants: {
635
+ variant: {
636
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
637
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
638
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
639
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
640
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
641
+ link: "text-primary underline-offset-4 hover:underline"
642
+ },
643
+ size: {
644
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
645
+ xs: "h-6 gap-1 rounded-none px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
646
+ sm: "h-7 gap-1 rounded-none px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
647
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
648
+ icon: "size-8",
649
+ "icon-xs": "size-6 rounded-none [&_svg:not([class*='size-'])]:size-3",
650
+ "icon-sm": "size-7 rounded-none",
651
+ "icon-lg": "size-9"
652
+ }
653
+ },
654
+ defaultVariants: {
655
+ variant: "default",
656
+ size: "default"
657
+ }
658
+ }
659
+ );
660
+ function Button({
661
+ className,
662
+ variant = "default",
663
+ size = "default",
664
+ ...props
665
+ }) {
666
+ return /* @__PURE__ */ jsxRuntime.jsx(
667
+ button.Button,
668
+ {
669
+ "data-slot": "button",
670
+ className: cn(buttonVariants({ variant, size, className })),
671
+ ...props
672
+ }
673
+ );
674
+ }
675
+ var Select = select.Select.Root;
676
+ function SelectValue({ className, ...props }) {
677
+ return /* @__PURE__ */ jsxRuntime.jsx(
678
+ select.Select.Value,
679
+ {
680
+ "data-slot": "select-value",
681
+ className: cn("flex flex-1 text-left", className),
682
+ ...props
683
+ }
684
+ );
685
+ }
686
+ function SelectTrigger({
687
+ className,
688
+ size = "default",
689
+ children,
690
+ ...props
691
+ }) {
692
+ return /* @__PURE__ */ jsxRuntime.jsxs(
693
+ select.Select.Trigger,
694
+ {
695
+ "data-slot": "select-trigger",
696
+ "data-size": size,
697
+ className: cn(
698
+ "flex w-fit items-center justify-between gap-1.5 rounded-none border border-input bg-transparent py-2 pr-2 pl-2.5 text-xs whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-none *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
699
+ className
700
+ ),
701
+ ...props,
702
+ children: [
703
+ children,
704
+ /* @__PURE__ */ jsxRuntime.jsx(
705
+ select.Select.Icon,
706
+ {
707
+ render: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "pointer-events-none size-4 text-muted-foreground" })
708
+ }
709
+ )
710
+ ]
711
+ }
712
+ );
713
+ }
714
+ function SelectContent({
715
+ className,
716
+ children,
717
+ side = "bottom",
718
+ sideOffset = 4,
719
+ align = "center",
720
+ alignOffset = 0,
721
+ alignItemWithTrigger = true,
722
+ ...props
723
+ }) {
724
+ return /* @__PURE__ */ jsxRuntime.jsx(select.Select.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
725
+ select.Select.Positioner,
726
+ {
727
+ side,
728
+ sideOffset,
729
+ align,
730
+ alignOffset,
731
+ alignItemWithTrigger,
732
+ className: "isolate z-50",
733
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
734
+ select.Select.Popup,
735
+ {
736
+ "data-slot": "select-content",
737
+ "data-align-trigger": alignItemWithTrigger,
738
+ className: cn(
739
+ "dark isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-none text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
740
+ className
741
+ ),
742
+ ...props,
743
+ children: [
744
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
745
+ /* @__PURE__ */ jsxRuntime.jsx(select.Select.List, { children }),
746
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
747
+ ]
748
+ }
749
+ )
750
+ }
751
+ ) });
752
+ }
753
+ function SelectItem({ className, children, ...props }) {
754
+ return /* @__PURE__ */ jsxRuntime.jsxs(
755
+ select.Select.Item,
756
+ {
757
+ "data-slot": "select-item",
758
+ className: cn(
759
+ "relative flex w-full cursor-default items-center gap-2 rounded-none py-2 pr-8 pl-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
760
+ className
761
+ ),
762
+ ...props,
763
+ children: [
764
+ /* @__PURE__ */ jsxRuntime.jsx(select.Select.ItemText, { className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap", children }),
765
+ /* @__PURE__ */ jsxRuntime.jsx(
766
+ select.Select.ItemIndicator,
767
+ {
768
+ render: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
769
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "pointer-events-none size-3.5" })
770
+ }
771
+ )
772
+ ]
773
+ }
774
+ );
775
+ }
776
+ function SelectScrollUpButton({
777
+ className,
778
+ ...props
779
+ }) {
780
+ return /* @__PURE__ */ jsxRuntime.jsx(
781
+ select.Select.ScrollUpArrow,
782
+ {
783
+ "data-slot": "select-scroll-up-button",
784
+ className: cn(
785
+ "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
786
+ className
787
+ ),
788
+ ...props,
789
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, {})
790
+ }
791
+ );
792
+ }
793
+ function SelectScrollDownButton({
794
+ className,
795
+ ...props
796
+ }) {
797
+ return /* @__PURE__ */ jsxRuntime.jsx(
798
+ select.Select.ScrollDownArrow,
799
+ {
800
+ "data-slot": "select-scroll-down-button",
801
+ className: cn(
802
+ "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
803
+ className
804
+ ),
805
+ ...props,
806
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, {})
807
+ }
808
+ );
809
+ }
810
+ function DataGridPaginationBar({
811
+ table,
812
+ pageSizes,
813
+ totalCount
814
+ }) {
815
+ const { pageIndex, pageSize } = table.getState().pagination;
816
+ const pageCount = table.getPageCount();
817
+ const pageSizeItems = pageSizes.map((size) => ({ label: String(size), value: size }));
818
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4 px-1 py-1 text-sm text-muted-foreground", children: [
819
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
820
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Rows per page" }),
821
+ /* @__PURE__ */ jsxRuntime.jsxs(
822
+ Select,
823
+ {
824
+ items: pageSizeItems,
825
+ value: pageSize,
826
+ onValueChange: (val) => table.setPageSize(val),
827
+ children: [
828
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { size: "sm", className: "w-16", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
829
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: pageSizeItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: item.value, children: item.label }, item.value)) })
830
+ ]
831
+ }
832
+ )
833
+ ] }),
834
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
835
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: totalCount !== void 0 ? `${pageIndex * pageSize + 1}\u2013${Math.min((pageIndex + 1) * pageSize, totalCount)} of ${totalCount}` : `Page ${pageIndex + 1} of ${Math.max(pageCount, 1)}` }),
836
+ /* @__PURE__ */ jsxRuntime.jsx(
837
+ Button,
838
+ {
839
+ variant: "ghost",
840
+ size: "icon-sm",
841
+ onClick: () => table.firstPage(),
842
+ disabled: !table.getCanPreviousPage(),
843
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsLeft, {})
844
+ }
845
+ ),
846
+ /* @__PURE__ */ jsxRuntime.jsx(
847
+ Button,
848
+ {
849
+ variant: "ghost",
850
+ size: "icon-sm",
851
+ onClick: () => table.previousPage(),
852
+ disabled: !table.getCanPreviousPage(),
853
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, {})
854
+ }
855
+ ),
856
+ /* @__PURE__ */ jsxRuntime.jsx(
857
+ Button,
858
+ {
859
+ variant: "ghost",
860
+ size: "icon-sm",
861
+ onClick: () => table.nextPage(),
862
+ disabled: !table.getCanNextPage(),
863
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, {})
864
+ }
865
+ ),
866
+ /* @__PURE__ */ jsxRuntime.jsx(
867
+ Button,
868
+ {
869
+ variant: "ghost",
870
+ size: "icon-sm",
871
+ onClick: () => table.lastPage(),
872
+ disabled: !table.getCanNextPage(),
873
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsRight, {})
874
+ }
875
+ )
876
+ ] })
877
+ ] });
878
+ }
879
+ function DataGridToolbar({
880
+ table,
881
+ leftFilters,
882
+ rightFilters
883
+ }) {
884
+ if (!(leftFilters || rightFilters)) {
885
+ return null;
886
+ }
887
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 shrink-0", children: [
888
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: leftFilters?.(table) }),
889
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: rightFilters?.(table) })
890
+ ] });
891
+ }
892
+ function Input({ className, type, ...props }) {
893
+ return /* @__PURE__ */ jsxRuntime.jsx(
894
+ input.Input,
895
+ {
896
+ type,
897
+ "data-slot": "input",
898
+ className: cn(
899
+ "h-8 w-full min-w-0 rounded-none border border-input bg-transparent px-2.5 py-1 text-xs transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-xs file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 md:text-xs dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
900
+ className
901
+ ),
902
+ ...props
903
+ }
904
+ );
905
+ }
906
+ function Checkbox({ className, ...props }) {
907
+ return /* @__PURE__ */ jsxRuntime.jsx(
908
+ checkbox.Checkbox.Root,
909
+ {
910
+ "data-slot": "checkbox",
911
+ className: cn(
912
+ "peer relative flex size-4 shrink-0 items-center justify-center rounded-none border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
913
+ className
914
+ ),
915
+ ...props,
916
+ children: /* @__PURE__ */ jsxRuntime.jsx(
917
+ checkbox.Checkbox.Indicator,
918
+ {
919
+ "data-slot": "checkbox-indicator",
920
+ className: "grid place-content-center text-current transition-none [&>svg]:size-3.5",
921
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, {})
922
+ }
923
+ )
924
+ }
925
+ );
926
+ }
927
+ function Popover({ ...props }) {
928
+ return /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Root, { "data-slot": "popover", ...props });
929
+ }
930
+ function PopoverTrigger({ ...props }) {
931
+ return /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
932
+ }
933
+ function PopoverContent({
934
+ className,
935
+ align = "center",
936
+ alignOffset = 0,
937
+ side = "bottom",
938
+ sideOffset = 4,
939
+ ...props
940
+ }) {
941
+ return /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
942
+ popover.Popover.Positioner,
943
+ {
944
+ align,
945
+ alignOffset,
946
+ side,
947
+ sideOffset,
948
+ className: "isolate z-50",
949
+ children: /* @__PURE__ */ jsxRuntime.jsx(
950
+ popover.Popover.Popup,
951
+ {
952
+ "data-slot": "popover-content",
953
+ className: cn(
954
+ "z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-none bg-popover p-2.5 text-xs text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
955
+ className
956
+ ),
957
+ ...props
958
+ }
959
+ )
960
+ }
961
+ ) });
962
+ }
963
+ var ScrollTable = React__namespace.forwardRef(
964
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
965
+ "div",
966
+ {
967
+ ref,
968
+ role: "table",
969
+ className: ["text-sm", className].filter(Boolean).join(" "),
970
+ ...props
971
+ }
972
+ )
973
+ );
974
+ ScrollTable.displayName = "ScrollTable";
975
+ function CustomScrollbar({ scrollRef, direction, className, style }) {
976
+ const isV = direction === "vertical";
977
+ const trackRef = React.useRef(null);
978
+ const scrollSizeRef = React.useRef(0);
979
+ const clientSizeRef = React.useRef(0);
980
+ const [thumbStart, setThumbStart] = React.useState(0);
981
+ const [thumbEnd, setThumbEnd] = React.useState(1);
982
+ const sync = React.useCallback(() => {
983
+ const el = scrollRef.current;
984
+ if (!el) return;
985
+ const scrollPos = isV ? el.scrollTop : el.scrollLeft;
986
+ const scrollSize = isV ? el.scrollHeight : el.scrollWidth;
987
+ const clientSize = isV ? el.clientHeight : el.clientWidth;
988
+ scrollSizeRef.current = scrollSize;
989
+ clientSizeRef.current = clientSize;
990
+ if (scrollSize <= clientSize) {
991
+ setThumbStart(0);
992
+ setThumbEnd(1);
993
+ return;
994
+ }
995
+ setThumbStart(scrollPos / scrollSize);
996
+ setThumbEnd((scrollPos + clientSize) / scrollSize);
997
+ }, [scrollRef, isV]);
998
+ React.useLayoutEffect(() => {
999
+ sync();
1000
+ }, [sync]);
1001
+ React.useEffect(() => {
1002
+ const el = scrollRef.current;
1003
+ if (!el) return;
1004
+ el.addEventListener("scroll", sync, { passive: true });
1005
+ const ro = new ResizeObserver(sync);
1006
+ ro.observe(el);
1007
+ if (el.firstElementChild) ro.observe(el.firstElementChild);
1008
+ return () => {
1009
+ el.removeEventListener("scroll", sync);
1010
+ ro.disconnect();
1011
+ };
1012
+ }, [scrollRef, sync]);
1013
+ const thumbFrac = thumbEnd - thumbStart;
1014
+ const visible = thumbFrac < 0.9999;
1015
+ const handleThumbMouseDown = (e) => {
1016
+ e.preventDefault();
1017
+ e.stopPropagation();
1018
+ const el = scrollRef.current;
1019
+ if (!el) return;
1020
+ const startMouse = isV ? e.clientY : e.clientX;
1021
+ const startScroll = isV ? el.scrollTop : el.scrollLeft;
1022
+ const onMove = (e2) => {
1023
+ const el2 = scrollRef.current;
1024
+ const track = trackRef.current;
1025
+ if (!el2 || !track) return;
1026
+ const trackPx = isV ? track.clientHeight : track.clientWidth;
1027
+ const thumbPx = trackPx * thumbFrac;
1028
+ const delta = (isV ? e2.clientY : e2.clientX) - startMouse;
1029
+ const maxScroll = scrollSizeRef.current - clientSizeRef.current;
1030
+ const scrollDelta = delta / (trackPx - thumbPx) * maxScroll;
1031
+ if (isV) el2.scrollTop = startScroll + scrollDelta;
1032
+ else el2.scrollLeft = startScroll + scrollDelta;
1033
+ };
1034
+ const onUp = () => {
1035
+ window.removeEventListener("mousemove", onMove);
1036
+ window.removeEventListener("mouseup", onUp);
1037
+ };
1038
+ window.addEventListener("mousemove", onMove);
1039
+ window.addEventListener("mouseup", onUp);
1040
+ };
1041
+ const handleTrackClick = (e) => {
1042
+ const track = trackRef.current;
1043
+ const el = scrollRef.current;
1044
+ if (!track || !el) return;
1045
+ const rect = track.getBoundingClientRect();
1046
+ const clickFrac = isV ? (e.clientY - rect.top) / rect.height : (e.clientX - rect.left) / rect.width;
1047
+ const targetFrac = Math.max(0, Math.min(1, (clickFrac - thumbFrac / 2) / (1 - thumbFrac)));
1048
+ const maxScroll = scrollSizeRef.current - clientSizeRef.current;
1049
+ if (isV) el.scrollTop = targetFrac * maxScroll;
1050
+ else el.scrollLeft = targetFrac * maxScroll;
1051
+ };
1052
+ return /* @__PURE__ */ jsxRuntime.jsx(
1053
+ "div",
1054
+ {
1055
+ ref: trackRef,
1056
+ onClick: handleTrackClick,
1057
+ className: cn("relative", !visible && "hidden", className),
1058
+ style,
1059
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1060
+ "div",
1061
+ {
1062
+ className: "absolute rounded-full bg-foreground/20 hover:bg-foreground/35 transition-colors cursor-pointer",
1063
+ onMouseDown: handleThumbMouseDown,
1064
+ onClick: (e) => e.stopPropagation(),
1065
+ style: isV ? { top: `${thumbStart * 100}%`, bottom: `${(1 - thumbEnd) * 100}%`, left: 2, right: 2 } : { left: `${thumbStart * 100}%`, right: `${(1 - thumbEnd) * 100}%`, top: 2, bottom: 2 }
1066
+ }
1067
+ )
1068
+ }
1069
+ );
1070
+ }
1071
+ var RowWrapperContext = React__namespace.default.createContext(null);
1072
+ function colStyle(col) {
1073
+ const pinned = col.getIsPinned();
1074
+ return {
1075
+ width: col.getSize(),
1076
+ flexShrink: 0,
1077
+ ...pinned === "left" && {
1078
+ position: "sticky",
1079
+ left: col.getStart("left"),
1080
+ zIndex: 1
1081
+ },
1082
+ ...pinned === "right" && {
1083
+ position: "sticky",
1084
+ right: col.getAfter("right"),
1085
+ zIndex: 1
1086
+ }
1087
+ };
1088
+ }
1089
+ function isPinnedEdge(col, table) {
1090
+ const pinned = col.getIsPinned();
1091
+ if (pinned === "left") {
1092
+ const leftCols = table.getLeftLeafColumns();
1093
+ return leftCols[leftCols.length - 1]?.id === col.id ? "left-edge" : false;
1094
+ }
1095
+ if (pinned === "right") {
1096
+ const rightCols = table.getRightLeafColumns();
1097
+ return rightCols[0]?.id === col.id ? "right-edge" : false;
1098
+ }
1099
+ return false;
1100
+ }
1101
+ function DataGridHeaderRow({
1102
+ headerGroup,
1103
+ table,
1104
+ enableColumnResizing,
1105
+ virtual,
1106
+ bordered,
1107
+ tableWidthMode = "spacer",
1108
+ enableColumnFilters,
1109
+ filterDisplay = "row",
1110
+ classNames
1111
+ }) {
1112
+ const headers = headerGroup.headers;
1113
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1114
+ "div",
1115
+ {
1116
+ role: "row",
1117
+ className: "border-b border-border",
1118
+ style: { display: "flex", width: "100%", height: "36px" },
1119
+ children: [
1120
+ headers.map((header, idx) => {
1121
+ const edge = isPinnedEdge(header.column, table);
1122
+ const isLast = idx === headers.length - 1;
1123
+ const isFillLast = tableWidthMode === "fill-last" && isLast;
1124
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1125
+ "div",
1126
+ {
1127
+ role: "columnheader",
1128
+ "data-col-id": header.column.id,
1129
+ className: cn(
1130
+ "relative px-3 text-xs font-medium h-full bg-muted",
1131
+ "text-muted-foreground whitespace-normal",
1132
+ "select-none group",
1133
+ header.column.getCanSort() && "cursor-pointer",
1134
+ bordered && "border-r border-border",
1135
+ edge === "left-edge" && "shadow-[1px_0_0_0_hsl(var(--border))]",
1136
+ edge === "right-edge" && "shadow-[-1px_0_0_0_hsl(var(--border))]",
1137
+ classNames?.headerCell
1138
+ ),
1139
+ style: virtual ? {
1140
+ display: "flex",
1141
+ alignItems: "center",
1142
+ width: isFillLast ? void 0 : header.getSize(),
1143
+ ...isFillLast && { flex: 1, minWidth: header.getSize() }
1144
+ } : {
1145
+ ...colStyle(header.column),
1146
+ display: "flex",
1147
+ alignItems: "center",
1148
+ overflow: "hidden",
1149
+ ...isFillLast && { flex: 1, width: "auto" }
1150
+ },
1151
+ onClick: header.column.getCanSort() ? header.column.getToggleSortingHandler() : void 0,
1152
+ children: [
1153
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 min-w-0 overflow-hidden flex-1", children: [
1154
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: header.isPlaceholder ? null : reactTable.flexRender(header.column.columnDef.header, header.getContext()) }),
1155
+ header.column.getCanSort() && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 shrink-0", children: header.column.getIsSorted() === "asc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3.5 w-3.5" }) : header.column.getIsSorted() === "desc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpDown, { className: "h-3.5 w-3.5 opacity-40 group-hover:opacity-100" }) })
1156
+ ] }),
1157
+ enableColumnFilters && filterDisplay === "icon" && /* @__PURE__ */ jsxRuntime.jsx(HeaderFilterPopover, { col: header.column, table }),
1158
+ enableColumnResizing && header.column.getCanResize() && /* @__PURE__ */ jsxRuntime.jsx(
1159
+ "div",
1160
+ {
1161
+ onMouseDown: (e) => {
1162
+ e.stopPropagation();
1163
+ header.getResizeHandler()(e);
1164
+ },
1165
+ onTouchStart: (e) => {
1166
+ e.stopPropagation();
1167
+ header.getResizeHandler()(e);
1168
+ },
1169
+ onClick: (e) => e.stopPropagation(),
1170
+ className: "absolute right-0 top-0 h-full w-3 cursor-col-resize select-none touch-none",
1171
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1172
+ "div",
1173
+ {
1174
+ className: cn(
1175
+ "absolute right-1.5 top-2 bottom-2 w-px rounded-full transition-colors",
1176
+ "opacity-0 group-hover:opacity-100",
1177
+ header.column.getIsResizing() ? "opacity-100 bg-primary" : "bg-border hover:bg-primary"
1178
+ )
1179
+ }
1180
+ )
1181
+ }
1182
+ )
1183
+ ]
1184
+ },
1185
+ header.id
1186
+ );
1187
+ }),
1188
+ !virtual && tableWidthMode === "spacer" && /* @__PURE__ */ jsxRuntime.jsx(
1189
+ "div",
1190
+ {
1191
+ role: "columnheader",
1192
+ style: { flex: 1, minWidth: 0, padding: 0 },
1193
+ className: "bg-muted"
1194
+ }
1195
+ )
1196
+ ]
1197
+ }
1198
+ );
1199
+ }
1200
+ function HeaderFilterPopover({
1201
+ col,
1202
+ table
1203
+ }) {
1204
+ const [open, setOpen] = React.useState(false);
1205
+ const focusRef = React.useCallback((el) => {
1206
+ el?.focus({ preventScroll: true });
1207
+ }, []);
1208
+ const ft = col.columnDef.meta?.filterType;
1209
+ if (ft === false || ft === void 0) return null;
1210
+ const hasFilter = col.getIsFiltered();
1211
+ const filterValue = col.getFilterValue() ?? "";
1212
+ const numFilter = col.getFilterValue();
1213
+ const min = numFilter?.[0] ?? "";
1214
+ const max = numFilter?.[1] ?? "";
1215
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
1216
+ /* @__PURE__ */ jsxRuntime.jsx(
1217
+ PopoverTrigger,
1218
+ {
1219
+ render: (props) => /* @__PURE__ */ jsxRuntime.jsx(
1220
+ Button,
1221
+ {
1222
+ ...props,
1223
+ variant: "ghost",
1224
+ size: "icon-xs",
1225
+ className: cn(
1226
+ "h-5 w-5 shrink-0",
1227
+ hasFilter ? "text-primary opacity-100" : "opacity-0 group-hover:opacity-60"
1228
+ ),
1229
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: "h-3 w-3" })
1230
+ }
1231
+ )
1232
+ }
1233
+ ),
1234
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { side: "bottom", align: "start", className: "w-52", children: ft === "select" ? /* @__PURE__ */ jsxRuntime.jsx(SelectFilterCell, { col, table, onSelect: () => setOpen(false) }) : ft === "multi-select" ? /* @__PURE__ */ jsxRuntime.jsx(MultiSelectContent, { col, table }) : ft === "number" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1235
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1236
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Min" }),
1237
+ /* @__PURE__ */ jsxRuntime.jsx(
1238
+ Input,
1239
+ {
1240
+ type: "number",
1241
+ placeholder: "Min",
1242
+ value: min,
1243
+ onChange: (e) => col.setFilterValue((old = ["", ""]) => [
1244
+ e.target.value,
1245
+ old[1]
1246
+ ]),
1247
+ className: "h-7 text-xs"
1248
+ }
1249
+ )
1250
+ ] }),
1251
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1252
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Max" }),
1253
+ /* @__PURE__ */ jsxRuntime.jsx(
1254
+ Input,
1255
+ {
1256
+ type: "number",
1257
+ placeholder: "Max",
1258
+ value: max,
1259
+ onChange: (e) => col.setFilterValue((old = ["", ""]) => [
1260
+ old[0],
1261
+ e.target.value
1262
+ ]),
1263
+ className: "h-7 text-xs"
1264
+ }
1265
+ )
1266
+ ] }),
1267
+ hasFilter && /* @__PURE__ */ jsxRuntime.jsx(
1268
+ Button,
1269
+ {
1270
+ variant: "ghost",
1271
+ size: "sm",
1272
+ className: "h-7 text-xs",
1273
+ onClick: () => col.setFilterValue(void 0),
1274
+ children: "Clear"
1275
+ }
1276
+ )
1277
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1278
+ /* @__PURE__ */ jsxRuntime.jsx(
1279
+ Input,
1280
+ {
1281
+ type: "text",
1282
+ placeholder: "Filter\u2026",
1283
+ value: filterValue,
1284
+ onChange: (e) => col.setFilterValue(e.target.value || void 0),
1285
+ className: "h-7 text-xs pr-6",
1286
+ ref: focusRef
1287
+ }
1288
+ ),
1289
+ filterValue && /* @__PURE__ */ jsxRuntime.jsx(
1290
+ Button,
1291
+ {
1292
+ variant: "ghost",
1293
+ size: "icon-xs",
1294
+ onClick: () => col.setFilterValue(void 0),
1295
+ className: "absolute right-0.5 top-1/2 -translate-y-1/2",
1296
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, {})
1297
+ }
1298
+ )
1299
+ ] }) })
1300
+ ] }) });
1301
+ }
1302
+ function NumberFilterPopover({ col }) {
1303
+ const numFilter = col.getFilterValue();
1304
+ const min = numFilter?.[0] ?? "";
1305
+ const max = numFilter?.[1] ?? "";
1306
+ const hasFilter = min !== "" || max !== "";
1307
+ const label = hasFilter ? [min && `\u2265${min}`, max && `\u2264${max}`].filter(Boolean).join(" ") : "Filter\u2026";
1308
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
1309
+ /* @__PURE__ */ jsxRuntime.jsx(
1310
+ PopoverTrigger,
1311
+ {
1312
+ render: (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1313
+ Button,
1314
+ {
1315
+ ...props,
1316
+ variant: hasFilter ? "outline" : "ghost",
1317
+ size: "sm",
1318
+ className: "h-7 w-full justify-start text-xs font-normal",
1319
+ children: [
1320
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SlidersHorizontal, { className: "h-3 w-3 shrink-0" }),
1321
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label })
1322
+ ]
1323
+ }
1324
+ )
1325
+ }
1326
+ ),
1327
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { side: "bottom", align: "start", className: "w-48", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1328
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1329
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Min" }),
1330
+ /* @__PURE__ */ jsxRuntime.jsx(
1331
+ Input,
1332
+ {
1333
+ type: "number",
1334
+ placeholder: "Min",
1335
+ value: min,
1336
+ onChange: (e) => col.setFilterValue((old = ["", ""]) => [e.target.value, old[1]]),
1337
+ className: "h-7 text-xs"
1338
+ }
1339
+ )
1340
+ ] }),
1341
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1342
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Max" }),
1343
+ /* @__PURE__ */ jsxRuntime.jsx(
1344
+ Input,
1345
+ {
1346
+ type: "number",
1347
+ placeholder: "Max",
1348
+ value: max,
1349
+ onChange: (e) => col.setFilterValue((old = ["", ""]) => [old[0], e.target.value]),
1350
+ className: "h-7 text-xs"
1351
+ }
1352
+ )
1353
+ ] }),
1354
+ hasFilter && /* @__PURE__ */ jsxRuntime.jsx(
1355
+ Button,
1356
+ {
1357
+ variant: "ghost",
1358
+ size: "sm",
1359
+ className: "h-7 text-xs",
1360
+ onClick: () => col.setFilterValue(void 0),
1361
+ children: "Clear"
1362
+ }
1363
+ )
1364
+ ] }) })
1365
+ ] });
1366
+ }
1367
+ function SelectFilterCell({
1368
+ col,
1369
+ table,
1370
+ onSelect
1371
+ }) {
1372
+ const [options, setOptions] = React.useState(null);
1373
+ const filterValue = col.getFilterValue() ?? "";
1374
+ const handleOpenChange = (open) => {
1375
+ if (open && options === null) {
1376
+ const vals = /* @__PURE__ */ new Set();
1377
+ table.getCoreRowModel().rows.forEach((row) => {
1378
+ const v = row.getValue(col.id);
1379
+ if (v != null) vals.add(String(v));
1380
+ });
1381
+ const sorted = Array.from(vals).sort();
1382
+ setOptions([{ label: "All", value: null }, ...sorted.map((v) => ({ label: v, value: v }))]);
1383
+ }
1384
+ };
1385
+ const items = options ?? [];
1386
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1387
+ Select,
1388
+ {
1389
+ items,
1390
+ value: filterValue || null,
1391
+ onValueChange: (val) => {
1392
+ col.setFilterValue(val ?? void 0);
1393
+ onSelect?.();
1394
+ },
1395
+ onOpenChange: handleOpenChange,
1396
+ children: [
1397
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { size: "sm", className: "h-7 w-full text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
1398
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: item.value, children: item.label }, item.value ?? "__all__")) })
1399
+ ]
1400
+ }
1401
+ );
1402
+ }
1403
+ function MultiSelectContent({ col, table }) {
1404
+ const [options, setOptions] = React.useState(null);
1405
+ const selected = col.getFilterValue() ?? [];
1406
+ React.useEffect(() => {
1407
+ const vals = /* @__PURE__ */ new Set();
1408
+ table.getCoreRowModel().rows.forEach((row) => {
1409
+ const v = row.getValue(col.id);
1410
+ if (v != null) vals.add(String(v));
1411
+ });
1412
+ setOptions(Array.from(vals).sort());
1413
+ }, []);
1414
+ const toggle = (val) => {
1415
+ const next = selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val];
1416
+ col.setFilterValue(next.length > 0 ? next : void 0);
1417
+ };
1418
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
1419
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-48 overflow-y-auto flex flex-col gap-0.5", children: (options ?? []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
1420
+ "label",
1421
+ {
1422
+ className: "flex items-center gap-2 px-1 py-1 cursor-pointer hover:bg-muted rounded-sm text-xs select-none",
1423
+ children: [
1424
+ /* @__PURE__ */ jsxRuntime.jsx(
1425
+ Checkbox,
1426
+ {
1427
+ checked: selected.includes(opt),
1428
+ onCheckedChange: () => toggle(opt),
1429
+ className: "shrink-0"
1430
+ }
1431
+ ),
1432
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: opt })
1433
+ ]
1434
+ },
1435
+ opt
1436
+ )) }),
1437
+ selected.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1438
+ Button,
1439
+ {
1440
+ variant: "ghost",
1441
+ size: "sm",
1442
+ className: "h-6 text-xs mt-1",
1443
+ onClick: () => col.setFilterValue(void 0),
1444
+ children: [
1445
+ "Clear (",
1446
+ selected.length,
1447
+ ")"
1448
+ ]
1449
+ }
1450
+ )
1451
+ ] });
1452
+ }
1453
+ function MultiSelectFilterCell({
1454
+ col,
1455
+ table
1456
+ }) {
1457
+ const selected = col.getFilterValue() ?? [];
1458
+ const label = selected.length > 0 ? `${selected.length} selected` : "Filter\u2026";
1459
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
1460
+ /* @__PURE__ */ jsxRuntime.jsx(
1461
+ PopoverTrigger,
1462
+ {
1463
+ render: (props) => /* @__PURE__ */ jsxRuntime.jsx(
1464
+ Button,
1465
+ {
1466
+ ...props,
1467
+ variant: selected.length > 0 ? "outline" : "ghost",
1468
+ size: "sm",
1469
+ className: "h-7 w-full justify-start text-xs font-normal",
1470
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label })
1471
+ }
1472
+ )
1473
+ }
1474
+ ),
1475
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { side: "bottom", align: "start", className: "w-48", children: /* @__PURE__ */ jsxRuntime.jsx(MultiSelectContent, { col, table }) })
1476
+ ] });
1477
+ }
1478
+ function DataGridFilterRow({
1479
+ visibleLeafColumns,
1480
+ table,
1481
+ virtual,
1482
+ bordered,
1483
+ tableWidthMode = "spacer"
1484
+ }) {
1485
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1486
+ "div",
1487
+ {
1488
+ role: "row",
1489
+ className: "border-b border-border bg-muted",
1490
+ style: { display: "flex", width: "100%", height: "36px" },
1491
+ children: [
1492
+ visibleLeafColumns.map((col) => {
1493
+ const ft = col.columnDef.meta?.filterType;
1494
+ const filterValue = col.getFilterValue() ?? "";
1495
+ const cellStyle = virtual ? { display: "flex", alignItems: "center", width: col.getSize() } : { ...colStyle(col), display: "flex", alignItems: "center" };
1496
+ if (ft === false) {
1497
+ return /* @__PURE__ */ jsxRuntime.jsx(
1498
+ "div",
1499
+ {
1500
+ role: "columnheader",
1501
+ className: cn("px-2 py-1", bordered && "border-r border-border"),
1502
+ style: cellStyle
1503
+ },
1504
+ col.id
1505
+ );
1506
+ }
1507
+ return /* @__PURE__ */ jsxRuntime.jsx(
1508
+ "div",
1509
+ {
1510
+ role: "columnheader",
1511
+ className: cn("px-2 py-1 font-normal", bordered && "border-r border-border"),
1512
+ style: cellStyle,
1513
+ children: ft === "select" ? /* @__PURE__ */ jsxRuntime.jsx(SelectFilterCell, { col, table }) : ft === "multi-select" ? /* @__PURE__ */ jsxRuntime.jsx(MultiSelectFilterCell, { col, table }) : ft === "number" ? /* @__PURE__ */ jsxRuntime.jsx(NumberFilterPopover, { col }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
1514
+ /* @__PURE__ */ jsxRuntime.jsx(
1515
+ Input,
1516
+ {
1517
+ type: "text",
1518
+ placeholder: "Filter\u2026",
1519
+ value: filterValue,
1520
+ onChange: (e) => col.setFilterValue(e.target.value || void 0),
1521
+ className: "h-7 text-xs pr-6"
1522
+ }
1523
+ ),
1524
+ filterValue && /* @__PURE__ */ jsxRuntime.jsx(
1525
+ Button,
1526
+ {
1527
+ variant: "ghost",
1528
+ size: "icon-xs",
1529
+ onClick: () => col.setFilterValue(void 0),
1530
+ className: "absolute right-0.5 top-1/2 -translate-y-1/2",
1531
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, {})
1532
+ }
1533
+ )
1534
+ ] })
1535
+ },
1536
+ col.id
1537
+ );
1538
+ }),
1539
+ !virtual && tableWidthMode === "spacer" && /* @__PURE__ */ jsxRuntime.jsx(
1540
+ "div",
1541
+ {
1542
+ role: "columnheader",
1543
+ style: { flex: 1, minWidth: 0, padding: 0 },
1544
+ className: "bg-muted"
1545
+ }
1546
+ )
1547
+ ]
1548
+ }
1549
+ );
1550
+ }
1551
+ function DataGridBodyRow({
1552
+ row,
1553
+ table,
1554
+ onRowClick,
1555
+ rowCursor,
1556
+ style,
1557
+ dataIndex,
1558
+ measureRef,
1559
+ showSpacer = false,
1560
+ fillLast = false,
1561
+ bordered = false,
1562
+ rowHeight,
1563
+ onActionTrigger,
1564
+ classNames
1565
+ }) {
1566
+ const visibleCells = row.getVisibleCells();
1567
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1568
+ "div",
1569
+ {
1570
+ role: "row",
1571
+ "data-index": dataIndex,
1572
+ ref: measureRef,
1573
+ onClick: onRowClick ? () => onRowClick(row.original) : void 0,
1574
+ className: cn(
1575
+ "flex w-full border-b border-border transition-colors",
1576
+ onRowClick || rowCursor ? "cursor-pointer hover:bg-muted/50" : "hover:bg-muted/30",
1577
+ classNames?.row
1578
+ ),
1579
+ style: { minHeight: rowHeight, ...style },
1580
+ children: [
1581
+ visibleCells.map((cell, idx) => {
1582
+ const meta = cell.column.columnDef.meta;
1583
+ const edge = isPinnedEdge(cell.column, table);
1584
+ const isLast = idx === visibleCells.length - 1;
1585
+ const isFillCell = fillLast && isLast;
1586
+ return /* @__PURE__ */ jsxRuntime.jsx(
1587
+ "div",
1588
+ {
1589
+ role: "gridcell",
1590
+ "data-col-id": cell.column.id,
1591
+ className: cn(
1592
+ "flex items-center px-3 py-1 overflow-hidden bg-background",
1593
+ meta?.align === "right" && "justify-end",
1594
+ meta?.align === "center" && "justify-center",
1595
+ meta?.wrap && "items-start whitespace-normal",
1596
+ bordered && "border-r border-border",
1597
+ edge === "left-edge" && "shadow-[1px_0_0_0_hsl(var(--border))]",
1598
+ edge === "right-edge" && "shadow-[-1px_0_0_0_hsl(var(--border))]",
1599
+ classNames?.cell
1600
+ ),
1601
+ style: { ...colStyle(cell.column), ...isFillCell && { flex: 1, width: "auto" } },
1602
+ children: meta?.actions != null ? /* @__PURE__ */ jsxRuntime.jsx(
1603
+ Button,
1604
+ {
1605
+ variant: "ghost",
1606
+ size: "icon-xs",
1607
+ className: "h-6 w-6",
1608
+ onClick: (e) => {
1609
+ e.stopPropagation();
1610
+ onActionTrigger?.(row.original, e.currentTarget);
1611
+ },
1612
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { className: "h-4 w-4" })
1613
+ }
1614
+ ) : reactTable.flexRender(cell.column.columnDef.cell, cell.getContext())
1615
+ },
1616
+ cell.id
1617
+ );
1618
+ }),
1619
+ showSpacer && /* @__PURE__ */ jsxRuntime.jsx(
1620
+ "div",
1621
+ {
1622
+ role: "gridcell",
1623
+ style: { flex: 1, minWidth: 0, padding: 0 },
1624
+ className: "bg-background"
1625
+ }
1626
+ )
1627
+ ]
1628
+ }
1629
+ );
1630
+ }
1631
+ function DataGridVirtualBody({
1632
+ rows,
1633
+ table,
1634
+ rowVirtualizer,
1635
+ onRowClick,
1636
+ rowCursor,
1637
+ bordered,
1638
+ rowHeight,
1639
+ onActionTrigger,
1640
+ tableWidthMode = "spacer",
1641
+ classNames
1642
+ }) {
1643
+ const virtualItems = rowVirtualizer.getVirtualItems();
1644
+ const totalSize = rowVirtualizer.getTotalSize();
1645
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "rowgroup", style: { display: "block", height: totalSize, position: "relative" }, children: virtualItems.map((virtualRow) => {
1646
+ const row = rows[virtualRow.index];
1647
+ return /* @__PURE__ */ jsxRuntime.jsx(
1648
+ DataGridBodyRow,
1649
+ {
1650
+ row,
1651
+ table,
1652
+ onRowClick,
1653
+ rowCursor,
1654
+ bordered,
1655
+ rowHeight,
1656
+ dataIndex: virtualRow.index,
1657
+ measureRef: rowVirtualizer.measureElement,
1658
+ style: {
1659
+ position: "absolute",
1660
+ width: "100%",
1661
+ transform: `translateY(${virtualRow.start}px)`
1662
+ },
1663
+ onActionTrigger,
1664
+ fillLast: tableWidthMode === "fill-last",
1665
+ classNames
1666
+ },
1667
+ row.id
1668
+ );
1669
+ }) });
1670
+ }
1671
+ function DataGridFlexBody({
1672
+ rows,
1673
+ table,
1674
+ visibleLeafColumns,
1675
+ isLoading,
1676
+ emptyMessage,
1677
+ emptyContent,
1678
+ onRowClick,
1679
+ rowCursor,
1680
+ bordered,
1681
+ rowHeight,
1682
+ onActionTrigger,
1683
+ tableWidthMode = "spacer",
1684
+ classNames
1685
+ }) {
1686
+ const showSpacer = tableWidthMode === "spacer";
1687
+ const fillLast = tableWidthMode === "fill-last";
1688
+ const RowWrapper = React.useContext(RowWrapperContext);
1689
+ if (isLoading) {
1690
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "rowgroup", style: { display: "block" }, className: "[&>div:last-child]:border-b-0", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1691
+ "div",
1692
+ {
1693
+ role: "row",
1694
+ className: "flex w-full border-b border-border",
1695
+ style: { minHeight: rowHeight },
1696
+ children: [
1697
+ visibleLeafColumns.map((col, colIdx) => {
1698
+ const isLast = colIdx === visibleLeafColumns.length - 1;
1699
+ return /* @__PURE__ */ jsxRuntime.jsx(
1700
+ "div",
1701
+ {
1702
+ role: "gridcell",
1703
+ "data-col-id": col.id,
1704
+ className: cn(
1705
+ "flex items-center px-3 py-1",
1706
+ bordered && "border-r border-border"
1707
+ ),
1708
+ style: {
1709
+ ...colStyle(col),
1710
+ ...fillLast && isLast && { flex: 1, width: "auto" }
1711
+ },
1712
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-full animate-pulse rounded bg-muted" })
1713
+ },
1714
+ col.id
1715
+ );
1716
+ }),
1717
+ showSpacer && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "gridcell", style: { flex: 1, minWidth: 0, padding: 0 } })
1718
+ ]
1719
+ },
1720
+ i
1721
+ )) });
1722
+ }
1723
+ if (rows.length === 0) {
1724
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "rowgroup", style: { display: "block" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { role: "row", className: "flex w-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { role: "gridcell", className: "flex-1", children: emptyContent ?? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-12 text-center text-muted-foreground text-sm", children: emptyMessage }) }) }) });
1725
+ }
1726
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "rowgroup", style: { display: "block" }, className: "[&>div:last-child]:border-b-0", children: rows.map((row) => {
1727
+ const bodyRow = /* @__PURE__ */ jsxRuntime.jsx(
1728
+ DataGridBodyRow,
1729
+ {
1730
+ row,
1731
+ table,
1732
+ onRowClick,
1733
+ rowCursor,
1734
+ bordered,
1735
+ rowHeight,
1736
+ showSpacer,
1737
+ fillLast,
1738
+ onActionTrigger,
1739
+ classNames
1740
+ }
1741
+ );
1742
+ if (RowWrapper) {
1743
+ return /* @__PURE__ */ jsxRuntime.jsx(RowWrapper, { row, children: bodyRow }, row.id);
1744
+ }
1745
+ return /* @__PURE__ */ jsxRuntime.jsx(React__namespace.default.Fragment, { children: bodyRow }, row.id);
1746
+ }) });
1747
+ }
1748
+ function DataGridTableView({
1749
+ table,
1750
+ rows,
1751
+ containerRef,
1752
+ isLoading,
1753
+ emptyMessage = "No data",
1754
+ emptyContent,
1755
+ showHeader = true,
1756
+ onRowClick,
1757
+ rowCursor,
1758
+ enableColumnResizing = true,
1759
+ enableColumnFilters = false,
1760
+ filterDisplay = "row",
1761
+ tableHeight,
1762
+ tableWidthMode = "spacer",
1763
+ rowHeight,
1764
+ estimateRowHeight,
1765
+ overscan = 10,
1766
+ loadMoreRef,
1767
+ isFetchingNextPage,
1768
+ bordered = false,
1769
+ onMeasureColumns,
1770
+ classNames
1771
+ }) {
1772
+ const effectiveEstimate = estimateRowHeight ?? rowHeight ?? 33;
1773
+ const [actionMenuOpen, setActionMenuOpen] = React.useState(false);
1774
+ const [activeRow, setActiveRow] = React.useState(null);
1775
+ const anchorRef = React.useRef(null);
1776
+ const handleActionTrigger = React.useCallback((row, el) => {
1777
+ const rect = el.getBoundingClientRect();
1778
+ anchorRef.current = { getBoundingClientRect: () => rect };
1779
+ setActiveRow(row);
1780
+ setActionMenuOpen(true);
1781
+ }, []);
1782
+ const headerGroups = table.getHeaderGroups();
1783
+ const visibleLeafColumns = table.getVisibleLeafColumns();
1784
+ const actionCol = visibleLeafColumns.find((col) => col.columnDef.meta?.actions != null);
1785
+ const actionItems = actionCol && activeRow ? actionCol.columnDef.meta.actions(activeRow) : [];
1786
+ const hasFixedHeight = tableHeight != null && tableHeight !== "auto";
1787
+ const virtual = hasFixedHeight && rows.length >= VIRTUAL_THRESHOLD;
1788
+ React.useEffect(() => {
1789
+ if (!table.getState().columnSizingInfo.isResizingColumn) {
1790
+ onMeasureColumns?.();
1791
+ }
1792
+ });
1793
+ const headerScrollRef = React.useRef(null);
1794
+ const bodyScrollRef = React.useRef(null);
1795
+ const syncScroll = React.useCallback((e) => {
1796
+ if (headerScrollRef.current) {
1797
+ headerScrollRef.current.scrollLeft = e.currentTarget.scrollLeft;
1798
+ }
1799
+ }, []);
1800
+ const rowVirtualizer = reactVirtual.useVirtualizer({
1801
+ count: rows.length,
1802
+ getScrollElement: () => bodyScrollRef.current,
1803
+ estimateSize: () => effectiveEstimate,
1804
+ overscan,
1805
+ enabled: virtual
1806
+ });
1807
+ const bodyWrapperStyle = {
1808
+ display: "flex",
1809
+ flexDirection: "column",
1810
+ position: "relative",
1811
+ ...tableHeight && tableHeight !== "auto" ? { height: tableHeight } : {}
1812
+ };
1813
+ const bodyStyle = {
1814
+ flex: 1,
1815
+ minHeight: 0,
1816
+ overflow: "auto"
1817
+ };
1818
+ const innerWidth = table.getTotalSize();
1819
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1820
+ /* @__PURE__ */ jsxRuntime.jsxs(
1821
+ "div",
1822
+ {
1823
+ ref: containerRef,
1824
+ style: {
1825
+ position: "relative",
1826
+ width: "100%",
1827
+ minWidth: 0,
1828
+ isolation: "isolate",
1829
+ // Limit reflow scope to this subtree.
1830
+ // 'layout paint' isolates layout and paint without size containment,
1831
+ // which is required since the outer div's height is determined by its children.
1832
+ contain: "layout paint"
1833
+ },
1834
+ className: cn("rounded-md border border-border", classNames?.container),
1835
+ children: [
1836
+ showHeader && /* @__PURE__ */ jsxRuntime.jsx("div", { ref: headerScrollRef, style: { overflow: "hidden" }, className: cn("bg-muted", classNames?.header), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { width: innerWidth, minWidth: "100%" }, children: [
1837
+ headerGroups.map((headerGroup) => /* @__PURE__ */ jsxRuntime.jsx(
1838
+ DataGridHeaderRow,
1839
+ {
1840
+ headerGroup,
1841
+ table,
1842
+ enableColumnResizing,
1843
+ enableColumnFilters,
1844
+ filterDisplay,
1845
+ virtual,
1846
+ bordered,
1847
+ tableWidthMode,
1848
+ classNames
1849
+ },
1850
+ headerGroup.id
1851
+ )),
1852
+ enableColumnFilters && filterDisplay !== "icon" && /* @__PURE__ */ jsxRuntime.jsx(
1853
+ DataGridFilterRow,
1854
+ {
1855
+ visibleLeafColumns,
1856
+ table,
1857
+ virtual,
1858
+ bordered,
1859
+ tableWidthMode
1860
+ }
1861
+ )
1862
+ ] }) }),
1863
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: bodyWrapperStyle, children: [
1864
+ /* @__PURE__ */ jsxRuntime.jsxs(
1865
+ "div",
1866
+ {
1867
+ ref: bodyScrollRef,
1868
+ style: bodyStyle,
1869
+ onScroll: syncScroll,
1870
+ className: "scrollbar-none",
1871
+ children: [
1872
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollTable, { style: { width: innerWidth, minWidth: "100%" }, children: virtual ? /* @__PURE__ */ jsxRuntime.jsx(
1873
+ DataGridVirtualBody,
1874
+ {
1875
+ rows,
1876
+ table,
1877
+ rowVirtualizer,
1878
+ onRowClick,
1879
+ rowCursor,
1880
+ bordered,
1881
+ rowHeight,
1882
+ onActionTrigger: actionCol ? handleActionTrigger : void 0,
1883
+ tableWidthMode,
1884
+ classNames
1885
+ }
1886
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1887
+ DataGridFlexBody,
1888
+ {
1889
+ rows,
1890
+ table,
1891
+ visibleLeafColumns,
1892
+ isLoading,
1893
+ emptyMessage,
1894
+ emptyContent,
1895
+ onRowClick,
1896
+ rowCursor,
1897
+ bordered,
1898
+ rowHeight,
1899
+ onActionTrigger: actionCol ? handleActionTrigger : void 0,
1900
+ tableWidthMode,
1901
+ classNames
1902
+ }
1903
+ ) }),
1904
+ loadMoreRef && /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: "py-2 flex justify-center", children: isFetchingNextPage && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }) })
1905
+ ]
1906
+ }
1907
+ ),
1908
+ /* @__PURE__ */ jsxRuntime.jsx(
1909
+ CustomScrollbar,
1910
+ {
1911
+ scrollRef: bodyScrollRef,
1912
+ direction: "vertical",
1913
+ className: "absolute right-0 top-0 bottom-0",
1914
+ style: { width: 8 }
1915
+ }
1916
+ ),
1917
+ /* @__PURE__ */ jsxRuntime.jsx(CustomScrollbar, { scrollRef: bodyScrollRef, direction: "horizontal", style: { height: 8 } })
1918
+ ] })
1919
+ ]
1920
+ }
1921
+ ),
1922
+ actionCol && /* @__PURE__ */ jsxRuntime.jsx(menu.Menu.Root, { open: actionMenuOpen, onOpenChange: setActionMenuOpen, children: /* @__PURE__ */ jsxRuntime.jsx(menu.Menu.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
1923
+ menu.Menu.Positioner,
1924
+ {
1925
+ anchor: anchorRef.current,
1926
+ side: "bottom",
1927
+ align: "end",
1928
+ sideOffset: 4,
1929
+ className: "isolate z-50 outline-none",
1930
+ children: /* @__PURE__ */ jsxRuntime.jsx(menu.Menu.Popup, { className: "min-w-32 origin-(--transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", children: actionItems.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1931
+ menu.Menu.Item,
1932
+ {
1933
+ disabled: item.disabled,
1934
+ "data-variant": item.variant ?? "default",
1935
+ onClick: () => {
1936
+ item.onClick(activeRow);
1937
+ setActionMenuOpen(false);
1938
+ },
1939
+ className: "relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1940
+ children: [
1941
+ item.icon,
1942
+ item.label
1943
+ ]
1944
+ },
1945
+ i
1946
+ )) })
1947
+ }
1948
+ ) }) })
1949
+ ] });
1950
+ }
1951
+ function DataGridShell({
1952
+ wrapperRef,
1953
+ containerRef,
1954
+ table,
1955
+ rows,
1956
+ isSized,
1957
+ measure,
1958
+ error,
1959
+ leftFilters,
1960
+ rightFilters,
1961
+ loadMoreRef,
1962
+ isFetchingNextPage,
1963
+ footer,
1964
+ ...viewConfig
1965
+ }) {
1966
+ if (error) {
1967
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12 text-sm text-destructive", children: error.message });
1968
+ }
1969
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: wrapperRef, className: "flex flex-col gap-3 w-full min-w-0 overflow-hidden", children: [
1970
+ /* @__PURE__ */ jsxRuntime.jsx(DataGridToolbar, { table, leftFilters, rightFilters }),
1971
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("min-w-0", !isSized && "invisible"), children: /* @__PURE__ */ jsxRuntime.jsx(
1972
+ DataGridTableView,
1973
+ {
1974
+ table,
1975
+ rows,
1976
+ containerRef,
1977
+ loadMoreRef,
1978
+ isFetchingNextPage,
1979
+ onMeasureColumns: measure,
1980
+ ...viewConfig
1981
+ }
1982
+ ) }),
1983
+ footer
1984
+ ] });
1985
+ }
1986
+ function DataGrid(props) {
1987
+ const {
1988
+ error,
1989
+ leftFilters,
1990
+ rightFilters,
1991
+ enablePagination = true,
1992
+ pageSizes = [10, 20, 50, 100],
1993
+ totalCount,
1994
+ tableRef
1995
+ } = props;
1996
+ const { wrapperRef, containerRef, table, rows, isSized, measure } = useDataGridBase(props);
1997
+ React.useEffect(() => {
1998
+ if (tableRef) {
1999
+ tableRef.current = table;
2000
+ return () => {
2001
+ tableRef.current = null;
2002
+ };
2003
+ }
2004
+ }, [table, tableRef]);
2005
+ return /* @__PURE__ */ jsxRuntime.jsx(
2006
+ DataGridShell,
2007
+ {
2008
+ ...props,
2009
+ wrapperRef,
2010
+ containerRef,
2011
+ table,
2012
+ rows,
2013
+ isSized,
2014
+ measure,
2015
+ error,
2016
+ leftFilters,
2017
+ rightFilters,
2018
+ footer: enablePagination ? /* @__PURE__ */ jsxRuntime.jsx(DataGridPaginationBar, { table, pageSizes, totalCount }) : null
2019
+ }
2020
+ );
2021
+ }
2022
+ function useInfiniteScroll({
2023
+ hasNextPage,
2024
+ isFetchingNextPage,
2025
+ fetchNextPage,
2026
+ rootMargin = "100px",
2027
+ enabled = true
2028
+ }) {
2029
+ const loadMoreRef = React.useRef(null);
2030
+ React.useEffect(() => {
2031
+ if (!enabled || !loadMoreRef.current) return;
2032
+ const observer = new IntersectionObserver(
2033
+ ([entry]) => {
2034
+ if (entry?.isIntersecting && hasNextPage && !isFetchingNextPage) {
2035
+ fetchNextPage?.();
2036
+ }
2037
+ },
2038
+ { rootMargin }
2039
+ );
2040
+ observer.observe(loadMoreRef.current);
2041
+ return () => observer.disconnect();
2042
+ }, [hasNextPage, isFetchingNextPage, fetchNextPage, rootMargin, enabled]);
2043
+ return { loadMoreRef };
2044
+ }
2045
+ function DataGridInfinity(props) {
2046
+ const {
2047
+ error,
2048
+ leftFilters,
2049
+ rightFilters,
2050
+ hasNextPage,
2051
+ isFetchingNextPage,
2052
+ fetchNextPage,
2053
+ rootMargin = "100px",
2054
+ isLoading
2055
+ } = props;
2056
+ const { wrapperRef, containerRef, table, rows, isSized, measure } = useDataGridBase({
2057
+ ...props,
2058
+ enablePagination: false
2059
+ });
2060
+ const { loadMoreRef } = useInfiniteScroll({
2061
+ hasNextPage,
2062
+ isFetchingNextPage,
2063
+ fetchNextPage,
2064
+ rootMargin,
2065
+ enabled: !isLoading
2066
+ });
2067
+ return /* @__PURE__ */ jsxRuntime.jsx(
2068
+ DataGridShell,
2069
+ {
2070
+ ...props,
2071
+ wrapperRef,
2072
+ containerRef,
2073
+ table,
2074
+ rows,
2075
+ isSized,
2076
+ measure,
2077
+ error,
2078
+ leftFilters,
2079
+ rightFilters,
2080
+ loadMoreRef,
2081
+ isFetchingNextPage
2082
+ }
2083
+ );
2084
+ }
2085
+ var RowDragContext = React__namespace.default.createContext(null);
2086
+ function SortableRow({ row, children }) {
2087
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = sortable.useSortable({
2088
+ id: row.id
2089
+ });
2090
+ return /* @__PURE__ */ jsxRuntime.jsx(RowDragContext.Provider, { value: { listeners, attributes, isDragging }, children: /* @__PURE__ */ jsxRuntime.jsx(
2091
+ "div",
2092
+ {
2093
+ ref: setNodeRef,
2094
+ style: {
2095
+ transform: utilities.CSS.Transform.toString(transform),
2096
+ transition,
2097
+ opacity: isDragging ? 0.4 : 1,
2098
+ position: "relative",
2099
+ zIndex: isDragging ? 1 : 0
2100
+ },
2101
+ children
2102
+ }
2103
+ ) });
2104
+ }
2105
+ function DataGridDrag(props) {
2106
+ const { data = [], onRowReorder, getRowId } = props;
2107
+ const { wrapperRef, containerRef, table, rows, isSized, measure } = useDataGridBase({
2108
+ ...props,
2109
+ // Sorting changes visual order — incompatible with manual row reordering
2110
+ enableSorting: false,
2111
+ // Pagination splits data — reorder applies within the loaded set only
2112
+ enablePagination: false,
2113
+ getRowId
2114
+ });
2115
+ const [activeRowId, setActiveRowId] = React.useState(null);
2116
+ const sensors = core.useSensors(
2117
+ core.useSensor(core.PointerSensor, {
2118
+ // Require a small move before drag starts — prevents click from triggering drag
2119
+ activationConstraint: { distance: 5 }
2120
+ })
2121
+ );
2122
+ const rowIds = rows.map((r) => r.id);
2123
+ const handleDragStart = ({ active }) => {
2124
+ setActiveRowId(String(active.id));
2125
+ };
2126
+ const handleDragEnd = ({ active, over }) => {
2127
+ setActiveRowId(null);
2128
+ if (!over || active.id === over.id) return;
2129
+ const activeRow2 = rows.find((r) => r.id === String(active.id));
2130
+ const overRow = rows.find((r) => r.id === String(over.id));
2131
+ if (!activeRow2 || !overRow) return;
2132
+ const fromIdx = data.indexOf(activeRow2.original);
2133
+ const toIdx = data.indexOf(overRow.original);
2134
+ if (fromIdx === -1 || toIdx === -1) return;
2135
+ onRowReorder(sortable.arrayMove(data, fromIdx, toIdx));
2136
+ };
2137
+ const activeRow = activeRowId ? rows.find((r) => r.id === activeRowId) : null;
2138
+ return /* @__PURE__ */ jsxRuntime.jsx(RowWrapperContext.Provider, { value: SortableRow, children: /* @__PURE__ */ jsxRuntime.jsxs(
2139
+ core.DndContext,
2140
+ {
2141
+ sensors,
2142
+ collisionDetection: core.closestCenter,
2143
+ onDragStart: handleDragStart,
2144
+ onDragEnd: handleDragEnd,
2145
+ children: [
2146
+ /* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items: rowIds, strategy: sortable.verticalListSortingStrategy, children: /* @__PURE__ */ jsxRuntime.jsx(
2147
+ DataGridShell,
2148
+ {
2149
+ ...props,
2150
+ wrapperRef,
2151
+ containerRef,
2152
+ table,
2153
+ rows,
2154
+ isSized,
2155
+ measure,
2156
+ footer: null
2157
+ }
2158
+ ) }),
2159
+ /* @__PURE__ */ jsxRuntime.jsx(core.DragOverlay, { children: activeRow && /* @__PURE__ */ jsxRuntime.jsx(
2160
+ "div",
2161
+ {
2162
+ className: "rounded border border-primary/40 bg-primary/5 shadow-xl ring-1 ring-primary/20",
2163
+ style: { height: props.rowHeight ?? 36 }
2164
+ }
2165
+ ) })
2166
+ ]
2167
+ }
2168
+ ) });
2169
+ }
2170
+ function ColumnVisibilityDropdown({ table }) {
2171
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
2172
+ /* @__PURE__ */ jsxRuntime.jsx(
2173
+ PopoverTrigger,
2174
+ {
2175
+ render: (props) => /* @__PURE__ */ jsxRuntime.jsxs(Button, { ...props, variant: "outline", size: "sm", className: "gap-1.5", children: [
2176
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Columns3, { className: "h-4 w-4" }),
2177
+ "Columns"
2178
+ ] })
2179
+ }
2180
+ ),
2181
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "end", className: "w-48", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1.5", children: table.getAllLeafColumns().filter((col) => col.id !== "__select__").map((col) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-sm", children: [
2182
+ /* @__PURE__ */ jsxRuntime.jsx(Checkbox, { checked: col.getIsVisible(), onCheckedChange: col.toggleVisibility }),
2183
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: typeof col.columnDef.header === "string" ? col.columnDef.header : col.id })
2184
+ ] }, col.id)) }) })
2185
+ ] });
2186
+ }
2187
+ function GlobalSearch({
2188
+ table,
2189
+ placeholder = "Search\u2026",
2190
+ className
2191
+ }) {
2192
+ const [value, setValue] = React.useState(String(table.getState().globalFilter ?? ""));
2193
+ React.useEffect(() => {
2194
+ const timeout = setTimeout(() => table.setGlobalFilter(value || void 0), 200);
2195
+ return () => clearTimeout(timeout);
2196
+ }, [value, table]);
2197
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", className), children: [
2198
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" }),
2199
+ /* @__PURE__ */ jsxRuntime.jsx(
2200
+ Input,
2201
+ {
2202
+ value,
2203
+ onChange: (e) => setValue(e.target.value),
2204
+ placeholder,
2205
+ className: "h-8 pl-8 pr-7 text-xs w-52"
2206
+ }
2207
+ ),
2208
+ value && /* @__PURE__ */ jsxRuntime.jsx(
2209
+ Button,
2210
+ {
2211
+ variant: "ghost",
2212
+ size: "icon-xs",
2213
+ onClick: () => {
2214
+ setValue("");
2215
+ table.setGlobalFilter(void 0);
2216
+ },
2217
+ className: "absolute right-0.5 top-1/2 -translate-y-1/2",
2218
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
2219
+ }
2220
+ )
2221
+ ] });
2222
+ }
2223
+ function SelectFilter({ table, columnId, label }) {
2224
+ const [options, setOptions] = React.useState(null);
2225
+ const col = table.getColumn(columnId);
2226
+ const value = col?.getFilterValue() ?? "";
2227
+ const handleOpenChange = (open) => {
2228
+ if (open && options === null) {
2229
+ const vals = /* @__PURE__ */ new Set();
2230
+ table.getCoreRowModel().rows.forEach((row) => {
2231
+ const v = row.getValue(columnId);
2232
+ if (v != null) vals.add(String(v));
2233
+ });
2234
+ setOptions(Array.from(vals).sort());
2235
+ }
2236
+ };
2237
+ if (!col) return null;
2238
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { onOpenChange: handleOpenChange, children: [
2239
+ /* @__PURE__ */ jsxRuntime.jsx(
2240
+ PopoverTrigger,
2241
+ {
2242
+ render: (props) => /* @__PURE__ */ jsxRuntime.jsxs(
2243
+ Button,
2244
+ {
2245
+ ...props,
2246
+ variant: value ? "secondary" : "outline",
2247
+ size: "sm",
2248
+ className: "h-8 gap-1.5 text-xs",
2249
+ children: [
2250
+ value ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2251
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-normal text-muted-foreground", children: [
2252
+ label,
2253
+ ":"
2254
+ ] }),
2255
+ " ",
2256
+ value
2257
+ ] }) : label,
2258
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })
2259
+ ]
2260
+ }
2261
+ )
2262
+ }
2263
+ ),
2264
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-44 p-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
2265
+ value && /* @__PURE__ */ jsxRuntime.jsxs(
2266
+ "button",
2267
+ {
2268
+ onClick: () => col.setFilterValue(void 0),
2269
+ className: "flex items-center gap-2 px-2 py-1.5 text-xs rounded-sm hover:bg-muted text-muted-foreground",
2270
+ children: [
2271
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" }),
2272
+ " Clear"
2273
+ ]
2274
+ }
2275
+ ),
2276
+ (options ?? []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
2277
+ "button",
2278
+ {
2279
+ onClick: () => col.setFilterValue(opt === value ? void 0 : opt),
2280
+ className: "flex items-center gap-2 px-2 py-1.5 text-xs rounded-sm hover:bg-muted",
2281
+ children: [
2282
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: cn("h-3 w-3", opt === value ? "opacity-100" : "opacity-0") }),
2283
+ opt
2284
+ ]
2285
+ },
2286
+ opt
2287
+ ))
2288
+ ] }) })
2289
+ ] });
2290
+ }
2291
+ function MultiSelectFilter({
2292
+ table,
2293
+ columnId,
2294
+ label
2295
+ }) {
2296
+ const [options, setOptions] = React.useState(null);
2297
+ const col = table.getColumn(columnId);
2298
+ const selected = col?.getFilterValue() ?? [];
2299
+ React.useEffect(() => {
2300
+ const vals = /* @__PURE__ */ new Set();
2301
+ table.getCoreRowModel().rows.forEach((row) => {
2302
+ const v = row.getValue(columnId);
2303
+ if (v != null) vals.add(String(v));
2304
+ });
2305
+ setOptions(Array.from(vals).sort());
2306
+ }, []);
2307
+ const toggle = (val) => {
2308
+ if (!col) return;
2309
+ const next = selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val];
2310
+ col.setFilterValue(next.length > 0 ? next : void 0);
2311
+ };
2312
+ if (!col) return null;
2313
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
2314
+ /* @__PURE__ */ jsxRuntime.jsx(
2315
+ PopoverTrigger,
2316
+ {
2317
+ render: (props) => /* @__PURE__ */ jsxRuntime.jsxs(
2318
+ Button,
2319
+ {
2320
+ ...props,
2321
+ variant: selected.length > 0 ? "secondary" : "outline",
2322
+ size: "sm",
2323
+ className: "h-8 gap-1.5 text-xs",
2324
+ children: [
2325
+ selected.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2326
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-normal text-muted-foreground", children: [
2327
+ label,
2328
+ ":"
2329
+ ] }),
2330
+ " ",
2331
+ selected.length,
2332
+ " selected"
2333
+ ] }) : label,
2334
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })
2335
+ ]
2336
+ }
2337
+ )
2338
+ }
2339
+ ),
2340
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { align: "start", className: "w-48", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
2341
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-52 overflow-y-auto flex flex-col gap-0.5", children: (options ?? []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
2342
+ "label",
2343
+ {
2344
+ className: "flex items-center gap-2 px-1 py-1 cursor-pointer hover:bg-muted rounded-sm text-xs select-none",
2345
+ children: [
2346
+ /* @__PURE__ */ jsxRuntime.jsx(
2347
+ Checkbox,
2348
+ {
2349
+ checked: selected.includes(opt),
2350
+ onCheckedChange: () => toggle(opt),
2351
+ className: "shrink-0"
2352
+ }
2353
+ ),
2354
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: opt })
2355
+ ]
2356
+ },
2357
+ opt
2358
+ )) }),
2359
+ selected.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
2360
+ Button,
2361
+ {
2362
+ variant: "ghost",
2363
+ size: "sm",
2364
+ className: "h-6 text-xs mt-1",
2365
+ onClick: () => col.setFilterValue(void 0),
2366
+ children: [
2367
+ "Clear (",
2368
+ selected.length,
2369
+ ")"
2370
+ ]
2371
+ }
2372
+ )
2373
+ ] }) })
2374
+ ] });
2375
+ }
2376
+ function TreeCell({ row, indentSize = 16, children }) {
2377
+ const canExpand = row.getCanExpand();
2378
+ const indent = row.depth * indentSize;
2379
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { paddingLeft: indent }, className: "flex items-center gap-1 min-w-0", children: [
2380
+ canExpand ? /* @__PURE__ */ jsxRuntime.jsx(
2381
+ "button",
2382
+ {
2383
+ type: "button",
2384
+ onClick: (e) => {
2385
+ e.stopPropagation();
2386
+ row.toggleExpanded();
2387
+ },
2388
+ className: "flex items-center justify-center w-5 h-5 shrink-0 rounded hover:bg-black/10 dark:hover:bg-white/10 transition-colors",
2389
+ children: row.getIsExpanded() ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5 text-foreground/60" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-3.5 w-3.5 text-foreground/60" })
2390
+ }
2391
+ ) : (
2392
+ // Leaf or non-expandable: spacer keeps content aligned with siblings
2393
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-5 h-5 shrink-0" })
2394
+ ),
2395
+ children
2396
+ ] });
2397
+ }
2398
+ function DragHandleCell() {
2399
+ const ctx = React.useContext(RowDragContext);
2400
+ return /* @__PURE__ */ jsxRuntime.jsx(
2401
+ "button",
2402
+ {
2403
+ type: "button",
2404
+ ...ctx?.listeners,
2405
+ ...ctx?.attributes,
2406
+ className: "flex items-center justify-center w-full h-full cursor-grab active:cursor-grabbing text-muted-foreground/40 hover:text-muted-foreground transition-colors",
2407
+ style: { touchAction: "none" },
2408
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-4 w-4" })
2409
+ }
2410
+ );
2411
+ }
2412
+
2413
+ // src/core/engine/DataStore.ts
2414
+ function createDataStore(getRowId) {
2415
+ const map = /* @__PURE__ */ new Map();
2416
+ const orderedIds = [];
2417
+ const listeners = /* @__PURE__ */ new Set();
2418
+ let version = 0;
2419
+ let cachedSnapshot = [];
2420
+ let cachedSnapshotVersion = -1;
2421
+ function notify() {
2422
+ version++;
2423
+ listeners.forEach((fn) => fn());
2424
+ }
2425
+ return {
2426
+ get: (id) => map.get(id),
2427
+ getSnapshot: () => {
2428
+ if (cachedSnapshotVersion !== version) {
2429
+ cachedSnapshot = orderedIds.map((id) => map.get(id));
2430
+ cachedSnapshotVersion = version;
2431
+ }
2432
+ return cachedSnapshot;
2433
+ },
2434
+ getVersion: () => version,
2435
+ subscribe: (listener) => {
2436
+ listeners.add(listener);
2437
+ return () => listeners.delete(listener);
2438
+ },
2439
+ applyTransaction: (tx) => {
2440
+ let changed = false;
2441
+ tx.add?.forEach((item, i) => {
2442
+ const id = getRowId(item, map.size + i);
2443
+ if (!map.has(id)) {
2444
+ map.set(id, item);
2445
+ orderedIds.push(id);
2446
+ changed = true;
2447
+ }
2448
+ });
2449
+ tx.update?.forEach(({ id, data }) => {
2450
+ if (map.has(id)) {
2451
+ map.set(id, { ...map.get(id), ...data });
2452
+ changed = true;
2453
+ }
2454
+ });
2455
+ tx.remove?.forEach((id) => {
2456
+ if (map.delete(id)) {
2457
+ const idx = orderedIds.indexOf(id);
2458
+ if (idx !== -1) orderedIds.splice(idx, 1);
2459
+ changed = true;
2460
+ }
2461
+ });
2462
+ if (changed) notify();
2463
+ }
2464
+ };
2465
+ }
2466
+
2467
+ // src/core/engine/useDataStore.ts
2468
+ function useDataStore(options) {
2469
+ const { getRowId } = options;
2470
+ return React.useMemo(() => createDataStore(getRowId), []);
2471
+ }
2472
+
2473
+ exports.ColumnVisibilityDropdown = ColumnVisibilityDropdown;
2474
+ exports.DataGrid = DataGrid;
2475
+ exports.DataGridDrag = DataGridDrag;
2476
+ exports.DataGridInfinity = DataGridInfinity;
2477
+ exports.DragHandleCell = DragHandleCell;
2478
+ exports.GlobalSearch = GlobalSearch;
2479
+ exports.MultiSelectFilter = MultiSelectFilter;
2480
+ exports.SelectFilter = SelectFilter;
2481
+ exports.TreeCell = TreeCell;
2482
+ exports.createDataStore = createDataStore;
2483
+ exports.useDataStore = useDataStore;
2484
+ //# sourceMappingURL=index.cjs.map
2485
+ //# sourceMappingURL=index.cjs.map