@startsimpli/ui 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +10 -32
  2. package/dist/chunk-6SXB6YDH.mjs +0 -62
  3. package/dist/chunk-6SXB6YDH.mjs.map +0 -1
  4. package/dist/chunk-FAPOEVMI.mjs +0 -3954
  5. package/dist/chunk-FAPOEVMI.mjs.map +0 -1
  6. package/dist/chunk-HDLZZ7IW.mjs +0 -210
  7. package/dist/chunk-HDLZZ7IW.mjs.map +0 -1
  8. package/dist/chunk-KKNZP334.mjs +0 -1026
  9. package/dist/chunk-KKNZP334.mjs.map +0 -1
  10. package/dist/chunk-NIL3JVRZ.mjs +0 -35
  11. package/dist/chunk-NIL3JVRZ.mjs.map +0 -1
  12. package/dist/components/index.d.mts +0 -472
  13. package/dist/components/index.d.ts +0 -472
  14. package/dist/components/index.js +0 -5148
  15. package/dist/components/index.js.map +0 -1
  16. package/dist/components/index.mjs +0 -6
  17. package/dist/components/index.mjs.map +0 -1
  18. package/dist/components/unified-table/index.d.mts +0 -725
  19. package/dist/components/unified-table/index.d.ts +0 -725
  20. package/dist/components/unified-table/index.js +0 -3999
  21. package/dist/components/unified-table/index.js.map +0 -1
  22. package/dist/components/unified-table/index.mjs +0 -5
  23. package/dist/components/unified-table/index.mjs.map +0 -1
  24. package/dist/index.d.mts +0 -26
  25. package/dist/index.d.ts +0 -26
  26. package/dist/index.js +0 -5448
  27. package/dist/index.js.map +0 -1
  28. package/dist/index.mjs +0 -12
  29. package/dist/index.mjs.map +0 -1
  30. package/dist/theme/index.d.mts +0 -20
  31. package/dist/theme/index.d.ts +0 -20
  32. package/dist/theme/index.js +0 -245
  33. package/dist/theme/index.js.map +0 -1
  34. package/dist/theme/index.mjs +0 -9
  35. package/dist/theme/index.mjs.map +0 -1
  36. package/dist/utils/index.d.mts +0 -38
  37. package/dist/utils/index.d.ts +0 -38
  38. package/dist/utils/index.js +0 -71
  39. package/dist/utils/index.js.map +0 -1
  40. package/dist/utils/index.mjs +0 -4
  41. package/dist/utils/index.mjs.map +0 -1
