@caffeinebounce/ui 0.62.3 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +1 -1
  2. package/dist/blog.js +7 -6
  3. package/dist/blog.mjs +2 -1
  4. package/dist/charts.d.cts +1 -1
  5. package/dist/charts.d.ts +1 -1
  6. package/dist/{chunk-6DVWMJ3H.mjs → chunk-2EFVMX6V.mjs} +3 -3
  7. package/dist/{chunk-6ZJCK6KH.js → chunk-375DECW4.js} +31 -31
  8. package/dist/{chunk-PZRI43EZ.js → chunk-46JYKBRX.js} +0 -43
  9. package/dist/chunk-5D55OVD5.js +75 -0
  10. package/dist/{chunk-JORVSG52.js → chunk-663CFAHS.js} +211 -269
  11. package/dist/chunk-6HKJDV43.mjs +48 -0
  12. package/dist/chunk-6NCRGHV3.mjs +64 -0
  13. package/dist/{chunk-OLAAXLXY.mjs → chunk-6QAKQG57.mjs} +4 -4
  14. package/dist/{chunk-N6AFNFZR.mjs → chunk-7ERVZXIN.mjs} +214 -270
  15. package/dist/chunk-APFEP5B7.mjs +100 -0
  16. package/dist/chunk-C3OADNSG.js +348 -0
  17. package/dist/{chunk-GIJQE3JR.js → chunk-DH5Z3ETN.js} +66 -45
  18. package/dist/chunk-DYWZ23QT.mjs +90 -0
  19. package/dist/{chunk-DTVWRRFN.mjs → chunk-E67IR6HU.mjs} +30 -9
  20. package/dist/{chunk-OVNLC5RD.mjs → chunk-EXQSWHLD.mjs} +6 -6
  21. package/dist/{chunk-ZOA7B32D.js → chunk-F35KL6TX.js} +9 -9
  22. package/dist/{chunk-YSLD2HLP.mjs → chunk-FFJC66KO.mjs} +1 -1
  23. package/dist/{chunk-MPZFPU4X.js → chunk-GMBLPQ3I.js} +3 -3
  24. package/dist/chunk-IJFPPLQW.js +50 -0
  25. package/dist/{chunk-BMFQG6RA.mjs → chunk-KJIX6V32.mjs} +107 -1885
  26. package/dist/chunk-LABHD4PU.js +109 -0
  27. package/dist/{chunk-GAIQJCAZ.js → chunk-LT5VD4KR.js} +149 -1953
  28. package/dist/{chunk-7DHKQODW.mjs → chunk-LTLCIRZY.mjs} +256 -474
  29. package/dist/chunk-N5BHXWR3.mjs +34 -0
  30. package/dist/chunk-OCO3Z5YA.js +37 -0
  31. package/dist/chunk-OIJ7XMDV.mjs +71 -0
  32. package/dist/chunk-ORBH4IBC.mjs +316 -0
  33. package/dist/chunk-OWWOAYIY.js +2652 -0
  34. package/dist/chunk-PFHOXDSW.mjs +10 -0
  35. package/dist/chunk-QRS7DJGS.js +103 -0
  36. package/dist/{chunk-NXPEJ2UR.mjs → chunk-R3X4B4JJ.mjs} +53 -6
  37. package/dist/chunk-RM7ACPTM.mjs +2601 -0
  38. package/dist/chunk-RODSGPBB.mjs +75 -0
  39. package/dist/{chunk-T6ZBA6VJ.js → chunk-UHAV34YX.js} +8 -8
  40. package/dist/{chunk-GUSTCU2W.mjs → chunk-ULANYJ7Y.mjs} +1 -59
  41. package/dist/chunk-ULDE7QSN.js +70 -0
  42. package/dist/chunk-UT36VNVZ.mjs +47 -0
  43. package/dist/chunk-UY3H4PKB.js +71 -0
  44. package/dist/{chunk-KB7UXIQZ.mjs → chunk-VIVWFQG5.mjs} +2 -1
  45. package/dist/{chunk-ESZ7CBA2.js → chunk-VKBJ3SGT.js} +6 -5
  46. package/dist/{chunk-UM7WMXIU.js → chunk-W3YOQ5ZD.js} +264 -505
  47. package/dist/{chunk-GHOFYK2E.js → chunk-XGJL6DLV.js} +55 -5
  48. package/dist/chunk-YRIBFIQ5.js +116 -0
  49. package/dist/chunk-ZWWJ3RRA.js +15 -0
  50. package/dist/data-grid.d.cts +241 -0
  51. package/dist/data-grid.d.ts +241 -0
  52. package/dist/data-grid.js +6486 -0
  53. package/dist/data-grid.mjs +6459 -0
  54. package/dist/data-table-CDQBo-Ve.d.ts +818 -0
  55. package/dist/data-table-jLzt6plY.d.cts +818 -0
  56. package/dist/data-table.d.cts +8 -42
  57. package/dist/data-table.d.ts +8 -42
  58. package/dist/data-table.js +61 -187
  59. package/dist/data-table.mjs +11 -5
  60. package/dist/finance.d.cts +534 -0
  61. package/dist/finance.d.ts +534 -0
  62. package/dist/finance.js +188 -0
  63. package/dist/finance.mjs +15 -0
  64. package/dist/forms.js +9 -6
  65. package/dist/forms.mjs +7 -4
  66. package/dist/{hooks-B2jU1GVT.d.ts → hooks-A55E0fgf.d.ts} +3 -2
  67. package/dist/{hooks-8ORd7T1A.d.cts → hooks-Bys_AZPI.d.cts} +3 -2
  68. package/dist/hooks.d.cts +1 -1
  69. package/dist/hooks.d.ts +1 -1
  70. package/dist/hooks.js +26 -23
  71. package/dist/hooks.mjs +6 -3
  72. package/dist/index.d.cts +536 -665
  73. package/dist/index.d.ts +536 -665
  74. package/dist/index.js +3829 -3408
  75. package/dist/index.mjs +2067 -1767
  76. package/dist/layouts.js +25 -25
  77. package/dist/layouts.mjs +4 -4
  78. package/dist/marketing.js +74 -74
  79. package/dist/marketing.mjs +4 -4
  80. package/dist/navigation.js +28 -28
  81. package/dist/navigation.mjs +6 -6
  82. package/dist/primitives.d.cts +72 -109
  83. package/dist/primitives.d.ts +72 -109
  84. package/dist/primitives.js +78 -72
  85. package/dist/primitives.mjs +8 -6
  86. package/dist/settings.js +9 -9
  87. package/dist/settings.mjs +4 -4
  88. package/package.json +17 -1
  89. package/dist/UnifiedTableControls-CG299y9h.d.ts +0 -1233
  90. package/dist/UnifiedTableControls-CIjRpNhy.d.cts +0 -1233
  91. package/dist/chunk-GMBGWOCR.mjs +0 -49
  92. package/dist/chunk-HBLKVQIQ.mjs +0 -23
  93. package/dist/chunk-HMAMMC72.js +0 -26
  94. package/dist/chunk-MVYRNJA6.js +0 -75
  95. /package/dist/{chunk-RVEFPVZF.mjs → chunk-C57MX3BG.mjs} +0 -0
  96. /package/dist/{chunk-C4UT5V3P.js → chunk-OWHI5VGJ.js} +0 -0
@@ -1,26 +1,49 @@
1
1
  "use client";
2
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from './chunk-5THF4YP6.mjs';
3
- import { Label } from './chunk-KHCC5UKW.mjs';
4
- import { Popover, PopoverTrigger, PopoverContent, useDataTableContext, buildSpreadsheetWorkbookXml, downloadExcelXml, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, getUnitDivisor } from './chunk-7DHKQODW.mjs';
2
+ import { getUnitDivisor } from './chunk-LTLCIRZY.mjs';
3
+ import { dataTableTopperButtonProps, dataTableTopperOpenStateClasses } from './chunk-PFHOXDSW.mjs';
4
+ import { Calendar as Calendar$1, Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from './chunk-ORBH4IBC.mjs';
5
+ import { Popover, PopoverTrigger, PopoverContent } from './chunk-RODSGPBB.mjs';
5
6
  import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-6BUBNZPG.mjs';
7
+ import { Button } from './chunk-VBD4Q5LL.mjs';
6
8
  import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter } from './chunk-FRRS2YZH.mjs';
7
- import { Spinner } from './chunk-HBLKVQIQ.mjs';
8
- import { buttonVariants, Button } from './chunk-VBD4Q5LL.mjs';
9
- import { DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem } from './chunk-OFQLWSUR.mjs';
9
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator } from './chunk-OFQLWSUR.mjs';
10
10
  import { Tooltip, TooltipTrigger, TooltipContent } from './chunk-YKZEKJMZ.mjs';
11
- import { Input } from './chunk-STEP6C4C.mjs';
12
11
  import { cn } from './chunk-IMKLN273.mjs';
12
+ import * as SwitchPrimitive from '@radix-ui/react-switch';
13
13
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
14
- import { ChevronLeft, ChevronRight, Calendar as Calendar$1, Percent, Check, Minus, Plus, ListOrdered, Download, FileText, FileSpreadsheet, ChevronsLeft, ChevronsRight, Settings, ChevronDown, Search, X, Settings2, GripVertical, Eye, EyeOff, LayoutGrid, Star, Trash2, Save, ChevronsUpDown, MoreHorizontal, Filter, CalendarDays, Building2, MapPin, BarChart3, ChevronUp, SlidersHorizontal, PieChartIcon, ChartColumn } from 'lucide-react';
15
- import { DayPicker } from 'react-day-picker';
16
- import * as React12 from 'react';
14
+ import { ChevronLeft, ChevronRight, Calendar, Percent, Check, Minus, Plus, Settings, ChevronsUpDown, MoreHorizontal, Filter, CalendarDays, Building2, MapPin, Download, FileText, FileSpreadsheet, Settings2, BarChart3, ChevronUp, ChevronDown, SlidersHorizontal, PieChartIcon, ChartColumn } from 'lucide-react';
15
+ import * as React5 from 'react';
17
16
  import { useState, useRef, useMemo, useEffect, useCallback, Fragment as Fragment$1 } from 'react';
18
17
  import { format, parseISO, isValid, endOfMonth, startOfMonth, addMonths, subDays, addYears } from 'date-fns';
19
- import { cva } from 'class-variance-authority';
20
- import { formatDate } from '@caffeinebounce/shared-utils';
21
- import { Command as Command$1 } from 'cmdk';
22
18
  import { ResponsiveContainer, ComposedChart, CartesianGrid, XAxis, YAxis, ReferenceLine, Tooltip as Tooltip$1, Line, Bar, Area, AreaChart, BarChart, PieChart, Pie, Cell, Legend, Sector } from 'recharts';
23
19
 
20
+ function Switch({
21
+ className,
22
+ size = "default",
23
+ ...props
24
+ }) {
25
+ return /* @__PURE__ */ jsx(
26
+ SwitchPrimitive.Root,
27
+ {
28
+ "data-slot": "switch",
29
+ "data-size": size,
30
+ className: cn(
31
+ "peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",
32
+ className
33
+ ),
34
+ ...props,
35
+ children: /* @__PURE__ */ jsx(
36
+ SwitchPrimitive.Thumb,
37
+ {
38
+ "data-slot": "switch-thumb",
39
+ className: cn(
40
+ "pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground"
41
+ )
42
+ }
43
+ )
44
+ }
45
+ );
46
+ }
24
47
  function ChartTooltipShell({
25
48
  children,
26
49
  className
@@ -68,62 +91,6 @@ function ChartTooltipRow({
68
91
  )
69
92
  ] });
70
93
  }
71
- function Calendar({
72
- className,
73
- classNames,
74
- showOutsideDays = true,
75
- captionLayout = "dropdown",
76
- ...props
77
- }) {
78
- return /* @__PURE__ */ jsx(
79
- DayPicker,
80
- {
81
- showOutsideDays,
82
- captionLayout,
83
- className: cn("p-3", className),
84
- classNames: {
85
- months: "flex flex-col sm:flex-row gap-2",
86
- month: "flex flex-col gap-4",
87
- dropdown_root: "relative",
88
- nav: "absolute inset-x-0 top-0 flex h-8 items-center justify-between",
89
- button_previous: cn(
90
- buttonVariants({ variant: "outline" }),
91
- "absolute left-1 top-1/2 h-7 w-7 -translate-y-1/2 bg-background p-0 opacity-70 hover:opacity-100"
92
- ),
93
- button_next: cn(
94
- buttonVariants({ variant: "outline" }),
95
- "absolute right-1 top-1/2 h-7 w-7 -translate-y-1/2 bg-background p-0 opacity-70 hover:opacity-100"
96
- ),
97
- month_grid: "w-full border-collapse",
98
- weekdays: "flex",
99
- weekday: "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
100
- week: "flex w-full mt-2",
101
- day: cn(
102
- "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50",
103
- props.mode === "range" ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" : "[&:has([aria-selected])]:rounded-md"
104
- ),
105
- day_button: cn(
106
- buttonVariants({ variant: "ghost" }),
107
- "h-8 w-8 p-0 font-normal aria-selected:opacity-100"
108
- ),
109
- range_start: "day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
110
- range_end: "day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
111
- selected: "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
112
- today: "bg-accent text-accent-foreground",
113
- outside: "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
114
- disabled: "text-muted-foreground opacity-50",
115
- range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground",
116
- hidden: "invisible",
117
- ...classNames
118
- },
119
- components: {
120
- Chevron: ({ orientation }) => orientation === "left" ? /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
121
- },
122
- ...props
123
- }
124
- );
125
- }
126
- Calendar.displayName = "Calendar";
127
94
  var MONTHS = [
128
95
  "Jan",
129
96
  "Feb",
@@ -162,9 +129,9 @@ function ModernCalendar({
162
129
  disabled = false
163
130
  }) {
164
131
  const today = /* @__PURE__ */ new Date();
165
- const [viewDate, setViewDate] = React12.useState(selected || today);
166
- const [viewMode, setViewMode] = React12.useState("days");
167
- const [yearRangeStart, setYearRangeStart] = React12.useState(
132
+ const [viewDate, setViewDate] = React5.useState(selected || today);
133
+ const [viewMode, setViewMode] = React5.useState("days");
134
+ const [yearRangeStart, setYearRangeStart] = React5.useState(
168
135
  Math.floor((selected?.getFullYear() || today.getFullYear()) / 12) * 12
169
136
  );
170
137
  const getDaysInMonth = (year, month) => {
@@ -460,7 +427,7 @@ function DatePicker({
460
427
  align = "start",
461
428
  error = false
462
429
  }) {
463
- const [open, setOpen] = React12.useState(false);
430
+ const [open, setOpen] = React5.useState(false);
464
431
  const handleSelect = (date) => {
465
432
  onChange?.(date);
466
433
  setOpen(false);
@@ -479,7 +446,7 @@ function DatePicker({
479
446
  className
480
447
  ),
481
448
  children: [
482
- /* @__PURE__ */ jsx(Calendar$1, { className: "mr-2 h-4 w-4" }),
449
+ /* @__PURE__ */ jsx(Calendar, { className: "mr-2 h-4 w-4" }),
483
450
  value ? format(value, dateFormat) : /* @__PURE__ */ jsx("span", { children: placeholder })
484
451
  ]
485
452
  }
@@ -508,8 +475,8 @@ function DateRangePicker({
508
475
  numberOfMonths = 2,
509
476
  align = "start"
510
477
  }) {
511
- const [open, setOpen] = React12.useState(false);
512
- const disabledMatcher = React12.useMemo(() => {
478
+ const [open, setOpen] = React5.useState(false);
479
+ const disabledMatcher = React5.useMemo(() => {
513
480
  const matchers = [];
514
481
  if (fromDate) {
515
482
  matchers.push({ before: fromDate });
@@ -532,7 +499,7 @@ function DateRangePicker({
532
499
  className
533
500
  ),
534
501
  children: [
535
- /* @__PURE__ */ jsx(Calendar$1, { className: "mr-2 h-4 w-4" }),
502
+ /* @__PURE__ */ jsx(Calendar, { className: "mr-2 h-4 w-4" }),
536
503
  value?.from ? value.to ? /* @__PURE__ */ jsxs(Fragment, { children: [
537
504
  format(value.from, dateFormat),
538
505
  " -",
@@ -543,9 +510,10 @@ function DateRangePicker({
543
510
  }
544
511
  ) }),
545
512
  /* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-0", align, children: /* @__PURE__ */ jsx(
546
- Calendar,
513
+ Calendar$1,
547
514
  {
548
515
  mode: "range",
516
+ captionLayout: "dropdown",
549
517
  selected: value,
550
518
  onSelect: onChange,
551
519
  disabled: disabledMatcher,
@@ -750,597 +718,9 @@ function ComparisonSelector({
750
718
  )
751
719
  ] });
752
720
  }
753
-
754
- // src/blocks/data-table/data-table-styles.ts
755
- var dataTableTopperButtonProps = {
756
- variant: "ghost-icon",
757
- size: "icon-sm"
758
- };
759
- var dataTableTopperResponsiveClasses = "w-8 @min-[450px]:w-auto @min-[450px]:px-2";
760
- var dataTableTopperOpenStateClasses = "data-[state=open]:text-foreground";
761
- var dataTableTopperIconClasses = "text-icon hover:text-icon-hover [&_svg]:transition-transform [&_svg]:duration-200 hover:[&_svg]:scale-110";
762
- function DataTableAddButton({
763
- label = "Add",
764
- icon,
765
- responsiveLabel = true,
766
- className,
767
- children,
768
- ...props
769
- }) {
770
- const context = useDataTableContext();
771
- const fontSizeClass = context?.fontSizeClass ?? "text-xs";
772
- const iconElement = icon ?? /* @__PURE__ */ jsx(Plus, { className: "size-4" });
773
- return /* @__PURE__ */ jsxs(
774
- Button,
775
- {
776
- ...dataTableTopperButtonProps,
777
- className: cn(
778
- fontSizeClass,
779
- responsiveLabel ? dataTableTopperResponsiveClasses : "px-2 w-auto",
780
- className
781
- ),
782
- ...props,
783
- children: [
784
- iconElement,
785
- responsiveLabel ? /* @__PURE__ */ jsx("span", { className: "hidden @min-[450px]:inline", children: children ?? label }) : /* @__PURE__ */ jsx("span", { children: children ?? label })
786
- ]
787
- }
788
- );
789
- }
790
- function DataTableColumnMenuSub({
791
- label,
792
- icon,
793
- children
794
- }) {
795
- return /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
796
- /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { className: "flex items-center gap-2", children: [
797
- icon ?? /* @__PURE__ */ jsx(ListOrdered, { className: "size-4 text-muted-foreground" }),
798
- label
799
- ] }),
800
- /* @__PURE__ */ jsx(DropdownMenuSubContent, { alignOffset: -5, sideOffset: 5, children })
801
- ] });
802
- }
803
- function escapeCsvField(value) {
804
- if (value.includes(",") || value.includes('"') || value.includes("\n") || value.includes("\r")) {
805
- return `"${value.replace(/"/g, '""')}"`;
806
- }
807
- return value;
808
- }
809
- function formatCsvValue(value) {
810
- if (value === null || value === void 0) {
811
- return "";
812
- }
813
- if (typeof value === "boolean") {
814
- return value ? "Yes" : "No";
815
- }
816
- if (value instanceof Date) {
817
- return value.toISOString();
818
- }
819
- if (typeof value === "object") {
820
- try {
821
- return JSON.stringify(value);
822
- } catch {
823
- return String(value);
824
- }
825
- }
826
- return String(value);
827
- }
828
- function serializeTableExport({
829
- table,
830
- visibleColumnsOnly = true,
831
- selectedRowsOnly = false,
832
- customHeaders = {},
833
- excludeColumns = [],
834
- formatters = {}
835
- }) {
836
- const allColumns = visibleColumnsOnly ? table.getVisibleLeafColumns() : table.getAllLeafColumns();
837
- const columns = allColumns.filter((col) => {
838
- const colId = col.id;
839
- if (["select", "selection", "actions", "_actions"].includes(colId)) {
840
- return false;
841
- }
842
- return !excludeColumns.includes(colId);
843
- });
844
- const tableRows = selectedRowsOnly ? table.getFilteredSelectedRowModel().rows : table.getFilteredRowModel().rows;
845
- return {
846
- headers: columns.map((col) => customHeaders[col.id] ?? col.id),
847
- rows: tableRows.map(
848
- (row) => columns.map((col) => {
849
- const cellValue = row.getValue(col.id);
850
- const formatter = formatters[col.id];
851
- return formatter ? formatter(cellValue) : formatCsvValue(cellValue);
852
- })
853
- )
854
- };
855
- }
856
- function exportToCsv({
857
- table,
858
- filename = "export",
859
- visibleColumnsOnly = true,
860
- selectedRowsOnly = false,
861
- customHeaders = {},
862
- excludeColumns = [],
863
- formatters = {}
864
- }) {
865
- const { headers, rows } = serializeTableExport({
866
- table,
867
- visibleColumnsOnly,
868
- selectedRowsOnly,
869
- customHeaders,
870
- excludeColumns,
871
- formatters
872
- });
873
- const csvContent = [
874
- headers.map(escapeCsvField).join(","),
875
- ...rows.map((row) => row.map(escapeCsvField).join(","))
876
- ].join("\n");
877
- const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
878
- const url = URL.createObjectURL(blob);
879
- const link = document.createElement("a");
880
- link.setAttribute("href", url);
881
- link.setAttribute("download", `${filename}.csv`);
882
- link.style.visibility = "hidden";
883
- document.body.appendChild(link);
884
- link.click();
885
- document.body.removeChild(link);
886
- URL.revokeObjectURL(url);
887
- }
888
- function exportToExcel({
889
- table,
890
- filename = "export",
891
- visibleColumnsOnly = true,
892
- selectedRowsOnly = false,
893
- customHeaders = {},
894
- excludeColumns = [],
895
- formatters = {}
896
- }) {
897
- const { headers, rows } = serializeTableExport({
898
- table,
899
- visibleColumnsOnly,
900
- selectedRowsOnly,
901
- customHeaders,
902
- excludeColumns,
903
- formatters
904
- });
905
- const sheetRows = [
906
- headers.map((header) => ({ value: header, style: "header" })),
907
- ...rows.map(
908
- (row) => row.map((cell) => {
909
- const trimmed = cell.trim();
910
- const isNumericPattern = trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed);
911
- const hasLeadingZero = /^0\d+$/.test(trimmed) || /^-0\d+$/.test(trimmed);
912
- const numValue = Number(trimmed);
913
- const isNumber = isNumericPattern && !hasLeadingZero && !Number.isNaN(numValue);
914
- if (isNumber) {
915
- return {
916
- value: numValue,
917
- type: "Number",
918
- style: "number"
919
- };
920
- }
921
- return { value: cell, style: "text" };
922
- })
923
- )
924
- ];
925
- const xmlContent = buildSpreadsheetWorkbookXml({
926
- worksheetName: "Data",
927
- rows: sheetRows
928
- });
929
- downloadExcelXml(xmlContent, filename);
930
- }
931
- function DataTableExportButton({
932
- table,
933
- onExportCsv,
934
- onExportExcel,
935
- onExport,
936
- // deprecated, maps to CSV
937
- filename = "export",
938
- label = "Export",
939
- disabled = false,
940
- selectedRowsOnly = false,
941
- excludeColumns = [],
942
- customHeaders = {},
943
- formatters = {}
944
- }) {
945
- const context = useDataTableContext();
946
- const fontSizeClass = context?.fontSizeClass ?? "text-xs";
947
- const [isExporting, setIsExporting] = React12.useState(false);
948
- const [exportingFormat, setExportingFormat] = React12.useState(null);
949
- const handleExport = React12.useCallback(
950
- async (format3) => {
951
- if (disabled) return;
952
- setIsExporting(true);
953
- setExportingFormat(format3);
954
- try {
955
- if (table) {
956
- if (format3 === "csv") {
957
- exportToCsv({
958
- table,
959
- filename,
960
- selectedRowsOnly,
961
- excludeColumns,
962
- customHeaders,
963
- formatters
964
- });
965
- } else {
966
- exportToExcel({
967
- table,
968
- filename,
969
- selectedRowsOnly,
970
- excludeColumns,
971
- customHeaders,
972
- formatters
973
- });
974
- }
975
- } else {
976
- if (format3 === "csv") {
977
- await (onExportCsv ?? onExport)?.();
978
- } else {
979
- await onExportExcel?.();
980
- }
981
- }
982
- } finally {
983
- setIsExporting(false);
984
- setExportingFormat(null);
985
- }
986
- },
987
- [
988
- table,
989
- onExportCsv,
990
- onExportExcel,
991
- onExport,
992
- filename,
993
- selectedRowsOnly,
994
- excludeColumns,
995
- customHeaders,
996
- formatters,
997
- disabled
998
- ]
999
- );
1000
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1001
- /* @__PURE__ */ jsxs(Tooltip, { children: [
1002
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
1003
- Button,
1004
- {
1005
- ...dataTableTopperButtonProps,
1006
- disabled: isExporting || disabled,
1007
- "aria-label": label,
1008
- className: dataTableTopperOpenStateClasses,
1009
- children: isExporting ? /* @__PURE__ */ jsx(Spinner, { className: "size-4" }) : /* @__PURE__ */ jsx(Download, { className: "size-4" })
1010
- }
1011
- ) }) }),
1012
- /* @__PURE__ */ jsx(TooltipContent, { className: fontSizeClass, children: label })
1013
- ] }),
1014
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-44", children: [
1015
- /* @__PURE__ */ jsx(
1016
- DropdownMenuLabel,
1017
- {
1018
- className: cn("font-normal text-muted-foreground", fontSizeClass),
1019
- children: "Export Data"
1020
- }
1021
- ),
1022
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1023
- /* @__PURE__ */ jsxs(
1024
- DropdownMenuItem,
1025
- {
1026
- onClick: () => handleExport("csv"),
1027
- disabled: isExporting,
1028
- className: cn("gap-2", fontSizeClass),
1029
- children: [
1030
- /* @__PURE__ */ jsx(FileText, { className: dataTableTopperIconClasses }),
1031
- /* @__PURE__ */ jsx("span", { children: "CSV" }),
1032
- exportingFormat === "csv" && /* @__PURE__ */ jsx(Spinner, { className: "size-3 ml-auto" })
1033
- ]
1034
- }
1035
- ),
1036
- /* @__PURE__ */ jsxs(
1037
- DropdownMenuItem,
1038
- {
1039
- onClick: () => handleExport("excel"),
1040
- disabled: isExporting,
1041
- className: cn("gap-2", fontSizeClass),
1042
- children: [
1043
- /* @__PURE__ */ jsx(FileSpreadsheet, { className: dataTableTopperIconClasses }),
1044
- /* @__PURE__ */ jsx("span", { children: "Excel" }),
1045
- exportingFormat === "excel" && /* @__PURE__ */ jsx(Spinner, { className: "size-3 ml-auto" })
1046
- ]
1047
- }
1048
- )
1049
- ] })
1050
- ] });
1051
- }
1052
- function DataTablePagination({
1053
- page,
1054
- pageSize,
1055
- pageCount,
1056
- totalCount,
1057
- selectedCount = 0,
1058
- pageSizeOptions = [10, 20, 30, 40, 50],
1059
- onPageChange,
1060
- onPageSizeChange,
1061
- className,
1062
- ...props
1063
- }) {
1064
- const displayPageCount = Math.max(1, pageCount);
1065
- const canGoPrevious = page > 1;
1066
- const canGoNext = page < displayPageCount;
1067
- return /* @__PURE__ */ jsxs(
1068
- "div",
1069
- {
1070
- className: cn(
1071
- "flex w-full flex-row items-center justify-between gap-3 overflow-auto p-1 sm:gap-8",
1072
- className
1073
- ),
1074
- ...props,
1075
- children: [
1076
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1077
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1 sm:gap-2 lg:gap-6", children: [
1078
- /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
1079
- /* @__PURE__ */ jsxs("p", { className: "whitespace-nowrap text-[length:var(--text-xs)] font-light text-muted-foreground", children: [
1080
- /* @__PURE__ */ jsx("span", { className: "hidden lg:inline", children: "Rows per page" }),
1081
- /* @__PURE__ */ jsx("span", { className: "lg:hidden", children: "Rows" })
1082
- ] }),
1083
- /* @__PURE__ */ jsxs(
1084
- Select,
1085
- {
1086
- value: `${pageSize}`,
1087
- onValueChange: (value) => onPageSizeChange(Number(value)),
1088
- children: [
1089
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-6 w-[60px] px-2 text-[length:var(--text-xs)] focus:ring-0 focus:ring-offset-0 hover:bg-accent transition-colors", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: pageSize }) }),
1090
- /* @__PURE__ */ jsx(SelectContent, { side: "top", children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx(
1091
- SelectItem,
1092
- {
1093
- value: `${size}`,
1094
- className: "text-[length:var(--text-xs)] h-7",
1095
- children: size
1096
- },
1097
- size
1098
- )) })
1099
- ]
1100
- }
1101
- )
1102
- ] }),
1103
- /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
1104
- /* @__PURE__ */ jsx(
1105
- Button,
1106
- {
1107
- "aria-label": "Go to first page",
1108
- variant: "ghost",
1109
- className: "hidden size-7 p-0 lg:flex hover:bg-accent",
1110
- onClick: () => onPageChange(1),
1111
- disabled: !canGoPrevious,
1112
- children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "size-3.5" })
1113
- }
1114
- ),
1115
- /* @__PURE__ */ jsx(
1116
- Button,
1117
- {
1118
- "aria-label": "Go to previous page",
1119
- variant: "ghost",
1120
- className: "size-7 p-0 hover:bg-accent",
1121
- onClick: () => onPageChange(page - 1),
1122
- disabled: !canGoPrevious,
1123
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5" })
1124
- }
1125
- ),
1126
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center text-[length:var(--text-xs)] font-light text-muted-foreground whitespace-nowrap px-1", children: [
1127
- /* @__PURE__ */ jsxs("span", { className: "hidden lg:inline", children: [
1128
- "Page ",
1129
- page,
1130
- " of ",
1131
- displayPageCount
1132
- ] }),
1133
- /* @__PURE__ */ jsxs("span", { className: "lg:hidden", children: [
1134
- page,
1135
- " / ",
1136
- displayPageCount
1137
- ] })
1138
- ] }),
1139
- /* @__PURE__ */ jsx(
1140
- Button,
1141
- {
1142
- "aria-label": "Go to next page",
1143
- variant: "ghost",
1144
- className: "size-7 p-0 hover:bg-accent",
1145
- onClick: () => onPageChange(page + 1),
1146
- disabled: !canGoNext,
1147
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5" })
1148
- }
1149
- ),
1150
- /* @__PURE__ */ jsx(
1151
- Button,
1152
- {
1153
- "aria-label": "Go to last page",
1154
- variant: "ghost",
1155
- className: "hidden size-7 p-0 lg:flex hover:bg-accent",
1156
- onClick: () => onPageChange(displayPageCount),
1157
- disabled: !canGoNext,
1158
- children: /* @__PURE__ */ jsx(ChevronsRight, { className: "size-3.5" })
1159
- }
1160
- )
1161
- ] })
1162
- ] })
1163
- ]
1164
- }
1165
- );
1166
- }
1167
- var iconButtonVariants = cva(
1168
- "inline-flex items-center justify-center rounded-box transition-colors duration-200 cursor-pointer disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1169
- {
1170
- variants: {
1171
- variant: {
1172
- default: "text-muted-foreground hover:text-foreground hover:bg-accent",
1173
- ghost: "text-muted-foreground/50 hover:text-foreground hover:bg-transparent",
1174
- delete: "text-muted-foreground/50 hover:text-destructive hover:bg-transparent",
1175
- edit: "text-muted-foreground/50 hover:text-foreground hover:bg-transparent",
1176
- destructive: "text-muted-foreground/50 hover:text-destructive hover:bg-transparent",
1177
- success: "text-muted-foreground/50 hover:text-green-600 hover:bg-transparent",
1178
- successActive: "text-green-600 hover:text-muted-foreground/60 hover:bg-transparent"
1179
- },
1180
- size: {
1181
- default: "h-8 w-8 [&_svg]:size-4",
1182
- sm: "h-6 w-6 [&_svg]:size-3.5",
1183
- xs: "h-5 w-5 [&_svg]:size-3",
1184
- lg: "h-10 w-10 [&_svg]:size-5"
1185
- }
1186
- },
1187
- defaultVariants: {
1188
- variant: "default",
1189
- size: "default"
1190
- }
1191
- }
1192
- );
1193
- function IconButton({ className, variant, size, ...props }) {
1194
- return /* @__PURE__ */ jsx(
1195
- "button",
1196
- {
1197
- type: "button",
1198
- "data-slot": "icon-button",
1199
- className: cn(iconButtonVariants({ variant, size, className })),
1200
- ...props
1201
- }
1202
- );
1203
- }
1204
- function useIsDesktop() {
1205
- const [isDesktop, setIsDesktop] = React12.useState(false);
1206
- React12.useEffect(() => {
1207
- const mediaQuery = window.matchMedia("(min-width: 768px)");
1208
- setIsDesktop(mediaQuery.matches);
1209
- const handler = (e) => setIsDesktop(e.matches);
1210
- mediaQuery.addEventListener("change", handler);
1211
- return () => mediaQuery.removeEventListener("change", handler);
1212
- }, []);
1213
- return isDesktop;
1214
- }
1215
- function ActionsDropdown({
1216
- actions,
1217
- fontSizeClass,
1218
- density
1219
- }) {
1220
- const isCompact = density === "compact";
1221
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1222
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1223
- IconButton,
1224
- {
1225
- variant: "ghost",
1226
- size: isCompact ? "xs" : "sm",
1227
- className: "data-[state=open]:bg-muted",
1228
- children: [
1229
- /* @__PURE__ */ jsx(MoreHorizontal, { className: isCompact ? "size-3.5" : "size-4" }),
1230
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Open menu" })
1231
- ]
1232
- }
1233
- ) }),
1234
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "w-40", children: actions.map((action, index) => /* @__PURE__ */ jsxs("div", { children: [
1235
- action.separatorBefore && index > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1236
- /* @__PURE__ */ jsxs(
1237
- DropdownMenuItem,
1238
- {
1239
- onClick: action.onClick,
1240
- disabled: action.disabled,
1241
- className: cn(
1242
- fontSizeClass,
1243
- action.destructive && "hover:text-destructive hover:bg-destructive/10",
1244
- action.success && "hover:text-success hover:bg-success/10",
1245
- action.successActive && "text-green-600 hover:text-muted-foreground hover:bg-accent"
1246
- ),
1247
- children: [
1248
- action.icon && /* @__PURE__ */ jsx(
1249
- "span",
1250
- {
1251
- className: cn(
1252
- "mr-2",
1253
- action.destructive && "group-hover:text-destructive",
1254
- action.success && "group-hover:text-success",
1255
- action.successActive && "text-green-600 group-hover:text-muted-foreground"
1256
- ),
1257
- children: action.icon
1258
- }
1259
- ),
1260
- action.label
1261
- ]
1262
- }
1263
- )
1264
- ] }, action.label)) })
1265
- ] });
1266
- }
1267
- function ActionsInline({
1268
- actions,
1269
- fontSizeClass,
1270
- density
1271
- }) {
1272
- const isCompact = density === "compact";
1273
- return /* @__PURE__ */ jsx("div", { className: cn("flex items-center", isCompact ? "gap-0.5" : "gap-1"), children: actions.map((action) => {
1274
- const variant = action.destructive ? "delete" : action.successActive ? "successActive" : action.success ? "success" : "ghost";
1275
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
1276
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1277
- IconButton,
1278
- {
1279
- variant,
1280
- size: isCompact ? "xs" : "default",
1281
- onClick: action.onClick,
1282
- disabled: action.disabled,
1283
- children: [
1284
- action.icon,
1285
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: action.label })
1286
- ]
1287
- }
1288
- ) }),
1289
- /* @__PURE__ */ jsx(TooltipContent, { className: fontSizeClass, children: action.label })
1290
- ] }, action.label);
1291
- }) });
1292
- }
1293
- function DataTableRowActions({
1294
- actions,
1295
- mode = "dropdown"
1296
- }) {
1297
- const context = useDataTableContext();
1298
- const fontSizeClass = context?.fontSizeClass ?? "text-xs";
1299
- const density = context?.density ?? "comfy";
1300
- const isDesktop = useIsDesktop();
1301
- if (actions.length === 0) return null;
1302
- if (mode === "dropdown") {
1303
- return /* @__PURE__ */ jsx(
1304
- ActionsDropdown,
1305
- {
1306
- actions,
1307
- fontSizeClass,
1308
- density
1309
- }
1310
- );
1311
- }
1312
- if (mode === "inline") {
1313
- return /* @__PURE__ */ jsx(
1314
- ActionsInline,
1315
- {
1316
- actions,
1317
- fontSizeClass,
1318
- density
1319
- }
1320
- );
1321
- }
1322
- if (isDesktop) {
1323
- return /* @__PURE__ */ jsx(
1324
- ActionsInline,
1325
- {
1326
- actions,
1327
- fontSizeClass,
1328
- density
1329
- }
1330
- );
1331
- }
1332
- return /* @__PURE__ */ jsx(
1333
- ActionsDropdown,
1334
- {
1335
- actions,
1336
- fontSizeClass,
1337
- density
1338
- }
1339
- );
1340
- }
1341
721
  var STORAGE_PREFIX = "dt-settings-";