@@ -1,3954 +0,0 @@
1
- import { cn } from './chunk-6SXB6YDH.mjs';
2
- import * as React5 from 'react';
3
- import { useState, useMemo, useCallback, useRef, useEffect } from 'react';
4
- import { useSearchParams, useRouter, usePathname } from 'next/navigation';
5
- import { Slot } from '@radix-ui/react-slot';
6
- import { cva } from 'class-variance-authority';
7
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import * as LabelPrimitive from '@radix-ui/react-label';
9
- import * as SelectPrimitive from '@radix-ui/react-select';
10
- import { ChevronDown, ChevronUp, Check, ChevronRight, Circle, X, Columns3, Loader2, MoreVertical, Download, FileText, FileSpreadsheet, BookmarkPlus, Star, Trash2, Filter, XCircle, MoreHorizontal, ChevronsLeft, ChevronLeft, ChevronsRight, GripVertical, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
11
- import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
12
- import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
13
- import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
14
- import * as XLSX from 'xlsx';
15
- import * as DialogPrimitive from '@radix-ui/react-dialog';
16
-
17
- function useTableState({
18
- initialData = [],
19
- initialPageSize = 25
20
- }) {
21
- const [data, setDataState] = useState(initialData);
22
- const [currentPage, setCurrentPage] = useState(1);
23
- const [pageSize, setPageSizeState] = useState(initialPageSize);
24
- const [searchTerm, setSearchTerm] = useState("");
25
- const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
26
- const [sortBy, setSortBy] = useState(null);
27
- const [sortDirection, setSortDirection] = useState("asc");
28
- const [loading, setLoadingState] = useState(false);
29
- const [loadingRows, setLoadingRowsState] = useState(/* @__PURE__ */ new Set());
30
- const [error, setErrorState] = useState(null);
31
- const [viewMode, setViewModeState] = useState("table");
32
- const totalCount = data.length;
33
- const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
34
- const state = useMemo(() => ({
35
- data,
36
- filteredData: data,
37
- // Will be computed by filters hook
38
- displayedData: data,
39
- // Will be computed by pagination
40
- currentPage,
41
- pageSize,
42
- totalPages,
43
- totalCount,
44
- selectedIds: /* @__PURE__ */ new Set(),
45
- selectAllPages: false,
46
- activeFilters: {},
47
- sortBy,
48
- sortDirection,
49
- searchTerm,
50
- debouncedSearchTerm,
51
- loading,
52
- loadingRows,
53
- error,
54
- viewMode
55
- }), [
56
- data,
57
- currentPage,
58
- pageSize,
59
- totalPages,
60
- totalCount,
61
- sortBy,
62
- sortDirection,
63
- searchTerm,
64
- debouncedSearchTerm,
65
- loading,
66
- loadingRows,
67
- error,
68
- viewMode
69
- ]);
70
- const setData = useCallback((newData) => {
71
- setDataState(newData);
72
- }, []);
73
- const setPage = useCallback((page) => {
74
- setCurrentPage(page);
75
- }, []);
76
- const setPageSize = useCallback((size) => {
77
- setPageSizeState(size);
78
- setCurrentPage(1);
79
- }, []);
80
- const setLoading = useCallback((isLoading) => {
81
- setLoadingState(isLoading);
82
- }, []);
83
- const setLoadingRows = useCallback((ids) => {
84
- setLoadingRowsState(ids);
85
- }, []);
86
- const setError = useCallback((err) => {
87
- setErrorState(err);
88
- }, []);
89
- const setViewMode = useCallback((mode) => {
90
- setViewModeState(mode);
91
- }, []);
92
- return {
93
- state,
94
- setData,
95
- setPage,
96
- setPageSize,
97
- setLoading,
98
- setLoadingRows,
99
- setError,
100
- setViewMode
101
- };
102
- }
103
- function useSelection({
104
- currentPageData,
105
- totalCount,
106
- getRowId,
107
- onSelectionChange,
108
- onSelectAllPages,
109
- externalSelectedIds,
110
- externalSelectAllPages
111
- }) {
112
- const [internalSelectedIds, setInternalSelectedIds] = useState(/* @__PURE__ */ new Set());
113
- const [internalSelectAllPages, setInternalSelectAllPages] = useState(false);
114
- const isControlled = externalSelectedIds !== void 0;
115
- const selectedIds = isControlled ? externalSelectedIds : internalSelectedIds;
116
- const selectAllPages = externalSelectAllPages ?? internalSelectAllPages;
117
- const onSelectionChangeRef = useRef(onSelectionChange);
118
- const onSelectAllPagesRef = useRef(onSelectAllPages);
119
- useEffect(() => {
120
- onSelectionChangeRef.current = onSelectionChange;
121
- onSelectAllPagesRef.current = onSelectAllPages;
122
- }, [onSelectionChange, onSelectAllPages]);
123
- const prevExternalIdsRef = useRef(void 0);
124
- useEffect(() => {
125
- if (externalSelectedIds !== void 0 && externalSelectedIds !== prevExternalIdsRef.current) {
126
- setInternalSelectedIds(new Set(externalSelectedIds));
127
- prevExternalIdsRef.current = externalSelectedIds;
128
- }
129
- }, [externalSelectedIds]);
130
- useEffect(() => {
131
- if (externalSelectAllPages !== void 0) {
132
- setInternalSelectAllPages(externalSelectAllPages);
133
- }
134
- }, [externalSelectAllPages]);
135
- const toggleRow = useCallback((id) => {
136
- setInternalSelectedIds((prev) => {
137
- const next = new Set(prev);
138
- if (next.has(id)) {
139
- next.delete(id);
140
- } else {
141
- next.add(id);
142
- }
143
- onSelectionChangeRef.current?.(next);
144
- return next;
145
- });
146
- setInternalSelectAllPages((prev) => {
147
- if (prev) {
148
- onSelectAllPagesRef.current?.(false);
149
- return false;
150
- }
151
- return prev;
152
- });
153
- }, []);
154
- const toggleAll = useCallback(() => {
155
- const currentPageIds = currentPageData.map(getRowId);
156
- setInternalSelectedIds((prev) => {
157
- const allSelected = currentPageIds.every((id) => prev.has(id));
158
- let next;
159
- if (allSelected) {
160
- next = new Set(prev);
161
- currentPageIds.forEach((id) => next.delete(id));
162
- } else {
163
- next = new Set(prev);
164
- currentPageIds.forEach((id) => next.add(id));
165
- }
166
- onSelectionChangeRef.current?.(next);
167
- return next;
168
- });
169
- setInternalSelectAllPages((prev) => {
170
- if (prev) {
171
- onSelectAllPagesRef.current?.(false);
172
- return false;
173
- }
174
- return prev;
175
- });
176
- }, [currentPageData, getRowId]);
177
- const selectAllPagesToggle = useCallback(() => {
178
- setInternalSelectAllPages((prev) => {
179
- const next = !prev;
180
- onSelectAllPagesRef.current?.(next);
181
- if (next) {
182
- setInternalSelectedIds(/* @__PURE__ */ new Set());
183
- onSelectionChangeRef.current?.(/* @__PURE__ */ new Set());
184
- }
185
- return next;
186
- });
187
- }, []);
188
- const clearSelection = useCallback(() => {
189
- setInternalSelectedIds(/* @__PURE__ */ new Set());
190
- setInternalSelectAllPages(false);
191
- onSelectionChangeRef.current?.(/* @__PURE__ */ new Set());
192
- onSelectAllPagesRef.current?.(false);
193
- }, []);
194
- const getSelectedCount = useCallback(() => {
195
- if (selectAllPages) {
196
- return totalCount;
197
- }
198
- return selectedIds.size;
199
- }, [selectAllPages, selectedIds, totalCount]);
200
- return {
201
- selectedIds,
202
- selectAllPages,
203
- toggleRow,
204
- toggleAll,
205
- selectAllPagesToggle,
206
- clearSelection,
207
- getSelectedCount
208
- };
209
- }
210
- function usePagination({
211
- totalCount,
212
- initialPageSize = 25,
213
- initialPage = 1,
214
- serverSide = false,
215
- onPageChange
216
- }) {
217
- const [currentPage, setCurrentPage] = useState(initialPage);
218
- const [pageSize, setPageSize] = useState(initialPageSize);
219
- const totalPages = useMemo(() => {
220
- return Math.max(1, Math.ceil(totalCount / pageSize));
221
- }, [totalCount, pageSize]);
222
- const canGoNext = useMemo(() => {
223
- return currentPage < totalPages;
224
- }, [currentPage, totalPages]);
225
- const canGoPrevious = useMemo(() => {
226
- return currentPage > 1;
227
- }, [currentPage]);
228
- const goToPage = useCallback((page) => {
229
- const validPage = Math.max(1, Math.min(page, totalPages));
230
- setCurrentPage(validPage);
231
- onPageChange?.(validPage);
232
- }, [totalPages, onPageChange]);
233
- const goToFirstPage = useCallback(() => {
234
- goToPage(1);
235
- }, [goToPage]);
236
- const goToLastPage = useCallback(() => {
237
- goToPage(totalPages);
238
- }, [goToPage, totalPages]);
239
- const goToNextPage = useCallback(() => {
240
- if (canGoNext) {
241
- goToPage(currentPage + 1);
242
- }
243
- }, [canGoNext, currentPage, goToPage]);
244
- const goToPreviousPage = useCallback(() => {
245
- if (canGoPrevious) {
246
- goToPage(currentPage - 1);
247
- }
248
- }, [canGoPrevious, currentPage, goToPage]);
249
- const getPageNumbers = useCallback(() => {
250
- const maxVisible = 7;
251
- if (totalPages <= maxVisible) {
252
- return Array.from({ length: totalPages }, (_, i) => i + 1);
253
- }
254
- const pages = [];
255
- pages.push(1);
256
- if (currentPage > 3) {
257
- pages.push("...");
258
- }
259
- const start = Math.max(2, currentPage - 1);
260
- const end = Math.min(totalPages - 1, currentPage + 1);
261
- for (let i = start; i <= end; i++) {
262
- if (!pages.includes(i)) {
263
- pages.push(i);
264
- }
265
- }
266
- if (currentPage < totalPages - 2) {
267
- pages.push("...");
268
- }
269
- if (!pages.includes(totalPages)) {
270
- pages.push(totalPages);
271
- }
272
- return pages;
273
- }, [currentPage, totalPages]);
274
- const getDisplayRange = useCallback(() => {
275
- const start = (currentPage - 1) * pageSize + 1;
276
- const end = Math.min(currentPage * pageSize, totalCount);
277
- return { start, end };
278
- }, [currentPage, pageSize, totalCount]);
279
- return {
280
- currentPage,
281
- pageSize,
282
- totalPages,
283
- totalCount,
284
- canGoNext,
285
- canGoPrevious,
286
- goToPage,
287
- goToFirstPage,
288
- goToLastPage,
289
- goToNextPage,
290
- goToPreviousPage,
291
- getPageNumbers,
292
- getDisplayRange
293
- };
294
- }
295
- function useFilters({
296
- initialFilters = {},
297
- onChange
298
- }) {
299
- const [filters, setFilters] = useState(initialFilters);
300
- const setFilter = useCallback((key, value) => {
301
- setFilters((prev) => {
302
- const next = { ...prev, [key]: value };
303
- onChange?.(next);
304
- return next;
305
- });
306
- }, [onChange]);
307
- const clearFilter = useCallback((key) => {
308
- setFilters((prev) => {
309
- const next = { ...prev };
310
- delete next[key];
311
- onChange?.(next);
312
- return next;
313
- });
314
- }, [onChange]);
315
- const clearAllFilters = useCallback(() => {
316
- setFilters({});
317
- onChange?.({});
318
- }, [onChange]);
319
- const hasActiveFilters = useCallback(() => {
320
- return Object.keys(filters).length > 0;
321
- }, [filters]);
322
- const getActiveFilterCount = useCallback(() => {
323
- return Object.keys(filters).length;
324
- }, [filters]);
325
- return {
326
- filters,
327
- setFilter,
328
- clearFilter,
329
- clearAllFilters,
330
- hasActiveFilters,
331
- getActiveFilterCount
332
- };
333
- }
334
- var DEFAULT_BREAKPOINTS = {
335
- mobile: 768,
336
- tablet: 1024,
337
- desktop: 1280
338
- };
339
- function useResponsive(breakpoints = DEFAULT_BREAKPOINTS) {
340
- const [viewMode, setViewMode] = useState("desktop");
341
- const [windowWidth, setWindowWidth] = useState(0);
342
- useEffect(() => {
343
- const handleResize = () => {
344
- const width = window.innerWidth;
345
- setWindowWidth(width);
346
- if (width < breakpoints.mobile) {
347
- setViewMode("mobile");
348
- } else if (width < breakpoints.tablet) {
349
- setViewMode("tablet");
350
- } else {
351
- setViewMode("desktop");
352
- }
353
- };
354
- handleResize();
355
- window.addEventListener("resize", handleResize);
356
- return () => window.removeEventListener("resize", handleResize);
357
- }, [breakpoints]);
358
- return {
359
- viewMode,
360
- windowWidth,
361
- isMobile: viewMode === "mobile",
362
- isTablet: viewMode === "tablet",
363
- isDesktop: viewMode === "desktop",
364
- isMobileOrTablet: viewMode === "mobile" || viewMode === "tablet"
365
- };
366
- }
367
- function useColumnVisibility({
368
- defaultVisibility = {},
369
- persistKey,
370
- onVisibilityChange
371
- } = {}) {
372
- const [columnVisibility, setColumnVisibility] = useState(() => {
373
- if (persistKey && typeof window !== "undefined") {
374
- const stored = localStorage.getItem(`column-visibility-${persistKey}`);
375
- if (stored) {
376
- try {
377
- return JSON.parse(stored);
378
- } catch {
379
- return defaultVisibility;
380
- }
381
- }
382
- }
383
- return defaultVisibility;
384
- });
385
- useEffect(() => {
386
- if (persistKey && typeof window !== "undefined") {
387
- localStorage.setItem(`column-visibility-${persistKey}`, JSON.stringify(columnVisibility));
388
- }
389
- onVisibilityChange?.(columnVisibility);
390
- }, [columnVisibility, persistKey, onVisibilityChange]);
391
- const toggleColumn = useCallback((columnId) => {
392
- setColumnVisibility((prev) => ({
393
- ...prev,
394
- [columnId]: !(prev[columnId] ?? true)
395
- }));
396
- }, []);
397
- const showColumn = useCallback((columnId) => {
398
- setColumnVisibility((prev) => ({
399
- ...prev,
400
- [columnId]: true
401
- }));
402
- }, []);
403
- const hideColumn = useCallback((columnId) => {
404
- setColumnVisibility((prev) => ({
405
- ...prev,
406
- [columnId]: false
407
- }));
408
- }, []);
409
- const showAllColumns = useCallback(() => {
410
- setColumnVisibility({});
411
- }, []);
412
- const hideAllColumns = useCallback((exceptColumns = []) => {
413
- setColumnVisibility((prev) => {
414
- const newVisibility = {};
415
- Object.keys(prev).forEach((key) => {
416
- newVisibility[key] = exceptColumns.includes(key);
417
- });
418
- return newVisibility;
419
- });
420
- }, []);
421
- const resetVisibility = useCallback(() => {
422
- setColumnVisibility(defaultVisibility);
423
- }, [defaultVisibility]);
424
- const isColumnVisible = useCallback((columnId) => {
425
- return columnVisibility[columnId] ?? true;
426
- }, [columnVisibility]);
427
- return {
428
- columnVisibility,
429
- setColumnVisibility,
430
- toggleColumn,
431
- showColumn,
432
- hideColumn,
433
- showAllColumns,
434
- hideAllColumns,
435
- resetVisibility,
436
- isColumnVisible
437
- };
438
- }
439
- function useTablePreferences({
440
- tableId,
441
- enabled = true,
442
- defaultColumnVisibility = {},
443
- defaultPageSize = 25,
444
- debounceMs = 500,
445
- apiFetch = fetch
446
- // Default to native fetch if not provided
447
- }) {
448
- const [preferences, setPreferences] = useState(null);
449
- const [isLoading, setIsLoading] = useState(true);
450
- const [error, setError] = useState(null);
451
- const saveTimeoutRef = useRef(null);
452
- const pendingUpdatesRef = useRef({});
453
- useEffect(() => {
454
- if (!enabled) {
455
- setIsLoading(false);
456
- return;
457
- }
458
- const loadPreferences = async () => {
459
- try {
460
- const response = await apiFetch(`/api/v1/table-preferences/?tableId=${encodeURIComponent(tableId)}`);
461
- if (!response.ok) {
462
- throw new Error("Failed to load table preferences");
463
- }
464
- const data = await response.json();
465
- if (data) {
466
- setPreferences(data);
467
- } else {
468
- setPreferences({
469
- columnVisibility: defaultColumnVisibility,
470
- columnOrder: null,
471
- defaultSortColumn: null,
472
- defaultSortDirection: null,
473
- pageSize: defaultPageSize
474
- });
475
- }
476
- } catch (err) {
477
- console.error("Error loading table preferences:", err);
478
- setError(err instanceof Error ? err : new Error("Unknown error"));
479
- setPreferences({
480
- columnVisibility: defaultColumnVisibility,
481
- columnOrder: null,
482
- defaultSortColumn: null,
483
- defaultSortDirection: null,
484
- pageSize: defaultPageSize
485
- });
486
- } finally {
487
- setIsLoading(false);
488
- }
489
- };
490
- loadPreferences();
491
- }, [tableId, enabled, defaultPageSize]);
492
- const savePreferences = useCallback(async (updates) => {
493
- pendingUpdatesRef.current = { ...pendingUpdatesRef.current, ...updates };
494
- if (saveTimeoutRef.current) {
495
- clearTimeout(saveTimeoutRef.current);
496
- }
497
- saveTimeoutRef.current = setTimeout(async () => {
498
- const updatesToSave = { ...pendingUpdatesRef.current };
499
- pendingUpdatesRef.current = {};
500
- try {
501
- const response = await apiFetch("/api/v1/table-preferences/", {
502
- method: "POST",
503
- headers: { "Content-Type": "application/json" },
504
- body: JSON.stringify({
505
- tableId,
506
- ...updatesToSave
507
- })
508
- });
509
- if (!response.ok) {
510
- throw new Error("Failed to save table preferences");
511
- }
512
- } catch (err) {
513
- console.error("Error saving table preferences:", err);
514
- }
515
- }, debounceMs);
516
- }, [tableId, debounceMs]);
517
- useEffect(() => {
518
- return () => {
519
- if (saveTimeoutRef.current) {
520
- clearTimeout(saveTimeoutRef.current);
521
- }
522
- };
523
- }, []);
524
- const updateColumnVisibility = useCallback((visibility) => {
525
- setPreferences((prev) => prev ? { ...prev, columnVisibility: visibility } : null);
526
- savePreferences({ columnVisibility: visibility });
527
- }, [savePreferences]);
528
- const updateColumnOrder = useCallback((order) => {
529
- setPreferences((prev) => prev ? { ...prev, columnOrder: order } : null);
530
- savePreferences({ columnOrder: order });
531
- }, [savePreferences]);
532
- const updateSortPreference = useCallback((sort) => {
533
- setPreferences((prev) => prev ? {
534
- ...prev,
535
- defaultSortColumn: sort.sortBy,
536
- defaultSortDirection: sort.sortDirection
537
- } : null);
538
- savePreferences({
539
- defaultSortColumn: sort.sortBy,
540
- defaultSortDirection: sort.sortDirection
541
- });
542
- }, [savePreferences]);
543
- const updatePageSize = useCallback((size) => {
544
- setPreferences((prev) => prev ? { ...prev, pageSize: size } : null);
545
- savePreferences({ pageSize: size });
546
- }, [savePreferences]);
547
- const resetPreferences = useCallback(async () => {
548
- try {
549
- const response = await apiFetch(`/api/v1/table-preferences/?tableId=${encodeURIComponent(tableId)}`, {
550
- method: "DELETE"
551
- });
552
- if (!response.ok) {
553
- throw new Error("Failed to reset table preferences");
554
- }
555
- setPreferences({
556
- columnVisibility: defaultColumnVisibility,
557
- columnOrder: null,
558
- defaultSortColumn: null,
559
- defaultSortDirection: null,
560
- pageSize: defaultPageSize
561
- });
562
- } catch (err) {
563
- console.error("Error resetting table preferences:", err);
564
- throw err;
565
- }
566
- }, [tableId, defaultColumnVisibility, defaultPageSize]);
567
- return {
568
- preferences,
569
- isLoading,
570
- error,
571
- updateColumnVisibility,
572
- updateColumnOrder,
573
- updateSortPreference,
574
- updatePageSize,
575
- resetPreferences
576
- };
577
- }
578
- function useTableKeyboard({
579
- data,
580
- getRowId,
581
- selectedIds,
582
- onToggleRow,
583
- onToggleAll,
584
- onRowClick,
585
- onDelete,
586
- tableRef,
587
- enabled = true
588
- }) {
589
- const [focusedRowIndex, setFocusedRowIndexState] = useState(-1);
590
- const focusedRowIndexRef = useRef(-1);
591
- const setFocusedRowIndex = useCallback((index) => {
592
- focusedRowIndexRef.current = index;
593
- setFocusedRowIndexState(index);
594
- }, []);
595
- const handleKeyDown = useCallback((event) => {
596
- if (!enabled || data.length === 0) return;
597
- const { key, ctrlKey, metaKey, shiftKey } = event;
598
- const isModifierPressed = ctrlKey || metaKey;
599
- if (key === "a" && isModifierPressed && onToggleAll) {
600
- event.preventDefault();
601
- onToggleAll();
602
- return;
603
- }
604
- if (key === "Delete" && onDelete && selectedIds && selectedIds.size > 0) {
605
- event.preventDefault();
606
- onDelete(selectedIds);
607
- return;
608
- }
609
- if (key === "ArrowDown") {
610
- event.preventDefault();
611
- const nextIndex = Math.min(focusedRowIndexRef.current + 1, data.length - 1);
612
- setFocusedRowIndex(nextIndex);
613
- if (tableRef.current) {
614
- const rowElement = tableRef.current.querySelector(
615
- `[data-row-index="${nextIndex}"]`
616
- );
617
- if (rowElement && typeof rowElement.focus === "function") {
618
- rowElement.focus();
619
- }
620
- }
621
- return;
622
- }
623
- if (key === "ArrowUp") {
624
- event.preventDefault();
625
- const prevIndex = Math.max(focusedRowIndexRef.current - 1, 0);
626
- setFocusedRowIndex(prevIndex);
627
- if (tableRef.current) {
628
- const rowElement = tableRef.current.querySelector(
629
- `[data-row-index="${prevIndex}"]`
630
- );
631
- if (rowElement) {
632
- rowElement.focus();
633
- }
634
- }
635
- return;
636
- }
637
- if (key === "Enter" && focusedRowIndexRef.current >= 0 && onRowClick) {
638
- event.preventDefault();
639
- const row = data[focusedRowIndexRef.current];
640
- if (row) {
641
- onRowClick(row);
642
- }
643
- return;
644
- }
645
- if (key === " " && focusedRowIndexRef.current >= 0 && onToggleRow) {
646
- event.preventDefault();
647
- const row = data[focusedRowIndexRef.current];
648
- if (row) {
649
- const rowId = getRowId(row);
650
- onToggleRow(rowId);
651
- }
652
- return;
653
- }
654
- if (key === "Home") {
655
- event.preventDefault();
656
- setFocusedRowIndex(0);
657
- if (tableRef.current) {
658
- const rowElement = tableRef.current.querySelector(
659
- '[data-row-index="0"]'
660
- );
661
- if (rowElement) {
662
- rowElement.focus();
663
- }
664
- }
665
- return;
666
- }
667
- if (key === "End") {
668
- event.preventDefault();
669
- const lastIndex = data.length - 1;
670
- setFocusedRowIndex(lastIndex);
671
- if (tableRef.current) {
672
- const rowElement = tableRef.current.querySelector(
673
- `[data-row-index="${lastIndex}"]`
674
- );
675
- if (rowElement) {
676
- rowElement.focus();
677
- }
678
- }
679
- return;
680
- }
681
- }, [
682
- enabled,
683
- data,
684
- getRowId,
685
- selectedIds,
686
- onToggleRow,
687
- onToggleAll,
688
- onRowClick,
689
- onDelete,
690
- tableRef,
691
- setFocusedRowIndex
692
- ]);
693
- useEffect(() => {
694
- if (!enabled || !tableRef.current) return;
695
- const handleKeyboardEvent = (event) => {
696
- const syntheticEvent = {
697
- key: event.key,
698
- ctrlKey: event.ctrlKey,
699
- metaKey: event.metaKey,
700
- shiftKey: event.shiftKey,
701
- preventDefault: () => event.preventDefault(),
702
- stopPropagation: () => event.stopPropagation()
703
- };
704
- handleKeyDown(syntheticEvent);
705
- };
706
- const tableElement = tableRef.current;
707
- tableElement.addEventListener("keydown", handleKeyboardEvent);
708
- return () => {
709
- tableElement.removeEventListener("keydown", handleKeyboardEvent);
710
- };
711
- }, [enabled, tableRef, handleKeyDown]);
712
- return {
713
- focusedRowIndex,
714
- setFocusedRowIndex,
715
- handleKeyDown
716
- };
717
- }
718
- function useTableURL({
719
- tableId,
720
- persistToUrl,
721
- debounceMs = 300
722
- }) {
723
- const searchParams = useSearchParams();
724
- const router = useRouter();
725
- const pathname = usePathname();
726
- const debounceTimerRef = useRef(null);
727
- const isApplyingURLRef = useRef(false);
728
- const getParamKey = useCallback((key) => {
729
- return `${tableId}_${key}`;
730
- }, [tableId]);
731
- const getURLState = useCallback(() => {
732
- if (!persistToUrl) {
733
- return {
734
- sortBy: null,
735
- sortDirection: "asc",
736
- filters: {},
737
- page: 1,
738
- search: ""
739
- };
740
- }
741
- const sortBy = searchParams.get(getParamKey("sortBy"));
742
- const sortDirection = searchParams.get(getParamKey("sortDir"));
743
- const page = parseInt(searchParams.get(getParamKey("page")) || "1", 10);
744
- const search = searchParams.get(getParamKey("search")) || "";
745
- const filters = {};
746
- const filterPrefix = getParamKey("filter_");
747
- searchParams.forEach((value, key) => {
748
- if (key.startsWith(filterPrefix)) {
749
- const filterKey = key.substring(filterPrefix.length);
750
- try {
751
- filters[filterKey] = JSON.parse(value);
752
- } catch {
753
- filters[filterKey] = value;
754
- }
755
- }
756
- });
757
- return {
758
- sortBy: sortBy || null,
759
- sortDirection: sortDirection || "asc",
760
- filters,
761
- page: isNaN(page) || page < 1 ? 1 : page,
762
- search
763
- };
764
- }, [searchParams, persistToUrl, getParamKey]);
765
- const updateURL = useCallback((updates) => {
766
- if (!persistToUrl) return;
767
- if (debounceTimerRef.current) {
768
- clearTimeout(debounceTimerRef.current);
769
- }
770
- debounceTimerRef.current = setTimeout(() => {
771
- const newParams = new URLSearchParams(searchParams.toString());
772
- Object.entries(updates).forEach(([key, value]) => {
773
- if (value === null || value === "") {
774
- newParams.delete(key);
775
- } else {
776
- newParams.set(key, value);
777
- }
778
- });
779
- const newParamsString = newParams.toString();
780
- const currentParamsString = searchParams.toString();
781
- if (newParamsString !== currentParamsString) {
782
- isApplyingURLRef.current = true;
783
- const newURL = newParamsString ? `${pathname}?${newParamsString}` : pathname;
784
- router.replace(newURL, { scroll: false });
785
- setTimeout(() => {
786
- isApplyingURLRef.current = false;
787
- }, 50);
788
- }
789
- }, debounceMs);
790
- }, [persistToUrl, searchParams, pathname, router, debounceMs]);
791
- const setSortToURL = useCallback((sort) => {
792
- if (!persistToUrl) return;
793
- updateURL({
794
- [getParamKey("sortBy")]: sort.sortBy,
795
- [getParamKey("sortDir")]: sort.sortBy ? sort.sortDirection : null,
796
- // Reset to page 1 when sorting changes
797
- [getParamKey("page")]: "1"
798
- });
799
- }, [persistToUrl, updateURL, getParamKey]);
800
- const setFiltersToURL = useCallback((filters) => {
801
- if (!persistToUrl) return;
802
- const updates = {};
803
- const filterPrefix = getParamKey("filter_");
804
- searchParams.forEach((_, key) => {
805
- if (key.startsWith(filterPrefix)) {
806
- updates[key] = null;
807
- }
808
- });
809
- Object.entries(filters).forEach(([key, value]) => {
810
- const paramKey = `${filterPrefix}${key}`;
811
- if (value === void 0 || value === null || value === "") {
812
- updates[paramKey] = null;
813
- } else if (typeof value === "object") {
814
- updates[paramKey] = JSON.stringify(value);
815
- } else {
816
- updates[paramKey] = String(value);
817
- }
818
- });
819
- updates[getParamKey("page")] = "1";
820
- updateURL(updates);
821
- }, [persistToUrl, updateURL, getParamKey, searchParams]);
822
- const setPageToURL = useCallback((page) => {
823
- if (!persistToUrl) return;
824
- updateURL({
825
- [getParamKey("page")]: page > 1 ? String(page) : null
826
- // Remove param if page 1
827
- });
828
- }, [persistToUrl, updateURL, getParamKey]);
829
- const setSearchToURL = useCallback((search) => {
830
- if (!persistToUrl) return;
831
- updateURL({
832
- [getParamKey("search")]: search || null,
833
- // Reset to page 1 when search changes
834
- [getParamKey("page")]: "1"
835
- });
836
- }, [persistToUrl, updateURL, getParamKey]);
837
- const clearURLState = useCallback(() => {
838
- if (!persistToUrl) return;
839
- const newParams = new URLSearchParams(searchParams.toString());
840
- const keysToDelete = [];
841
- newParams.forEach((_, key) => {
842
- if (key.startsWith(`${tableId}_`)) {
843
- keysToDelete.push(key);
844
- }
845
- });
846
- keysToDelete.forEach((key) => newParams.delete(key));
847
- const newURL = newParams.toString() ? `${pathname}?${newParams.toString()}` : pathname;
848
- router.replace(newURL, { scroll: false });
849
- }, [persistToUrl, searchParams, pathname, router, tableId]);
850
- const hasURLState = useCallback(() => {
851
- if (!persistToUrl) return false;
852
- let hasState = false;
853
- searchParams.forEach((_, key) => {
854
- if (key.startsWith(`${tableId}_`)) {
855
- hasState = true;
856
- }
857
- });
858
- return hasState;
859
- }, [persistToUrl, searchParams, tableId]);
860
- useEffect(() => {
861
- return () => {
862
- if (debounceTimerRef.current) {
863
- clearTimeout(debounceTimerRef.current);
864
- }
865
- };
866
- }, []);
867
- return {
868
- getURLState,
869
- setSortToURL,
870
- setFiltersToURL,
871
- setPageToURL,
872
- setSearchToURL,
873
- clearURLState,
874
- hasURLState
875
- };
876
- }
877
- function useColumnReorder({
878
- columns,
879
- initialOrder = null,
880
- enabled = true,
881
- onOrderChange
882
- }) {
883
- const defaultOrder = useMemo(() => columns.map((c) => c.id), [columns]);
884
- const [columnOrder, setColumnOrder] = useState(() => {
885
- if (initialOrder && initialOrder.length > 0) {
886
- const validOrder = initialOrder.filter((id) => columns.some((c) => c.id === id));
887
- const newColumns = columns.filter((c) => !initialOrder.includes(c.id)).map((c) => c.id);
888
- return [...validOrder, ...newColumns];
889
- }
890
- return defaultOrder;
891
- });
892
- const orderedColumns = useMemo(() => {
893
- if (!enabled) return columns;
894
- const columnMap = new Map(columns.map((c) => [c.id, c]));
895
- const ordered = [];
896
- for (const id of columnOrder) {
897
- const column = columnMap.get(id);
898
- if (column) {
899
- ordered.push(column);
900
- }
901
- }
902
- for (const column of columns) {
903
- if (!columnOrder.includes(column.id)) {
904
- ordered.push(column);
905
- }
906
- }
907
- return ordered;
908
- }, [columns, columnOrder, enabled]);
909
- const handleDragEnd = useCallback((result) => {
910
- if (!result.destination) return;
911
- if (result.source.index === result.destination.index) return;
912
- const newOrder = Array.from(columnOrder);
913
- const [removed] = newOrder.splice(result.source.index, 1);
914
- newOrder.splice(result.destination.index, 0, removed);
915
- setColumnOrder(newOrder);
916
- onOrderChange?.(newOrder);
917
- }, [columnOrder, onOrderChange]);
918
- const resetOrder = useCallback(() => {
919
- setColumnOrder(defaultOrder);
920
- onOrderChange?.(defaultOrder);
921
- }, [defaultOrder, onOrderChange]);
922
- return {
923
- orderedColumns,
924
- columnOrder,
925
- handleDragEnd,
926
- resetOrder
927
- };
928
- }
929
- function useColumnResize({
930
- enabled = true,
931
- initialWidths = null,
932
- minWidth = 50,
933
- onWidthChange
934
- } = {}) {
935
- const [columnWidths, setColumnWidths] = useState(() => initialWidths ?? {});
936
- const [resizingColumn, setResizingColumn] = useState(null);
937
- const resizeRef = useRef(null);
938
- const minWidthRef = useRef(minWidth);
939
- const onWidthChangeRef = useRef(onWidthChange);
940
- useEffect(() => {
941
- minWidthRef.current = minWidth;
942
- onWidthChangeRef.current = onWidthChange;
943
- }, [minWidth, onWidthChange]);
944
- const getColumnWidth = useCallback((columnId, defaultWidth) => {
945
- if (!enabled) return defaultWidth;
946
- return columnWidths[columnId] ?? defaultWidth;
947
- }, [enabled, columnWidths]);
948
- const startResize = useCallback((columnId, startX, startWidth) => {
949
- if (!enabled) return;
950
- resizeRef.current = { columnId, startX, startWidth };
951
- setResizingColumn(columnId);
952
- }, [enabled]);
953
- const handlePointerMove = useCallback((e) => {
954
- if (!resizeRef.current) return;
955
- const { columnId, startX, startWidth } = resizeRef.current;
956
- const diff = e.clientX - startX;
957
- const newWidth = Math.max(minWidthRef.current, startWidth + diff);
958
- setColumnWidths((prev) => ({ ...prev, [columnId]: newWidth }));
959
- }, []);
960
- const handlePointerUp = useCallback(() => {
961
- if (!resizeRef.current) return;
962
- resizeRef.current = null;
963
- setResizingColumn(null);
964
- setColumnWidths((current) => {
965
- onWidthChangeRef.current?.(current);
966
- return current;
967
- });
968
- }, []);
969
- useEffect(() => {
970
- if (resizingColumn) {
971
- document.addEventListener("pointermove", handlePointerMove);
972
- document.addEventListener("pointerup", handlePointerUp);
973
- document.addEventListener("mousemove", handlePointerMove);
974
- document.addEventListener("mouseup", handlePointerUp);
975
- document.body.style.cursor = "col-resize";
976
- document.body.style.userSelect = "none";
977
- return () => {
978
- document.removeEventListener("pointermove", handlePointerMove);
979
- document.removeEventListener("pointerup", handlePointerUp);
980
- document.removeEventListener("mousemove", handlePointerMove);
981
- document.removeEventListener("mouseup", handlePointerUp);
982
- document.body.style.cursor = "";
983
- document.body.style.userSelect = "";
984
- };
985
- }
986
- }, [resizingColumn, handlePointerMove, handlePointerUp]);
987
- const resetWidths = useCallback(() => {
988
- setColumnWidths({});
989
- onWidthChange?.({});
990
- }, [onWidthChange]);
991
- return {
992
- columnWidths,
993
- getColumnWidth,
994
- startResize,
995
- resetWidths,
996
- isResizing: resizingColumn !== null,
997
- resizingColumn
998
- };
999
- }
1000
- var buttonVariants = cva(
1001
- "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
1002
- {
1003
- variants: {
1004
- variant: {
1005
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
1006
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
1007
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
1008
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
1009
- ghost: "hover:bg-accent hover:text-accent-foreground",
1010
- link: "text-primary underline-offset-4 hover:underline"
1011
- },
1012
- size: {
1013
- default: "h-9 px-4 py-2",
1014
- sm: "h-8 rounded-md px-3 text-xs",
1015
- lg: "h-10 rounded-md px-8",
1016
- icon: "h-9 w-9"
1017
- }
1018
- },
1019
- defaultVariants: {
1020
- variant: "default",
1021
- size: "default"
1022
- }
1023
- }
1024
- );
1025
- var Button = React5.forwardRef(
1026
- ({ className, variant, size, asChild = false, ...props }, ref) => {
1027
- const Comp = asChild ? Slot : "button";
1028
- return /* @__PURE__ */ jsx(
1029
- Comp,
1030
- {
1031
- className: cn(buttonVariants({ variant, size, className })),
1032
- ref,
1033
- ...props
1034
- }
1035
- );
1036
- }
1037
- );
1038
- Button.displayName = "Button";
1039
- var badgeVariants = cva(
1040
- "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
1041
- {
1042
- variants: {
1043
- variant: {
1044
- default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
1045
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
1046
- destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
1047
- outline: "text-foreground"
1048
- }
1049
- },
1050
- defaultVariants: {
1051
- variant: "default"
1052
- }
1053
- }
1054
- );
1055
- function Badge({ className, variant, ...props }) {
1056
- return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
1057
- }
1058
- var Input = React5.forwardRef(
1059
- ({ className, type, ...props }, ref) => {
1060
- return /* @__PURE__ */ jsx(
1061
- "input",
1062
- {
1063
- type,
1064
- className: cn(
1065
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
1066
- className
1067
- ),
1068
- ref,
1069
- ...props
1070
- }
1071
- );
1072
- }
1073
- );
1074
- Input.displayName = "Input";
1075
- var labelVariants = cva(
1076
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
1077
- );
1078
- var Label = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1079
- LabelPrimitive.Root,
1080
- {
1081
- ref,
1082
- className: cn(labelVariants(), className),
1083
- ...props
1084
- }
1085
- ));
1086
- Label.displayName = LabelPrimitive.Root.displayName;
1087
- var Select = SelectPrimitive.Root;
1088
- var SelectGroup = SelectPrimitive.Group;
1089
- var SelectValue = SelectPrimitive.Value;
1090
- var SelectTrigger = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1091
- SelectPrimitive.Trigger,
1092
- {
1093
- ref,
1094
- className: cn(
1095
- "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
1096
- className
1097
- ),
1098
- ...props,
1099
- children: [
1100
- children,
1101
- /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
1102
- ]
1103
- }
1104
- ));
1105
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
1106
- var SelectScrollUpButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1107
- SelectPrimitive.ScrollUpButton,
1108
- {
1109
- ref,
1110
- className: cn(
1111
- "flex cursor-default items-center justify-center py-1",
1112
- className
1113
- ),
1114
- ...props,
1115
- children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
1116
- }
1117
- ));
1118
- SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
1119
- var SelectScrollDownButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1120
- SelectPrimitive.ScrollDownButton,
1121
- {
1122
- ref,
1123
- className: cn(
1124
- "flex cursor-default items-center justify-center py-1",
1125
- className
1126
- ),
1127
- ...props,
1128
- children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
1129
- }
1130
- ));
1131
- SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
1132
- var SelectContent = React5.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
1133
- SelectPrimitive.Content,
1134
- {
1135
- ref,
1136
- className: cn(
1137
- "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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",
1138
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
1139
- className
1140
- ),
1141
- position,
1142
- ...props,
1143
- children: [
1144
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
1145
- /* @__PURE__ */ jsx(
1146
- SelectPrimitive.Viewport,
1147
- {
1148
- className: cn(
1149
- "p-1",
1150
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
1151
- ),
1152
- children
1153
- }
1154
- ),
1155
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
1156
- ]
1157
- }
1158
- ) }));
1159
- SelectContent.displayName = SelectPrimitive.Content.displayName;
1160
- var SelectLabel = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1161
- SelectPrimitive.Label,
1162
- {
1163
- ref,
1164
- className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
1165
- ...props
1166
- }
1167
- ));
1168
- SelectLabel.displayName = SelectPrimitive.Label.displayName;
1169
- var SelectItem = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1170
- SelectPrimitive.Item,
1171
- {
1172
- ref,
1173
- className: cn(
1174
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1175
- className
1176
- ),
1177
- ...props,
1178
- children: [
1179
- /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
1180
- /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
1181
- ]
1182
- }
1183
- ));
1184
- SelectItem.displayName = SelectPrimitive.Item.displayName;
1185
- var SelectSeparator = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1186
- SelectPrimitive.Separator,
1187
- {
1188
- ref,
1189
- className: cn("-mx-1 my-1 h-px bg-muted", className),
1190
- ...props
1191
- }
1192
- ));
1193
- SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
1194
- function TableFilters({ config, filters, className, defaultExpanded = false }) {
1195
- const [isExpanded, setIsExpanded] = useState(defaultExpanded);
1196
- const [expandedSections, setExpandedSections] = useState(/* @__PURE__ */ new Set());
1197
- const toggleSection = (sectionId) => {
1198
- setExpandedSections((prev) => {
1199
- const next = new Set(prev);
1200
- if (next.has(sectionId)) {
1201
- next.delete(sectionId);
1202
- } else {
1203
- next.add(sectionId);
1204
- }
1205
- return next;
1206
- });
1207
- };
1208
- const renderChipsFilter = (section) => {
1209
- if (section.type !== "chips" || !section.filters) return null;
1210
- return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-3", children: section.filters.map((filter) => {
1211
- if (!filter.options) return null;
1212
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1213
- /* @__PURE__ */ jsxs(Label, { className: "text-sm text-muted-foreground whitespace-nowrap", children: [
1214
- filter.label,
1215
- ":"
1216
- ] }),
1217
- /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: filter.options.map((option) => {
1218
- const isActive = filters.filters[filter.id] === option;
1219
- return /* @__PURE__ */ jsx(
1220
- Button,
1221
- {
1222
- variant: isActive ? "default" : "outline",
1223
- size: "sm",
1224
- onClick: () => {
1225
- if (isActive) {
1226
- filters.clearFilter(filter.id);
1227
- } else {
1228
- filters.setFilter(filter.id, option);
1229
- }
1230
- },
1231
- className: "h-7 px-2 text-xs",
1232
- children: option
1233
- },
1234
- option
1235
- );
1236
- }) })
1237
- ] }, filter.id);
1238
- }) });
1239
- };
1240
- const renderBucketsFilter = (section) => {
1241
- if (section.type !== "buckets" || !section.buckets) return null;
1242
- const currentValue = filters.filters[section.id] || "all";
1243
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
1244
- /* @__PURE__ */ jsxs(Label, { htmlFor: section.id, className: "text-xs text-muted-foreground whitespace-nowrap", children: [
1245
- section.label,
1246
- ":"
1247
- ] }),
1248
- /* @__PURE__ */ jsxs(Select, { value: currentValue, onValueChange: (value) => filters.setFilter(section.id, value), children: [
1249
- /* @__PURE__ */ jsx(SelectTrigger, { id: section.id, className: "w-32 h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select range" }) }),
1250
- /* @__PURE__ */ jsx(SelectContent, { children: section.buckets.map((bucket, idx) => /* @__PURE__ */ jsx(SelectItem, { value: bucket.label.toLowerCase().replace(/\s+/g, "-"), children: bucket.label }, idx)) })
1251
- ] })
1252
- ] });
1253
- };
1254
- const renderDropdownFilter = (filter) => {
1255
- if (!filter.options) return null;
1256
- const currentValue = filters.filters[filter.id] || "all";
1257
- const filteredOptions = filter.options.filter((option) => option.toLowerCase() !== "all");
1258
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
1259
- /* @__PURE__ */ jsxs(Label, { htmlFor: filter.id, className: "text-xs text-muted-foreground whitespace-nowrap", children: [
1260
- filter.label,
1261
- ":"
1262
- ] }),
1263
- /* @__PURE__ */ jsxs(Select, { value: currentValue, onValueChange: (value) => filters.setFilter(filter.id, value), children: [
1264
- /* @__PURE__ */ jsx(SelectTrigger, { id: filter.id, className: "w-28 h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "All" }) }),
1265
- /* @__PURE__ */ jsxs(SelectContent, { children: [
1266
- /* @__PURE__ */ jsx(SelectItem, { value: "all", children: "All" }),
1267
- filteredOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: option === "null" ? "Not Set" : option }, option))
1268
- ] })
1269
- ] })
1270
- ] }, filter.id);
1271
- };
1272
- const renderSearchFilter = (filter) => {
1273
- const currentValue = filters.filters[filter.id] || "";
1274
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
1275
- /* @__PURE__ */ jsxs(Label, { htmlFor: filter.id, className: "text-xs text-muted-foreground whitespace-nowrap", children: [
1276
- filter.label,
1277
- ":"
1278
- ] }),
1279
- /* @__PURE__ */ jsx(
1280
- Input,
1281
- {
1282
- id: filter.id,
1283
- type: "text",
1284
- placeholder: filter.label,
1285
- value: currentValue,
1286
- onChange: (e) => filters.setFilter(filter.id, e.target.value),
1287
- className: "w-32 h-8 text-xs"
1288
- }
1289
- )
1290
- ] }, filter.id);
1291
- };
1292
- const renderCollapsibleSection = (section) => {
1293
- if (section.type !== "collapsible" || !section.filters) return null;
1294
- const isExpanded2 = expandedSections.has(section.id);
1295
- return /* @__PURE__ */ jsxs("div", { className: "border rounded-lg p-3", children: [
1296
- /* @__PURE__ */ jsxs(
1297
- Button,
1298
- {
1299
- variant: "ghost",
1300
- size: "sm",
1301
- onClick: () => toggleSection(section.id),
1302
- className: "w-full justify-between -ml-2",
1303
- children: [
1304
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: section.label }),
1305
- isExpanded2 ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
1306
- ]
1307
- }
1308
- ),
1309
- isExpanded2 && /* @__PURE__ */ jsx("div", { className: "mt-3 space-y-3", children: section.filters.map((filter) => {
1310
- if (filter.type === "dropdown") {
1311
- return renderDropdownFilter(filter);
1312
- }
1313
- if (filter.type === "search") {
1314
- return renderSearchFilter(filter);
1315
- }
1316
- return null;
1317
- }) })
1318
- ] }, section.id);
1319
- };
1320
- const renderCustomFilter = useCallback((section) => {
1321
- if (section.type !== "custom" || !section.customFilter) return null;
1322
- const currentValue = filters.filters[section.id] ?? section.customFilter.defaultValue;
1323
- const onChange = (value) => {
1324
- if (value === section.customFilter?.defaultValue || value === void 0 || value === null) {
1325
- filters.clearFilter(section.id);
1326
- } else {
1327
- filters.setFilter(section.id, value);
1328
- }
1329
- };
1330
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
1331
- section.label && /* @__PURE__ */ jsxs(Label, { className: "text-xs text-muted-foreground whitespace-nowrap", children: [
1332
- section.label,
1333
- ":"
1334
- ] }),
1335
- section.customFilter.render(currentValue, onChange)
1336
- ] }, section.id);
1337
- }, [filters]);
1338
- const hasActiveFilters = filters.hasActiveFilters();
1339
- const activeFilterCount = filters.getActiveFilterCount();
1340
- return /* @__PURE__ */ jsxs("div", { className: cn("rounded-lg border bg-muted/30", className), children: [
1341
- /* @__PURE__ */ jsxs(
1342
- "button",
1343
- {
1344
- type: "button",
1345
- onClick: () => setIsExpanded(!isExpanded),
1346
- className: "w-full flex items-center justify-between px-3 py-2 hover:bg-muted/50 transition-colors",
1347
- children: [
1348
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1349
- /* @__PURE__ */ jsx(Filter, { className: "h-4 w-4 text-muted-foreground" }),
1350
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Filters" }),
1351
- hasActiveFilters && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: activeFilterCount })
1352
- ] }),
1353
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1354
- hasActiveFilters && /* @__PURE__ */ jsxs(
1355
- Button,
1356
- {
1357
- variant: "ghost",
1358
- size: "sm",
1359
- onClick: (e) => {
1360
- e.stopPropagation();
1361
- filters.clearAllFilters();
1362
- },
1363
- className: "gap-1 h-6 px-2 text-xs",
1364
- children: [
1365
- /* @__PURE__ */ jsx(XCircle, { className: "h-3 w-3" }),
1366
- "Clear"
1367
- ]
1368
- }
1369
- ),
1370
- isExpanded ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
1371
- ] })
1372
- ]
1373
- }
1374
- ),
1375
- isExpanded && /* @__PURE__ */ jsx("div", { className: "px-3 pb-3 pt-1", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-2", children: config.sections.map((section) => {
1376
- if (section.type === "chips") {
1377
- return /* @__PURE__ */ jsx("div", { children: renderChipsFilter(section) }, section.id);
1378
- }
1379
- if (section.type === "buckets") {
1380
- return /* @__PURE__ */ jsx("div", { children: renderBucketsFilter(section) }, section.id);
1381
- }
1382
- if (section.type === "collapsible") {
1383
- return renderCollapsibleSection(section);
1384
- }
1385
- if (section.type === "dropdown" && section.filters) {
1386
- return section.filters.map((filter) => /* @__PURE__ */ jsx("div", { children: renderDropdownFilter(filter) }, filter.id));
1387
- }
1388
- if (section.type === "search" && section.filters) {
1389
- return section.filters.map((filter) => /* @__PURE__ */ jsx("div", { children: renderSearchFilter(filter) }, filter.id));
1390
- }
1391
- if (section.type === "custom") {
1392
- return /* @__PURE__ */ jsx("div", { children: renderCustomFilter(section) }, section.id);
1393
- }
1394
- return null;
1395
- }) }) })
1396
- ] });
1397
- }
1398
- function BulkActionBar({
1399
- selectedCount,
1400
- selectAllPages,
1401
- totalCount,
1402
- bulkActions,
1403
- onClearSelection,
1404
- onExecuteAction,
1405
- className
1406
- }) {
1407
- const isVisible = selectedCount > 0;
1408
- const getVariantClassName = (variant) => {
1409
- switch (variant) {
1410
- case "gradient-purple":
1411
- return "bg-gradient-to-r from-purple-600 to-blue-600 text-white hover:from-purple-700 hover:to-blue-700";
1412
- case "gradient-green":
1413
- return "bg-gradient-to-r from-green-600 to-teal-600 text-white hover:from-green-700 hover:to-teal-700";
1414
- case "gradient-indigo":
1415
- return "bg-gradient-to-r from-indigo-600 to-purple-600 text-white hover:from-indigo-700 hover:to-purple-700";
1416
- case "gradient-orange":
1417
- return "bg-gradient-to-r from-orange-600 to-red-600 text-white hover:from-orange-700 hover:to-red-700";
1418
- case "gradient-blue":
1419
- return "bg-gradient-to-r from-blue-600 to-indigo-600 text-white hover:from-blue-700 hover:to-indigo-700";
1420
- default:
1421
- return "";
1422
- }
1423
- };
1424
- return /* @__PURE__ */ jsx(
1425
- "div",
1426
- {
1427
- className: cn(
1428
- "transition-all duration-200 ease-in-out overflow-hidden",
1429
- isVisible ? "opacity-100 max-h-20" : "opacity-0 max-h-0 pointer-events-none",
1430
- className
1431
- ),
1432
- "aria-hidden": !isVisible,
1433
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-3 bg-muted/50 rounded-lg border", children: [
1434
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 text-sm font-medium", children: selectAllPages ? /* @__PURE__ */ jsxs("span", { className: "text-blue-600", children: [
1435
- "All ",
1436
- /* @__PURE__ */ jsx("span", { className: "font-semibold", children: totalCount }),
1437
- " items selected"
1438
- ] }) : /* @__PURE__ */ jsx("span", { children: `${selectedCount} ${selectedCount === 1 ? "item" : "items"} selected` }) }),
1439
- /* @__PURE__ */ jsx("div", { className: "h-6 w-px bg-border mx-1" }),
1440
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 flex-1", children: bulkActions.map((action) => {
1441
- const Icon2 = action.icon;
1442
- const isDisabled = action.disabled?.(/* @__PURE__ */ new Set()) || false;
1443
- const exceedsMaxSelection = !!(action.maxSelection && selectedCount > action.maxSelection);
1444
- return /* @__PURE__ */ jsxs(
1445
- Button,
1446
- {
1447
- size: "sm",
1448
- variant: action.variant === "default" ? "default" : "default",
1449
- className: cn(
1450
- getVariantClassName(action.variant),
1451
- "gap-2"
1452
- ),
1453
- onClick: () => onExecuteAction(action),
1454
- disabled: isDisabled || exceedsMaxSelection,
1455
- title: exceedsMaxSelection ? `Maximum ${action.maxSelection} items can be selected for this action` : action.label,
1456
- children: [
1457
- /* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4" }),
1458
- action.label,
1459
- selectAllPages && totalCount > 0 && ` (${totalCount})`
1460
- ]
1461
- },
1462
- action.id
1463
- );
1464
- }) }),
1465
- /* @__PURE__ */ jsxs(
1466
- Button,
1467
- {
1468
- variant: "outline",
1469
- size: "sm",
1470
- onClick: onClearSelection,
1471
- className: "gap-2",
1472
- children: [
1473
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
1474
- "Clear"
1475
- ]
1476
- }
1477
- )
1478
- ] })
1479
- }
1480
- );
1481
- }
1482
- var Table = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx(
1483
- "table",
1484
- {
1485
- ref,
1486
- className: cn("w-full caption-bottom text-sm", className),
1487
- ...props
1488
- }
1489
- ) }));
1490
- Table.displayName = "Table";
1491
- var TableHeader = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
1492
- TableHeader.displayName = "TableHeader";
1493
- var TableBody = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1494
- "tbody",
1495
- {
1496
- ref,
1497
- className: cn("[&_tr:last-child]:border-0", className),
1498
- ...props
1499
- }
1500
- ));
1501
- TableBody.displayName = "TableBody";
1502
- var TableFooter = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1503
- "tfoot",
1504
- {
1505
- ref,
1506
- className: cn(
1507
- "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
1508
- className
1509
- ),
1510
- ...props
1511
- }
1512
- ));
1513
- TableFooter.displayName = "TableFooter";
1514
- var TableRow = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1515
- "tr",
1516
- {
1517
- ref,
1518
- className: cn(
1519
- "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
1520
- className
1521
- ),
1522
- ...props
1523
- }
1524
- ));
1525
- TableRow.displayName = "TableRow";
1526
- var TableHead = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1527
- "th",
1528
- {
1529
- ref,
1530
- className: cn(
1531
- "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
1532
- className
1533
- ),
1534
- ...props
1535
- }
1536
- ));
1537
- TableHead.displayName = "TableHead";
1538
- var TableCell = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1539
- "td",
1540
- {
1541
- ref,
1542
- className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className),
1543
- ...props
1544
- }
1545
- ));
1546
- TableCell.displayName = "TableCell";
1547
- var TableCaption = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1548
- "caption",
1549
- {
1550
- ref,
1551
- className: cn("mt-4 text-sm text-muted-foreground", className),
1552
- ...props
1553
- }
1554
- ));
1555
- TableCaption.displayName = "TableCaption";
1556
- var Checkbox = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1557
- CheckboxPrimitive.Root,
1558
- {
1559
- ref,
1560
- className: cn(
1561
- "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
1562
- className
1563
- ),
1564
- ...props,
1565
- children: /* @__PURE__ */ jsx(
1566
- CheckboxPrimitive.Indicator,
1567
- {
1568
- className: cn("flex items-center justify-center text-current"),
1569
- children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })
1570
- }
1571
- )
1572
- }
1573
- ));
1574
- Checkbox.displayName = CheckboxPrimitive.Root.displayName;
1575
- var DropdownMenu = DropdownMenuPrimitive.Root;
1576
- var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
1577
- var DropdownMenuGroup = DropdownMenuPrimitive.Group;
1578
- var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
1579
- var DropdownMenuSub = DropdownMenuPrimitive.Sub;
1580
- var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
1581
- var DropdownMenuSubTrigger = React5.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1582
- DropdownMenuPrimitive.SubTrigger,
1583
- {
1584
- ref,
1585
- className: cn(
1586
- "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
1587
- inset && "pl-8",
1588
- className
1589
- ),
1590
- ...props,
1591
- children: [
1592
- children,
1593
- /* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto h-4 w-4" })
1594
- ]
1595
- }
1596
- ));
1597
- DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
1598
- var DropdownMenuSubContent = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1599
- DropdownMenuPrimitive.SubContent,
1600
- {
1601
- ref,
1602
- className: cn(
1603
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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",
1604
- className
1605
- ),
1606
- ...props
1607
- }
1608
- ));
1609
- DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
1610
- var DropdownMenuContent = React5.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(
1611
- DropdownMenuPrimitive.Content,
1612
- {
1613
- ref,
1614
- sideOffset,
1615
- className: cn(
1616
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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",
1617
- className
1618
- ),
1619
- ...props
1620
- }
1621
- ) }));
1622
- DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
1623
- var DropdownMenuItem = React5.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
1624
- DropdownMenuPrimitive.Item,
1625
- {
1626
- ref,
1627
- className: cn(
1628
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1629
- inset && "pl-8",
1630
- className
1631
- ),
1632
- ...props
1633
- }
1634
- ));
1635
- DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
1636
- var DropdownMenuCheckboxItem = React5.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(
1637
- DropdownMenuPrimitive.CheckboxItem,
1638
- {
1639
- ref,
1640
- className: cn(
1641
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1642
- className
1643
- ),
1644
- checked,
1645
- ...props,
1646
- children: [
1647
- /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
1648
- children
1649
- ]
1650
- }
1651
- ));
1652
- DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
1653
- var DropdownMenuRadioItem = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1654
- DropdownMenuPrimitive.RadioItem,
1655
- {
1656
- ref,
1657
- className: cn(
1658
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1659
- className
1660
- ),
1661
- ...props,
1662
- children: [
1663
- /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Circle, { className: "h-2 w-2 fill-current" }) }) }),
1664
- children
1665
- ]
1666
- }
1667
- ));
1668
- DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
1669
- var DropdownMenuLabel = React5.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
1670
- DropdownMenuPrimitive.Label,
1671
- {
1672
- ref,
1673
- className: cn(
1674
- "px-2 py-1.5 text-sm font-semibold",
1675
- inset && "pl-8",
1676
- className
1677
- ),
1678
- ...props
1679
- }
1680
- ));
1681
- DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
1682
- var DropdownMenuSeparator = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1683
- DropdownMenuPrimitive.Separator,
1684
- {
1685
- ref,
1686
- className: cn("-mx-1 my-1 h-px bg-muted", className),
1687
- ...props
1688
- }
1689
- ));
1690
- DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
1691
- var DropdownMenuShortcut = ({
1692
- className,
1693
- ...props
1694
- }) => {
1695
- return /* @__PURE__ */ jsx(
1696
- "span",
1697
- {
1698
- className: cn("ml-auto text-xs tracking-widest opacity-60", className),
1699
- ...props
1700
- }
1701
- );
1702
- };
1703
- DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
1704
- function InlineEditCell({
1705
- value: initialValue,
1706
- columnId,
1707
- rowId,
1708
- editType = "text",
1709
- editOptions = [],
1710
- onSave,
1711
- onCancel,
1712
- validate
1713
- }) {
1714
- const [value, setValue] = useState(initialValue ?? "");
1715
- const [error, setError] = useState(null);
1716
- const [isSaving, setIsSaving] = useState(false);
1717
- const inputRef = useRef(null);
1718
- useEffect(() => {
1719
- if (inputRef.current) {
1720
- inputRef.current.focus();
1721
- inputRef.current.select();
1722
- }
1723
- }, []);
1724
- const handleSave = useCallback(async () => {
1725
- if (validate) {
1726
- const validationError = validate(value);
1727
- if (validationError) {
1728
- setError(validationError);
1729
- return;
1730
- }
1731
- }
1732
- setIsSaving(true);
1733
- setError(null);
1734
- try {
1735
- await onSave(value);
1736
- } catch (err) {
1737
- setError(err instanceof Error ? err.message : "Failed to save");
1738
- setIsSaving(false);
1739
- }
1740
- }, [value, validate, onSave]);
1741
- const handleKeyDown = useCallback((e) => {
1742
- if (e.key === "Enter" && !e.shiftKey) {
1743
- e.preventDefault();
1744
- handleSave();
1745
- } else if (e.key === "Escape") {
1746
- e.preventDefault();
1747
- onCancel();
1748
- }
1749
- }, [handleSave, onCancel]);
1750
- if (editType === "select") {
1751
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1752
- /* @__PURE__ */ jsxs(
1753
- Select,
1754
- {
1755
- value: String(value),
1756
- onValueChange: (newValue) => {
1757
- setValue(newValue);
1758
- onSave(newValue);
1759
- },
1760
- children: [
1761
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
1762
- /* @__PURE__ */ jsx(SelectContent, { children: editOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: option }, option)) })
1763
- ]
1764
- }
1765
- ),
1766
- /* @__PURE__ */ jsx(
1767
- "button",
1768
- {
1769
- onClick: onCancel,
1770
- className: "p-1 hover:bg-muted rounded",
1771
- disabled: isSaving,
1772
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4 text-muted-foreground" })
1773
- }
1774
- )
1775
- ] });
1776
- }
1777
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1778
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1779
- /* @__PURE__ */ jsx(
1780
- Input,
1781
- {
1782
- ref: inputRef,
1783
- type: editType === "number" ? "number" : editType === "date" ? "date" : "text",
1784
- value,
1785
- onChange: (e) => {
1786
- setValue(e.target.value);
1787
- setError(null);
1788
- },
1789
- onKeyDown: handleKeyDown,
1790
- className: cn(
1791
- "h-8 w-full",
1792
- error && "border-red-500 focus-visible:ring-red-500"
1793
- ),
1794
- disabled: isSaving
1795
- }
1796
- ),
1797
- /* @__PURE__ */ jsx(
1798
- "button",
1799
- {
1800
- onClick: handleSave,
1801
- className: "p-1 hover:bg-green-100 rounded",
1802
- disabled: isSaving,
1803
- children: isSaving ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) : /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 text-green-600" })
1804
- }
1805
- ),
1806
- /* @__PURE__ */ jsx(
1807
- "button",
1808
- {
1809
- onClick: onCancel,
1810
- className: "p-1 hover:bg-red-100 rounded",
1811
- disabled: isSaving,
1812
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4 text-red-600" })
1813
- }
1814
- )
1815
- ] }),
1816
- error && /* @__PURE__ */ jsx("span", { className: "text-xs text-red-500", children: error })
1817
- ] });
1818
- }
1819
- function DataTableCore({
1820
- data,
1821
- columns,
1822
- getRowId,
1823
- selectionEnabled,
1824
- selectedIds = /* @__PURE__ */ new Set(),
1825
- onToggleRow,
1826
- onToggleAll,
1827
- allRowsSelected,
1828
- renderSelectionCell,
1829
- sortingEnabled,
1830
- sortBy,
1831
- sortDirection,
1832
- onSort,
1833
- columnReorderEnabled,
1834
- onColumnDragEnd,
1835
- columnResizeEnabled,
1836
- columnWidths = {},
1837
- onColumnResizeStart,
1838
- isResizing,
1839
- inlineEditEnabled,
1840
- onCellEdit,
1841
- rowActions,
1842
- onRowClick,
1843
- loadingRows = /* @__PURE__ */ new Set(),
1844
- className
1845
- }) {
1846
- const [editingCell, setEditingCell] = useState(null);
1847
- const [isDraggingColumn, setIsDraggingColumn] = useState(false);
1848
- const handleDragStart = useCallback((_) => {
1849
- setIsDraggingColumn(true);
1850
- }, []);
1851
- const handleDragEnd = useCallback((result) => {
1852
- setIsDraggingColumn(false);
1853
- onColumnDragEnd?.(result);
1854
- }, [onColumnDragEnd]);
1855
- const getEffectiveWidth = (column) => {
1856
- if (columnResizeEnabled && columnWidths[column.id]) {
1857
- return columnWidths[column.id];
1858
- }
1859
- return column.width;
1860
- };
1861
- const handleResizeMouseDown = useCallback((columnId, e) => {
1862
- e.stopPropagation();
1863
- e.preventDefault();
1864
- const th = e.target.closest("th");
1865
- if (th && onColumnResizeStart) {
1866
- onColumnResizeStart(columnId, e.clientX, th.offsetWidth);
1867
- }
1868
- }, [onColumnResizeStart]);
1869
- const renderResizeHandle = (columnId) => {
1870
- if (!columnResizeEnabled) return null;
1871
- return /* @__PURE__ */ jsx(
1872
- "div",
1873
- {
1874
- "data-resize-handle": "true",
1875
- className: "absolute right-0 top-0 h-full w-5 cursor-col-resize z-50 flex items-center justify-center group/resize pointer-events-auto",
1876
- style: { marginRight: "-10px" },
1877
- onMouseDown: (e) => handleResizeMouseDown(columnId, e),
1878
- onPointerDown: (e) => handleResizeMouseDown(columnId, e),
1879
- onDragStart: (e) => e.preventDefault(),
1880
- children: /* @__PURE__ */ jsx("div", { className: "w-[3px] h-full bg-transparent group-hover/resize:bg-primary transition-colors rounded-full" })
1881
- }
1882
- );
1883
- };
1884
- const renderCellContent = (column, row) => {
1885
- if (column.cell) {
1886
- return column.cell(row);
1887
- }
1888
- if (column.accessorFn) {
1889
- return column.accessorFn(row);
1890
- }
1891
- if (column.accessorKey) {
1892
- const value = row[column.accessorKey];
1893
- return value !== null && value !== void 0 ? String(value) : "-";
1894
- }
1895
- return "-";
1896
- };
1897
- const SortIndicator = ({ columnId }) => {
1898
- const isSorted = sortBy === columnId;
1899
- const direction = isSorted ? sortDirection : null;
1900
- if (direction === "asc") {
1901
- return /* @__PURE__ */ jsx(ArrowUp, { className: "ml-2 h-4 w-4" });
1902
- } else if (direction === "desc") {
1903
- return /* @__PURE__ */ jsx(ArrowDown, { className: "ml-2 h-4 w-4" });
1904
- }
1905
- return /* @__PURE__ */ jsx(ArrowUpDown, { className: "ml-2 h-4 w-4 opacity-50" });
1906
- };
1907
- const handleSortClick = useCallback((columnId) => {
1908
- if (isDraggingColumn) return;
1909
- onSort?.(columnId);
1910
- }, [isDraggingColumn, onSort]);
1911
- const renderHeader = (column, inDragContext = false) => {
1912
- if (typeof column.header === "function") {
1913
- return column.header({});
1914
- }
1915
- if (inDragContext) {
1916
- return /* @__PURE__ */ jsx("span", { children: column.header });
1917
- }
1918
- if (sortingEnabled && column.sortable !== false) {
1919
- return /* @__PURE__ */ jsxs(
1920
- Button,
1921
- {
1922
- variant: "ghost",
1923
- onClick: () => handleSortClick(column.id),
1924
- className: "-ml-4 h-8 hover:bg-transparent",
1925
- children: [
1926
- column.header,
1927
- /* @__PURE__ */ jsx(SortIndicator, { columnId: column.id })
1928
- ]
1929
- }
1930
- );
1931
- }
1932
- return column.header;
1933
- };
1934
- const renderColumnHeaders = () => {
1935
- if (columnReorderEnabled && onColumnDragEnd) {
1936
- return /* @__PURE__ */ jsx(DragDropContext, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(Droppable, { droppableId: "columns", direction: "horizontal", children: (provided) => /* @__PURE__ */ jsxs(
1937
- TableRow,
1938
- {
1939
- ref: provided.innerRef,
1940
- ...provided.droppableProps,
1941
- children: [
1942
- selectionEnabled && /* @__PURE__ */ jsx(TableHead, { className: "w-12", children: /* @__PURE__ */ jsx(
1943
- Checkbox,
1944
- {
1945
- checked: allRowsSelected,
1946
- onCheckedChange: onToggleAll,
1947
- "aria-label": "Select all"
1948
- }
1949
- ) }),
1950
- columns.map((column, index) => /* @__PURE__ */ jsx(Draggable, { draggableId: column.id, index, children: (provided2, snapshot) => /* @__PURE__ */ jsxs(
1951
- TableHead,
1952
- {
1953
- ref: provided2.innerRef,
1954
- ...provided2.draggableProps,
1955
- style: {
1956
- ...provided2.draggableProps.style,
1957
- width: getEffectiveWidth(column),
1958
- minWidth: column.minWidth,
1959
- maxWidth: column.maxWidth
1960
- },
1961
- className: cn(
1962
- "relative group overflow-visible",
1963
- snapshot.isDragging && "bg-primary/10 shadow-lg ring-2 ring-primary/20 z-50",
1964
- columnResizeEnabled && "select-none"
1965
- ),
1966
- children: [
1967
- /* @__PURE__ */ jsxs(
1968
- "div",
1969
- {
1970
- ...provided2.dragHandleProps,
1971
- className: cn(
1972
- "flex items-center gap-2 cursor-grab active:cursor-grabbing",
1973
- "hover:bg-muted/50 rounded px-1 -mx-1 py-1 transition-colors",
1974
- columnResizeEnabled && "pr-4"
1975
- ),
1976
- children: [
1977
- /* @__PURE__ */ jsx(GripVertical, { className: "h-4 w-4 text-muted-foreground flex-shrink-0" }),
1978
- /* @__PURE__ */ jsxs(
1979
- "div",
1980
- {
1981
- className: "flex-1 flex items-center",
1982
- onClick: (e) => {
1983
- if (!snapshot.isDragging && sortingEnabled && column.sortable !== false) {
1984
- e.stopPropagation();
1985
- handleSortClick(column.id);
1986
- }
1987
- },
1988
- children: [
1989
- renderHeader(column, true),
1990
- sortingEnabled && column.sortable !== false && /* @__PURE__ */ jsx(SortIndicator, { columnId: column.id })
1991
- ]
1992
- }
1993
- )
1994
- ]
1995
- }
1996
- ),
1997
- renderResizeHandle(column.id)
1998
- ]
1999
- }
2000
- ) }, column.id)),
2001
- provided.placeholder,
2002
- rowActions && rowActions.length > 0 && /* @__PURE__ */ jsx(TableHead, { className: "w-12", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Actions" }) })
2003
- ]
2004
- }
2005
- ) }) }) });
2006
- }
2007
- return /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
2008
- selectionEnabled && /* @__PURE__ */ jsx(TableHead, { className: "w-12", children: /* @__PURE__ */ jsx(
2009
- Checkbox,
2010
- {
2011
- checked: allRowsSelected,
2012
- onCheckedChange: onToggleAll,
2013
- "aria-label": "Select all"
2014
- }
2015
- ) }),
2016
- columns.map((column) => /* @__PURE__ */ jsxs(
2017
- TableHead,
2018
- {
2019
- className: cn("relative group overflow-visible", columnResizeEnabled && "select-none"),
2020
- style: {
2021
- width: getEffectiveWidth(column),
2022
- minWidth: column.minWidth,
2023
- maxWidth: column.maxWidth
2024
- },
2025
- children: [
2026
- renderHeader(column),
2027
- renderResizeHandle(column.id)
2028
- ]
2029
- },
2030
- column.id
2031
- )),
2032
- rowActions && rowActions.length > 0 && /* @__PURE__ */ jsx(TableHead, { className: "w-12", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Actions" }) })
2033
- ] }) });
2034
- };
2035
- return /* @__PURE__ */ jsx("div", { className: cn("rounded-md border", className), children: /* @__PURE__ */ jsxs(Table, { children: [
2036
- renderColumnHeaders(),
2037
- /* @__PURE__ */ jsx(TableBody, { children: data.length === 0 ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
2038
- TableCell,
2039
- {
2040
- colSpan: columns.length + (selectionEnabled ? 1 : 0) + (rowActions ? 1 : 0),
2041
- className: "h-24 text-center",
2042
- children: "No results found."
2043
- }
2044
- ) }) : data.map((row, rowIndex) => {
2045
- const rowId = getRowId(row);
2046
- const isSelected = selectedIds.has(rowId);
2047
- const isLoading = loadingRows.has(rowId);
2048
- return /* @__PURE__ */ jsxs(
2049
- TableRow,
2050
- {
2051
- "data-row-index": rowIndex,
2052
- "data-state": isSelected && "selected",
2053
- onClick: () => onRowClick?.(row),
2054
- tabIndex: 0,
2055
- className: cn(
2056
- onRowClick && "cursor-pointer hover:bg-muted/50",
2057
- isLoading && "opacity-50"
2058
- ),
2059
- children: [
2060
- selectionEnabled && /* @__PURE__ */ jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2061
- /* @__PURE__ */ jsx(
2062
- Checkbox,
2063
- {
2064
- checked: isSelected,
2065
- onCheckedChange: () => onToggleRow?.(rowId),
2066
- "aria-label": "Select row"
2067
- }
2068
- ),
2069
- renderSelectionCell?.(row)
2070
- ] }) }),
2071
- columns.map((column) => {
2072
- const isEditing = editingCell?.rowId === rowId && editingCell?.columnId === column.id;
2073
- const canEdit = inlineEditEnabled && column.editable && onCellEdit;
2074
- const getCellValue = () => {
2075
- if (column.accessorFn) return column.accessorFn(row);
2076
- if (column.accessorKey) {
2077
- const keys = column.accessorKey.split(".");
2078
- return keys.reduce((obj, key) => obj?.[key], row);
2079
- }
2080
- return void 0;
2081
- };
2082
- return /* @__PURE__ */ jsx(
2083
- TableCell,
2084
- {
2085
- style: {
2086
- width: getEffectiveWidth(column),
2087
- minWidth: column.minWidth,
2088
- maxWidth: column.maxWidth
2089
- },
2090
- className: cn(
2091
- canEdit && !isEditing && "cursor-pointer hover:bg-muted/50"
2092
- ),
2093
- onClick: (e) => {
2094
- if (canEdit && !isEditing) {
2095
- e.stopPropagation();
2096
- setEditingCell({ rowId, columnId: column.id });
2097
- }
2098
- },
2099
- children: isEditing ? /* @__PURE__ */ jsx(
2100
- InlineEditCell,
2101
- {
2102
- value: getCellValue(),
2103
- columnId: column.id,
2104
- rowId,
2105
- editType: column.editType,
2106
- editOptions: column.editOptions,
2107
- validate: column.validate ? (v) => column.validate(v, row) : void 0,
2108
- onSave: async (value) => {
2109
- await onCellEdit(rowId, column.id, value, row);
2110
- setEditingCell(null);
2111
- },
2112
- onCancel: () => setEditingCell(null)
2113
- }
2114
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2115
- isLoading && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin inline-block mr-2" }),
2116
- renderCellContent(column, row)
2117
- ] })
2118
- },
2119
- column.id
2120
- );
2121
- }),
2122
- rowActions && rowActions.length > 0 && /* @__PURE__ */ jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2123
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", className: "h-8 w-8 p-0", children: [
2124
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Open menu" }),
2125
- /* @__PURE__ */ jsx(MoreHorizontal, { className: "h-4 w-4" })
2126
- ] }) }),
2127
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
2128
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Actions" }),
2129
- rowActions.map((action, idx) => {
2130
- const isDisabled = action.disabled?.(row) || false;
2131
- return /* @__PURE__ */ jsxs(
2132
- DropdownMenuItem,
2133
- {
2134
- onClick: () => !isDisabled && action.onClick(row),
2135
- disabled: isDisabled,
2136
- className: cn(action.destructive && "text-red-600"),
2137
- children: [
2138
- action.icon && /* @__PURE__ */ jsx(action.icon, { className: "mr-2 h-4 w-4" }),
2139
- action.label
2140
- ]
2141
- },
2142
- action.id
2143
- );
2144
- })
2145
- ] })
2146
- ] }) })
2147
- ]
2148
- },
2149
- rowId
2150
- );
2151
- }) })
2152
- ] }) });
2153
- }
2154
- function TablePagination({
2155
- pagination,
2156
- showSelectAllPrompt,
2157
- onSelectAllPages,
2158
- totalFilteredCount,
2159
- className
2160
- }) {
2161
- const {
2162
- currentPage,
2163
- totalPages,
2164
- totalCount,
2165
- canGoNext,
2166
- canGoPrevious,
2167
- goToPage,
2168
- goToFirstPage,
2169
- goToLastPage,
2170
- goToNextPage,
2171
- goToPreviousPage,
2172
- getPageNumbers,
2173
- getDisplayRange
2174
- } = pagination;
2175
- const { start, end } = getDisplayRange();
2176
- const pageNumbers = getPageNumbers();
2177
- return /* @__PURE__ */ jsxs("div", { className, children: [
2178
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4", children: [
2179
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
2180
- "Showing ",
2181
- /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
2182
- start,
2183
- "-",
2184
- end
2185
- ] }),
2186
- " of",
2187
- " ",
2188
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: totalCount }),
2189
- totalFilteredCount && totalFilteredCount !== totalCount && /* @__PURE__ */ jsx("span", { children: " filtered items" })
2190
- ] }),
2191
- totalPages > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2192
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground hidden sm:inline", children: [
2193
- "Page ",
2194
- currentPage,
2195
- " of ",
2196
- totalPages
2197
- ] }),
2198
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
2199
- /* @__PURE__ */ jsx(
2200
- Button,
2201
- {
2202
- variant: "outline",
2203
- size: "sm",
2204
- onClick: goToFirstPage,
2205
- disabled: !canGoPrevious,
2206
- className: "h-8 w-8 p-0",
2207
- title: "First page",
2208
- children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" })
2209
- }
2210
- ),
2211
- /* @__PURE__ */ jsx(
2212
- Button,
2213
- {
2214
- variant: "outline",
2215
- size: "sm",
2216
- onClick: goToPreviousPage,
2217
- disabled: !canGoPrevious,
2218
- className: "h-8 w-8 p-0",
2219
- title: "Previous page",
2220
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
2221
- }
2222
- ),
2223
- pageNumbers.map((pageNum, idx) => {
2224
- if (pageNum === "...") {
2225
- return /* @__PURE__ */ jsx(
2226
- "span",
2227
- {
2228
- className: "flex items-center px-2 text-muted-foreground",
2229
- children: "\u2022\u2022\u2022"
2230
- },
2231
- `ellipsis-${idx}`
2232
- );
2233
- }
2234
- const page = pageNum;
2235
- return /* @__PURE__ */ jsx(
2236
- Button,
2237
- {
2238
- variant: page === currentPage ? "default" : "outline",
2239
- size: "sm",
2240
- onClick: () => goToPage(page),
2241
- className: "h-8 w-8 p-0",
2242
- children: page
2243
- },
2244
- page
2245
- );
2246
- }),
2247
- /* @__PURE__ */ jsx(
2248
- Button,
2249
- {
2250
- variant: "outline",
2251
- size: "sm",
2252
- onClick: goToNextPage,
2253
- disabled: !canGoNext,
2254
- className: "h-8 w-8 p-0",
2255
- title: "Next page",
2256
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
2257
- }
2258
- ),
2259
- /* @__PURE__ */ jsx(
2260
- Button,
2261
- {
2262
- variant: "outline",
2263
- size: "sm",
2264
- onClick: goToLastPage,
2265
- disabled: !canGoNext,
2266
- className: "h-8 w-8 p-0",
2267
- title: "Last page",
2268
- children: /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" })
2269
- }
2270
- )
2271
- ] })
2272
- ] })
2273
- ] }),
2274
- showSelectAllPrompt && onSelectAllPages && totalFilteredCount && totalFilteredCount > end && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center justify-center p-3 bg-blue-50 border border-blue-200 rounded-lg", children: [
2275
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-blue-900 mr-2", children: [
2276
- "Select all ",
2277
- /* @__PURE__ */ jsx("span", { className: "font-semibold", children: totalFilteredCount }),
2278
- " items?"
2279
- ] }),
2280
- /* @__PURE__ */ jsxs(
2281
- Button,
2282
- {
2283
- size: "sm",
2284
- variant: "link",
2285
- onClick: onSelectAllPages,
2286
- className: "text-blue-700 hover:text-blue-900 font-semibold p-0 h-auto",
2287
- children: [
2288
- "Select all ",
2289
- totalFilteredCount,
2290
- " items"
2291
- ]
2292
- }
2293
- )
2294
- ] })
2295
- ] });
2296
- }
2297
- function getColumnValue(row, column) {
2298
- if (column.accessorFn) {
2299
- return column.accessorFn(row);
2300
- }
2301
- if (column.accessorKey) {
2302
- const keys = column.accessorKey.split(".");
2303
- let value = row;
2304
- for (const key of keys) {
2305
- value = value?.[key];
2306
- }
2307
- return value;
2308
- }
2309
- return "";
2310
- }
2311
- function formatValueForExport(value) {
2312
- if (value == null) {
2313
- return "";
2314
- }
2315
- if (value instanceof Date) {
2316
- return value.toISOString();
2317
- }
2318
- if (typeof value === "object") {
2319
- return JSON.stringify(value);
2320
- }
2321
- if (typeof value === "boolean") {
2322
- return value ? "Yes" : "No";
2323
- }
2324
- return String(value);
2325
- }
2326
- function getColumnHeader(column) {
2327
- if (typeof column.header === "string") {
2328
- return column.header;
2329
- }
2330
- return column.id;
2331
- }
2332
- function prepareExportData(data, columns, includeHeaders = true) {
2333
- const rows = [];
2334
- if (includeHeaders) {
2335
- rows.push(columns.map(getColumnHeader));
2336
- }
2337
- for (const row of data) {
2338
- const rowData = [];
2339
- for (const column of columns) {
2340
- const value = getColumnValue(row, column);
2341
- rowData.push(formatValueForExport(value));
2342
- }
2343
- rows.push(rowData);
2344
- }
2345
- return rows;
2346
- }
2347
- function arrayToCSV(data) {
2348
- return data.map(
2349
- (row) => row.map((cell) => {
2350
- const needsQuotes = /[,"\n\r]/.test(cell);
2351
- if (needsQuotes) {
2352
- return `"${cell.replace(/"/g, '""')}"`;
2353
- }
2354
- return cell;
2355
- }).join(",")
2356
- ).join("\n");
2357
- }
2358
- function downloadFile(content, filename, mimeType) {
2359
- const blob = content instanceof Blob ? content : new Blob([content], { type: mimeType });
2360
- const url = URL.createObjectURL(blob);
2361
- const link = document.createElement("a");
2362
- link.href = url;
2363
- link.download = filename;
2364
- document.body.appendChild(link);
2365
- link.click();
2366
- document.body.removeChild(link);
2367
- URL.revokeObjectURL(url);
2368
- }
2369
- function exportToCSV(data, columns, filename, includeHeaders = true) {
2370
- if (!data || data.length === 0) {
2371
- throw new Error("No data to export");
2372
- }
2373
- if (!columns || columns.length === 0) {
2374
- throw new Error("No columns specified for export");
2375
- }
2376
- const exportData2 = prepareExportData(data, columns, includeHeaders);
2377
- const csv = arrayToCSV(exportData2);
2378
- downloadFile(csv, `${filename}.csv`, "text/csv;charset=utf-8;");
2379
- }
2380
- function exportToExcel(data, columns, filename, includeHeaders = true) {
2381
- if (!data || data.length === 0) {
2382
- throw new Error("No data to export");
2383
- }
2384
- if (!columns || columns.length === 0) {
2385
- throw new Error("No columns specified for export");
2386
- }
2387
- const exportData2 = prepareExportData(data, columns, includeHeaders);
2388
- const ws = XLSX.utils.aoa_to_sheet(exportData2);
2389
- const wb = XLSX.utils.book_new();
2390
- XLSX.utils.book_append_sheet(wb, ws, "Data");
2391
- const columnWidths = columns.map((column, idx) => {
2392
- const header = getColumnHeader(column);
2393
- const maxWidth = exportData2.reduce((max, row) => {
2394
- const cellValue = row[idx] || "";
2395
- return Math.max(max, cellValue.length);
2396
- }, header.length);
2397
- return { wch: Math.min(maxWidth + 2, 50) };
2398
- });
2399
- ws["!cols"] = columnWidths;
2400
- const excelBuffer = XLSX.write(wb, { bookType: "xlsx", type: "array" });
2401
- const blob = new Blob([excelBuffer], {
2402
- type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
2403
- });
2404
- downloadFile(blob, `${filename}.xlsx`, blob.type);
2405
- }
2406
- function exportData(data, columns, options) {
2407
- const { format, filename, includeHeaders = true } = options;
2408
- switch (format) {
2409
- case "csv":
2410
- exportToCSV(data, columns, filename, includeHeaders);
2411
- break;
2412
- case "excel":
2413
- exportToExcel(data, columns, filename, includeHeaders);
2414
- break;
2415
- default:
2416
- throw new Error(`Unsupported export format: ${format}`);
2417
- }
2418
- }
2419
- function generateExportFilename(baseFilename) {
2420
- const now = /* @__PURE__ */ new Date();
2421
- const timestamp = now.toISOString().replace(/[:.]/g, "-").slice(0, -5);
2422
- return `${baseFilename}_${timestamp}`;
2423
- }
2424
- function ExportButton({
2425
- data,
2426
- filteredData,
2427
- selectedData,
2428
- columns,
2429
- baseFilename = "export",
2430
- disabled = false,
2431
- className,
2432
- showProgress = true,
2433
- onExportStart,
2434
- onExportComplete,
2435
- onExportError
2436
- }) {
2437
- const [isExporting, setIsExporting] = useState(false);
2438
- const [progress, setProgress] = useState(0);
2439
- const hasFilteredData = filteredData && filteredData.length > 0 && filteredData.length < data.length;
2440
- const hasSelectedData = selectedData && selectedData.length > 0;
2441
- const handleExport = useCallback(
2442
- async (format, scope) => {
2443
- setIsExporting(true);
2444
- setProgress(0);
2445
- try {
2446
- onExportStart?.(format, scope);
2447
- let dataToExport;
2448
- let scopeLabel;
2449
- switch (scope) {
2450
- case "selected":
2451
- if (!selectedData || selectedData.length === 0) {
2452
- throw new Error("No rows selected");
2453
- }
2454
- dataToExport = selectedData;
2455
- scopeLabel = "selected";
2456
- break;
2457
- case "filtered":
2458
- if (!filteredData || filteredData.length === 0) {
2459
- throw new Error("No filtered data available");
2460
- }
2461
- dataToExport = filteredData;
2462
- scopeLabel = "filtered";
2463
- break;
2464
- case "all":
2465
- default:
2466
- dataToExport = data;
2467
- scopeLabel = "all";
2468
- break;
2469
- }
2470
- if (showProgress && dataToExport.length > 100) {
2471
- setProgress(25);
2472
- await new Promise((resolve) => setTimeout(resolve, 100));
2473
- setProgress(50);
2474
- }
2475
- const filename = `${baseFilename}_${scopeLabel}_${generateExportFilename("")}`.replace(
2476
- /__/g,
2477
- "_"
2478
- );
2479
- if (format === "csv") {
2480
- exportToCSV(dataToExport, columns, filename);
2481
- } else {
2482
- exportToExcel(dataToExport, columns, filename);
2483
- }
2484
- if (showProgress) {
2485
- setProgress(100);
2486
- }
2487
- onExportComplete?.(format, scope, dataToExport.length);
2488
- } catch (error) {
2489
- console.error("Export failed:", error);
2490
- const errorObj = error instanceof Error ? error : new Error("Export failed");
2491
- onExportError?.(errorObj);
2492
- } finally {
2493
- if (showProgress) {
2494
- setTimeout(() => {
2495
- setIsExporting(false);
2496
- setProgress(0);
2497
- }, 500);
2498
- } else {
2499
- setIsExporting(false);
2500
- setProgress(0);
2501
- }
2502
- }
2503
- },
2504
- [
2505
- data,
2506
- filteredData,
2507
- selectedData,
2508
- columns,
2509
- baseFilename,
2510
- showProgress,
2511
- onExportStart,
2512
- onExportComplete,
2513
- onExportError
2514
- ]
2515
- );
2516
- const isDisabled = disabled || isExporting || data.length === 0;
2517
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2518
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
2519
- Button,
2520
- {
2521
- variant: "outline",
2522
- size: "sm",
2523
- disabled: isDisabled,
2524
- className,
2525
- "aria-label": "Export data",
2526
- children: isExporting ? /* @__PURE__ */ jsxs(Fragment, { children: [
2527
- /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
2528
- "Exporting",
2529
- showProgress && progress > 0 ? ` ${progress}%` : "..."
2530
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2531
- /* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }),
2532
- "Export"
2533
- ] })
2534
- }
2535
- ) }),
2536
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
2537
- /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground", children: "CSV Format" }),
2538
- /* @__PURE__ */ jsxs(
2539
- DropdownMenuItem,
2540
- {
2541
- onClick: () => handleExport("csv", "all"),
2542
- disabled: isExporting || data.length === 0,
2543
- children: [
2544
- /* @__PURE__ */ jsx(FileText, { className: "mr-2 h-4 w-4" }),
2545
- "Export All (",
2546
- data.length,
2547
- " rows)"
2548
- ]
2549
- }
2550
- ),
2551
- hasFilteredData && /* @__PURE__ */ jsxs(
2552
- DropdownMenuItem,
2553
- {
2554
- onClick: () => handleExport("csv", "filtered"),
2555
- disabled: isExporting,
2556
- children: [
2557
- /* @__PURE__ */ jsx(FileText, { className: "mr-2 h-4 w-4" }),
2558
- "Export Filtered (",
2559
- filteredData?.length || 0,
2560
- " rows)"
2561
- ]
2562
- }
2563
- ),
2564
- hasSelectedData && /* @__PURE__ */ jsxs(
2565
- DropdownMenuItem,
2566
- {
2567
- onClick: () => handleExport("csv", "selected"),
2568
- disabled: isExporting,
2569
- children: [
2570
- /* @__PURE__ */ jsx(FileText, { className: "mr-2 h-4 w-4" }),
2571
- "Export Selected (",
2572
- selectedData?.length || 0,
2573
- " rows)"
2574
- ]
2575
- }
2576
- ),
2577
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
2578
- /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground", children: "Excel Format" }),
2579
- /* @__PURE__ */ jsxs(
2580
- DropdownMenuItem,
2581
- {
2582
- onClick: () => handleExport("excel", "all"),
2583
- disabled: isExporting || data.length === 0,
2584
- children: [
2585
- /* @__PURE__ */ jsx(FileSpreadsheet, { className: "mr-2 h-4 w-4" }),
2586
- "Export All (",
2587
- data.length,
2588
- " rows)"
2589
- ]
2590
- }
2591
- ),
2592
- hasFilteredData && /* @__PURE__ */ jsxs(
2593
- DropdownMenuItem,
2594
- {
2595
- onClick: () => handleExport("excel", "filtered"),
2596
- disabled: isExporting,
2597
- children: [
2598
- /* @__PURE__ */ jsx(FileSpreadsheet, { className: "mr-2 h-4 w-4" }),
2599
- "Export Filtered (",
2600
- filteredData?.length || 0,
2601
- " rows)"
2602
- ]
2603
- }
2604
- ),
2605
- hasSelectedData && /* @__PURE__ */ jsxs(
2606
- DropdownMenuItem,
2607
- {
2608
- onClick: () => handleExport("excel", "selected"),
2609
- disabled: isExporting,
2610
- children: [
2611
- /* @__PURE__ */ jsx(FileSpreadsheet, { className: "mr-2 h-4 w-4" }),
2612
- "Export Selected (",
2613
- selectedData?.length || 0,
2614
- " rows)"
2615
- ]
2616
- }
2617
- )
2618
- ] })
2619
- ] });
2620
- }
2621
- var Dialog = DialogPrimitive.Root;
2622
- var DialogTrigger = DialogPrimitive.Trigger;
2623
- var DialogPortal = DialogPrimitive.Portal;
2624
- var DialogClose = DialogPrimitive.Close;
2625
- var DialogOverlay = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2626
- DialogPrimitive.Overlay,
2627
- {
2628
- ref,
2629
- className: cn(
2630
- "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
2631
- className
2632
- ),
2633
- ...props
2634
- }
2635
- ));
2636
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
2637
- var DialogContent = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
2638
- /* @__PURE__ */ jsx(DialogOverlay, {}),
2639
- /* @__PURE__ */ jsxs(
2640
- DialogPrimitive.Content,
2641
- {
2642
- ref,
2643
- className: cn(
2644
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-4 sm:p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg max-h-[95vh] max-sm:max-h-[90vh] max-sm:m-4 max-sm:w-[calc(100%-2rem)] overflow-hidden",
2645
- className
2646
- ),
2647
- ...props,
2648
- children: [
2649
- children,
2650
- /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-3 top-3 sm:right-4 sm:top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground min-h-[44px] min-w-[44px] flex items-center justify-center", children: [
2651
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
2652
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2653
- ] })
2654
- ]
2655
- }
2656
- )
2657
- ] }));
2658
- DialogContent.displayName = DialogPrimitive.Content.displayName;
2659
- var DialogHeader = ({
2660
- className,
2661
- ...props
2662
- }) => /* @__PURE__ */ jsx(
2663
- "div",
2664
- {
2665
- className: cn(
2666
- "flex flex-col space-y-1.5 text-center sm:text-left",
2667
- className
2668
- ),
2669
- ...props
2670
- }
2671
- );
2672
- DialogHeader.displayName = "DialogHeader";
2673
- var DialogFooter = ({
2674
- className,
2675
- ...props
2676
- }) => /* @__PURE__ */ jsx(
2677
- "div",
2678
- {
2679
- className: cn(
2680
- "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 space-y-2 space-y-reverse sm:space-y-0",
2681
- className
2682
- ),
2683
- ...props
2684
- }
2685
- );
2686
- DialogFooter.displayName = "DialogFooter";
2687
- var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2688
- DialogPrimitive.Title,
2689
- {
2690
- ref,
2691
- className: cn(
2692
- "text-lg font-semibold leading-none tracking-tight",
2693
- className
2694
- ),
2695
- ...props
2696
- }
2697
- ));
2698
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
2699
- var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2700
- DialogPrimitive.Description,
2701
- {
2702
- ref,
2703
- className: cn("text-sm text-muted-foreground", className),
2704
- ...props
2705
- }
2706
- ));
2707
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
2708
- function SavedViewsDropdown({
2709
- views,
2710
- currentViewId,
2711
- onSaveView,
2712
- onUpdateView,
2713
- onDeleteView,
2714
- onLoadView,
2715
- getCurrentViewState
2716
- }) {
2717
- const [isOpen, setIsOpen] = useState(false);
2718
- const [isSaveDialogOpen, setIsSaveDialogOpen] = useState(false);
2719
- const [viewName, setViewName] = useState("");
2720
- const [isDefault, setIsDefault] = useState(false);
2721
- const [isSaving, setIsSaving] = useState(false);
2722
- const [deleteConfirmId, setDeleteConfirmId] = useState(null);
2723
- const currentView = views.find((v) => v.id === currentViewId);
2724
- const handleSaveView = async () => {
2725
- if (!viewName.trim()) return;
2726
- setIsSaving(true);
2727
- try {
2728
- const viewState = getCurrentViewState();
2729
- await onSaveView({
2730
- name: viewName.trim(),
2731
- isDefault,
2732
- ...viewState
2733
- });
2734
- setIsSaveDialogOpen(false);
2735
- setViewName("");
2736
- setIsDefault(false);
2737
- } catch (error) {
2738
- console.error("Failed to save view:", error);
2739
- } finally {
2740
- setIsSaving(false);
2741
- }
2742
- };
2743
- const handleDeleteView = async (viewId) => {
2744
- if (!onDeleteView) return;
2745
- try {
2746
- await onDeleteView(viewId);
2747
- setDeleteConfirmId(null);
2748
- } catch (error) {
2749
- console.error("Failed to delete view:", error);
2750
- }
2751
- };
2752
- const handleSetDefault = async (viewId) => {
2753
- if (!onUpdateView) return;
2754
- try {
2755
- for (const view of views) {
2756
- if (view.isDefault && view.id !== viewId) {
2757
- await onUpdateView(view.id, { isDefault: false });
2758
- }
2759
- }
2760
- await onUpdateView(viewId, { isDefault: true });
2761
- } catch (error) {
2762
- console.error("Failed to set default view:", error);
2763
- }
2764
- };
2765
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2766
- /* @__PURE__ */ jsxs(DropdownMenu, { open: isOpen, onOpenChange: setIsOpen, children: [
2767
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
2768
- /* @__PURE__ */ jsx(BookmarkPlus, { className: "mr-2 h-4 w-4" }),
2769
- currentView ? currentView.name : "Views",
2770
- /* @__PURE__ */ jsx(ChevronDown, { className: "ml-2 h-4 w-4" })
2771
- ] }) }),
2772
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
2773
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Saved Views" }),
2774
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
2775
- views.length === 0 ? /* @__PURE__ */ jsx(DropdownMenuItem, { disabled: true, children: "No saved views" }) : views.map((view) => /* @__PURE__ */ jsxs(
2776
- DropdownMenuItem,
2777
- {
2778
- className: "flex items-center justify-between cursor-pointer",
2779
- onSelect: (e) => {
2780
- e.preventDefault();
2781
- if (deleteConfirmId === view.id) return;
2782
- onLoadView(view.id);
2783
- setIsOpen(false);
2784
- },
2785
- children: [
2786
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2787
- currentViewId === view.id && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 text-primary" }),
2788
- currentViewId !== view.id && /* @__PURE__ */ jsx("div", { className: "w-4" }),
2789
- /* @__PURE__ */ jsx("span", { children: view.name }),
2790
- view.isDefault && /* @__PURE__ */ jsx(Star, { className: "h-3 w-3 text-yellow-500 fill-yellow-500" })
2791
- ] }),
2792
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2793
- onUpdateView && !view.isDefault && /* @__PURE__ */ jsx(
2794
- "button",
2795
- {
2796
- onClick: (e) => {
2797
- e.stopPropagation();
2798
- handleSetDefault(view.id);
2799
- },
2800
- className: "p-1 hover:bg-muted rounded opacity-50 hover:opacity-100",
2801
- title: "Set as default",
2802
- children: /* @__PURE__ */ jsx(Star, { className: "h-3 w-3" })
2803
- }
2804
- ),
2805
- onDeleteView && /* @__PURE__ */ jsx(
2806
- "button",
2807
- {
2808
- onClick: (e) => {
2809
- e.stopPropagation();
2810
- if (deleteConfirmId === view.id) {
2811
- handleDeleteView(view.id);
2812
- } else {
2813
- setDeleteConfirmId(view.id);
2814
- setTimeout(() => setDeleteConfirmId(null), 3e3);
2815
- }
2816
- },
2817
- className: cn(
2818
- "p-1 hover:bg-muted rounded",
2819
- deleteConfirmId === view.id ? "text-red-600 hover:text-red-700" : "opacity-50 hover:opacity-100"
2820
- ),
2821
- title: deleteConfirmId === view.id ? "Click again to confirm" : "Delete view",
2822
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
2823
- }
2824
- )
2825
- ] })
2826
- ]
2827
- },
2828
- view.id
2829
- )),
2830
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
2831
- /* @__PURE__ */ jsxs(
2832
- DropdownMenuItem,
2833
- {
2834
- onSelect: (e) => {
2835
- e.preventDefault();
2836
- setIsSaveDialogOpen(true);
2837
- setIsOpen(false);
2838
- },
2839
- children: [
2840
- /* @__PURE__ */ jsx(BookmarkPlus, { className: "mr-2 h-4 w-4" }),
2841
- "Save Current View"
2842
- ]
2843
- }
2844
- )
2845
- ] })
2846
- ] }),
2847
- /* @__PURE__ */ jsx(Dialog, { open: isSaveDialogOpen, onOpenChange: setIsSaveDialogOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[425px]", children: [
2848
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2849
- /* @__PURE__ */ jsx(DialogTitle, { children: "Save View" }),
2850
- /* @__PURE__ */ jsx(DialogDescription, { children: "Save your current table configuration as a named view." })
2851
- ] }),
2852
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 py-4", children: [
2853
- /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
2854
- /* @__PURE__ */ jsx(Label, { htmlFor: "view-name", children: "View Name" }),
2855
- /* @__PURE__ */ jsx(
2856
- Input,
2857
- {
2858
- id: "view-name",
2859
- value: viewName,
2860
- onChange: (e) => setViewName(e.target.value),
2861
- placeholder: "My Custom View",
2862
- onKeyDown: (e) => {
2863
- if (e.key === "Enter") handleSaveView();
2864
- }
2865
- }
2866
- )
2867
- ] }),
2868
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2869
- /* @__PURE__ */ jsx(
2870
- Checkbox,
2871
- {
2872
- id: "is-default",
2873
- checked: isDefault,
2874
- onCheckedChange: (checked) => setIsDefault(checked === true)
2875
- }
2876
- ),
2877
- /* @__PURE__ */ jsx(Label, { htmlFor: "is-default", className: "cursor-pointer", children: "Set as default view" })
2878
- ] })
2879
- ] }),
2880
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
2881
- /* @__PURE__ */ jsx(
2882
- Button,
2883
- {
2884
- variant: "outline",
2885
- onClick: () => setIsSaveDialogOpen(false),
2886
- disabled: isSaving,
2887
- children: "Cancel"
2888
- }
2889
- ),
2890
- /* @__PURE__ */ jsx(
2891
- Button,
2892
- {
2893
- onClick: handleSaveView,
2894
- disabled: !viewName.trim() || isSaving,
2895
- children: isSaving ? "Saving..." : "Save View"
2896
- }
2897
- )
2898
- ] })
2899
- ] }) })
2900
- ] });
2901
- }
2902
- function StandardTableToolbar({
2903
- searchEnabled,
2904
- searchPlaceholder = "Search...",
2905
- searchValue = "",
2906
- onSearchChange,
2907
- searchInputRef,
2908
- searchAutoFocus,
2909
- columnVisibilityEnabled,
2910
- hideableColumns = [],
2911
- columnVisibility = {},
2912
- onToggleColumnVisibility,
2913
- exportEnabled,
2914
- exportProps,
2915
- exportData: exportData2 = [],
2916
- exportColumns = [],
2917
- savedViewsEnabled,
2918
- savedViews = [],
2919
- currentViewId,
2920
- savedViewsProps,
2921
- className
2922
- }) {
2923
- const hasRightSideButtons = exportEnabled || columnVisibilityEnabled || savedViewsEnabled;
2924
- return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4", className), children: [
2925
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 flex-1", children: searchEnabled ? /* @__PURE__ */ jsx(
2926
- Input,
2927
- {
2928
- type: "text",
2929
- placeholder: searchPlaceholder,
2930
- value: searchValue,
2931
- onChange: (e) => onSearchChange?.(e.target.value),
2932
- ref: searchInputRef,
2933
- autoFocus: searchAutoFocus,
2934
- className: "max-w-xl w-full"
2935
- }
2936
- ) : /* @__PURE__ */ jsx("div", {}) }),
2937
- hasRightSideButtons && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2938
- exportEnabled && exportProps && /* @__PURE__ */ jsx(
2939
- ExportButton,
2940
- {
2941
- data: exportData2,
2942
- columns: exportColumns,
2943
- ...exportProps
2944
- }
2945
- ),
2946
- columnVisibilityEnabled && hideableColumns.length > 0 && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2947
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
2948
- /* @__PURE__ */ jsx(Columns3, { className: "mr-2 h-4 w-4" }),
2949
- "Columns",
2950
- /* @__PURE__ */ jsx(ChevronDown, { className: "ml-2 h-4 w-4" })
2951
- ] }) }),
2952
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "w-48", children: hideableColumns.map((column) => /* @__PURE__ */ jsx(
2953
- DropdownMenuCheckboxItem,
2954
- {
2955
- checked: columnVisibility[column.id] !== false,
2956
- onCheckedChange: () => onToggleColumnVisibility?.(column.id),
2957
- children: typeof column.header === "string" ? column.header : column.id
2958
- },
2959
- column.id
2960
- )) })
2961
- ] }),
2962
- savedViewsEnabled && savedViewsProps && /* @__PURE__ */ jsx(
2963
- SavedViewsDropdown,
2964
- {
2965
- views: savedViews,
2966
- currentViewId,
2967
- ...savedViewsProps
2968
- }
2969
- )
2970
- ] })
2971
- ] });
2972
- }
2973
- function UnifiedTable({
2974
- data,
2975
- columns,
2976
- tableId,
2977
- getRowId,
2978
- pagination: paginationConfig,
2979
- selection: selectionConfig,
2980
- filters: filtersConfig,
2981
- bulkActions = [],
2982
- rowActions = [],
2983
- search: searchConfig,
2984
- sorting: sortingConfig,
2985
- columnVisibility: columnVisibilityConfig,
2986
- columnReorder: columnReorderConfig,
2987
- columnResize: columnResizeConfig,
2988
- inlineEdit: inlineEditConfig,
2989
- savedViews: savedViewsConfig,
2990
- urlPersistence: urlPersistenceConfig,
2991
- export: exportConfig,
2992
- onRowClick,
2993
- mobileConfig,
2994
- loading = false,
2995
- loadingRows = /* @__PURE__ */ new Set(),
2996
- className,
2997
- emptyState,
2998
- errorState
2999
- }) {
3000
- const searchInputRef = useRef(null);
3001
- const shouldRestoreFocusRef = useRef(false);
3002
- const tableRef = useRef(null);
3003
- const urlState = useTableURL({
3004
- tableId,
3005
- persistToUrl: urlPersistenceConfig?.enabled ?? false,
3006
- debounceMs: urlPersistenceConfig?.debounceMs ?? 300
3007
- });
3008
- const initialURLState = useMemo(() => {
3009
- if (!urlPersistenceConfig?.enabled) return null;
3010
- return urlState.getURLState();
3011
- }, []);
3012
- const [columnVisibility, setColumnVisibility] = useState(() => {
3013
- if (columnVisibilityConfig?.persistKey && typeof window !== "undefined") {
3014
- try {
3015
- const stored = localStorage.getItem(`table-columns-${columnVisibilityConfig.persistKey}`);
3016
- if (stored) {
3017
- return JSON.parse(stored);
3018
- }
3019
- } catch {
3020
- }
3021
- }
3022
- const initial = {};
3023
- columns.forEach((col) => {
3024
- if (columnVisibilityConfig?.defaultVisible) {
3025
- initial[col.id] = columnVisibilityConfig.defaultVisible.includes(col.id);
3026
- } else {
3027
- initial[col.id] = true;
3028
- }
3029
- });
3030
- return initial;
3031
- });
3032
- const [localSort, setLocalSort] = useState(() => {
3033
- if (initialURLState) {
3034
- return {
3035
- sortBy: initialURLState.sortBy,
3036
- sortDirection: initialURLState.sortDirection
3037
- };
3038
- }
3039
- return { sortBy: null, sortDirection: "asc" };
3040
- });
3041
- useEffect(() => {
3042
- if (columnVisibilityConfig?.persistKey && typeof window !== "undefined") {
3043
- try {
3044
- localStorage.setItem(
3045
- `table-columns-${columnVisibilityConfig.persistKey}`,
3046
- JSON.stringify(columnVisibility)
3047
- );
3048
- } catch {
3049
- }
3050
- }
3051
- }, [columnVisibility, columnVisibilityConfig?.persistKey]);
3052
- const toggleColumnVisibility = useCallback((columnId) => {
3053
- if (columnVisibilityConfig?.alwaysVisible?.includes(columnId)) {
3054
- return;
3055
- }
3056
- setColumnVisibility((prev) => ({
3057
- ...prev,
3058
- [columnId]: !prev[columnId]
3059
- }));
3060
- }, [columnVisibilityConfig?.alwaysVisible]);
3061
- const visibleColumns = useMemo(() => {
3062
- if (!columnVisibilityConfig?.enabled) {
3063
- return columns;
3064
- }
3065
- return columns.filter((col) => columnVisibility[col.id] !== false);
3066
- }, [columns, columnVisibility, columnVisibilityConfig?.enabled]);
3067
- const hideableColumns = useMemo(() => {
3068
- return columns.filter((col) => {
3069
- if (col.hideable === false) return false;
3070
- if (columnVisibilityConfig?.alwaysVisible?.includes(col.id)) return false;
3071
- return true;
3072
- });
3073
- }, [columns, columnVisibilityConfig?.alwaysVisible]);
3074
- const columnReorder = useColumnReorder({
3075
- columns: visibleColumns,
3076
- initialOrder: columnReorderConfig?.initialOrder,
3077
- enabled: columnReorderConfig?.enabled ?? false,
3078
- onOrderChange: columnReorderConfig?.onOrderChange
3079
- });
3080
- const displayColumns = columnReorderConfig?.enabled ? columnReorder.orderedColumns : visibleColumns;
3081
- const columnResize = useColumnResize({
3082
- enabled: columnResizeConfig?.enabled ?? false,
3083
- initialWidths: columnResizeConfig?.initialWidths,
3084
- minWidth: columnResizeConfig?.minWidth ?? 50,
3085
- onWidthChange: columnResizeConfig?.onWidthChange
3086
- });
3087
- const sortingEnabled = sortingConfig?.enabled ?? true;
3088
- const currentSortBy = sortingConfig?.value?.sortBy ?? localSort.sortBy;
3089
- const currentSortDirection = sortingConfig?.value?.sortDirection ?? localSort.sortDirection;
3090
- const handleSort = useCallback((columnId) => {
3091
- const newDirection = currentSortBy === columnId && currentSortDirection === "asc" ? "desc" : "asc";
3092
- const newSort = { sortBy: columnId, sortDirection: newDirection };
3093
- if (sortingConfig?.onChange) {
3094
- sortingConfig.onChange(newSort);
3095
- } else {
3096
- setLocalSort(newSort);
3097
- }
3098
- urlState.setSortToURL(newSort);
3099
- }, [currentSortBy, currentSortDirection, sortingConfig, urlState]);
3100
- const safeData = useMemo(() => Array.isArray(data) ? data : [], [data]);
3101
- const sortedData = useMemo(() => {
3102
- if (sortingConfig?.serverSide || !currentSortBy) {
3103
- return safeData;
3104
- }
3105
- const column = columns.find((c) => c.id === currentSortBy);
3106
- if (!column) return safeData;
3107
- return [...safeData].sort((a, b) => {
3108
- let valueA;
3109
- let valueB;
3110
- if (column.sortingFn) {
3111
- return currentSortDirection === "asc" ? column.sortingFn(a, b) : column.sortingFn(b, a);
3112
- }
3113
- if (column.accessorFn) {
3114
- valueA = column.accessorFn(a);
3115
- valueB = column.accessorFn(b);
3116
- } else if (column.accessorKey) {
3117
- const keys = column.accessorKey.split(".");
3118
- valueA = keys.reduce((obj, key) => obj?.[key], a);
3119
- valueB = keys.reduce((obj, key) => obj?.[key], b);
3120
- } else {
3121
- return 0;
3122
- }
3123
- if (valueA == null && valueB == null) return 0;
3124
- if (valueA == null) return currentSortDirection === "asc" ? 1 : -1;
3125
- if (valueB == null) return currentSortDirection === "asc" ? -1 : 1;
3126
- if (typeof valueA === "string" && typeof valueB === "string") {
3127
- const comparison2 = valueA.localeCompare(valueB, void 0, { sensitivity: "base" });
3128
- return currentSortDirection === "asc" ? comparison2 : -comparison2;
3129
- }
3130
- if (typeof valueA === "number" && typeof valueB === "number") {
3131
- return currentSortDirection === "asc" ? valueA - valueB : valueB - valueA;
3132
- }
3133
- const comparison = String(valueA).localeCompare(String(valueB));
3134
- return currentSortDirection === "asc" ? comparison : -comparison;
3135
- });
3136
- }, [safeData, columns, currentSortBy, currentSortDirection, sortingConfig?.serverSide]);
3137
- const pagination = usePagination({
3138
- totalCount: paginationConfig?.totalCount || sortedData.length,
3139
- initialPageSize: paginationConfig?.pageSize || 25,
3140
- initialPage: paginationConfig?.currentPage || (initialURLState?.page ?? 1),
3141
- serverSide: paginationConfig?.serverSide || false,
3142
- onPageChange: paginationConfig?.onPageChange
3143
- });
3144
- const selection = useSelection({
3145
- currentPageData: sortedData,
3146
- totalCount: paginationConfig?.totalCount || sortedData.length,
3147
- getRowId,
3148
- onSelectionChange: selectionConfig?.onSelectionChange,
3149
- onSelectAllPages: selectionConfig?.onSelectAllPages,
3150
- // External controlled state
3151
- externalSelectedIds: selectionConfig?.selectedIds,
3152
- externalSelectAllPages: selectionConfig?.selectAllPages
3153
- });
3154
- const filters = useFilters({
3155
- initialFilters: filtersConfig?.value || (initialURLState?.filters ?? {}),
3156
- onChange: filtersConfig?.onChange
3157
- });
3158
- const getCurrentViewState = useCallback(() => {
3159
- return {
3160
- columnVisibility: columnVisibilityConfig?.enabled ? columnVisibility : void 0,
3161
- columnOrder: columnReorderConfig?.enabled ? columnReorder.columnOrder : void 0,
3162
- sortBy: currentSortBy,
3163
- sortDirection: currentSortDirection,
3164
- filters: filtersConfig?.enabled ? filters.filters : void 0,
3165
- pageSize: paginationConfig?.pageSize
3166
- };
3167
- }, [
3168
- columnVisibility,
3169
- columnVisibilityConfig?.enabled,
3170
- columnReorder.columnOrder,
3171
- columnReorderConfig?.enabled,
3172
- currentSortBy,
3173
- currentSortDirection,
3174
- filters.filters,
3175
- filtersConfig?.enabled,
3176
- paginationConfig?.pageSize
3177
- ]);
3178
- useTableKeyboard({
3179
- data: sortedData,
3180
- getRowId,
3181
- selectedIds: selection.selectedIds,
3182
- onToggleRow: selection.toggleRow,
3183
- onToggleAll: selection.toggleAll,
3184
- onRowClick,
3185
- onDelete: bulkActions.find((action) => action.id === "delete")?.onClick ? (ids) => {
3186
- const deleteAction = bulkActions.find((action) => action.id === "delete");
3187
- if (deleteAction) {
3188
- deleteAction.onClick(ids);
3189
- }
3190
- } : void 0,
3191
- tableRef,
3192
- enabled: true
3193
- });
3194
- useEffect(() => {
3195
- if (!searchConfig?.enabled || !searchConfig.preserveFocus) return;
3196
- if (!shouldRestoreFocusRef.current) return;
3197
- if (searchInputRef.current) {
3198
- searchInputRef.current.focus();
3199
- const len = searchConfig.value?.length ?? 0;
3200
- try {
3201
- searchInputRef.current.setSelectionRange(len, len);
3202
- } catch {
3203
- }
3204
- }
3205
- shouldRestoreFocusRef.current = false;
3206
- }, [searchConfig?.enabled, searchConfig?.preserveFocus, searchConfig?.value]);
3207
- useEffect(() => {
3208
- if (!urlPersistenceConfig?.enabled) return;
3209
- if (!paginationConfig?.enabled) return;
3210
- urlState.setPageToURL(pagination.currentPage);
3211
- }, [pagination.currentPage, urlPersistenceConfig?.enabled, paginationConfig?.enabled, urlState]);
3212
- useEffect(() => {
3213
- if (!urlPersistenceConfig?.enabled) return;
3214
- if (!filtersConfig?.enabled) return;
3215
- urlState.setFiltersToURL(filters.filters);
3216
- }, [filters.filters, urlPersistenceConfig?.enabled, filtersConfig?.enabled, urlState]);
3217
- useEffect(() => {
3218
- if (!urlPersistenceConfig?.enabled) return;
3219
- if (!searchConfig?.enabled) return;
3220
- if (!initialURLState?.search) return;
3221
- if (!searchConfig.value && initialURLState.search) {
3222
- searchConfig.onChange(initialURLState.search);
3223
- }
3224
- }, []);
3225
- const allRowsSelected = useMemo(() => {
3226
- if (sortedData.length === 0) return false;
3227
- return sortedData.every((row) => selection.selectedIds.has(getRowId(row)));
3228
- }, [sortedData, selection.selectedIds, getRowId]);
3229
- const selectedDataForExport = useMemo(() => {
3230
- if (!selectionConfig?.enabled || selection.selectedIds.size === 0) {
3231
- return void 0;
3232
- }
3233
- return sortedData.filter((row) => selection.selectedIds.has(getRowId(row)));
3234
- }, [sortedData, selection.selectedIds, getRowId, selectionConfig?.enabled]);
3235
- const handleExecuteBulkAction = async (action) => {
3236
- const selectedIds = selection.selectAllPages ? new Set(sortedData.map(getRowId)) : selection.selectedIds;
3237
- if (action.confirmMessage) {
3238
- const message = action.confirmMessage.replace("{count}", selectedIds.size.toString());
3239
- if (!confirm(message)) {
3240
- return;
3241
- }
3242
- }
3243
- if (action.maxSelection && selectedIds.size > action.maxSelection) {
3244
- alert(`Maximum ${action.maxSelection} items can be selected for this action`);
3245
- return;
3246
- }
3247
- try {
3248
- await action.onClick(selectedIds);
3249
- selection.clearSelection();
3250
- } catch (error) {
3251
- console.error("Bulk action failed:", error);
3252
- }
3253
- };
3254
- if (loading && sortedData.length === 0) {
3255
- return /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-center p-12", className), children: /* @__PURE__ */ jsx(Loader2, { className: "h-8 w-8 animate-spin text-muted-foreground" }) });
3256
- }
3257
- if (!loading && sortedData.length === 0 && emptyState) {
3258
- return /* @__PURE__ */ jsx("div", { className, children: emptyState });
3259
- }
3260
- return /* @__PURE__ */ jsxs("div", { ref: tableRef, className: cn("space-y-4", className), tabIndex: 0, children: [
3261
- /* @__PURE__ */ jsx(
3262
- StandardTableToolbar,
3263
- {
3264
- searchEnabled: searchConfig?.enabled,
3265
- searchPlaceholder: searchConfig?.placeholder,
3266
- searchValue: searchConfig?.value,
3267
- onSearchChange: (value) => {
3268
- if (searchConfig) {
3269
- shouldRestoreFocusRef.current = true;
3270
- searchConfig.onChange(value);
3271
- urlState.setSearchToURL(value);
3272
- }
3273
- },
3274
- searchInputRef,
3275
- searchAutoFocus: searchConfig?.autoFocus,
3276
- columnVisibilityEnabled: columnVisibilityConfig?.enabled,
3277
- hideableColumns,
3278
- columnVisibility,
3279
- onToggleColumnVisibility: toggleColumnVisibility,
3280
- exportEnabled: exportConfig?.enabled,
3281
- exportData: sortedData,
3282
- exportColumns: displayColumns,
3283
- exportProps: exportConfig?.enabled ? {
3284
- filteredData: sortedData.length < safeData.length ? sortedData : void 0,
3285
- selectedData: selectedDataForExport,
3286
- baseFilename: exportConfig.baseFilename,
3287
- showProgress: exportConfig.showProgress,
3288
- onExportStart: exportConfig.onExportStart,
3289
- onExportComplete: exportConfig.onExportComplete,
3290
- onExportError: exportConfig.onExportError
3291
- } : void 0,
3292
- savedViewsEnabled: savedViewsConfig?.enabled,
3293
- savedViews: savedViewsConfig?.views,
3294
- currentViewId: savedViewsConfig?.currentViewId,
3295
- savedViewsProps: savedViewsConfig?.enabled ? {
3296
- onSaveView: savedViewsConfig.onSaveView,
3297
- onUpdateView: savedViewsConfig.onUpdateView,
3298
- onDeleteView: savedViewsConfig.onDeleteView,
3299
- onLoadView: savedViewsConfig.onLoadView || (() => {
3300
- }),
3301
- getCurrentViewState
3302
- } : void 0
3303
- }
3304
- ),
3305
- filtersConfig?.enabled && filtersConfig.config && /* @__PURE__ */ jsx(TableFilters, { config: filtersConfig.config, filters }),
3306
- selectionConfig?.enabled && bulkActions.length > 0 && /* @__PURE__ */ jsx(
3307
- BulkActionBar,
3308
- {
3309
- selectedCount: selection.getSelectedCount(),
3310
- selectAllPages: selection.selectAllPages,
3311
- totalCount: paginationConfig?.totalCount || sortedData.length,
3312
- bulkActions,
3313
- onClearSelection: selection.clearSelection,
3314
- onExecuteAction: handleExecuteBulkAction
3315
- }
3316
- ),
3317
- selectionConfig?.enabled && selection.getSelectedCount() > 0 && bulkActions.length === 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [
3318
- /* @__PURE__ */ jsx("div", { children: selection.selectAllPages ? /* @__PURE__ */ jsxs("span", { className: "font-medium text-blue-600", children: [
3319
- "All ",
3320
- paginationConfig?.totalCount || sortedData.length,
3321
- " items selected"
3322
- ] }) : /* @__PURE__ */ jsxs("span", { children: [
3323
- selection.getSelectedCount(),
3324
- " item",
3325
- selection.getSelectedCount() === 1 ? "" : "s",
3326
- " selected",
3327
- allRowsSelected ? " on this page" : ""
3328
- ] }) }),
3329
- !selection.selectAllPages && allRowsSelected && paginationConfig && (paginationConfig.totalCount || 0) > sortedData.length && /* @__PURE__ */ jsxs(
3330
- "button",
3331
- {
3332
- onClick: selection.selectAllPagesToggle,
3333
- className: "text-blue-600 hover:text-blue-800 font-medium",
3334
- children: [
3335
- "Select all ",
3336
- paginationConfig.totalCount,
3337
- " items"
3338
- ]
3339
- }
3340
- )
3341
- ] }),
3342
- selectionConfig?.enabled && bulkActions.length > 0 && !selection.selectAllPages && allRowsSelected && paginationConfig && (paginationConfig.totalCount || 0) > sortedData.length && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-2 bg-blue-50 border border-blue-200 rounded-lg", children: /* @__PURE__ */ jsxs(
3343
- "button",
3344
- {
3345
- onClick: selection.selectAllPagesToggle,
3346
- className: "text-blue-600 hover:text-blue-800 font-medium text-sm",
3347
- children: [
3348
- "Select all ",
3349
- paginationConfig.totalCount,
3350
- " items across all pages"
3351
- ]
3352
- }
3353
- ) }),
3354
- /* @__PURE__ */ jsx(
3355
- DataTableCore,
3356
- {
3357
- data: sortedData,
3358
- columns: displayColumns,
3359
- getRowId,
3360
- selectionEnabled: selectionConfig?.enabled,
3361
- selectedIds: selection.selectedIds,
3362
- onToggleRow: selection.toggleRow,
3363
- onToggleAll: selection.toggleAll,
3364
- allRowsSelected,
3365
- renderSelectionCell: selectionConfig?.renderSelectionCell,
3366
- sortingEnabled,
3367
- sortBy: currentSortBy,
3368
- sortDirection: currentSortDirection,
3369
- onSort: handleSort,
3370
- columnReorderEnabled: columnReorderConfig?.enabled,
3371
- onColumnDragEnd: columnReorder.handleDragEnd,
3372
- columnResizeEnabled: columnResizeConfig?.enabled,
3373
- columnWidths: columnResize.columnWidths,
3374
- onColumnResizeStart: columnResize.startResize,
3375
- isResizing: columnResize.isResizing,
3376
- inlineEditEnabled: inlineEditConfig?.enabled,
3377
- onCellEdit: inlineEditConfig?.onSave,
3378
- rowActions,
3379
- onRowClick,
3380
- loadingRows
3381
- }
3382
- ),
3383
- paginationConfig?.enabled && /* @__PURE__ */ jsx(
3384
- TablePagination,
3385
- {
3386
- pagination,
3387
- totalFilteredCount: paginationConfig.totalCount
3388
- }
3389
- )
3390
- ] });
3391
- }
3392
- var Card = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3393
- "div",
3394
- {
3395
- ref,
3396
- className: cn(
3397
- "rounded-xl border bg-card text-card-foreground shadow",
3398
- className
3399
- ),
3400
- ...props
3401
- }
3402
- ));
3403
- Card.displayName = "Card";
3404
- var CardHeader = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props }));
3405
- CardHeader.displayName = "CardHeader";
3406
- var CardTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3407
- "h3",
3408
- {
3409
- ref,
3410
- className: cn("font-semibold leading-none tracking-tight", className),
3411
- ...props
3412
- }
3413
- ));
3414
- CardTitle.displayName = "CardTitle";
3415
- var CardDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3416
- "p",
3417
- {
3418
- ref,
3419
- className: cn("text-sm text-muted-foreground", className),
3420
- ...props
3421
- }
3422
- ));
3423
- CardDescription.displayName = "CardDescription";
3424
- var CardContent = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
3425
- CardContent.displayName = "CardContent";
3426
- var CardFooter = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex items-center p-6 pt-0", className), ...props }));
3427
- CardFooter.displayName = "CardFooter";
3428
- function CardActions({
3429
- actions,
3430
- row,
3431
- className,
3432
- maxVisibleActions = 2
3433
- }) {
3434
- const [isExecuting, setIsExecuting] = useState(null);
3435
- const visibleActions = actions.filter((action) => {
3436
- return !(action.hidden && action.hidden(row));
3437
- });
3438
- if (visibleActions.length === 0) {
3439
- return null;
3440
- }
3441
- const handleActionClick = async (action) => {
3442
- if (action.disabled && action.disabled(row)) {
3443
- return;
3444
- }
3445
- setIsExecuting(action.id);
3446
- try {
3447
- await action.onClick(row);
3448
- } finally {
3449
- setIsExecuting(null);
3450
- }
3451
- };
3452
- const primaryActions = visibleActions.slice(0, maxVisibleActions);
3453
- const overflowActions = visibleActions.slice(maxVisibleActions);
3454
- return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-1", className), children: [
3455
- primaryActions.map((action) => {
3456
- const Icon2 = action.icon;
3457
- const isDisabled = action.disabled ? action.disabled(row) : false;
3458
- const isLoading = isExecuting === action.id;
3459
- return /* @__PURE__ */ jsxs(
3460
- Button,
3461
- {
3462
- variant: action.variant || "ghost",
3463
- size: "sm",
3464
- className: cn(
3465
- "h-9 min-w-[44px]",
3466
- Icon2 && !action.label && "w-9 p-0",
3467
- action.className
3468
- ),
3469
- onClick: (e) => {
3470
- e.stopPropagation();
3471
- handleActionClick(action);
3472
- },
3473
- disabled: isDisabled || isLoading,
3474
- children: [
3475
- Icon2 && /* @__PURE__ */ jsx(Icon2, { className: cn(
3476
- "h-4 w-4",
3477
- action.label && "mr-1.5"
3478
- ) }),
3479
- action.label && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: action.label })
3480
- ]
3481
- },
3482
- action.id
3483
- );
3484
- }),
3485
- overflowActions.length > 0 && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
3486
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
3487
- Button,
3488
- {
3489
- variant: "ghost",
3490
- size: "sm",
3491
- className: "h-9 w-9 p-0",
3492
- onClick: (e) => e.stopPropagation(),
3493
- children: [
3494
- /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" }),
3495
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "More actions" })
3496
- ]
3497
- }
3498
- ) }),
3499
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "w-48", children: overflowActions.map((action, index) => {
3500
- const Icon2 = action.icon;
3501
- const isDisabled = action.disabled ? action.disabled(row) : false;
3502
- const isLoading = isExecuting === action.id;
3503
- return /* @__PURE__ */ jsxs(
3504
- DropdownMenuItem,
3505
- {
3506
- onClick: (e) => {
3507
- e.stopPropagation();
3508
- handleActionClick(action);
3509
- },
3510
- disabled: isDisabled || isLoading,
3511
- className: cn(
3512
- action.variant === "destructive" && "text-destructive focus:text-destructive",
3513
- action.className
3514
- ),
3515
- children: [
3516
- Icon2 && /* @__PURE__ */ jsx(Icon2, { className: "mr-2 h-4 w-4" }),
3517
- /* @__PURE__ */ jsx("span", { children: action.label || "Action" })
3518
- ]
3519
- },
3520
- action.id
3521
- );
3522
- }) })
3523
- ] })
3524
- ] });
3525
- }
3526
- function getNestedValue(obj, path) {
3527
- if (!path || !obj) return obj;
3528
- const keys = path.split(".");
3529
- let value = obj;
3530
- for (const key of keys) {
3531
- if (value === null || value === void 0) return void 0;
3532
- value = value[key];
3533
- }
3534
- return value;
3535
- }
3536
- function renderField(field, row) {
3537
- const value = getNestedValue(row, field.key);
3538
- if (field.render) {
3539
- return field.render(value, row);
3540
- }
3541
- if (value === null || value === void 0) {
3542
- return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" });
3543
- }
3544
- if (typeof value === "boolean") {
3545
- return /* @__PURE__ */ jsx("span", { children: value ? "Yes" : "No" });
3546
- }
3547
- return /* @__PURE__ */ jsx("span", { children: String(value) });
3548
- }
3549
- function Card2({
3550
- row,
3551
- config,
3552
- rowId,
3553
- isLoading = false,
3554
- className
3555
- }) {
3556
- const {
3557
- titleKey,
3558
- titleRender,
3559
- subtitleKey,
3560
- subtitleRender,
3561
- imageKey,
3562
- imageRender,
3563
- primaryFields,
3564
- secondaryFields,
3565
- actions,
3566
- renderCustomContent,
3567
- renderCustomHeader,
3568
- renderCustomFooter,
3569
- showSelection,
3570
- onSelectionChange,
3571
- isSelected,
3572
- onClick,
3573
- headerClassName,
3574
- contentClassName,
3575
- footerClassName
3576
- } = config;
3577
- const handleClick = () => {
3578
- if (onClick && !isLoading) {
3579
- onClick(row);
3580
- }
3581
- };
3582
- const handleSelectionChange = (checked) => {
3583
- if (onSelectionChange && rowId) {
3584
- onSelectionChange(rowId, checked);
3585
- }
3586
- };
3587
- const selected = isSelected ? isSelected(row) : false;
3588
- return /* @__PURE__ */ jsxs(
3589
- Card,
3590
- {
3591
- className: cn(
3592
- "relative transition-all duration-200",
3593
- onClick && !isLoading && "cursor-pointer hover:shadow-md hover:border-primary/50",
3594
- selected && "ring-2 ring-primary border-primary",
3595
- isLoading && "opacity-60 pointer-events-none",
3596
- className,
3597
- config.className
3598
- ),
3599
- onClick: handleClick,
3600
- children: [
3601
- isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background/50 rounded-lg z-10", children: /* @__PURE__ */ jsx(Loader2, { className: "h-6 w-6 animate-spin text-primary" }) }),
3602
- /* @__PURE__ */ jsx(CardHeader, { className: cn("pb-3", headerClassName), children: renderCustomHeader ? renderCustomHeader(row) : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
3603
- showSelection && /* @__PURE__ */ jsx(
3604
- "div",
3605
- {
3606
- className: "flex items-center pt-1",
3607
- onClick: (e) => e.stopPropagation(),
3608
- children: /* @__PURE__ */ jsx(
3609
- Checkbox,
3610
- {
3611
- checked: selected,
3612
- onCheckedChange: handleSelectionChange,
3613
- "aria-label": "Select row",
3614
- className: "h-5 w-5"
3615
- }
3616
- )
3617
- }
3618
- ),
3619
- (imageKey || imageRender) && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: imageRender ? imageRender(row) : /* @__PURE__ */ jsx("div", { className: "h-12 w-12 rounded-full bg-gradient-to-br from-primary/20 to-primary/10 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-lg font-semibold text-primary", children: String(getNestedValue(row, titleKey) || "?").charAt(0).toUpperCase() }) }) }),
3620
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
3621
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
3622
- /* @__PURE__ */ jsx("h3", { className: "font-semibold text-base leading-tight truncate", children: titleRender ? titleRender(row) : getNestedValue(row, titleKey) }),
3623
- subtitleKey && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1 truncate", children: subtitleRender ? subtitleRender(row) : getNestedValue(row, subtitleKey) })
3624
- ] }),
3625
- actions && actions.length > 0 && /* @__PURE__ */ jsx("div", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
3626
- CardActions,
3627
- {
3628
- actions,
3629
- row,
3630
- maxVisibleActions: 2
3631
- }
3632
- ) })
3633
- ] }) })
3634
- ] }) }),
3635
- /* @__PURE__ */ jsx(CardContent, { className: cn("pt-0 space-y-4", contentClassName), children: renderCustomContent ? renderCustomContent(row) : /* @__PURE__ */ jsxs(Fragment, { children: [
3636
- primaryFields && primaryFields.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: primaryFields.map((field) => /* @__PURE__ */ jsxs(
3637
- "div",
3638
- {
3639
- className: cn(
3640
- "flex justify-between items-center gap-2",
3641
- field.className
3642
- ),
3643
- children: [
3644
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-muted-foreground flex-shrink-0", children: [
3645
- field.label || field.key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()),
3646
- ":"
3647
- ] }),
3648
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-right truncate", children: renderField(field, row) })
3649
- ]
3650
- },
3651
- field.key
3652
- )) }),
3653
- secondaryFields && secondaryFields.length > 0 && /* @__PURE__ */ jsx("div", { className: "border-t pt-3", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: secondaryFields.map((field) => /* @__PURE__ */ jsxs(
3654
- "div",
3655
- {
3656
- className: cn("space-y-1", field.className),
3657
- children: [
3658
- /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: field.label || field.key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()) }),
3659
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold truncate", children: renderField(field, row) })
3660
- ]
3661
- },
3662
- field.key
3663
- )) }) })
3664
- ] }) }),
3665
- renderCustomFooter && /* @__PURE__ */ jsx(CardFooter, { className: cn("pt-0", footerClassName), children: renderCustomFooter(row) })
3666
- ]
3667
- }
3668
- );
3669
- }
3670
-
3671
- // src/components/unified-table/components/MobileView/types.ts
3672
- var MOBILE_BREAKPOINT = 768;
3673
- function MobileView({
3674
- data,
3675
- config,
3676
- getRowId,
3677
- loading = false,
3678
- loadingRows,
3679
- emptyState,
3680
- className
3681
- }) {
3682
- const [isMobile, setIsMobile] = useState(false);
3683
- useEffect(() => {
3684
- const checkMobile = () => {
3685
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
3686
- };
3687
- checkMobile();
3688
- window.addEventListener("resize", checkMobile);
3689
- return () => window.removeEventListener("resize", checkMobile);
3690
- }, []);
3691
- if (data.length === 0 && !loading) {
3692
- return /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-center py-12", className), children: emptyState || /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "No data to display" }) }) });
3693
- }
3694
- return /* @__PURE__ */ jsx(
3695
- "div",
3696
- {
3697
- className: cn(
3698
- "space-y-4",
3699
- isMobile && "space-y-3",
3700
- className
3701
- ),
3702
- children: data.map((row, index) => {
3703
- const rowId = getRowId ? getRowId(row) : String(index);
3704
- const isRowLoading = loadingRows?.has(rowId) || false;
3705
- return /* @__PURE__ */ jsx(
3706
- Card2,
3707
- {
3708
- row,
3709
- config,
3710
- rowId,
3711
- isLoading: isRowLoading
3712
- },
3713
- rowId
3714
- );
3715
- })
3716
- }
3717
- );
3718
- }
3719
-
3720
- // src/components/unified-table/utils/renderers.ts
3721
- function createCellRenderer(render) {
3722
- return render;
3723
- }
3724
- function getNestedValue2(obj, path) {
3725
- if (!path || !obj) return obj;
3726
- const keys = path.split(".");
3727
- let value = obj;
3728
- for (const key of keys) {
3729
- if (value === null || value === void 0) return void 0;
3730
- value = value[key];
3731
- }
3732
- return value;
3733
- }
3734
- var commonRenderers = {
3735
- text: createCellRenderer((value) => String(value || "-")),
3736
- number: createCellRenderer((value) => {
3737
- if (value === null || value === void 0) return "-";
3738
- return new Intl.NumberFormat().format(Number(value));
3739
- }),
3740
- currency: createCellRenderer((value, row, options) => {
3741
- if (value === null || value === void 0) return "-";
3742
- return new Intl.NumberFormat(options?.locale || "en-US", {
3743
- style: "currency",
3744
- currency: options?.currency || "USD"
3745
- }).format(Number(value));
3746
- }),
3747
- percentage: createCellRenderer((value) => {
3748
- if (value === null || value === void 0) return "-";
3749
- return `${Number(value).toFixed(2)}%`;
3750
- }),
3751
- date: createCellRenderer((value) => {
3752
- if (!value) return "-";
3753
- const date = new Date(value);
3754
- return date.toLocaleDateString();
3755
- }),
3756
- datetime: createCellRenderer((value) => {
3757
- if (!value) return "-";
3758
- const date = new Date(value);
3759
- return date.toLocaleString();
3760
- }),
3761
- boolean: createCellRenderer((value) => {
3762
- if (value === null || value === void 0) return "-";
3763
- return value ? "Yes" : "No";
3764
- }),
3765
- badge: createCellRenderer((value, row, options) => {
3766
- if (!value) return null;
3767
- return value;
3768
- }),
3769
- truncate: createCellRenderer((value, row, options) => {
3770
- if (!value) return "-";
3771
- const str = String(value);
3772
- const maxLength = options?.maxLength || 50;
3773
- if (str.length <= maxLength) return str;
3774
- return `${str.substring(0, maxLength)}...`;
3775
- }),
3776
- array: createCellRenderer((value, row, options) => {
3777
- if (!Array.isArray(value) || value.length === 0) return "-";
3778
- const maxItems = options?.maxItems || value.length;
3779
- const items = value.slice(0, maxItems);
3780
- const separator = options?.separator || ", ";
3781
- const display = items.join(separator);
3782
- if (value.length > maxItems) {
3783
- return `${display} +${value.length - maxItems} more`;
3784
- }
3785
- return display;
3786
- })
3787
- };
3788
- function combineRenderers(...renderers) {
3789
- return (value, row) => {
3790
- for (const renderer of renderers) {
3791
- const result = renderer(value, row);
3792
- if (result !== null && result !== void 0) {
3793
- return result;
3794
- }
3795
- }
3796
- return value;
3797
- };
3798
- }
3799
-
3800
- // src/components/unified-table/utils/themes.ts
3801
- var defaultTheme = {
3802
- name: "default",
3803
- container: "w-full",
3804
- header: "bg-muted/50",
3805
- headerCell: "px-4 py-3 text-left text-sm font-medium text-muted-foreground",
3806
- body: "",
3807
- row: "border-b transition-colors",
3808
- rowHover: "hover:bg-muted/50",
3809
- rowSelected: "bg-primary/10 border-primary",
3810
- cell: "px-4 py-3 text-sm",
3811
- pagination: "flex items-center justify-between px-4 py-3 border-t",
3812
- emptyState: "flex items-center justify-center py-12 text-muted-foreground",
3813
- mobileCard: "rounded-lg border bg-card shadow-sm",
3814
- mobileCardHeader: "p-4 pb-2",
3815
- mobileCardContent: "p-4 pt-2"
3816
- };
3817
- var compactTheme = {
3818
- ...defaultTheme,
3819
- name: "compact",
3820
- headerCell: "px-2 py-2 text-left text-xs font-medium text-muted-foreground",
3821
- cell: "px-2 py-2 text-xs",
3822
- pagination: "flex items-center justify-between px-2 py-2 border-t",
3823
- mobileCardHeader: "p-3 pb-1",
3824
- mobileCardContent: "p-3 pt-1"
3825
- };
3826
- var spaciousTheme = {
3827
- ...defaultTheme,
3828
- name: "spacious",
3829
- headerCell: "px-6 py-4 text-left text-sm font-medium text-muted-foreground",
3830
- cell: "px-6 py-4 text-sm",
3831
- pagination: "flex items-center justify-between px-6 py-4 border-t",
3832
- mobileCardHeader: "p-6 pb-3",
3833
- mobileCardContent: "p-6 pt-3"
3834
- };
3835
- var minimalTheme = {
3836
- ...defaultTheme,
3837
- name: "minimal",
3838
- header: "",
3839
- headerCell: "px-4 py-3 text-left text-sm font-semibold",
3840
- row: "border-b border-border/50",
3841
- rowHover: "hover:bg-muted/30",
3842
- mobileCard: "rounded-lg border border-border/50 bg-card"
3843
- };
3844
- var themes = {
3845
- default: defaultTheme,
3846
- compact: compactTheme,
3847
- spacious: spaciousTheme,
3848
- minimal: minimalTheme
3849
- };
3850
- function getTheme(themeName = "default") {
3851
- return themes[themeName] || defaultTheme;
3852
- }
3853
- function createCustomTheme(baseTheme = "default", overrides) {
3854
- return {
3855
- ...themes[baseTheme],
3856
- ...overrides,
3857
- name: overrides.name || `custom-${baseTheme}`
3858
- };
3859
- }
3860
-
3861
- // src/components/unified-table/utils/validation.ts
3862
- function validateMobileCardConfig(config) {
3863
- const errors = [];
3864
- const warnings = [];
3865
- if (!config.titleKey && !config.titleRender) {
3866
- errors.push("Mobile card config must have either titleKey or titleRender");
3867
- }
3868
- if (!config.primaryFields || config.primaryFields.length === 0) {
3869
- warnings.push("Mobile card config has no primary fields defined");
3870
- }
3871
- if (config.primaryFields) {
3872
- config.primaryFields.forEach((field, index) => {
3873
- if (!field.key && !field.render) {
3874
- errors.push(`Primary field at index ${index} must have either key or render function`);
3875
- }
3876
- });
3877
- }
3878
- if (config.secondaryFields) {
3879
- config.secondaryFields.forEach((field, index) => {
3880
- if (!field.key && !field.render) {
3881
- errors.push(`Secondary field at index ${index} must have either key or render function`);
3882
- }
3883
- });
3884
- }
3885
- if (config.actions) {
3886
- config.actions.forEach((action, index) => {
3887
- if (!action.id) {
3888
- errors.push(`Action at index ${index} must have an id`);
3889
- }
3890
- if (!action.onClick) {
3891
- errors.push(`Action '${action.id || index}' must have an onClick handler`);
3892
- }
3893
- if (!action.label && !action.icon) {
3894
- warnings.push(`Action '${action.id || index}' should have either a label or icon`);
3895
- }
3896
- });
3897
- }
3898
- if (config.showSelection && !config.onSelectionChange) {
3899
- warnings.push("showSelection is enabled but onSelectionChange handler is not provided");
3900
- }
3901
- return {
3902
- valid: errors.length === 0,
3903
- errors,
3904
- warnings
3905
- };
3906
- }
3907
- function validateColumnConfig(columns) {
3908
- const errors = [];
3909
- const warnings = [];
3910
- if (!columns || columns.length === 0) {
3911
- errors.push("At least one column must be defined");
3912
- return { valid: false, errors, warnings };
3913
- }
3914
- const columnIds = /* @__PURE__ */ new Set();
3915
- columns.forEach((column, index) => {
3916
- if (!column.id && !column.accessorKey) {
3917
- errors.push(`Column at index ${index} must have either id or accessorKey`);
3918
- }
3919
- const columnId = column.id || column.accessorKey;
3920
- if (columnIds.has(columnId)) {
3921
- errors.push(`Duplicate column id: '${columnId}'`);
3922
- }
3923
- columnIds.add(columnId);
3924
- if (!column.header && !column.hideHeader) {
3925
- warnings.push(`Column '${columnId}' has no header defined`);
3926
- }
3927
- if (!column.cell && !column.accessorKey && !column.accessorFn) {
3928
- warnings.push(`Column '${columnId}' has no way to access data (missing cell, accessorKey, or accessorFn)`);
3929
- }
3930
- });
3931
- return {
3932
- valid: errors.length === 0,
3933
- errors,
3934
- warnings
3935
- };
3936
- }
3937
- function logValidationResults(configName, result, throwOnError = false) {
3938
- if (result.errors.length > 0) {
3939
- console.error(`[${configName}] Validation Errors:`, result.errors);
3940
- if (throwOnError) {
3941
- throw new Error(`${configName} validation failed: ${result.errors.join(", ")}`);
3942
- }
3943
- }
3944
- if (result.warnings.length > 0) {
3945
- console.warn(`[${configName}] Validation Warnings:`, result.warnings);
3946
- }
3947
- if (result.valid && result.warnings.length === 0) {
3948
- console.log(`[${configName}] Configuration is valid`);
3949
- }
3950
- }
3951
-
3952
- export { Badge, Button, Card, Card2, CardActions, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Input, Label, MOBILE_BREAKPOINT, MobileView, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, StandardTableToolbar, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, UnifiedTable, badgeVariants, buttonVariants, combineRenderers, commonRenderers, compactTheme, createCellRenderer, createCustomTheme, defaultTheme, exportData, exportToCSV, exportToExcel, generateExportFilename, getNestedValue2 as getNestedValue, getTheme, logValidationResults, minimalTheme, spaciousTheme, themes, useColumnReorder, useColumnResize, useColumnVisibility, useFilters, usePagination, useResponsive, useSelection, useTableKeyboard, useTablePreferences, useTableState, useTableURL, validateColumnConfig, validateMobileCardConfig };
3953
- //# sourceMappingURL=chunk-FAPOEVMI.mjs.map
3954
- //# sourceMappingURL=chunk-FAPOEVMI.mjs.map