1342
722
  function useDataTableSettings(tableId, settings) {
1343
- const [values, setValues] = React12.useState(() => {
723
+ const [values, setValues] = React5.useState(() => {
1344
724
  const defaults = {};
1345
725
  for (const s of settings) {
1346
726
  defaults[s.id] = s.defaultValue;
@@ -1357,7 +737,7 @@ function useDataTableSettings(tableId, settings) {
1357
737
  }
1358
738
  return defaults;
1359
739
  });
1360
- const onChange = React12.useCallback(
740
+ const onChange = React5.useCallback(
1361
741
  (id, value) => {
1362
742
  setValues((prev) => {
1363
743
  const next = { ...prev, [id]: value };
@@ -1375,7 +755,7 @@ function useDataTableSettings(tableId, settings) {
1375
755
  },
1376
756
  [tableId]
1377
757
  );
1378
- const getSetting = React12.useCallback(
758
+ const getSetting = React5.useCallback(
1379
759
  (id) => values[id],
1380
760
  [values]
1381
761
  );
@@ -1441,1149 +821,25 @@ function DataTableSettings({
1441
821
  ] }) })
1442
822
  ] }) });
1443
823
  }
1444
- function Skeleton({ className, ...props }) {
1445
- return /* @__PURE__ */ jsx(
1446
- "div",
1447
- {
1448
- "data-slot": "skeleton",
1449
- className: cn("bg-accent animate-pulse rounded-box", className),
1450
- ...props
1451
- }
1452
- );
1453
- }
1454
- function DataTableSkeleton({
1455
- columnCount,
1456
- rowCount = 10,
1457
- filterCount = 0,
1458
- cellWidths = ["auto"],
1459
- withViewOptions = true,
1460
- withPagination = true,
1461
- shrinkZero = false,
824
+ function Combobox({
825
+ options,
826
+ value,
827
+ onValueChange,
828
+ placeholder = "Select...",
829
+ mutedPlaceholder = true,
830
+ searchPlaceholder = "Search...",
831
+ emptyText = "No results found.",
832
+ disabled = false,
833
+ allowCreate = false,
834
+ createLabel = "Create new",
835
+ onCreateNew,
836
+ loading = false,
1462
837
  className,
1463
- ...props
1464
- }) {
1465
- const widths = Array.from(
1466
- { length: columnCount },
1467
- (_, index) => cellWidths[index % cellWidths.length] ?? "auto"
1468
- );
1469
- return /* @__PURE__ */ jsxs(
1470
- "div",
1471
- {
1472
- className: cn("flex w-full flex-col gap-2.5 overflow-auto", className),
1473
- ...props,
1474
- children: [
1475
- /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center justify-between gap-2 overflow-auto p-1", children: [
1476
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2", children: [
1477
- /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-[150px] lg:w-[250px]" }),
1478
- filterCount > 0 && Array.from({ length: filterCount }).map((_, i) => /* @__PURE__ */ jsx(
1479
- Skeleton,
1480
- {
1481
- className: "h-8 w-20 border-dashed"
1482
- },
1483
- `filter-${i}`
1484
- ))
1485
- ] }),
1486
- withViewOptions && /* @__PURE__ */ jsx(Skeleton, { className: "hidden h-8 w-20 lg:block" })
1487
- ] }),
1488
- /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-md border", children: /* @__PURE__ */ jsxs(Table, { children: [
1489
- /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: widths.map((width, index) => /* @__PURE__ */ jsx(
1490
- TableHead,
1491
- {
1492
- style: {
1493
- width: shrinkZero && width === "auto" ? 0 : width,
1494
- minWidth: shrinkZero ? 0 : void 0
1495
- },
1496
- children: /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-full" })
1497
- },
1498
- `header-${index}`
1499
- )) }) }),
1500
- /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rowCount }).map((_, rowIndex) => /* @__PURE__ */ jsx(
1501
- TableRow,
1502
- {
1503
- className: "hover:bg-transparent",
1504
- children: widths.map((width, cellIndex) => /* @__PURE__ */ jsx(
1505
- TableCell,
1506
- {
1507
- style: {
1508
- width: shrinkZero && width === "auto" ? 0 : width,
1509
- minWidth: shrinkZero ? 0 : void 0
1510
- },
1511
- children: /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-full" })
1512
- },
1513
- `cell-${rowIndex}-${cellIndex}`
1514
- ))
1515
- },
1516
- `row-${rowIndex}`
1517
- )) })
1518
- ] }) }),
1519
- withPagination && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center justify-between gap-4 overflow-auto p-1 sm:gap-8", children: [
1520
- /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-40 shrink-0" }),
1521
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 sm:gap-6 lg:gap-8", children: [
1522
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1523
- /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-24" }),
1524
- /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-[70px]" })
1525
- ] }),
1526
- /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-20" }),
1527
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1528
- /* @__PURE__ */ jsx(Skeleton, { className: "hidden size-8 lg:block" }),
1529
- /* @__PURE__ */ jsx(Skeleton, { className: "size-8" }),
1530
- /* @__PURE__ */ jsx(Skeleton, { className: "size-8" }),
1531
- /* @__PURE__ */ jsx(Skeleton, { className: "hidden size-8 lg:block" })
1532
- ] })
1533
- ] })
1534
- ] })
1535
- ]
1536
- }
1537
- );
1538
- }
1539
- var summaryOptions = {
1540
- text: [
1541
- { value: "none", label: "None" },
1542
- { value: "count", label: "Count" },
1543
- { value: "count_unique", label: "Count unique" },
1544
- { value: "percent_empty", label: "% Empty" },
1545
- { value: "percent_filled", label: "% Filled" }
1546
- ],
1547
- number: [
1548
- { value: "none", label: "None" },
1549
- { value: "count", label: "Count" },
1550
- { value: "sum", label: "Sum" },
1551
- { value: "average", label: "Average" },
1552
- { value: "median", label: "Median" },
1553
- { value: "min", label: "Min" },
1554
- { value: "max", label: "Max" },
1555
- { value: "range", label: "Range" },
1556
- { value: "percent_empty", label: "% Empty" },
1557
- { value: "percent_filled", label: "% Filled" }
1558
- ],
1559
- boolean: [
1560
- { value: "none", label: "None" },
1561
- { value: "count", label: "Count" },
1562
- { value: "percent_empty", label: "% Empty" },
1563
- { value: "percent_filled", label: "% Filled" }
1564
- ],
1565
- date: [
1566
- { value: "none", label: "None" },
1567
- { value: "count", label: "Count" },
1568
- { value: "min", label: "Earliest" },
1569
- { value: "max", label: "Latest" },
1570
- { value: "range", label: "Date range" },
1571
- { value: "percent_empty", label: "% Empty" },
1572
- { value: "percent_filled", label: "% Filled" }
1573
- ]
1574
- };
1575
- function calculateSummary(column, allData, type, dataType) {
1576
- if (type === "none") return "";
1577
- const accessor = column.accessorFn;
1578
- if (!accessor) return "";
1579
- const allValues = allData.map((row) => accessor(row, 0)).filter((v) => v !== null && v !== void 0 && v !== "");
1580
- const totalCount = allData.length;
1581
- const filledCount = allValues.length;
1582
- const emptyCount = totalCount - filledCount;
1583
- switch (type) {
1584
- case "count":
1585
- return `Count: ${filledCount.toLocaleString()}`;
1586
- case "count_unique": {
1587
- const uniqueValues = new Set(allValues.map((v) => String(v)));
1588
- return `Unique: ${uniqueValues.size.toLocaleString()}`;
1589
- }
1590
- case "percent_empty":
1591
- return totalCount > 0 ? `Empty: ${(emptyCount / totalCount * 100).toFixed(1)}%` : "Empty: 0%";
1592
- case "percent_filled":
1593
- return totalCount > 0 ? `Filled: ${(filledCount / totalCount * 100).toFixed(1)}%` : "Filled: 0%";
1594
- case "sum":
1595
- case "average":
1596
- case "median":
1597
- case "min":
1598
- case "max":
1599
- case "range": {
1600
- if (dataType === "number") {
1601
- const numericValues = allValues.map((v) => Number(v)).filter((n) => !Number.isNaN(n));
1602
- if (numericValues.length === 0) return "\u2014";
1603
- switch (type) {
1604
- case "sum": {
1605
- const sum = numericValues.reduce((a, b) => a + b, 0);
1606
- return `Sum: ${formatNumber(sum)}`;
1607
- }
1608
- case "average": {
1609
- const avg = numericValues.reduce((a, b) => a + b, 0) / numericValues.length;
1610
- return `Avg: ${formatNumber(avg)}`;
1611
- }
1612
- case "median": {
1613
- const sorted = [...numericValues].sort((a, b) => a - b);
1614
- const mid = Math.floor(sorted.length / 2);
1615
- const median = sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
1616
- return `Median: ${formatNumber(median)}`;
1617
- }
1618
- case "min":
1619
- return `Min: ${formatNumber(Math.min(...numericValues))}`;
1620
- case "max":
1621
- return `Max: ${formatNumber(Math.max(...numericValues))}`;
1622
- case "range": {
1623
- const min = Math.min(...numericValues);
1624
- const max = Math.max(...numericValues);
1625
- return `${formatNumber(min)} \u2013 ${formatNumber(max)}`;
1626
- }
1627
- }
1628
- }
1629
- if (dataType === "date") {
1630
- const dateValues = allValues.map((v) => {
1631
- const d = v instanceof Date ? v : new Date(String(v));
1632
- return d.getTime();
1633
- }).filter((t) => !Number.isNaN(t));
1634
- if (dateValues.length === 0) return "\u2014";
1635
- switch (type) {
1636
- case "min":
1637
- return formatDate(new Date(Math.min(...dateValues)));
1638
- case "max":
1639
- return formatDate(new Date(Math.max(...dateValues)));
1640
- case "range": {
1641
- const min = new Date(Math.min(...dateValues));
1642
- const max = new Date(Math.max(...dateValues));
1643
- return `${formatDate(min)} \u2013 ${formatDate(max)}`;
1644
- }
1645
- default:
1646
- return "\u2014";
1647
- }
1648
- }
1649
- return "\u2014";
1650
- }
1651
- default:
1652
- return "";
1653
- }
1654
- }
1655
- function formatNumber(n) {
1656
- if (Number.isInteger(n)) {
1657
- return n.toLocaleString();
1658
- }
1659
- return n.toLocaleString(void 0, {
1660
- minimumFractionDigits: 2,
1661
- maximumFractionDigits: 2
1662
- });
1663
- }
1664
- function DataTableSummary({
1665
- table,
1666
- allData,
1667
- summaryConfig,
1668
- onSummaryChange,
1669
- getColumnDataType,
1670
- gutterWidth = 0,
1671
- className
1672
- }) {
1673
- const visibleColumns = table.getVisibleLeafColumns().filter((col) => col.id !== "select" && col.id !== "actions");
1674
- const [hoveredColumnId, setHoveredColumnId] = React12.useState(
1675
- null
1676
- );
1677
- const detectDataType = React12.useCallback(
1678
- (columnId) => {
1679
- if (getColumnDataType) return getColumnDataType(columnId);
1680
- const column = table.getColumn(columnId);
1681
- if (!column) return "text";
1682
- const meta = column.columnDef.meta;
1683
- if (meta?.filterType === "number") return "number";
1684
- if (meta?.filterType === "boolean") return "boolean";
1685
- const accessor = column.accessorFn;
1686
- if (!accessor) return "text";
1687
- for (const row of allData) {
1688
- const value = accessor(row, 0);
1689
- if (value === null || value === void 0) continue;
1690
- if (typeof value === "number") return "number";
1691
- if (typeof value === "boolean") return "boolean";
1692
- if (value instanceof Date) return "date";
1693
- if (typeof value === "string" && !Number.isNaN(Date.parse(value))) {
1694
- const parsed = new Date(value);
1695
- if (parsed.getFullYear() > 1900 && parsed.getFullYear() < 2100) {
1696
- return "date";
1697
- }
1698
- }
1699
- break;
1700
- }
1701
- return "text";
1702
- },
1703
- [getColumnDataType, table, allData]
1704
- );
1705
- return /* @__PURE__ */ jsxs(
1706
- TableRow,
1707
- {
1708
- className: cn("hover:bg-transparent border-0 bg-transparent", className),
1709
- children: [
1710
- gutterWidth > 0 && /* @__PURE__ */ jsx(TableCell, { className: "p-0 border-0", style: { width: gutterWidth } }),
1711
- visibleColumns.map((column, index) => {
1712
- const columnId = column.id;
1713
- const dataType = detectDataType(columnId);
1714
- const currentType = summaryConfig[columnId] ?? "none";
1715
- const options = summaryOptions[dataType] ?? summaryOptions.text;
1716
- const summaryValue = calculateSummary(
1717
- column,
1718
- allData,
1719
- currentType,
1720
- dataType
1721
- );
1722
- const isHovered = hoveredColumnId === columnId;
1723
- const isLastColumn = index === visibleColumns.length - 1;
1724
- const isFirstColumn = index === 0;
1725
- return /* @__PURE__ */ jsx(
1726
- TableCell,
1727
- {
1728
- className: cn(
1729
- "p-0 border-0",
1730
- isLastColumn && "rounded-br-md",
1731
- isFirstColumn && "rounded-bl-md"
1732
- ),
1733
- style: { width: column.getSize(), minWidth: column.getSize() },
1734
- children: /* @__PURE__ */ jsx(
1735
- SummaryCell,
1736
- {
1737
- columnId,
1738
- column,
1739
- currentType,
1740
- summaryValue,
1741
- options,
1742
- isHovered,
1743
- onSummaryChange,
1744
- onMouseEnter: () => setHoveredColumnId(columnId),
1745
- onMouseLeave: () => setHoveredColumnId(null)
1746
- }
1747
- )
1748
- },
1749
- columnId
1750
- );
1751
- })
1752
- ]
1753
- }
1754
- );
1755
- }
1756
- function SummaryCell({
1757
- columnId,
1758
- currentType,
1759
- summaryValue,
1760
- options,
1761
- isHovered,
1762
- onSummaryChange,
1763
- onMouseEnter,
1764
- onMouseLeave
1765
- }) {
1766
- const [isOpen, setIsOpen] = React12.useState(false);
1767
- const showCalculate = currentType === "none" && (isHovered || isOpen);
1768
- const context = useDataTableContext();
1769
- const isCompact = context?.density === "compact";
1770
- return (
1771
- // biome-ignore lint/a11y/noStaticElementInteractions: Hover state for visual feedback only
1772
- /* @__PURE__ */ jsx(
1773
- "div",
1774
- {
1775
- className: cn(
1776
- "flex items-center min-w-0",
1777
- isCompact ? "px-1.5 py-0.5" : "px-2 py-1"
1778
- ),
1779
- onMouseEnter,
1780
- onMouseLeave,
1781
- role: "presentation",
1782
- children: /* @__PURE__ */ jsxs(DropdownMenu, { open: isOpen, onOpenChange: setIsOpen, children: [
1783
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1784
- "button",
1785
- {
1786
- type: "button",
1787
- className: cn(
1788
- "group flex items-center gap-1 text-xs transition-colors rounded px-1 py-0.5 -mx-1 focus:outline-none focus-visible:outline-none",
1789
- currentType === "none" ? "text-muted-foreground/50 hover:text-muted-foreground" : "text-muted-foreground hover:text-foreground"
1790
- ),
1791
- children: [
1792
- currentType === "none" ? /* @__PURE__ */ jsx(
1793
- "span",
1794
- {
1795
- className: cn(
1796
- "italic transition-opacity",
1797
- showCalculate ? "opacity-100" : "opacity-0"
1798
- ),
1799
- children: "Calculate"
1800
- }
1801
- ) : /* @__PURE__ */ jsx("span", { className: "truncate italic pr-1", children: summaryValue }),
1802
- /* @__PURE__ */ jsx(
1803
- ChevronDown,
1804
- {
1805
- className: cn(
1806
- "size-3 shrink-0 transition-opacity",
1807
- currentType !== "none" ? "opacity-0 group-hover:opacity-50" : "opacity-0"
1808
- )
1809
- }
1810
- )
1811
- ]
1812
- }
1813
- ) }),
1814
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-40", children: options.map((option) => /* @__PURE__ */ jsx(
1815
- DropdownMenuItem,
1816
- {
1817
- onClick: () => onSummaryChange(columnId, option.value),
1818
- className: cn(
1819
- "text-xs",
1820
- currentType === option.value && "bg-accent"
1821
- ),
1822
- children: option.label
1823
- },
1824
- option.value
1825
- )) })
1826
- ] })
1827
- }
1828
- )
1829
- );
1830
- }
1831
- function DataTableSearch({
1832
- searchValue = "",
1833
- searchPlaceholder = "Search...",
1834
- onSearchChange,
1835
- className
1836
- }) {
1837
- const context = useDataTableContext();
1838
- const fontSizeClass = context?.fontSizeClass ?? "text-xs";
1839
- const [isExpanded, setIsExpanded] = React12.useState(false);
1840
- const inputRef = React12.useRef(null);
1841
- React12.useEffect(() => {
1842
- if (searchValue && !isExpanded) {
1843
- setIsExpanded(true);
1844
- }
1845
- }, [searchValue, isExpanded]);
1846
- React12.useEffect(() => {
1847
- if (isExpanded && inputRef.current) {
1848
- inputRef.current.focus();
1849
- }
1850
- }, [isExpanded]);
1851
- const handleBlur = () => {
1852
- if (!searchValue) {
1853
- setIsExpanded(false);
1854
- }
1855
- };
1856
- const handleClear = () => {
1857
- onSearchChange?.("");
1858
- setIsExpanded(false);
1859
- };
1860
- return /* @__PURE__ */ jsx("div", { className: cn("relative flex items-center", className), children: /* @__PURE__ */ jsxs(
1861
- "div",
1862
- {
1863
- className: cn(
1864
- "flex items-center rounded-md border border-transparent transition-all duration-200 ease-out",
1865
- isExpanded ? "w-48 border-input bg-background" : "w-8 cursor-pointer"
1866
- ),
1867
- children: [
1868
- /* @__PURE__ */ jsx(
1869
- "button",
1870
- {
1871
- type: "button",
1872
- onClick: () => !isExpanded && setIsExpanded(true),
1873
- className: cn(
1874
- "flex items-center justify-center shrink-0 transition-colors rounded-md",
1875
- dataTableTopperIconClasses,
1876
- isExpanded ? "size-8 cursor-default" : "size-8"
1877
- ),
1878
- "aria-label": isExpanded ? void 0 : "Search",
1879
- children: /* @__PURE__ */ jsx(Search, { className: "size-4" })
1880
- }
1881
- ),
1882
- /* @__PURE__ */ jsx(
1883
- "input",
1884
- {
1885
- ref: inputRef,
1886
- type: "text",
1887
- placeholder: searchPlaceholder,
1888
- value: searchValue,
1889
- onChange: (e) => onSearchChange?.(e.target.value),
1890
- onBlur: handleBlur,
1891
- className: cn(
1892
- "h-8 bg-transparent outline-none placeholder:text-muted-foreground transition-all duration-200",
1893
- fontSizeClass,
1894
- isExpanded ? "w-full pr-7 opacity-100" : "w-0 opacity-0"
1895
- )
1896
- }
1897
- ),
1898
- isExpanded && searchValue && /* @__PURE__ */ jsx(
1899
- "button",
1900
- {
1901
- type: "button",
1902
- onClick: handleClear,
1903
- className: "absolute right-1.5 flex items-center justify-center size-5 rounded-sm hover:bg-accent/50",
1904
- "aria-label": "Clear search",
1905
- children: /* @__PURE__ */ jsx(X, { className: "size-3.5 text-muted-foreground" })
1906
- }
1907
- )
1908
- ]
1909
- }
1910
- ) });
1911
- }
1912
- function DataTableToolbar({
1913
- searchValue = "",
1914
- searchPlaceholder = "Search...",
1915
- onSearchChange,
1916
- className,
1917
- children,
1918
- ...props
1919
- }) {
1920
- return /* @__PURE__ */ jsxs(
1921
- "div",
1922
- {
1923
- role: "toolbar",
1924
- "aria-orientation": "horizontal",
1925
- className: cn(
1926
- "flex w-full items-center justify-end gap-1 px-1",
1927
- className
1928
- ),
1929
- ...props,
1930
- children: [
1931
- onSearchChange && /* @__PURE__ */ jsx(
1932
- DataTableSearch,
1933
- {
1934
- searchValue,
1935
- searchPlaceholder,
1936
- onSearchChange
1937
- }
1938
- ),
1939
- children
1940
- ]
1941
- }
1942
- );
1943
- }
1944
- function DataTableTopper({
1945
- tabs,
1946
- activeTab,
1947
- onTabChange,
1948
- gutterWidth = 0,
1949
- className,
1950
- children,
1951
- ...props
1952
- }) {
1953
- return /* @__PURE__ */ jsx(
1954
- "div",
1955
- {
1956
- className: cn(
1957
- // Container context for responsive layout based on available width
1958
- "@container w-full min-w-0 shrink-0",
1959
- // Visual styling
1960
- "border-b border-border/40 pb-1",
1961
- className
1962
- ),
1963
- ...props,
1964
- children: /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-row items-center justify-between gap-3", children: [
1965
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-0.5", children: [
1966
- gutterWidth > 0 && /* @__PURE__ */ jsx("div", { className: "shrink-0", style: { width: gutterWidth } }),
1967
- tabs && tabs.length > 0 && /* @__PURE__ */ jsx("div", { className: "block @min-[450px]:hidden", children: /* @__PURE__ */ jsxs(Select, { value: activeTab, onValueChange: onTabChange, children: [
1968
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-7 w-[100px] text-xs", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select tab" }) }),
1969
- /* @__PURE__ */ jsx(SelectContent, { children: tabs.map((tab) => /* @__PURE__ */ jsx(SelectItem, { value: tab.id, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
1970
- tab.label,
1971
- tab.count !== void 0 && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: tab.count })
1972
- ] }) }, tab.id)) })
1973
- ] }) }),
1974
- /* @__PURE__ */ jsx(
1975
- "div",
1976
- {
1977
- className: cn(
1978
- "items-center gap-0.5 overflow-x-auto no-scrollbar",
1979
- tabs && tabs.length > 0 ? "hidden @min-[450px]:flex" : "flex"
1980
- ),
1981
- children: tabs?.map((tab) => /* @__PURE__ */ jsxs(
1982
- "button",
1983
- {
1984
- type: "button",
1985
- onClick: () => onTabChange?.(tab.id),
1986
- className: cn(
1987
- "relative flex items-center gap-1.5 px-2 py-1 text-sm transition-colors rounded-md whitespace-nowrap",
1988
- activeTab === tab.id ? "text-foreground font-medium" : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
1989
- ),
1990
- children: [
1991
- tab.label,
1992
- tab.count !== void 0 && /* @__PURE__ */ jsx(
1993
- "span",
1994
- {
1995
- className: cn(
1996
- "text-xs tabular-nums",
1997
- activeTab === tab.id ? "text-muted-foreground" : "text-muted-foreground/60"
1998
- ),
1999
- children: tab.count
2000
- }
2001
- ),
2002
- activeTab === tab.id && /* @__PURE__ */ jsx("span", { className: "absolute bottom-0 left-2 right-2 h-0.5 bg-foreground rounded-full" })
2003
- ]
2004
- },
2005
- tab.id
2006
- ))
2007
- }
2008
- )
2009
- ] }),
2010
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children })
2011
- ] })
2012
- }
2013
- );
2014
- }
2015
- function getColumnDisplayName(column) {
2016
- const meta = column.columnDef.meta;
2017
- if (meta?.displayName) {
2018
- return meta.displayName;
2019
- }
2020
- if (typeof column.columnDef.header === "string") {
2021
- return column.columnDef.header;
2022
- }
2023
- return column.id;
2024
- }
2025
- function DataTableViewOptions({
2026
- table
2027
- }) {
2028
- const context = useDataTableContext();
2029
- const fontSizeClass = context?.fontSizeClass ?? "text-xs";
2030
- const [draggedId, setDraggedId] = React12.useState(null);
2031
- const [dragOverId, setDragOverId] = React12.useState(null);
2032
- const columns = React12.useMemo(
2033
- () => table.getAllColumns().filter(
2034
- (column) => typeof column.accessorFn !== "undefined" && column.getCanHide()
2035
- ),
2036
- [table]
2037
- );
2038
- const columnOrder = table.getState().columnOrder;
2039
- const orderedColumns = React12.useMemo(() => {
2040
- if (!columnOrder || columnOrder.length === 0) {
2041
- return columns;
2042
- }
2043
- return [...columns].sort((a, b) => {
2044
- const aIndex = columnOrder.indexOf(a.id);
2045
- const bIndex = columnOrder.indexOf(b.id);
2046
- if (aIndex === -1 && bIndex === -1) return 0;
2047
- if (aIndex === -1) return 1;
2048
- if (bIndex === -1) return -1;
2049
- return aIndex - bIndex;
2050
- });
2051
- }, [columns, columnOrder]);
2052
- const handleDragStart = (e, columnId) => {
2053
- setDraggedId(columnId);
2054
- e.dataTransfer.effectAllowed = "move";
2055
- e.dataTransfer.setData("text/plain", columnId);
2056
- };
2057
- const handleDragOver = (e, columnId) => {
2058
- e.preventDefault();
2059
- if (draggedId && draggedId !== columnId) {
2060
- setDragOverId(columnId);
2061
- }
2062
- };
2063
- const handleDragLeave = () => {
2064
- setDragOverId(null);
2065
- };
2066
- const handleDrop = (e, targetColumnId) => {
2067
- e.preventDefault();
2068
- if (!draggedId || draggedId === targetColumnId) {
2069
- setDraggedId(null);
2070
- setDragOverId(null);
2071
- return;
2072
- }
2073
- const allColumnIds = table.getAllColumns().map((c) => c.id);
2074
- const currentOrder = columnOrder && columnOrder.length > 0 ? [...columnOrder] : [...allColumnIds];
2075
- const draggedIndex = currentOrder.indexOf(draggedId);
2076
- const targetIndex = currentOrder.indexOf(targetColumnId);
2077
- if (draggedIndex === -1 || targetIndex === -1) {
2078
- setDraggedId(null);
2079
- setDragOverId(null);
2080
- return;
2081
- }
2082
- currentOrder.splice(draggedIndex, 1);
2083
- currentOrder.splice(targetIndex, 0, draggedId);
2084
- table.setColumnOrder(currentOrder);
2085
- setDraggedId(null);
2086
- setDragOverId(null);
2087
- };
2088
- const handleDragEnd = () => {
2089
- setDraggedId(null);
2090
- setDragOverId(null);
2091
- };
2092
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2093
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
2094
- Button,
2095
- {
2096
- ...dataTableTopperButtonProps,
2097
- "aria-label": "View columns",
2098
- className: dataTableTopperOpenStateClasses,
2099
- children: /* @__PURE__ */ jsx(Settings2, { className: "size-4" })
2100
- }
2101
- ) }),
2102
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-52", children: [
2103
- /* @__PURE__ */ jsx(
2104
- DropdownMenuLabel,
2105
- {
2106
- className: cn("font-normal text-muted-foreground", fontSizeClass),
2107
- children: "View Columns"
2108
- }
2109
- ),
2110
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
2111
- /* @__PURE__ */ jsx("div", { role: "list", className: "max-h-64 overflow-y-auto", children: orderedColumns.map((column) => {
2112
- const label = getColumnDisplayName(column);
2113
- const isVisible = column.getIsVisible();
2114
- const isDragging = draggedId === column.id;
2115
- const isDragOver = dragOverId === column.id;
2116
- return (
2117
- // biome-ignore lint/a11y/useSemanticElements: Using div with role for dropdown menu compatibility
2118
- /* @__PURE__ */ jsxs(
2119
- "div",
2120
- {
2121
- role: "listitem",
2122
- draggable: true,
2123
- onDragStart: (e) => handleDragStart(e, column.id),
2124
- onDragOver: (e) => handleDragOver(e, column.id),
2125
- onDragLeave: handleDragLeave,
2126
- onDrop: (e) => handleDrop(e, column.id),
2127
- onDragEnd: handleDragEnd,
2128
- className: cn(
2129
- "flex items-center gap-2 px-2 py-1.5 rounded-sm cursor-default",
2130
- fontSizeClass,
2131
- "hover:bg-accent hover:text-accent-foreground",
2132
- isDragging && "opacity-50",
2133
- isDragOver && "bg-accent/50"
2134
- ),
2135
- children: [
2136
- /* @__PURE__ */ jsx(
2137
- "div",
2138
- {
2139
- className: cn(
2140
- "flex items-center justify-center size-5 cursor-grab active:cursor-grabbing shrink-0",
2141
- dataTableTopperIconClasses
2142
- ),
2143
- children: /* @__PURE__ */ jsx(GripVertical, { className: "size-3.5" })
2144
- }
2145
- ),
2146
- /* @__PURE__ */ jsx(
2147
- "span",
2148
- {
2149
- className: cn(
2150
- "flex-1",
2151
- !isVisible && "text-muted-foreground"
2152
- ),
2153
- children: label
2154
- }
2155
- ),
2156
- /* @__PURE__ */ jsx(
2157
- "button",
2158
- {
2159
- type: "button",
2160
- onClick: () => column.toggleVisibility(!isVisible),
2161
- className: cn(
2162
- "flex items-center justify-center size-5 rounded-sm shrink-0",
2163
- dataTableTopperIconClasses
2164
- ),
2165
- "aria-label": isVisible ? "Hide column" : "Show column",
2166
- children: isVisible ? /* @__PURE__ */ jsx(Eye, { className: "size-3.5" }) : /* @__PURE__ */ jsx(EyeOff, { className: "size-3.5" })
2167
- }
2168
- )
2169
- ]
2170
- },
2171
- column.id
2172
- )
2173
- );
2174
- }) })
2175
- ] })
2176
- ] });
2177
- }
2178
- function hasUnsavedChanges(currentState, activeView) {
2179
- if (!activeView) return false;
2180
- const visA = JSON.stringify(currentState.columnVisibility ?? {});
2181
- const visB = JSON.stringify(activeView.columnVisibility ?? {});
2182
- if (visA !== visB) return true;
2183
- const filtA = JSON.stringify(currentState.filters ?? []);
2184
- const filtB = JSON.stringify(activeView.filters ?? []);
2185
- if (filtA !== filtB) return true;
2186
- const sortA = JSON.stringify(currentState.sorting ?? []);
2187
- const sortB = JSON.stringify(activeView.sorting ?? []);
2188
- if (sortA !== sortB) return true;
2189
- const orderA = JSON.stringify(currentState.columnOrder ?? []);
2190
- const orderB = JSON.stringify(activeView.columnOrder ?? []);
2191
- if (orderA !== orderB) return true;
2192
- const wrapA = JSON.stringify(currentState.columnWrapping ?? {});
2193
- const wrapB = JSON.stringify(activeView.columnWrapping ?? {});
2194
- if (wrapA !== wrapB) return true;
2195
- const rowA = JSON.stringify(currentState.rowOrder ?? []);
2196
- const rowB = JSON.stringify(activeView.rowOrder ?? []);
2197
- if (rowA !== rowB) return true;
2198
- return false;
2199
- }
2200
- function DataTableViews({
2201
- views,
2202
- activeViewId,
2203
- currentState,
2204
- onSelectView,
2205
- onSaveView,
2206
- onUpdateView,
2207
- onDeleteView,
2208
- onSetDefaultView,
2209
- className
2210
- }) {
2211
- const context = useDataTableContext();
2212
- const fontSizeClass = context?.fontSizeClass ?? "text-xs";
2213
- const [saveDialogOpen, setSaveDialogOpen] = React12.useState(false);
2214
- const [newViewName, setNewViewName] = React12.useState("");
2215
- const [setAsDefault, setSetAsDefault] = React12.useState(false);
2216
- const [nameError, setNameError] = React12.useState(null);
2217
- const activeView = views.find((v) => v.id === activeViewId);
2218
- views.find((v) => v.isDefault);
2219
- const hasChanges = hasUnsavedChanges(currentState, activeView);
2220
- const checkDuplicateName = (name) => {
2221
- const trimmedName = name.trim();
2222
- if (!trimmedName) {
2223
- setNameError(null);
2224
- return;
2225
- }
2226
- const isDuplicate = views.some(
2227
- (v) => v.name.toLowerCase() === trimmedName.toLowerCase()
2228
- );
2229
- setNameError(isDuplicate ? "A view with this name already exists" : null);
2230
- };
2231
- const handleSaveNewView = () => {
2232
- if (!newViewName.trim()) return;
2233
- const trimmedName = newViewName.trim();
2234
- const isDuplicate = views.some(
2235
- (v) => v.name.toLowerCase() === trimmedName.toLowerCase()
2236
- );
2237
- if (isDuplicate) {
2238
- setNameError("A view with this name already exists");
2239
- return;
2240
- }
2241
- if (onSaveView) {
2242
- onSaveView({
2243
- name: trimmedName,
2244
- columnVisibility: currentState.columnVisibility,
2245
- columnOrder: currentState.columnOrder,
2246
- filters: currentState.filters,
2247
- sorting: currentState.sorting,
2248
- isDefault: setAsDefault
2249
- });
2250
- }
2251
- setNewViewName("");
2252
- setSetAsDefault(false);
2253
- setNameError(null);
2254
- setSaveDialogOpen(false);
2255
- };
2256
- const handleUpdateCurrentView = () => {
2257
- if (!activeViewId || !onUpdateView) return;
2258
- onUpdateView(activeViewId, {
2259
- columnVisibility: currentState.columnVisibility,
2260
- columnOrder: currentState.columnOrder,
2261
- filters: currentState.filters,
2262
- sorting: currentState.sorting,
2263
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2264
- });
2265
- };
2266
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2267
- /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-1", className), children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2268
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
2269
- Button,
2270
- {
2271
- ...dataTableTopperButtonProps,
2272
- className: cn(
2273
- fontSizeClass,
2274
- dataTableTopperOpenStateClasses,
2275
- dataTableTopperResponsiveClasses
2276
- ),
2277
- children: [
2278
- /* @__PURE__ */ jsx(LayoutGrid, { className: "size-4" }),
2279
- /* @__PURE__ */ jsx("span", { className: "hidden @min-[450px]:inline", children: activeView?.name ?? "Default View" })
2280
- ]
2281
- }
2282
- ) }),
2283
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "start", className: "w-56", children: [
2284
- views.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2285
- views.map((view) => /* @__PURE__ */ jsxs(
2286
- "div",
2287
- {
2288
- className: "group relative flex items-center",
2289
- children: [
2290
- /* @__PURE__ */ jsxs(
2291
- DropdownMenuItem,
2292
- {
2293
- onClick: () => onSelectView(view),
2294
- className: cn(
2295
- "flex-1 flex items-center justify-between pr-16",
2296
- fontSizeClass
2297
- ),
2298
- children: [
2299
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
2300
- view.isDefault && /* @__PURE__ */ jsx(Star, { className: "size-3 fill-yellow-400 text-yellow-400" }),
2301
- /* @__PURE__ */ jsx("span", { children: view.name })
2302
- ] }),
2303
- activeViewId === view.id && /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary" })
2304
- ]
2305
- }
2306
- ),
2307
- /* @__PURE__ */ jsxs("div", { className: "absolute right-2 flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
2308
- !view.isDefault && onSetDefaultView && /* @__PURE__ */ jsx(
2309
- "button",
2310
- {
2311
- type: "button",
2312
- onClick: (e) => {
2313
- e.stopPropagation();
2314
- onSetDefaultView(view.id);
2315
- },
2316
- className: "p-1 rounded hover:bg-accent text-muted-foreground hover:text-foreground",
2317
- title: "Set as default",
2318
- children: /* @__PURE__ */ jsx(Star, { className: "size-3" })
2319
- }
2320
- ),
2321
- views.length > 1 && onDeleteView && /* @__PURE__ */ jsx(
2322
- "button",
2323
- {
2324
- type: "button",
2325
- onClick: (e) => {
2326
- e.stopPropagation();
2327
- onDeleteView(view.id);
2328
- },
2329
- className: "p-1 rounded hover:bg-accent text-muted-foreground hover:text-destructive",
2330
- title: "Delete view",
2331
- children: /* @__PURE__ */ jsx(Trash2, { className: "size-3" })
2332
- }
2333
- )
2334
- ] })
2335
- ]
2336
- },
2337
- view.id
2338
- )),
2339
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
2340
- ] }) : null,
2341
- onUpdateView && hasChanges && activeView && /* @__PURE__ */ jsxs(
2342
- DropdownMenuItem,
2343
- {
2344
- onClick: handleUpdateCurrentView,
2345
- className: fontSizeClass,
2346
- children: [
2347
- /* @__PURE__ */ jsx(Save, { className: "mr-2 size-3.5" }),
2348
- "Save changes"
2349
- ]
2350
- }
2351
- ),
2352
- onSaveView && /* @__PURE__ */ jsxs(
2353
- DropdownMenuItem,
2354
- {
2355
- onClick: () => setSaveDialogOpen(true),
2356
- className: fontSizeClass,
2357
- children: [
2358
- /* @__PURE__ */ jsx(Plus, { className: "mr-2 size-3.5" }),
2359
- "Save as new view"
2360
- ]
2361
- }
2362
- )
2363
- ] })
2364
- ] }) }),
2365
- /* @__PURE__ */ jsx(
2366
- Dialog,
2367
- {
2368
- open: saveDialogOpen,
2369
- onOpenChange: (open) => {
2370
- setSaveDialogOpen(open);
2371
- if (!open) {
2372
- setNewViewName("");
2373
- setSetAsDefault(false);
2374
- setNameError(null);
2375
- }
2376
- },
2377
- children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
2378
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2379
- /* @__PURE__ */ jsx(DialogTitle, { children: "Save View" }),
2380
- /* @__PURE__ */ jsx(DialogDescription, { children: "Save your current table configuration as a reusable view." })
2381
- ] }),
2382
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-4", children: [
2383
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2384
- /* @__PURE__ */ jsx(Label, { htmlFor: "view-name", children: "View name" }),
2385
- /* @__PURE__ */ jsx(
2386
- Input,
2387
- {
2388
- id: "view-name",
2389
- placeholder: "e.g., Active Users, Recent Signups",
2390
- value: newViewName,
2391
- onChange: (e) => {
2392
- setNewViewName(e.target.value);
2393
- checkDuplicateName(e.target.value);
2394
- },
2395
- onKeyDown: (e) => {
2396
- if (e.key === "Enter") handleSaveNewView();
2397
- },
2398
- className: nameError ? "border-destructive" : "",
2399
- autoFocus: true
2400
- }
2401
- ),
2402
- nameError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: nameError })
2403
- ] }),
2404
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2405
- /* @__PURE__ */ jsx(
2406
- "input",
2407
- {
2408
- type: "checkbox",
2409
- id: "set-default",
2410
- checked: setAsDefault,
2411
- onChange: (e) => setSetAsDefault(e.target.checked),
2412
- className: "size-4 rounded border-muted-foreground/30"
2413
- }
2414
- ),
2415
- /* @__PURE__ */ jsx(
2416
- Label,
2417
- {
2418
- htmlFor: "set-default",
2419
- className: "text-sm font-normal cursor-pointer",
2420
- children: "Set as default view"
2421
- }
2422
- )
2423
- ] })
2424
- ] }),
2425
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
2426
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setSaveDialogOpen(false), children: "Cancel" }),
2427
- /* @__PURE__ */ jsx(
2428
- Button,
2429
- {
2430
- onClick: handleSaveNewView,
2431
- disabled: !newViewName.trim() || !!nameError,
2432
- children: "Save View"
2433
- }
2434
- )
2435
- ] })
2436
- ] })
2437
- }
2438
- )
2439
- ] });
2440
- }
2441
- function Command({
2442
- className,
2443
- ...props
2444
- }) {
2445
- return /* @__PURE__ */ jsx(
2446
- Command$1,
2447
- {
2448
- className: cn(
2449
- "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-box",
2450
- className
2451
- ),
2452
- ...props
2453
- }
2454
- );
2455
- }
2456
- function CommandDialog({
2457
- children,
2458
- title = "Command palette",
2459
- ...props
2460
- }) {
2461
- return /* @__PURE__ */ jsx(Dialog, { ...props, children: /* @__PURE__ */ jsxs(DialogContent, { className: "overflow-hidden p-0", children: [
2462
- /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: title }),
2463
- /* @__PURE__ */ jsx(Command, { className: "[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", children })
2464
- ] }) });
2465
- }
2466
- function CommandInput({
2467
- className,
2468
- ...props
2469
- }) {
2470
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center border-b px-3", "cmdk-input-wrapper": "", children: [
2471
- /* @__PURE__ */ jsx(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
2472
- /* @__PURE__ */ jsx(
2473
- Command$1.Input,
2474
- {
2475
- className: cn(
2476
- "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-10 w-full rounded-box bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50",
2477
- className
2478
- ),
2479
- ...props
2480
- }
2481
- )
2482
- ] });
2483
- }
2484
- function CommandList({
2485
- className,
2486
- ...props
2487
- }) {
2488
- return /* @__PURE__ */ jsx(
2489
- Command$1.List,
2490
- {
2491
- className: cn(
2492
- "max-h-[300px] overflow-y-auto overflow-x-hidden scroll-py-1",
2493
- className
2494
- ),
2495
- ...props
2496
- }
2497
- );
2498
- }
2499
- function CommandEmpty({
2500
- className,
2501
- ...props
2502
- }) {
2503
- return /* @__PURE__ */ jsx(
2504
- Command$1.Empty,
2505
- {
2506
- className: cn("py-6 text-center text-sm", className),
2507
- ...props
2508
- }
2509
- );
2510
- }
2511
- function CommandGroup({
2512
- className,
2513
- ...props
2514
- }) {
2515
- return /* @__PURE__ */ jsx(
2516
- Command$1.Group,
2517
- {
2518
- className: cn(
2519
- "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
2520
- className
2521
- ),
2522
- ...props
2523
- }
2524
- );
2525
- }
2526
- function CommandSeparator({
2527
- className,
2528
- ...props
2529
- }) {
2530
- return /* @__PURE__ */ jsx(
2531
- Command$1.Separator,
2532
- {
2533
- className: cn("bg-border -mx-1 h-px", className),
2534
- ...props
2535
- }
2536
- );
2537
- }
2538
- function CommandItem({
2539
- className,
2540
- ...props
2541
- }) {
2542
- return /* @__PURE__ */ jsx(
2543
- Command$1.Item,
2544
- {
2545
- className: cn(
2546
- "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
2547
- className
2548
- ),
2549
- ...props
2550
- }
2551
- );
2552
- }
2553
- function CommandShortcut({
2554
- className,
2555
- ...props
2556
- }) {
2557
- return /* @__PURE__ */ jsx(
2558
- "span",
2559
- {
2560
- className: cn(
2561
- "text-muted-foreground ml-auto text-xs tracking-widest",
2562
- className
2563
- ),
2564
- ...props
2565
- }
2566
- );
2567
- }
2568
- function Combobox({
2569
- options,
2570
- value,
2571
- onValueChange,
2572
- placeholder = "Select...",
2573
- mutedPlaceholder = true,
2574
- searchPlaceholder = "Search...",
2575
- emptyText = "No results found.",
2576
- disabled = false,
2577
- allowCreate = false,
2578
- createLabel = "Create new",
2579
- onCreateNew,
2580
- loading = false,
2581
- className,
2582
- size = "default",
2583
- align = "start",
2584
- error = false,
2585
- icon,
2586
- "aria-label": ariaLabel
838
+ size = "default",
839
+ align = "start",
840
+ error = false,
841
+ icon,
842
+ "aria-label": ariaLabel
2587
843
  }) {
2588
844
  const [open, setOpen] = useState(false);
2589
845
  const [searchValue, setSearchValue] = useState("");
@@ -2754,40 +1010,6 @@ function Combobox({
2754
1010
  )
2755
1011
  ] });
2756
1012
  }
2757
- var Switch = React12.forwardRef(
2758
- ({ className, checked = false, onCheckedChange, disabled, ...props }, ref) => {
2759
- return /* @__PURE__ */ jsx(
2760
- "button",
2761
- {
2762
- type: "button",
2763
- role: "switch",
2764
- "aria-checked": checked,
2765
- "data-state": checked ? "checked" : "unchecked",
2766
- disabled,
2767
- ref,
2768
- onClick: () => onCheckedChange?.(!checked),
2769
- className: cn(
2770
- "peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors",
2771
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
2772
- "disabled:cursor-not-allowed disabled:opacity-50",
2773
- checked ? "bg-primary" : "bg-input",
2774
- className
2775
- ),
2776
- ...props,
2777
- children: /* @__PURE__ */ jsx(
2778
- "span",
2779
- {
2780
- className: cn(
2781
- "pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform",
2782
- checked ? "translate-x-4" : "translate-x-0"
2783
- )
2784
- }
2785
- )
2786
- }
2787
- );
2788
- }
2789
- );
2790
- Switch.displayName = "Switch";
2791
1013
  function MobilePeriodStepper({
2792
1014
  periodLabel,
2793
1015
  onPrev,
@@ -3053,11 +1275,11 @@ function PeriodSelector({
3053
1275
  className
3054
1276
  }) {
3055
1277
  const fiscalMonth = normalizeFiscalYearEndMonth(fiscalYearEndMonth);
3056
- const now = React12.useMemo(() => /* @__PURE__ */ new Date(), []);
1278
+ const now = React5.useMemo(() => /* @__PURE__ */ new Date(), []);
3057
1279
  const selectedStart = safeParseDate(periodStart, now);
3058
1280
  const selectedEnd = safeParseDate(periodEnd, now);
3059
1281
  const anchorDate = granularity === "custom" ? selectedEnd : selectedStart;
3060
- const ranges = React12.useMemo(() => {
1282
+ const ranges = React5.useMemo(() => {
3061
1283
  const map = /* @__PURE__ */ new Map();
3062
1284
  for (const row of GRANULARITY_ROWS) {
3063
1285
  const rowAnchor = row.key === granularity ? anchorDate : now;
@@ -3069,7 +1291,7 @@ function PeriodSelector({
3069
1291
  return map;
3070
1292
  }, [anchorDate, fiscalMonth, granularity, now]);
3071
1293
  const activeLabel = allDates ? "All dates" : formatRangeLabel(granularity, selectedStart, selectedEnd);
3072
- const applyRange = React12.useCallback(
1294
+ const applyRange = React5.useCallback(
3073
1295
  (nextGranularity, nextRange) => {
3074
1296
  onChange({
3075
1297
  granularity: nextGranularity,
@@ -3081,7 +1303,7 @@ function PeriodSelector({
3081
1303
  },
3082
1304
  [onChange]
3083
1305
  );
3084
- const applyAllDates = React12.useCallback(() => {
1306
+ const applyAllDates = React5.useCallback(() => {
3085
1307
  onChange({
3086
1308
  granularity,
3087
1309
  periodStart: "",
@@ -3090,7 +1312,7 @@ function PeriodSelector({
3090
1312
  allDates: true
3091
1313
  });
3092
1314
  }, [granularity, onChange]);
3093
- const selectGranularity = React12.useCallback(
1315
+ const selectGranularity = React5.useCallback(
3094
1316
  (nextGranularity) => {
3095
1317
  const range = ranges.get(nextGranularity);
3096
1318
  if (!range) return;
@@ -3098,7 +1320,7 @@ function PeriodSelector({
3098
1320
  },
3099
1321
  [applyRange, ranges]
3100
1322
  );
3101
- const navigate = React12.useCallback(
1323
+ const navigate = React5.useCallback(
3102
1324
  (rowGranularity, direction) => {
3103
1325
  const current = !allDates && granularity === rowGranularity ? { start: selectedStart, end: selectedEnd } : ranges.get(rowGranularity);
3104
1326
  if (!current) return;
@@ -3919,7 +2141,7 @@ function PieChartView({
3919
2141
  fill: card.color
3920
2142
  }));
3921
2143
  const pieTotal = pieData.reduce((sum, item) => sum + item.value, 0);
3922
- const activeSliceKeys = React12.useMemo(() => {
2144
+ const activeSliceKeys = React5.useMemo(() => {
3923
2145
  if (!activeMetric) return /* @__PURE__ */ new Set();
3924
2146
  const computed = computedMetrics?.find((m) => m.key === activeMetric);
3925
2147
  if (computed) {
@@ -3927,7 +2149,7 @@ function PieChartView({
3927
2149
  }
3928
2150
  return /* @__PURE__ */ new Set([activeMetric]);
3929
2151
  }, [activeMetric, computedMetrics]);
3930
- const renderSlice = React12.useCallback(
2152
+ const renderSlice = React5.useCallback(
3931
2153
  (props) => {
3932
2154
  const {
3933
2155
  cx,
@@ -4127,38 +2349,38 @@ function FinancialSummaryChart({
4127
2349
  periodLabel: periodLabelProp,
4128
2350
  className
4129
2351
  }) {
4130
- const [expanded, setExpanded] = React12.useState(
2352
+ const [expanded, setExpanded] = React5.useState(
4131
2353
  () => resolveDefaultExpanded(config.defaultExpanded)
4132
2354
  );
4133
- const [activeChartType, setActiveChartType] = React12.useState(
2355
+ const [activeChartType, setActiveChartType] = React5.useState(
4134
2356
  config.chartType ?? "area"
4135
2357
  );
4136
- const [activeMetric, setActiveMetric] = React12.useState(null);
4137
- const [flippedCards, setFlippedCards] = React12.useState(
2358
+ const [activeMetric, setActiveMetric] = React5.useState(null);
2359
+ const [flippedCards, setFlippedCards] = React5.useState(
4138
2360
  /* @__PURE__ */ new Set()
4139
2361
  );
4140
- const [mobileCardIndex, setMobileCardIndex] = React12.useState(0);
4141
- const [useSwipeCards, setUseSwipeCards] = React12.useState(false);
4142
- const touchStartRef = React12.useRef(null);
4143
- const touchDeltaRef = React12.useRef({ x: 0, y: 0 });
2362
+ const [mobileCardIndex, setMobileCardIndex] = React5.useState(0);
2363
+ const [useSwipeCards, setUseSwipeCards] = React5.useState(false);
2364
+ const touchStartRef = React5.useRef(null);
2365
+ const touchDeltaRef = React5.useRef({ x: 0, y: 0 });
4144
2366
  const divisor = config.chartDisplayUnits && config.chartDisplayUnits !== "auto" ? getUnitDivisor(config.chartDisplayUnits) : 1;
4145
- const { chartData, summaryCards } = React12.useMemo(
2367
+ const { chartData, summaryCards } = React5.useMemo(
4146
2368
  () => buildChartData(data, config, timeUnit, priorTotalsProp),
4147
2369
  [data, config, timeUnit, priorTotalsProp]
4148
2370
  );
4149
- const visibleSummaryCards = React12.useMemo(() => {
2371
+ const visibleSummaryCards = React5.useMemo(() => {
4150
2372
  if (!config.statCardKeys?.length) return summaryCards;
4151
2373
  const cardByKey = new Map(summaryCards.map((card) => [card.key, card]));
4152
2374
  return config.statCardKeys.map((key) => cardByKey.get(key)).filter((card) => Boolean(card));
4153
2375
  }, [config.statCardKeys, summaryCards]);
4154
2376
  const cardFlipEnabled = config.enableCardFlip ?? true;
4155
- React12.useEffect(() => {
2377
+ React5.useEffect(() => {
4156
2378
  setExpanded(resolveDefaultExpanded(config.defaultExpanded));
4157
2379
  }, [config.defaultExpanded]);
4158
- React12.useEffect(() => {
2380
+ React5.useEffect(() => {
4159
2381
  setActiveChartType(config.chartType ?? "area");
4160
2382
  }, [config.chartType]);
4161
- const allMetrics = React12.useMemo(() => {
2383
+ const allMetrics = React5.useMemo(() => {
4162
2384
  const baseChartType = config.chartType && config.chartType !== "pie" ? config.chartType : "area";
4163
2385
  return [
4164
2386
  ...config.metrics.map((m) => ({
@@ -4179,7 +2401,7 @@ function FinancialSummaryChart({
4179
2401
  }))
4180
2402
  ];
4181
2403
  }, [config]);
4182
- const axisValueDisplay = React12.useMemo(() => {
2404
+ const axisValueDisplay = React5.useMemo(() => {
4183
2405
  if (allMetrics.length === 0) return { format: "currency" };
4184
2406
  const first = allMetrics[0]?.valueDisplay ?? { format: "currency" };
4185
2407
  const firstFormat = first.format ?? "currency";
@@ -4190,12 +2412,12 @@ function FinancialSummaryChart({
4190
2412
  });
4191
2413
  return sameBase ? first : { format: "currency" };
4192
2414
  }, [allMetrics]);
4193
- React12.useEffect(() => {
2415
+ React5.useEffect(() => {
4194
2416
  setMobileCardIndex(
4195
2417
  (prev) => Math.min(Math.max(prev, 0), Math.max(visibleSummaryCards.length - 1, 0))
4196
2418
  );
4197
2419
  }, [visibleSummaryCards.length]);
4198
- React12.useEffect(() => {
2420
+ React5.useEffect(() => {
4199
2421
  if (typeof window === "undefined") return;
4200
2422
  const coarsePointerQuery = window.matchMedia(
4201
2423
  "(hover: none) and (pointer: coarse)"
@@ -4214,10 +2436,10 @@ function FinancialSummaryChart({
4214
2436
  desktopWidthQuery.removeEventListener("change", syncSwipeMode);
4215
2437
  };
4216
2438
  }, []);
4217
- const handleCardClick = React12.useCallback((key) => {
2439
+ const handleCardClick = React5.useCallback((key) => {
4218
2440
  setActiveMetric((prev) => prev === key ? null : key);
4219
2441
  }, []);
4220
- const handleFlip = React12.useCallback(
2442
+ const handleFlip = React5.useCallback(
4221
2443
  (e, key) => {
4222
2444
  e.stopPropagation();
4223
2445
  setFlippedCards((prev) => {
@@ -4229,7 +2451,7 @@ function FinancialSummaryChart({
4229
2451
  },
4230
2452
  []
4231
2453
  );
4232
- const goToMobileCard = React12.useCallback(
2454
+ const goToMobileCard = React5.useCallback(
4233
2455
  (nextIndex) => {
4234
2456
  setMobileCardIndex(
4235
2457
  Math.min(
@@ -4240,7 +2462,7 @@ function FinancialSummaryChart({
4240
2462
  },
4241
2463
  [visibleSummaryCards.length]
4242
2464
  );
4243
- const handleTouchStart = React12.useCallback(
2465
+ const handleTouchStart = React5.useCallback(
4244
2466
  (e) => {
4245
2467
  if (visibleSummaryCards.length <= 1) return;
4246
2468
  const touch = e.touches[0];
@@ -4249,7 +2471,7 @@ function FinancialSummaryChart({
4249
2471
  },
4250
2472
  [visibleSummaryCards.length]
4251
2473
  );
4252
- const handleTouchMove = React12.useCallback(
2474
+ const handleTouchMove = React5.useCallback(
4253
2475
  (e) => {
4254
2476
  if (!touchStartRef.current || visibleSummaryCards.length <= 1) return;
4255
2477
  const touch = e.touches[0];
@@ -4260,7 +2482,7 @@ function FinancialSummaryChart({
4260
2482
  },
4261
2483
  [visibleSummaryCards.length]
4262
2484
  );
4263
- const handleTouchEnd = React12.useCallback(() => {
2485
+ const handleTouchEnd = React5.useCallback(() => {
4264
2486
  if (!touchStartRef.current || visibleSummaryCards.length <= 1) return;
4265
2487
  const { x, y } = touchDeltaRef.current;
4266
2488
  const swipeThreshold = 40;
@@ -4271,7 +2493,7 @@ function FinancialSummaryChart({
4271
2493
  touchStartRef.current = null;
4272
2494
  touchDeltaRef.current = { x: 0, y: 0 };
4273
2495
  }, [goToMobileCard, mobileCardIndex, visibleSummaryCards.length]);
4274
- const getMetricOpacity = React12.useCallback(
2496
+ const getMetricOpacity = React5.useCallback(
4275
2497
  (key) => {
4276
2498
  if (!activeMetric) return 1;
4277
2499
  if (key === activeMetric) return 1;
@@ -4287,7 +2509,7 @@ function FinancialSummaryChart({
4287
2509
  [activeMetric, config.computedMetrics]
4288
2510
  );
4289
2511
  const hasChartToggle = !!config.chartTypes && config.chartTypes.length > 1;
4290
- const renderSummaryCard = React12.useCallback(
2512
+ const renderSummaryCard = React5.useCallback(
4291
2513
  (card) => {
4292
2514
  const cardValueDisplay = card.valueDisplay ?? axisValueDisplay;
4293
2515
  const isPositiveTrend = card.key === "expenses" ? (card.trend ?? 0) < 0 : (card.trend ?? 0) > 0;
@@ -5185,8 +3407,8 @@ function SummaryPanel({
5185
3407
  renderChart,
5186
3408
  chartControls
5187
3409
  }) {
5188
- const [expanded, setExpanded] = React12.useState(defaultExpanded);
5189
- const [activeChartType, setActiveChartType] = React12.useState(defaultChartType);
3410
+ const [expanded, setExpanded] = React5.useState(defaultExpanded);
3411
+ const [activeChartType, setActiveChartType] = React5.useState(defaultChartType);
5190
3412
  const formatFn = formatValue ?? defaultFormat;
5191
3413
  const hasChart = !!chartData && chartData.length > 0 && !!chartSeries || !!renderChart;
5192
3414
  const hasChartToggle = chartTypes && chartTypes.length > 1;
@@ -5358,7 +3580,7 @@ function SummaryPanel({
5358
3580
  ] });
5359
3581
  }
5360
3582
 
5361
- // src/blocks/data-table/UnifiedTableControls.schema.ts
3583
+ // src/blocks/finance/UnifiedTableControls.schema.ts
5362
3584
  var unifiedTableControlOrder = [
5363
3585
  "period",
5364
3586
  "comparison",
@@ -5403,11 +3625,11 @@ function useUnifiedTableSettings(tableId, settings) {
5403
3625
  );
5404
3626
  }
5405
3627
  function useIsMobile(breakpoint = 768) {
5406
- const [isMobile, setIsMobile] = React12.useState(() => {
3628
+ const [isMobile, setIsMobile] = React5.useState(() => {
5407
3629
  if (typeof window === "undefined") return false;
5408
3630
  return window.innerWidth < breakpoint;
5409
3631
  });
5410
- React12.useEffect(() => {
3632
+ React5.useEffect(() => {
5411
3633
  const onResize = () => setIsMobile(window.innerWidth < breakpoint);
5412
3634
  onResize();
5413
3635
  window.addEventListener("resize", onResize);
@@ -5421,12 +3643,12 @@ function UnifiedTableControls({
5421
3643
  className,
5422
3644
  ...props
5423
3645
  }) {
5424
- const normalized = React12.useMemo(
3646
+ const normalized = React5.useMemo(
5425
3647
  () => normalizeUnifiedTableControls(controls),
5426
3648
  [controls]
5427
3649
  );
5428
3650
  const isMobile = useIsMobile();
5429
- const [mobileOpen, setMobileOpen] = React12.useState(false);
3651
+ const [mobileOpen, setMobileOpen] = React5.useState(false);
5430
3652
  if (!normalized.length) return null;
5431
3653
  if (!isMobile) {
5432
3654
  return /* @__PURE__ */ jsx(
@@ -5535,4 +3757,4 @@ function UnifiedTableControls({
5535
3757
  ] });
5536
3758
  }
5537
3759
 
5538
- export { AreaLineChart, CASH_FLOW_CHART_CONFIG, Calendar, ChartTooltipRow, ChartTooltipShell, ChartTooltipTitle, ColumnChart, Combobox, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ComparisonSelector, DataTableAddButton, DataTableColumnMenuSub, DataTableExportButton, DataTablePagination, DataTableRowActions, DataTableSearch, DataTableSettings, DataTableSkeleton, DataTableSummary, DataTableToolbar, DataTableTopper, DataTableViewOptions, DataTableViews, DatePicker, DateRangePicker, FinancialStatementControls, FinancialSummaryChart, INCOME_STATEMENT_CHART_CONFIG, IconButton, MobilePeriodStepper, MobileStatementActions, MobileStatementFilters, ModernCalendar, PeriodSelector, PieChartView2 as PieChartView, Skeleton, SummaryPanel, Switch, UnifiedTableControls, createFinanceTableSettingsItems, exportToCsv, exportToExcel, iconButtonVariants, normalizeUnifiedTableControls, unifiedTableControlOrder, useDataTableSettings, useUnifiedTableSettings };
3760
+ export { AreaLineChart, CASH_FLOW_CHART_CONFIG, ChartTooltipRow, ChartTooltipShell, ChartTooltipTitle, ColumnChart, Combobox, ComparisonSelector, DataTableSettings, DatePicker, DateRangePicker, FinancialStatementControls, FinancialSummaryChart, INCOME_STATEMENT_CHART_CONFIG, MobilePeriodStepper, MobileStatementActions, MobileStatementFilters, ModernCalendar, PeriodSelector, PieChartView2 as PieChartView, SummaryPanel, Switch, UnifiedTableControls, createFinanceTableSettingsItems, normalizeUnifiedTableControls, unifiedTableControlOrder, useDataTableSettings, useUnifiedTableSettings };