@caffeinebounce/ui 0.62.2

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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/dist/FinancialStatementTable-BNUrdTU4.d.cts +249 -0
  4. package/dist/FinancialStatementTable-BNUrdTU4.d.ts +249 -0
  5. package/dist/NewsletterSignup-BPreFi5W.d.cts +37 -0
  6. package/dist/NewsletterSignup-BPreFi5W.d.ts +37 -0
  7. package/dist/TestimonialsSection-DtPu2OeR.d.ts +736 -0
  8. package/dist/TestimonialsSection-z-yotRfv.d.cts +736 -0
  9. package/dist/UnifiedTableControls-CG299y9h.d.ts +1233 -0
  10. package/dist/UnifiedTableControls-CIjRpNhy.d.cts +1233 -0
  11. package/dist/blog.d.cts +99 -0
  12. package/dist/blog.d.ts +99 -0
  13. package/dist/blog.js +292 -0
  14. package/dist/blog.mjs +279 -0
  15. package/dist/button-BHnoRUkF.d.cts +34 -0
  16. package/dist/button-BHnoRUkF.d.ts +34 -0
  17. package/dist/charts.d.cts +80 -0
  18. package/dist/charts.d.ts +80 -0
  19. package/dist/charts.js +19 -0
  20. package/dist/charts.mjs +2 -0
  21. package/dist/chunk-33JR5SYE.js +44 -0
  22. package/dist/chunk-3NDB4YPC.js +247 -0
  23. package/dist/chunk-5RW4PYZ6.js +1504 -0
  24. package/dist/chunk-5THF4YP6.mjs +155 -0
  25. package/dist/chunk-6BUBNZPG.mjs +164 -0
  26. package/dist/chunk-6DVWMJ3H.mjs +7029 -0
  27. package/dist/chunk-6TGV2DNP.js +120 -0
  28. package/dist/chunk-72JGQ7IJ.mjs +1498 -0
  29. package/dist/chunk-7DHKQODW.mjs +2932 -0
  30. package/dist/chunk-7IKHZKAC.js +160 -0
  31. package/dist/chunk-ADIDI7AJ.js +11 -0
  32. package/dist/chunk-BFWX5GJZ.mjs +940 -0
  33. package/dist/chunk-BMFQG6RA.mjs +5537 -0
  34. package/dist/chunk-C4UT5V3P.js +65 -0
  35. package/dist/chunk-CC4KUIKC.js +2004 -0
  36. package/dist/chunk-D3QXPL47.mjs +1814 -0
  37. package/dist/chunk-DQWHYBV2.js +36 -0
  38. package/dist/chunk-E42B4GSB.js +993 -0
  39. package/dist/chunk-ESZ7CBA2.js +1007 -0
  40. package/dist/chunk-EYJJQVV4.js +111 -0
  41. package/dist/chunk-FRRS2YZH.mjs +129 -0
  42. package/dist/chunk-G5TQWWQA.mjs +37 -0
  43. package/dist/chunk-GAIQJCAZ.js +5612 -0
  44. package/dist/chunk-GMBGWOCR.mjs +48 -0
  45. package/dist/chunk-GUSTCU2W.mjs +105 -0
  46. package/dist/chunk-HBLKVQIQ.mjs +23 -0
  47. package/dist/chunk-HMAMMC72.js +26 -0
  48. package/dist/chunk-IMKLN273.mjs +9 -0
  49. package/dist/chunk-J6OLBSZR.js +250 -0
  50. package/dist/chunk-JAE45W2T.js +28 -0
  51. package/dist/chunk-JORVSG52.js +606 -0
  52. package/dist/chunk-KB7UXIQZ.mjs +984 -0
  53. package/dist/chunk-KGGQ5NZV.js +186 -0
  54. package/dist/chunk-KHCC5UKW.mjs +22 -0
  55. package/dist/chunk-KRRIRS6A.mjs +1945 -0
  56. package/dist/chunk-LQCVRWVF.js +48 -0
  57. package/dist/chunk-MHZXUOGN.mjs +168 -0
  58. package/dist/chunk-MPZFPU4X.js +349 -0
  59. package/dist/chunk-MVYRNJA6.js +74 -0
  60. package/dist/chunk-N6AFNFZR.mjs +565 -0
  61. package/dist/chunk-OFQLWSUR.mjs +214 -0
  62. package/dist/chunk-OLAAXLXY.mjs +219 -0
  63. package/dist/chunk-OZZWDIOY.js +1859 -0
  64. package/dist/chunk-PZRI43EZ.js +111 -0
  65. package/dist/chunk-Q256HRYD.js +176 -0
  66. package/dist/chunk-Q52ZEL62.mjs +223 -0
  67. package/dist/chunk-R26Y7LGJ.js +142 -0
  68. package/dist/chunk-RBUYW6QQ.js +39 -0
  69. package/dist/chunk-RVEFPVZF.mjs +43 -0
  70. package/dist/chunk-SORWLKOK.js +96 -0
  71. package/dist/chunk-STEP6C4C.mjs +33 -0
  72. package/dist/chunk-T5R6HUDG.mjs +22 -0
  73. package/dist/chunk-T6ZBA6VJ.js +7109 -0
  74. package/dist/chunk-UM7WMXIU.js +2995 -0
  75. package/dist/chunk-VBD4Q5LL.mjs +117 -0
  76. package/dist/chunk-WOITB4JL.js +195 -0
  77. package/dist/chunk-YKZEKJMZ.mjs +71 -0
  78. package/dist/chunk-YL7RZRHM.mjs +103 -0
  79. package/dist/chunk-YSLD2HLP.mjs +346 -0
  80. package/dist/chunk-ZFLZA7UP.mjs +140 -0
  81. package/dist/chunk-ZKUIUE7W.mjs +26 -0
  82. package/dist/chunk-ZOA7B32D.js +248 -0
  83. package/dist/data-table.d.cts +46 -0
  84. package/dist/data-table.d.ts +46 -0
  85. package/dist/data-table.js +270 -0
  86. package/dist/data-table.mjs +13 -0
  87. package/dist/editor.d.cts +89 -0
  88. package/dist/editor.d.ts +89 -0
  89. package/dist/editor.js +10 -0
  90. package/dist/editor.mjs +1 -0
  91. package/dist/forms.d.cts +142 -0
  92. package/dist/forms.d.ts +142 -0
  93. package/dist/forms.js +24 -0
  94. package/dist/forms.mjs +11 -0
  95. package/dist/hooks-8ORd7T1A.d.cts +751 -0
  96. package/dist/hooks-B2jU1GVT.d.ts +751 -0
  97. package/dist/hooks.d.cts +6 -0
  98. package/dist/hooks.d.ts +6 -0
  99. package/dist/hooks.js +99 -0
  100. package/dist/hooks.mjs +10 -0
  101. package/dist/index.d.cts +3141 -0
  102. package/dist/index.d.ts +3141 -0
  103. package/dist/index.js +7332 -0
  104. package/dist/index.mjs +5876 -0
  105. package/dist/layouts-DPfDDF53.d.cts +1001 -0
  106. package/dist/layouts-DPfDDF53.d.ts +1001 -0
  107. package/dist/layouts.d.cts +4 -0
  108. package/dist/layouts.d.ts +4 -0
  109. package/dist/layouts.js +100 -0
  110. package/dist/layouts.mjs +11 -0
  111. package/dist/marketing-3d.d.cts +51 -0
  112. package/dist/marketing-3d.d.ts +51 -0
  113. package/dist/marketing-3d.js +553 -0
  114. package/dist/marketing-3d.mjs +526 -0
  115. package/dist/marketing.d.cts +583 -0
  116. package/dist/marketing.d.ts +583 -0
  117. package/dist/marketing.js +299 -0
  118. package/dist/marketing.mjs +10 -0
  119. package/dist/media.d.cts +29 -0
  120. package/dist/media.d.ts +29 -0
  121. package/dist/media.js +189 -0
  122. package/dist/media.mjs +182 -0
  123. package/dist/navigation.d.cts +591 -0
  124. package/dist/navigation.d.ts +591 -0
  125. package/dist/navigation.js +108 -0
  126. package/dist/navigation.mjs +15 -0
  127. package/dist/portal.d.cts +58 -0
  128. package/dist/portal.d.ts +58 -0
  129. package/dist/portal.js +403 -0
  130. package/dist/portal.mjs +395 -0
  131. package/dist/primitives.d.cts +508 -0
  132. package/dist/primitives.d.ts +508 -0
  133. package/dist/primitives.js +295 -0
  134. package/dist/primitives.mjs +14 -0
  135. package/dist/settings.d.cts +216 -0
  136. package/dist/settings.d.ts +216 -0
  137. package/dist/settings.js +34 -0
  138. package/dist/settings.mjs +9 -0
  139. package/dist/shared-Dqn36QHZ.d.cts +4 -0
  140. package/dist/shared-Dqn36QHZ.d.ts +4 -0
  141. package/dist/styles.css +1213 -0
  142. package/package.json +189 -0
  143. package/styles.css +1 -0
@@ -0,0 +1,2932 @@
1
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-6BUBNZPG.mjs';
2
+ import { Button } from './chunk-VBD4Q5LL.mjs';
3
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent } from './chunk-OFQLWSUR.mjs';
4
+ import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from './chunk-YKZEKJMZ.mjs';
5
+ import { Input } from './chunk-STEP6C4C.mjs';
6
+ import { cn } from './chunk-IMKLN273.mjs';
7
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
8
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+ import { MoreHorizontal, Trash2, Check, X, Plus, GripVertical, ChevronDown, ChevronRight, Filter, ArrowDown, ArrowUp, ListOrdered, WrapText, EyeOff, ChevronsDownUp, ChevronsUpDown, Text, List, CheckSquare, Calendar, Hash, BookOpen, Info, DollarSign, TrendingUp, CheckCircle2, AlertTriangle } from 'lucide-react';
10
+ import * as React3 from 'react';
11
+ import { flexRender, useReactTable, getExpandedRowModel, getCoreRowModel } from '@tanstack/react-table';
12
+
13
+ var Popover = PopoverPrimitive.Root;
14
+ var PopoverTrigger = PopoverPrimitive.Trigger;
15
+ var PopoverAnchor = PopoverPrimitive.Anchor;
16
+ function PopoverContent({
17
+ className,
18
+ align = "center",
19
+ sideOffset = 4,
20
+ ...props
21
+ }) {
22
+ return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
23
+ PopoverPrimitive.Content,
24
+ {
25
+ align,
26
+ sideOffset,
27
+ className: cn(
28
+ "bg-popover text-popover-foreground 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 z-50 w-72 rounded-box border p-4 shadow-md outline-none",
29
+ className
30
+ ),
31
+ ...props
32
+ }
33
+ ) });
34
+ }
35
+ function Table({ className, ...props }) {
36
+ return /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx(
37
+ "table",
38
+ {
39
+ "data-slot": "table",
40
+ className: cn("w-full caption-bottom text-sm", className),
41
+ ...props
42
+ }
43
+ ) });
44
+ }
45
+ function TableHeader({ className, ...props }) {
46
+ return /* @__PURE__ */ jsx(
47
+ "thead",
48
+ {
49
+ "data-slot": "table-header",
50
+ className: cn("[&_tr]:border-b", className),
51
+ ...props
52
+ }
53
+ );
54
+ }
55
+ function TableBody({ className, ...props }) {
56
+ return /* @__PURE__ */ jsx(
57
+ "tbody",
58
+ {
59
+ "data-slot": "table-body",
60
+ className: cn("[&_tr:last-child]:border-0", className),
61
+ ...props
62
+ }
63
+ );
64
+ }
65
+ function TableFooter({ className, ...props }) {
66
+ return /* @__PURE__ */ jsx(
67
+ "tfoot",
68
+ {
69
+ "data-slot": "table-footer",
70
+ className: cn(
71
+ "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
72
+ className
73
+ ),
74
+ ...props
75
+ }
76
+ );
77
+ }
78
+ function TableRow({ className, ...props }) {
79
+ return /* @__PURE__ */ jsx(
80
+ "tr",
81
+ {
82
+ "data-slot": "table-row",
83
+ className: cn(
84
+ "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
85
+ className
86
+ ),
87
+ ...props
88
+ }
89
+ );
90
+ }
91
+ function TableHead({ className, ...props }) {
92
+ return /* @__PURE__ */ jsx(
93
+ "th",
94
+ {
95
+ "data-slot": "table-head",
96
+ className: cn(
97
+ "text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
98
+ className
99
+ ),
100
+ ...props
101
+ }
102
+ );
103
+ }
104
+ function TableCell({ className, ...props }) {
105
+ return /* @__PURE__ */ jsx(
106
+ "td",
107
+ {
108
+ "data-slot": "table-cell",
109
+ className: cn(
110
+ "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
111
+ className
112
+ ),
113
+ ...props
114
+ }
115
+ );
116
+ }
117
+ function TableCaption({
118
+ className,
119
+ ...props
120
+ }) {
121
+ return /* @__PURE__ */ jsx(
122
+ "caption",
123
+ {
124
+ "data-slot": "table-caption",
125
+ className: cn("text-muted-foreground mt-4 text-sm", className),
126
+ ...props
127
+ }
128
+ );
129
+ }
130
+ var colorMap = {
131
+ green: "#22c55e",
132
+ red: "#ef4444",
133
+ blue: "#3b82f6",
134
+ yellow: "#eab308",
135
+ orange: "#f97316",
136
+ purple: "#a855f7",
137
+ pink: "#ec4899",
138
+ gray: "#6b7280",
139
+ slate: "#64748b"
140
+ };
141
+ function getColorValue(color) {
142
+ if (!color) return void 0;
143
+ return colorMap[color.toLowerCase()] ?? color;
144
+ }
145
+ function TextLikeFilterBody({
146
+ type,
147
+ placeholder,
148
+ value,
149
+ onValueChange,
150
+ onApply,
151
+ onClear
152
+ }) {
153
+ return /* @__PURE__ */ jsxs("div", { className: "px-2 py-2", children: [
154
+ /* @__PURE__ */ jsx(
155
+ Input,
156
+ {
157
+ type: type === "number" ? "number" : void 0,
158
+ placeholder,
159
+ value,
160
+ onChange: (e) => onValueChange(e.target.value),
161
+ onKeyDown: (e) => {
162
+ if (e.key === "Enter") {
163
+ e.preventDefault();
164
+ onApply();
165
+ }
166
+ },
167
+ className: "h-8 text-xs",
168
+ autoFocus: true
169
+ }
170
+ ),
171
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mt-2 gap-2", children: [
172
+ value && /* @__PURE__ */ jsx(
173
+ Button,
174
+ {
175
+ variant: "ghost",
176
+ size: "sm",
177
+ className: "h-7 text-xs px-2 text-muted-foreground hover:text-foreground",
178
+ onClick: onClear,
179
+ children: "Clear"
180
+ }
181
+ ),
182
+ /* @__PURE__ */ jsx(
183
+ Button,
184
+ {
185
+ size: "sm",
186
+ className: "h-7 text-xs px-3 ml-auto",
187
+ onClick: onApply,
188
+ children: "Apply"
189
+ }
190
+ )
191
+ ] })
192
+ ] });
193
+ }
194
+ function DataTableColumnFilterContent({
195
+ title,
196
+ value,
197
+ onChange,
198
+ onClear,
199
+ onDelete,
200
+ filterType = "text",
201
+ filterOptions,
202
+ placeholder,
203
+ showDeleteMenu = true
204
+ }) {
205
+ const [localValue, setLocalValue] = React3.useState(value ?? "");
206
+ const [operator, setOperator] = React3.useState("is");
207
+ React3.useEffect(() => setLocalValue(value ?? ""), [value]);
208
+ const handleApply = () => onChange(localValue || void 0);
209
+ const handleClear = () => {
210
+ setLocalValue("");
211
+ onClear?.();
212
+ onChange(void 0);
213
+ };
214
+ const selectedValues = React3.useMemo(() => {
215
+ if (!localValue) return /* @__PURE__ */ new Set();
216
+ return new Set(localValue.split(",").filter(Boolean));
217
+ }, [localValue]);
218
+ const toggleOption = (optionValue) => {
219
+ const newSelected = new Set(selectedValues);
220
+ if (newSelected.has(optionValue)) {
221
+ newSelected.delete(optionValue);
222
+ } else {
223
+ newSelected.add(optionValue);
224
+ }
225
+ const newValue = Array.from(newSelected).join(",");
226
+ setLocalValue(newValue);
227
+ onChange(newValue || void 0);
228
+ };
229
+ const hasValue = Boolean(localValue);
230
+ return /* @__PURE__ */ jsxs("div", { className: "w-52", children: [
231
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-3 py-2 border-b border-border", children: [
232
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: title }),
233
+ /* @__PURE__ */ jsxs(
234
+ Select,
235
+ {
236
+ value: operator,
237
+ onValueChange: (v) => setOperator(v),
238
+ children: [
239
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "!h-auto !min-h-0 !py-0.5 w-auto gap-1 border-0 shadow-none bg-muted/50 px-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-muted rounded focus:ring-0 focus:ring-offset-0 focus-visible:ring-0", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
240
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
241
+ /* @__PURE__ */ jsx(SelectItem, { value: "is", className: "text-xs", children: "is" }),
242
+ /* @__PURE__ */ jsx(SelectItem, { value: "is_not", className: "text-xs", children: "is not" }),
243
+ filterType === "text" && /* @__PURE__ */ jsxs(Fragment, { children: [
244
+ /* @__PURE__ */ jsx(SelectItem, { value: "contains", className: "text-xs", children: "contains" }),
245
+ /* @__PURE__ */ jsx(SelectItem, { value: "does_not_contain", className: "text-xs", children: "does not contain" })
246
+ ] }),
247
+ /* @__PURE__ */ jsx(SelectItem, { value: "is_empty", className: "text-xs", children: "is empty" }),
248
+ /* @__PURE__ */ jsx(SelectItem, { value: "is_not_empty", className: "text-xs", children: "is not empty" })
249
+ ] })
250
+ ]
251
+ }
252
+ ),
253
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
254
+ showDeleteMenu && hasValue && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
255
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost-icon", size: "icon-sm", className: "size-5", children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-3.5" }) }) }),
256
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", side: "right", sideOffset: 4, children: /* @__PURE__ */ jsxs(
257
+ DropdownMenuItem,
258
+ {
259
+ onClick: onDelete ?? handleClear,
260
+ className: "text-xs gap-2",
261
+ children: [
262
+ /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" }),
263
+ "Delete filter"
264
+ ]
265
+ }
266
+ ) })
267
+ ] })
268
+ ] }),
269
+ /* @__PURE__ */ jsxs("div", { className: "p-1", children: [
270
+ filterType === "text" && /* @__PURE__ */ jsx(
271
+ TextLikeFilterBody,
272
+ {
273
+ placeholder: placeholder ?? `Search ${title.toLowerCase()}...`,
274
+ value: localValue,
275
+ onValueChange: setLocalValue,
276
+ onApply: handleApply,
277
+ onClear: handleClear
278
+ }
279
+ ),
280
+ filterType === "number" && /* @__PURE__ */ jsx(
281
+ TextLikeFilterBody,
282
+ {
283
+ type: "number",
284
+ placeholder: placeholder ?? `Enter ${title.toLowerCase()}...`,
285
+ value: localValue,
286
+ onValueChange: setLocalValue,
287
+ onApply: handleApply,
288
+ onClear: handleClear
289
+ }
290
+ ),
291
+ filterType === "select" && filterOptions && /* @__PURE__ */ jsx("div", { className: "max-h-64 overflow-y-auto px-1", children: filterOptions.map((option) => {
292
+ const isSelected = selectedValues.has(option.value);
293
+ const colorValue = getColorValue(option.color);
294
+ return /* @__PURE__ */ jsxs(
295
+ "button",
296
+ {
297
+ type: "button",
298
+ className: cn(
299
+ "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-xs transition-colors",
300
+ "hover:bg-muted",
301
+ isSelected && "bg-muted/50"
302
+ ),
303
+ onClick: () => toggleOption(option.value),
304
+ children: [
305
+ /* @__PURE__ */ jsx(
306
+ "div",
307
+ {
308
+ className: cn(
309
+ "flex size-3.5 shrink-0 items-center justify-center rounded-sm border self-center",
310
+ isSelected ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/30"
311
+ ),
312
+ children: isSelected && /* @__PURE__ */ jsx(Check, { className: "size-2.5" })
313
+ }
314
+ ),
315
+ option.badge ? /* @__PURE__ */ jsx("span", { className: "shrink-0 flex items-center", children: option.badge }) : /* @__PURE__ */ jsxs(Fragment, { children: [
316
+ (colorValue || option.icon) && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: option.icon ? option.icon : /* @__PURE__ */ jsx(
317
+ "span",
318
+ {
319
+ className: "inline-block size-2 rounded-full",
320
+ style: { backgroundColor: colorValue }
321
+ }
322
+ ) }),
323
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label })
324
+ ] })
325
+ ]
326
+ },
327
+ option.value
328
+ );
329
+ }) }),
330
+ filterType === "boolean" && /* @__PURE__ */ jsx("div", { className: "px-1", children: (filterOptions ?? [
331
+ { label: "Yes", value: "true", color: "green" },
332
+ { label: "No", value: "false", color: "red" }
333
+ ]).map((opt) => {
334
+ const isSelected = selectedValues.has(opt.value);
335
+ const colorValue = opt.color ? getColorValue(opt.color) : void 0;
336
+ let iconElement = null;
337
+ if (opt.icon) {
338
+ if (React3.isValidElement(opt.icon)) {
339
+ iconElement = opt.icon;
340
+ } else {
341
+ const Icon = opt.icon;
342
+ iconElement = /* @__PURE__ */ jsx(Icon, { className: "size-3" });
343
+ }
344
+ }
345
+ return /* @__PURE__ */ jsxs(
346
+ "button",
347
+ {
348
+ type: "button",
349
+ className: cn(
350
+ "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-xs transition-colors",
351
+ "hover:bg-muted",
352
+ isSelected && "bg-muted/50"
353
+ ),
354
+ onClick: () => toggleOption(opt.value),
355
+ children: [
356
+ /* @__PURE__ */ jsx(
357
+ "div",
358
+ {
359
+ className: cn(
360
+ "flex size-3.5 shrink-0 items-center justify-center rounded-sm border self-center",
361
+ isSelected ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/30"
362
+ ),
363
+ children: isSelected && /* @__PURE__ */ jsx(Check, { className: "size-2.5" })
364
+ }
365
+ ),
366
+ opt.badge ? /* @__PURE__ */ jsx("span", { className: "flex items-center", children: opt.badge }) : /* @__PURE__ */ jsxs(Fragment, { children: [
367
+ colorValue && /* @__PURE__ */ jsx(
368
+ "span",
369
+ {
370
+ className: "inline-block size-2 rounded-full",
371
+ style: { backgroundColor: colorValue }
372
+ }
373
+ ),
374
+ iconElement && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground [&>svg]:size-3", children: iconElement }),
375
+ /* @__PURE__ */ jsx("span", { children: opt.label })
376
+ ] })
377
+ ]
378
+ },
379
+ opt.value
380
+ );
381
+ }) })
382
+ ] }),
383
+ (filterType === "select" || filterType === "boolean") && selectedValues.size > 0 && /* @__PURE__ */ jsx("div", { className: "border-t border-border px-1 py-1", children: /* @__PURE__ */ jsx(
384
+ "button",
385
+ {
386
+ type: "button",
387
+ className: "flex w-full items-center rounded-md px-2 py-1.5 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
388
+ onClick: handleClear,
389
+ children: "Clear filter"
390
+ }
391
+ ) })
392
+ ] });
393
+ }
394
+ function DataTableColumnFilter({
395
+ column,
396
+ title,
397
+ filterType = "text",
398
+ filterOptions,
399
+ placeholder,
400
+ children,
401
+ onFilterChange
402
+ }) {
403
+ const [open, setOpen] = React3.useState(false);
404
+ const current = column.getFilterValue();
405
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
406
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children }),
407
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "w-56 p-0", sideOffset: 4, children: /* @__PURE__ */ jsx(
408
+ DataTableColumnFilterContent,
409
+ {
410
+ title,
411
+ value: current,
412
+ onChange: (v) => {
413
+ column.setFilterValue(v);
414
+ onFilterChange?.(column.id, v);
415
+ },
416
+ onClear: () => column.setFilterValue(void 0),
417
+ filterType,
418
+ filterOptions,
419
+ placeholder,
420
+ showDeleteMenu: false
421
+ }
422
+ ) })
423
+ ] });
424
+ }
425
+ function DataTableFilterBadges({
426
+ filters,
427
+ onRemoveFilter,
428
+ onChangeFilter,
429
+ onClearAll,
430
+ onAddFilter,
431
+ className,
432
+ ...props
433
+ }) {
434
+ if (filters.length === 0 && !onAddFilter) {
435
+ return null;
436
+ }
437
+ return /* @__PURE__ */ jsxs(
438
+ "div",
439
+ {
440
+ className: cn("flex flex-wrap items-center gap-1.5", className),
441
+ ...props,
442
+ children: [
443
+ filters.map((filter) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [
444
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
445
+ "button",
446
+ {
447
+ type: "button",
448
+ className: cn(
449
+ "inline-flex items-center gap-1 rounded border border-sky-200 dark:border-sky-800 bg-sky-50 dark:bg-sky-950/30 px-1.5 py-0.5",
450
+ "text-xs hover:bg-sky-100 dark:hover:bg-sky-900/40 transition-colors cursor-pointer"
451
+ ),
452
+ children: [
453
+ filter.icon && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground shrink-0 [&>svg]:size-3", children: React3.isValidElement(filter.icon) ? filter.icon : (() => {
454
+ const Icon = filter.icon;
455
+ return /* @__PURE__ */ jsx(Icon, { className: "size-3" });
456
+ })() }),
457
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: filter.label }),
458
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/50", children: "\xB7" }),
459
+ filter.valueBadge ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center", children: filter.valueBadge }) : /* @__PURE__ */ jsx("span", { className: "font-medium truncate max-w-28", children: filter.value }),
460
+ /* @__PURE__ */ jsx(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ onPointerDown: (e) => {
465
+ e.stopPropagation();
466
+ e.preventDefault();
467
+ },
468
+ onClick: (e) => {
469
+ e.stopPropagation();
470
+ e.preventDefault();
471
+ onRemoveFilter(filter.id);
472
+ },
473
+ className: "ml-0.5 rounded-sm p-0.5 hover:bg-sky-200/50 dark:hover:bg-sky-800/50 -mr-0.5",
474
+ "aria-label": `Remove ${filter.label} filter`,
475
+ children: /* @__PURE__ */ jsx(X, { className: "size-3 text-muted-foreground" })
476
+ }
477
+ )
478
+ ]
479
+ }
480
+ ) }),
481
+ /* @__PURE__ */ jsx(DropdownMenuContent, { sideOffset: 4, align: "start", className: "p-0", children: /* @__PURE__ */ jsx(
482
+ DataTableColumnFilterContent,
483
+ {
484
+ title: filter.label,
485
+ value: filter.rawValue ?? filter.value,
486
+ onChange: (v) => onChangeFilter?.(filter.id, v),
487
+ onClear: () => onRemoveFilter(filter.id),
488
+ onDelete: () => onRemoveFilter(filter.id),
489
+ filterType: filter.filterType,
490
+ filterOptions: filter.filterOptions,
491
+ placeholder: filter.filterPlaceholder,
492
+ showDeleteMenu: true
493
+ }
494
+ ) })
495
+ ] }, filter.id)),
496
+ onAddFilter && /* @__PURE__ */ jsxs(
497
+ "button",
498
+ {
499
+ type: "button",
500
+ onClick: onAddFilter,
501
+ className: cn(
502
+ "inline-flex items-center gap-1 rounded px-1.5 py-0.5",
503
+ "text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
504
+ ),
505
+ children: [
506
+ /* @__PURE__ */ jsx(Plus, { className: "size-3" }),
507
+ /* @__PURE__ */ jsx("span", { children: "Filter" })
508
+ ]
509
+ }
510
+ ),
511
+ onClearAll && filters.length > 0 && /* @__PURE__ */ jsx(
512
+ "button",
513
+ {
514
+ type: "button",
515
+ onClick: onClearAll,
516
+ className: "text-xs text-muted-foreground hover:text-foreground ml-0.5",
517
+ children: "Clear"
518
+ }
519
+ )
520
+ ]
521
+ }
522
+ );
523
+ }
524
+ var DataTableContext = React3.createContext(null);
525
+ function useDataTableContext() {
526
+ return React3.useContext(DataTableContext);
527
+ }
528
+ function TreeExpandCycleButton({
529
+ table,
530
+ density
531
+ }) {
532
+ const depthRef = React3.useRef(-1);
533
+ const maxDepth = React3.useMemo(() => {
534
+ let max = 0;
535
+ const walk = (rows) => {
536
+ for (const row of rows) {
537
+ if (row.depth > max) max = row.depth;
538
+ if (row.subRows?.length) walk(row.subRows);
539
+ }
540
+ };
541
+ walk(
542
+ table.getPreExpandedRowModel?.()?.rows ?? table.getCoreRowModel().rows
543
+ );
544
+ return max;
545
+ }, [table]);
546
+ const handleClick = React3.useCallback(() => {
547
+ const nextDepth = depthRef.current + 1;
548
+ if (nextDepth > maxDepth) {
549
+ table.toggleAllRowsExpanded(false);
550
+ depthRef.current = -1;
551
+ return;
552
+ }
553
+ const newExpanded = {};
554
+ const allRows = table.getPreExpandedRowModel?.()?.rows ?? table.getCoreRowModel().rows;
555
+ const walk = (rows) => {
556
+ for (const row of rows) {
557
+ if (row.depth <= nextDepth && row.getCanExpand()) {
558
+ newExpanded[row.id] = true;
559
+ }
560
+ if (row.subRows?.length) walk(row.subRows);
561
+ }
562
+ };
563
+ walk(allRows);
564
+ table.setExpanded(newExpanded);
565
+ depthRef.current = nextDepth;
566
+ }, [table, maxDepth]);
567
+ const isAllExpanded = table.getIsAllRowsExpanded();
568
+ const isSomeExpanded = table.getIsSomeRowsExpanded();
569
+ const iconSize = density === "compact" ? "size-3.5" : "size-4";
570
+ return /* @__PURE__ */ jsx(
571
+ "button",
572
+ {
573
+ type: "button",
574
+ onClick: handleClick,
575
+ className: cn(
576
+ "inline-flex items-center justify-center shrink-0 text-muted-foreground hover:text-foreground rounded hover:bg-accent transition-colors",
577
+ density === "compact" ? "size-5 ml-0.5" : "size-6 ml-1"
578
+ ),
579
+ "aria-label": isAllExpanded ? "Collapse all rows" : "Expand next level",
580
+ title: isAllExpanded ? "Collapse all" : isSomeExpanded ? "Expand next level" : "Expand all",
581
+ children: isAllExpanded ? /* @__PURE__ */ jsx(ChevronsDownUp, { className: iconSize }) : isSomeExpanded ? /* @__PURE__ */ jsx(ChevronsUpDown, { className: iconSize }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: iconSize })
582
+ }
583
+ );
584
+ }
585
+ function DataTable({
586
+ table,
587
+ columns,
588
+ actionBar,
589
+ floatingBar,
590
+ filters,
591
+ onRemoveFilter,
592
+ onChangeFilter,
593
+ onClearAllFilters,
594
+ onAddFilter,
595
+ density = "comfy",
596
+ fontSize = "sm",
597
+ enableColumnResizing = false,
598
+ rowSelectionStyle = "hover",
599
+ enableRowDrag = true,
600
+ onRowDragEnd,
601
+ enableColumnDrag = false,
602
+ columnOrder: externalColumnOrder,
603
+ onColumnOrderChange,
604
+ columnWrapping: externalColumnWrapping,
605
+ onColumnWrappingChange,
606
+ onRowClick,
607
+ summary,
608
+ enableTreeView = false,
609
+ treeIndentPx = 20,
610
+ getRowClassName: getRowClassName2,
611
+ stickyColumns = 0,
612
+ stickyColumnIds: explicitStickyColumnIds,
613
+ className,
614
+ children,
615
+ ...props
616
+ }) {
617
+ const cellPadding = density === "compact" ? "px-1.5 py-0.5" : "px-2 py-1";
618
+ const headerHeight = density === "compact" ? "h-7" : "h-8";
619
+ const fontSizeClass = {
620
+ xs: "text-[10px]",
621
+ sm: "text-xs",
622
+ md: "text-sm",
623
+ lg: "text-base"
624
+ }[fontSize];
625
+ const checkboxSize = {
626
+ xs: "size-3.5",
627
+ sm: "size-4",
628
+ md: "size-4.5",
629
+ lg: "size-5"
630
+ }[fontSize];
631
+ const checkmarkSize = {
632
+ xs: "size-2",
633
+ sm: "size-2.5",
634
+ md: "size-3",
635
+ lg: "size-3.5"
636
+ }[fontSize];
637
+ const [columnSizing, setColumnSizing] = React3.useState({});
638
+ const [resizingColumn, setResizingColumn] = React3.useState(
639
+ null
640
+ );
641
+ const [hoveredRowId, setHoveredRowId] = React3.useState(null);
642
+ const defaultColumnOrder = React3.useMemo(
643
+ () => columns.map(
644
+ (col) => col.id ? col.id : "accessorKey" in col ? String(col.accessorKey) : ""
645
+ ),
646
+ [columns]
647
+ );
648
+ const [internalColumnOrder, setInternalColumnOrder] = React3.useState(defaultColumnOrder);
649
+ const columnOrder = externalColumnOrder ?? internalColumnOrder;
650
+ const setColumnOrder = onColumnOrderChange ?? setInternalColumnOrder;
651
+ const renderedLeafColumns = React3.useMemo(() => {
652
+ let leafColumns = table.getVisibleLeafColumns();
653
+ if (rowSelectionStyle === "hover" || enableRowDrag) {
654
+ leafColumns = leafColumns.filter((column) => column.id !== "select");
655
+ }
656
+ if (enableColumnDrag) {
657
+ leafColumns = [...leafColumns].sort((a, b) => {
658
+ const aIndex = columnOrder.indexOf(a.id);
659
+ const bIndex = columnOrder.indexOf(b.id);
660
+ if (aIndex === -1) return 1;
661
+ if (bIndex === -1) return -1;
662
+ return aIndex - bIndex;
663
+ });
664
+ }
665
+ return leafColumns;
666
+ }, [table, rowSelectionStyle, enableRowDrag, enableColumnDrag, columnOrder]);
667
+ const renderedLeafIndexById = React3.useMemo(() => {
668
+ const indexById = /* @__PURE__ */ new Map();
669
+ renderedLeafColumns.forEach((column, index) => {
670
+ indexById.set(column.id, index);
671
+ });
672
+ return indexById;
673
+ }, [renderedLeafColumns]);
674
+ const stickyLeafColumns = React3.useMemo(() => {
675
+ if (explicitStickyColumnIds && explicitStickyColumnIds.length > 0) {
676
+ const stickyIdSet = new Set(explicitStickyColumnIds);
677
+ return renderedLeafColumns.filter((column) => stickyIdSet.has(column.id));
678
+ }
679
+ if (!stickyColumns || stickyColumns <= 0) return [];
680
+ return renderedLeafColumns.slice(0, stickyColumns);
681
+ }, [renderedLeafColumns, stickyColumns, explicitStickyColumnIds]);
682
+ const stickyColumnIds = React3.useMemo(
683
+ () => new Set(stickyLeafColumns.map((column) => column.id)),
684
+ [stickyLeafColumns]
685
+ );
686
+ const stickyColumnLefts = React3.useMemo(() => {
687
+ const lefts = /* @__PURE__ */ new Map();
688
+ let runningLeft = 0;
689
+ for (const column of stickyLeafColumns) {
690
+ lefts.set(column.id, runningLeft);
691
+ runningLeft += column.getSize();
692
+ }
693
+ return lefts;
694
+ }, [stickyLeafColumns]);
695
+ const renderedTableWidth = React3.useMemo(() => {
696
+ return renderedLeafColumns.reduce((total, column) => {
697
+ const resizedWidth = columnSizing[column.id];
698
+ const baseWidth = resizedWidth ?? column.getSize();
699
+ const minWidth = column.columnDef.minSize ?? 0;
700
+ const maxWidth = column.columnDef.maxSize;
701
+ const clamped = Math.max(
702
+ minWidth,
703
+ maxWidth !== void 0 ? Math.min(baseWidth, maxWidth) : baseWidth
704
+ );
705
+ return total + clamped;
706
+ }, 0);
707
+ }, [renderedLeafColumns, columnSizing]);
708
+ const [internalColumnWrapping, setInternalColumnWrapping] = React3.useState({});
709
+ const columnWrapping = externalColumnWrapping ?? internalColumnWrapping;
710
+ const setColumnWrapping = onColumnWrappingChange ?? setInternalColumnWrapping;
711
+ const [draggedColumnId, setDraggedColumnId] = React3.useState(
712
+ null
713
+ );
714
+ const [dragOverColumnId, setDragOverColumnId] = React3.useState(
715
+ null
716
+ );
717
+ const [dragOverColumnPosition, setDragOverColumnPosition] = React3.useState(null);
718
+ const handleColumnDragStart = React3.useCallback(
719
+ (e, columnId) => {
720
+ setDraggedColumnId(columnId);
721
+ e.dataTransfer.effectAllowed = "move";
722
+ e.dataTransfer.setData("text/plain", columnId);
723
+ requestAnimationFrame(() => {
724
+ e.target.closest("th")?.classList.add("opacity-50");
725
+ });
726
+ },
727
+ []
728
+ );
729
+ const handleColumnDragOver = React3.useCallback(
730
+ (e, columnId) => {
731
+ e.preventDefault();
732
+ e.dataTransfer.dropEffect = "move";
733
+ if (draggedColumnId !== null && draggedColumnId !== columnId) {
734
+ const rect = e.currentTarget.getBoundingClientRect();
735
+ const midX = rect.left + rect.width / 2;
736
+ const position = e.clientX < midX ? "left" : "right";
737
+ setDragOverColumnId(columnId);
738
+ setDragOverColumnPosition(position);
739
+ }
740
+ },
741
+ [draggedColumnId]
742
+ );
743
+ const handleColumnDragLeave = React3.useCallback(() => {
744
+ setDragOverColumnId(null);
745
+ setDragOverColumnPosition(null);
746
+ }, []);
747
+ const handleColumnDrop = React3.useCallback(
748
+ (e, targetColumnId) => {
749
+ e.preventDefault();
750
+ if (draggedColumnId && draggedColumnId !== targetColumnId) {
751
+ const currentOrder = [...columnOrder];
752
+ const fromIndex = currentOrder.indexOf(draggedColumnId);
753
+ let toIndex = currentOrder.indexOf(targetColumnId);
754
+ if (fromIndex !== -1 && toIndex !== -1) {
755
+ currentOrder.splice(fromIndex, 1);
756
+ toIndex = currentOrder.indexOf(targetColumnId);
757
+ if (dragOverColumnPosition === "right") {
758
+ toIndex += 1;
759
+ }
760
+ currentOrder.splice(toIndex, 0, draggedColumnId);
761
+ setColumnOrder(currentOrder);
762
+ }
763
+ }
764
+ setDraggedColumnId(null);
765
+ setDragOverColumnId(null);
766
+ setDragOverColumnPosition(null);
767
+ },
768
+ [draggedColumnId, columnOrder, dragOverColumnPosition, setColumnOrder]
769
+ );
770
+ const handleColumnDragEnd = React3.useCallback((e) => {
771
+ e.target.closest("th")?.classList.remove("opacity-50");
772
+ setDraggedColumnId(null);
773
+ setDragOverColumnId(null);
774
+ setDragOverColumnPosition(null);
775
+ }, []);
776
+ const toggleColumnWrapping = React3.useCallback(
777
+ (columnId) => {
778
+ setColumnWrapping({
779
+ ...columnWrapping,
780
+ [columnId]: !columnWrapping[columnId]
781
+ });
782
+ },
783
+ [columnWrapping, setColumnWrapping]
784
+ );
785
+ const [draggedRowIndex, setDraggedRowIndex] = React3.useState(
786
+ null
787
+ );
788
+ const [dragOverRowIndex, setDragOverRowIndex] = React3.useState(
789
+ null
790
+ );
791
+ const [dragOverPosition, setDragOverPosition] = React3.useState(null);
792
+ const handleRowDragStart = React3.useCallback(
793
+ (e, rowIndex) => {
794
+ setDraggedRowIndex(rowIndex);
795
+ e.dataTransfer.effectAllowed = "move";
796
+ e.dataTransfer.setData("text/plain", String(rowIndex));
797
+ requestAnimationFrame(() => {
798
+ e.target.closest("tr")?.classList.add("opacity-50");
799
+ });
800
+ },
801
+ []
802
+ );
803
+ const handleRowDragOver = React3.useCallback(
804
+ (e, rowIndex) => {
805
+ e.preventDefault();
806
+ e.dataTransfer.dropEffect = "move";
807
+ if (draggedRowIndex !== null && draggedRowIndex !== rowIndex) {
808
+ const rect = e.currentTarget.getBoundingClientRect();
809
+ const midY = rect.top + rect.height / 2;
810
+ const position = e.clientY < midY ? "above" : "below";
811
+ setDragOverRowIndex(rowIndex);
812
+ setDragOverPosition(position);
813
+ }
814
+ },
815
+ [draggedRowIndex]
816
+ );
817
+ const handleRowDragLeave = React3.useCallback(() => {
818
+ setDragOverRowIndex(null);
819
+ setDragOverPosition(null);
820
+ }, []);
821
+ const handleRowDrop = React3.useCallback(
822
+ (e, toIndex) => {
823
+ e.preventDefault();
824
+ const fromIndex = draggedRowIndex;
825
+ if (fromIndex !== null && fromIndex !== toIndex && onRowDragEnd) {
826
+ let targetIndex = toIndex;
827
+ if (dragOverPosition === "below") {
828
+ targetIndex = toIndex + 1;
829
+ }
830
+ if (fromIndex < targetIndex) {
831
+ targetIndex -= 1;
832
+ }
833
+ if (fromIndex !== targetIndex) {
834
+ onRowDragEnd(fromIndex, targetIndex);
835
+ }
836
+ }
837
+ setDraggedRowIndex(null);
838
+ setDragOverRowIndex(null);
839
+ setDragOverPosition(null);
840
+ },
841
+ [draggedRowIndex, dragOverPosition, onRowDragEnd]
842
+ );
843
+ const handleRowDragEnd = React3.useCallback((e) => {
844
+ e.target.closest("tr")?.classList.remove("opacity-50");
845
+ setDraggedRowIndex(null);
846
+ setDragOverRowIndex(null);
847
+ setDragOverPosition(null);
848
+ }, []);
849
+ const handleResizeStart = React3.useCallback(
850
+ (columnId, startX, startWidth) => {
851
+ setResizingColumn(columnId);
852
+ const handleMouseMove = (e) => {
853
+ const delta = e.clientX - startX;
854
+ const newWidth = Math.max(20, startWidth + delta);
855
+ setColumnSizing((prev) => ({ ...prev, [columnId]: newWidth }));
856
+ };
857
+ const handleMouseUp = () => {
858
+ setResizingColumn(null);
859
+ document.removeEventListener("mousemove", handleMouseMove);
860
+ document.removeEventListener("mouseup", handleMouseUp);
861
+ document.body.style.cursor = "";
862
+ document.body.style.userSelect = "";
863
+ };
864
+ document.body.style.cursor = "col-resize";
865
+ document.body.style.userSelect = "none";
866
+ document.addEventListener("mousemove", handleMouseMove);
867
+ document.addEventListener("mouseup", handleMouseUp);
868
+ },
869
+ []
870
+ );
871
+ const contextValue = React3.useMemo(
872
+ () => ({
873
+ columnWrapping,
874
+ toggleColumnWrapping,
875
+ enableColumnDrag: enableColumnDrag ?? false,
876
+ onColumnDragStart: handleColumnDragStart,
877
+ onColumnDragEnd: handleColumnDragEnd,
878
+ fontSize,
879
+ fontSizeClass,
880
+ density
881
+ }),
882
+ [
883
+ columnWrapping,
884
+ toggleColumnWrapping,
885
+ enableColumnDrag,
886
+ handleColumnDragStart,
887
+ handleColumnDragEnd,
888
+ fontSize,
889
+ fontSizeClass,
890
+ density
891
+ ]
892
+ );
893
+ return /* @__PURE__ */ jsx(DataTableContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
894
+ "div",
895
+ {
896
+ className: cn(
897
+ "flex w-full max-w-full min-w-0 flex-col gap-2.5",
898
+ className
899
+ ),
900
+ ...props,
901
+ children: [
902
+ children,
903
+ filters && filters.length > 0 || onAddFilter ? /* @__PURE__ */ jsx("div", { className: "px-1", children: /* @__PURE__ */ jsx(
904
+ DataTableFilterBadges,
905
+ {
906
+ filters: filters ?? [],
907
+ onRemoveFilter: (id) => onRemoveFilter?.(id),
908
+ onChangeFilter: (id, value) => onChangeFilter?.(id, value),
909
+ onClearAll: onClearAllFilters,
910
+ onAddFilter
911
+ }
912
+ ) }) : null,
913
+ /* @__PURE__ */ jsx("div", { className: cn("mx-1", fontSizeClass), children: /* @__PURE__ */ jsx(
914
+ "div",
915
+ {
916
+ className: cn(
917
+ "overflow-x-auto rounded-t-md [&::-webkit-scrollbar-track]:bg-transparent",
918
+ summary ? "border-t" : "border rounded-b-md"
919
+ ),
920
+ children: /* @__PURE__ */ jsxs(
921
+ "table",
922
+ {
923
+ className: cn(
924
+ "caption-bottom border-separate border-spacing-0",
925
+ fontSizeClass
926
+ ),
927
+ style: {
928
+ width: `max(100%, ${renderedTableWidth}px)`,
929
+ tableLayout: "fixed"
930
+ },
931
+ children: [
932
+ /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => {
933
+ const renderableHeaders = headerGroup.headers.filter(
934
+ (h) => !((rowSelectionStyle === "hover" || enableRowDrag) && h.id === "select")
935
+ );
936
+ const orderedHeaders = enableColumnDrag ? [...renderableHeaders].sort((a, b) => {
937
+ const aIndex = columnOrder.indexOf(a.id);
938
+ const bIndex = columnOrder.indexOf(b.id);
939
+ if (aIndex === -1) return 1;
940
+ if (bIndex === -1) return -1;
941
+ return aIndex - bIndex;
942
+ }) : renderableHeaders;
943
+ return /* @__PURE__ */ jsxs(
944
+ TableRow,
945
+ {
946
+ className: "hover:bg-transparent",
947
+ children: [
948
+ (rowSelectionStyle === "hover" || enableRowDrag) && /* @__PURE__ */ jsx(
949
+ TableHead,
950
+ {
951
+ className: cn(
952
+ "p-0 border-r-0 whitespace-nowrap border-l border-b border-border group/header-gutter",
953
+ headerHeight
954
+ ),
955
+ style: { width: "1%" },
956
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-0.5 px-1 h-full", children: [
957
+ enableRowDrag && /* @__PURE__ */ jsx(
958
+ "div",
959
+ {
960
+ className: cn(
961
+ "shrink-0",
962
+ density === "compact" ? "size-5" : "size-6"
963
+ ),
964
+ "aria-hidden": "true"
965
+ }
966
+ ),
967
+ /* @__PURE__ */ jsxs(
968
+ "button",
969
+ {
970
+ type: "button",
971
+ onClick: () => table.toggleAllPageRowsSelected(
972
+ !table.getIsAllPageRowsSelected()
973
+ ),
974
+ className: cn(
975
+ "flex items-center justify-center rounded border transition-all",
976
+ checkboxSize,
977
+ table.getIsAllPageRowsSelected() ? "bg-primary border-primary text-primary-foreground opacity-100" : table.getIsSomePageRowsSelected() ? "bg-primary/50 border-primary text-primary-foreground opacity-100" : "border-border hover:border-primary/50 hover:bg-accent opacity-0 group-hover/header-gutter:opacity-100"
978
+ ),
979
+ "aria-label": table.getIsAllPageRowsSelected() ? "Deselect all rows" : "Select all rows",
980
+ children: [
981
+ table.getIsAllPageRowsSelected() && /* @__PURE__ */ jsx(
982
+ "svg",
983
+ {
984
+ className: checkmarkSize,
985
+ viewBox: "0 0 12 12",
986
+ fill: "none",
987
+ "aria-hidden": "true",
988
+ children: /* @__PURE__ */ jsx(
989
+ "path",
990
+ {
991
+ d: "M2.5 6L5 8.5L9.5 3.5",
992
+ stroke: "currentColor",
993
+ strokeWidth: "2",
994
+ strokeLinecap: "round",
995
+ strokeLinejoin: "round"
996
+ }
997
+ )
998
+ }
999
+ ),
1000
+ table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected() && /* @__PURE__ */ jsx(
1001
+ "svg",
1002
+ {
1003
+ className: checkmarkSize,
1004
+ viewBox: "0 0 12 12",
1005
+ fill: "none",
1006
+ "aria-hidden": "true",
1007
+ children: /* @__PURE__ */ jsx(
1008
+ "path",
1009
+ {
1010
+ d: "M2.5 6H9.5",
1011
+ stroke: "currentColor",
1012
+ strokeWidth: "2",
1013
+ strokeLinecap: "round"
1014
+ }
1015
+ )
1016
+ }
1017
+ )
1018
+ ]
1019
+ }
1020
+ )
1021
+ ] })
1022
+ }
1023
+ ),
1024
+ orderedHeaders.map((header, headerIndex) => {
1025
+ const isLastColumn = headerIndex === orderedHeaders.length - 1;
1026
+ const hasGutter = rowSelectionStyle === "hover" || enableRowDrag;
1027
+ const isFirstColumn = headerIndex === 0 && !hasGutter;
1028
+ const headerLeafColumnIds = header.getLeafHeaders().map((leafHeader) => leafHeader.column.id).filter(
1029
+ (columnId) => renderedLeafIndexById.has(columnId)
1030
+ );
1031
+ const stickyHeaderLeafIds = headerLeafColumnIds.filter(
1032
+ (columnId) => stickyColumnIds.has(columnId)
1033
+ );
1034
+ const isStickyColumn = headerLeafColumnIds.length > 0 && stickyHeaderLeafIds.length === headerLeafColumnIds.length;
1035
+ const stickyHeaderColumnId = isStickyColumn ? stickyHeaderLeafIds.sort(
1036
+ (a, b) => (renderedLeafIndexById.get(a) ?? 0) - (renderedLeafIndexById.get(b) ?? 0)
1037
+ )[0] : void 0;
1038
+ const stickyLeft = stickyHeaderColumnId ? stickyColumnLefts.get(stickyHeaderColumnId) ?? 0 : 0;
1039
+ const headerDef = header.column.columnDef.header;
1040
+ const isSimpleHeader = typeof headerDef === "string";
1041
+ const colDef = header.column.columnDef;
1042
+ const isFixedWidth = colDef.size && colDef.minSize === colDef.size && colDef.maxSize === colDef.size;
1043
+ const isActionsColumn = header.id === "actions" || header.id === "select";
1044
+ const resizedWidth = isFixedWidth ? void 0 : columnSizing[header.id];
1045
+ const canDragColumn = enableColumnDrag && !isFixedWidth && !isActionsColumn;
1046
+ const isDragOverLeft = dragOverColumnId === header.id && dragOverColumnPosition === "left";
1047
+ const isDragOverRight = dragOverColumnId === header.id && dragOverColumnPosition === "right";
1048
+ const firstLeafIndex = Math.min(
1049
+ ...headerLeafColumnIds.map(
1050
+ (columnId) => renderedLeafIndexById.get(columnId) ?? 0
1051
+ )
1052
+ );
1053
+ const isFirstNonStickyAfterSticky = !isStickyColumn && firstLeafIndex > 0 && stickyColumnIds.has(
1054
+ renderedLeafColumns[firstLeafIndex - 1]?.id ?? ""
1055
+ );
1056
+ return /* @__PURE__ */ jsxs(
1057
+ TableHead,
1058
+ {
1059
+ "data-sticky-column": isStickyColumn ? "true" : void 0,
1060
+ colSpan: header.colSpan,
1061
+ onDragOver: canDragColumn ? (e) => handleColumnDragOver(e, header.id) : void 0,
1062
+ onDragLeave: canDragColumn ? handleColumnDragLeave : void 0,
1063
+ onDrop: canDragColumn ? (e) => handleColumnDrop(e, header.id) : void 0,
1064
+ className: cn(
1065
+ isSimpleHeader ? "px-2" : "p-0",
1066
+ // Add padding for simple headers, let component headers handle their own
1067
+ headerHeight,
1068
+ "relative",
1069
+ "overflow-hidden text-ellipsis border-b border-border",
1070
+ !columnWrapping[header.id] && "whitespace-nowrap",
1071
+ columnWrapping[header.id] && "whitespace-normal",
1072
+ isFirstColumn && "border-l border-border",
1073
+ !isStickyColumn && "border-r border-border",
1074
+ isStickyColumn && "border-r border-border",
1075
+ isFirstNonStickyAfterSticky && "border-l border-border",
1076
+ isDragOverLeft && "border-l-2 border-l-primary",
1077
+ isDragOverRight && "border-r-2 border-r-primary"
1078
+ ),
1079
+ style: {
1080
+ ...isActionsColumn ? { width: "1%" } : {
1081
+ width: resizedWidth ?? header.getSize(),
1082
+ minWidth: header.column.columnDef.minSize,
1083
+ maxWidth: header.column.columnDef.maxSize
1084
+ },
1085
+ ...isStickyColumn ? {
1086
+ position: "sticky",
1087
+ left: stickyLeft,
1088
+ zIndex: 60,
1089
+ background: "hsl(var(--background) / 1)",
1090
+ backgroundColor: "hsl(var(--background) / 1)",
1091
+ opacity: 1,
1092
+ backgroundClip: "padding-box",
1093
+ boxShadow: "inset -1px 0 0 hsl(var(--border)), inset 0 -1px 0 hsl(var(--border))",
1094
+ isolation: "isolate",
1095
+ contain: "paint",
1096
+ backfaceVisibility: "hidden",
1097
+ mixBlendMode: "normal",
1098
+ backdropFilter: "none",
1099
+ WebkitBackdropFilter: "none",
1100
+ transform: "translateZ(0)",
1101
+ WebkitTransform: "translateZ(0)"
1102
+ } : {}
1103
+ },
1104
+ children: [
1105
+ isStickyColumn && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 bottom-px z-0 bg-background" }),
1106
+ header.isPlaceholder ? null : /* @__PURE__ */ jsxs(
1107
+ "div",
1108
+ {
1109
+ className: cn(
1110
+ "relative z-10",
1111
+ enableTreeView && headerIndex === 0 && "flex items-center"
1112
+ ),
1113
+ children: [
1114
+ enableTreeView && headerIndex === 0 && /* @__PURE__ */ jsx(
1115
+ TreeExpandCycleButton,
1116
+ {
1117
+ table,
1118
+ density
1119
+ }
1120
+ ),
1121
+ isSimpleHeader ? /* @__PURE__ */ jsx(
1122
+ "span",
1123
+ {
1124
+ className: cn("font-medium", fontSizeClass),
1125
+ children: headerDef
1126
+ }
1127
+ ) : flexRender(
1128
+ header.column.columnDef.header,
1129
+ header.getContext()
1130
+ )
1131
+ ]
1132
+ }
1133
+ ),
1134
+ enableColumnResizing && !isLastColumn && !isFixedWidth && // biome-ignore lint/a11y/noStaticElementInteractions: Column resize uses mouse drag interaction only
1135
+ /* @__PURE__ */ jsx(
1136
+ "div",
1137
+ {
1138
+ onMouseDown: (e) => {
1139
+ e.preventDefault();
1140
+ e.stopPropagation();
1141
+ const th = e.currentTarget.parentElement;
1142
+ if (th) {
1143
+ handleResizeStart(
1144
+ header.id,
1145
+ e.clientX,
1146
+ th.offsetWidth
1147
+ );
1148
+ }
1149
+ },
1150
+ className: cn(
1151
+ "absolute -right-1 top-0 z-10 h-full w-3 cursor-col-resize select-none",
1152
+ "before:absolute before:right-1 before:top-0 before:h-full before:w-px before:bg-transparent before:transition-colors",
1153
+ "hover:before:bg-primary/50",
1154
+ resizingColumn === header.id && "before:bg-primary"
1155
+ )
1156
+ }
1157
+ )
1158
+ ]
1159
+ },
1160
+ header.id
1161
+ );
1162
+ })
1163
+ ]
1164
+ },
1165
+ headerGroup.id
1166
+ );
1167
+ }) }),
1168
+ /* @__PURE__ */ jsx(
1169
+ TableBody,
1170
+ {
1171
+ className: summary ? "[&_tr:last-child]:border-b" : void 0,
1172
+ children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row, rowIndex) => {
1173
+ const isHovered = hoveredRowId === row.id;
1174
+ const isSelected = row.getIsSelected();
1175
+ const showRowControls = isHovered || isSelected;
1176
+ const isDragOverAbove = dragOverRowIndex === rowIndex && dragOverPosition === "above";
1177
+ const isDragOverBelow = dragOverRowIndex === rowIndex && dragOverPosition === "below";
1178
+ return /* @__PURE__ */ jsxs(
1179
+ TableRow,
1180
+ {
1181
+ "data-state": isSelected && "selected",
1182
+ "data-hovered": isHovered,
1183
+ onMouseEnter: () => setHoveredRowId(row.id),
1184
+ onMouseLeave: () => setHoveredRowId(null),
1185
+ onClick: () => onRowClick?.(row),
1186
+ onDragOver: enableRowDrag ? (e) => handleRowDragOver(e, rowIndex) : void 0,
1187
+ onDragLeave: enableRowDrag ? handleRowDragLeave : void 0,
1188
+ onDrop: enableRowDrag ? (e) => handleRowDrop(e, rowIndex) : void 0,
1189
+ className: cn(
1190
+ "group/row",
1191
+ onRowClick && "cursor-pointer hover:bg-muted/50",
1192
+ getRowClassName2?.(row),
1193
+ // Add bottom border to last row when summary exists
1194
+ summary && rowIndex === table.getRowModel().rows.length - 1 && "border-b"
1195
+ ),
1196
+ children: [
1197
+ (rowSelectionStyle === "hover" || enableRowDrag) && /* @__PURE__ */ jsx(
1198
+ TableCell,
1199
+ {
1200
+ className: cn(
1201
+ "p-0 border-r-0 whitespace-nowrap border-l border-border",
1202
+ // All rows get bottom border except last row without summary
1203
+ rowIndex !== table.getRowModel().rows.length - 1 && "border-b border-border",
1204
+ // Last row only gets border-b when summary exists, plus rounding
1205
+ summary && rowIndex === table.getRowModel().rows.length - 1 && "border-b border-border rounded-bl-md overflow-hidden",
1206
+ // Drag-over indicator
1207
+ isDragOverAbove && "border-t-2 border-t-primary",
1208
+ isDragOverBelow && "border-b-2 border-b-primary"
1209
+ ),
1210
+ style: { width: "1%" },
1211
+ children: /* @__PURE__ */ jsxs(
1212
+ "div",
1213
+ {
1214
+ className: cn(
1215
+ "flex items-center justify-end gap-0.5 px-1 h-full transition-opacity",
1216
+ showRowControls ? "opacity-100" : "opacity-0"
1217
+ ),
1218
+ children: [
1219
+ enableRowDrag && /* @__PURE__ */ jsx(
1220
+ "div",
1221
+ {
1222
+ draggable: true,
1223
+ onDragStart: (e) => handleRowDragStart(e, rowIndex),
1224
+ onDragEnd: handleRowDragEnd,
1225
+ className: cn(
1226
+ "flex items-center justify-center text-muted-foreground/50 hover:text-muted-foreground cursor-grab active:cursor-grabbing rounded hover:bg-accent",
1227
+ density === "compact" ? "size-5" : "size-6"
1228
+ ),
1229
+ "aria-hidden": "true",
1230
+ children: /* @__PURE__ */ jsx(
1231
+ GripVertical,
1232
+ {
1233
+ className: density === "compact" ? "size-3" : "size-4"
1234
+ }
1235
+ )
1236
+ }
1237
+ ),
1238
+ /* @__PURE__ */ jsx(
1239
+ "button",
1240
+ {
1241
+ type: "button",
1242
+ onClick: () => row.toggleSelected(!row.getIsSelected()),
1243
+ className: cn(
1244
+ "flex items-center justify-center rounded border transition-colors",
1245
+ checkboxSize,
1246
+ isSelected ? "bg-primary border-primary text-primary-foreground" : "border-border hover:border-primary/50 hover:bg-accent"
1247
+ ),
1248
+ "aria-label": isSelected ? "Deselect row" : "Select row",
1249
+ children: isSelected && /* @__PURE__ */ jsx(
1250
+ "svg",
1251
+ {
1252
+ className: checkmarkSize,
1253
+ viewBox: "0 0 12 12",
1254
+ fill: "none",
1255
+ "aria-hidden": "true",
1256
+ children: /* @__PURE__ */ jsx(
1257
+ "path",
1258
+ {
1259
+ d: "M2.5 6L5 8.5L9.5 3.5",
1260
+ stroke: "currentColor",
1261
+ strokeWidth: "2",
1262
+ strokeLinecap: "round",
1263
+ strokeLinejoin: "round"
1264
+ }
1265
+ )
1266
+ }
1267
+ )
1268
+ }
1269
+ )
1270
+ ]
1271
+ }
1272
+ )
1273
+ }
1274
+ ),
1275
+ row.getVisibleCells().map((cell) => {
1276
+ if ((rowSelectionStyle === "hover" || enableRowDrag) && cell.column.id === "select") {
1277
+ return null;
1278
+ }
1279
+ return cell;
1280
+ }).filter(
1281
+ (cell) => cell !== null
1282
+ ).sort((a, b) => {
1283
+ if (!enableColumnDrag) return 0;
1284
+ const aIndex = columnOrder.indexOf(a.column.id);
1285
+ const bIndex = columnOrder.indexOf(b.column.id);
1286
+ if (aIndex === -1) return 1;
1287
+ if (bIndex === -1) return -1;
1288
+ return aIndex - bIndex;
1289
+ }).map((cell, cellIndex, sortedCells) => {
1290
+ const isLastColumn = cellIndex === sortedCells.length - 1;
1291
+ const hasGutter = rowSelectionStyle === "hover" || enableRowDrag;
1292
+ const isFirstColumn = cellIndex === 0 && !hasGutter;
1293
+ const isStickyCell = stickyColumnIds.has(
1294
+ cell.column.id
1295
+ );
1296
+ const stickyLeft = stickyColumnLefts.get(cell.column.id) ?? 0;
1297
+ const isFirstNonStickyAfterSticky = !isStickyCell && cellIndex > 0 && stickyColumnIds.has(
1298
+ sortedCells[cellIndex - 1]?.column.id ?? ""
1299
+ );
1300
+ const colDef = cell.column.columnDef;
1301
+ const isFixedWidth = colDef.size && colDef.minSize === colDef.size && colDef.maxSize === colDef.size;
1302
+ const isActionsColumn = cell.column.id === "actions" || cell.column.id === "select";
1303
+ const resizedWidth = isFixedWidth ? void 0 : columnSizing[cell.column.id];
1304
+ const isWrapped = columnWrapping[cell.column.id];
1305
+ const colMeta = cell.column.columnDef.meta;
1306
+ const colAlign = colMeta?.align ?? "left";
1307
+ const allowHorizontalScroll = colMeta?.allowHorizontalScroll === true;
1308
+ const alignClass = colAlign === "right" ? "text-right" : colAlign === "center" ? "text-center" : "";
1309
+ const isFirstDataCell = cellIndex === 0;
1310
+ const showTreeUI = enableTreeView && isFirstDataCell;
1311
+ const treeIndent = showTreeUI ? row.depth * treeIndentPx : 0;
1312
+ const canExpand = showTreeUI && row.getCanExpand();
1313
+ return /* @__PURE__ */ jsxs(
1314
+ TableCell,
1315
+ {
1316
+ "data-sticky-column": isStickyCell ? "true" : void 0,
1317
+ className: cn(
1318
+ cellPadding,
1319
+ isStickyCell && "relative",
1320
+ !isWrapped && "overflow-hidden text-ellipsis whitespace-nowrap",
1321
+ isWrapped && "whitespace-normal break-words",
1322
+ !isLastColumn && !isStickyCell && "border-r border-border",
1323
+ isStickyCell && "border-r border-border",
1324
+ isFirstColumn && "border-l border-border",
1325
+ isFirstNonStickyAfterSticky && "border-l border-border",
1326
+ isLastColumn && "border-r border-border",
1327
+ isActionsColumn && "whitespace-nowrap",
1328
+ // All rows get bottom border except last row without summary
1329
+ rowIndex !== table.getRowModel().rows.length - 1 && "border-b border-border",
1330
+ // Last row only gets border-b when summary exists, plus rounding on corners
1331
+ summary && rowIndex === table.getRowModel().rows.length - 1 && "border-b border-border",
1332
+ summary && rowIndex === table.getRowModel().rows.length - 1 && isLastColumn && "rounded-br-md overflow-hidden",
1333
+ summary && rowIndex === table.getRowModel().rows.length - 1 && isFirstColumn && "rounded-bl-md overflow-hidden"
1334
+ ),
1335
+ style: {
1336
+ width: resizedWidth || cell.column.getSize(),
1337
+ minWidth: cell.column.columnDef.minSize,
1338
+ maxWidth: cell.column.columnDef.maxSize,
1339
+ ...isStickyCell ? {
1340
+ position: "sticky",
1341
+ left: stickyLeft,
1342
+ zIndex: 50,
1343
+ background: "hsl(var(--background) / 1)",
1344
+ backgroundColor: "hsl(var(--background) / 1)",
1345
+ opacity: 1,
1346
+ backgroundClip: "padding-box",
1347
+ boxShadow: "inset 0 0 0 9999px hsl(var(--background)), inset -1px 0 0 hsl(var(--border))",
1348
+ borderBottom: "1px solid hsl(var(--border))",
1349
+ isolation: "isolate",
1350
+ contain: "paint",
1351
+ backfaceVisibility: "hidden",
1352
+ mixBlendMode: "normal",
1353
+ backdropFilter: "none",
1354
+ WebkitBackdropFilter: "none",
1355
+ transform: "translateZ(0)",
1356
+ WebkitTransform: "translateZ(0)"
1357
+ } : {}
1358
+ },
1359
+ children: [
1360
+ isStickyCell && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 z-0 border-b border-border bg-background" }),
1361
+ showTreeUI ? /* @__PURE__ */ jsxs(
1362
+ "div",
1363
+ {
1364
+ className: "relative z-10 flex items-center gap-1",
1365
+ style: { paddingLeft: `${treeIndent}px` },
1366
+ children: [
1367
+ canExpand ? /* @__PURE__ */ jsx(
1368
+ "button",
1369
+ {
1370
+ type: "button",
1371
+ onClick: (e) => {
1372
+ e.stopPropagation();
1373
+ row.toggleExpanded();
1374
+ },
1375
+ className: "shrink-0 text-muted-foreground hover:text-foreground",
1376
+ "aria-label": row.getIsExpanded() ? "Collapse" : "Expand",
1377
+ children: row.getIsExpanded() ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1378
+ }
1379
+ ) : /* @__PURE__ */ jsx(
1380
+ "span",
1381
+ {
1382
+ className: "inline-block shrink-0 size-4",
1383
+ "aria-hidden": "true"
1384
+ }
1385
+ ),
1386
+ /* @__PURE__ */ jsx(
1387
+ "div",
1388
+ {
1389
+ className: cn(
1390
+ "min-w-0",
1391
+ !isWrapped && !allowHorizontalScroll && "truncate",
1392
+ allowHorizontalScroll && "overflow-x-auto whitespace-nowrap [&::-webkit-scrollbar]:hidden"
1393
+ ),
1394
+ style: allowHorizontalScroll ? {
1395
+ scrollbarWidth: "none",
1396
+ msOverflowStyle: "none"
1397
+ } : void 0,
1398
+ children: flexRender(
1399
+ cell.column.columnDef.cell,
1400
+ cell.getContext()
1401
+ )
1402
+ }
1403
+ )
1404
+ ]
1405
+ }
1406
+ ) : /* @__PURE__ */ jsx(
1407
+ "div",
1408
+ {
1409
+ className: cn(
1410
+ "relative z-10",
1411
+ !isWrapped && !allowHorizontalScroll && "truncate",
1412
+ allowHorizontalScroll && "overflow-x-auto whitespace-nowrap [&::-webkit-scrollbar]:hidden",
1413
+ alignClass
1414
+ ),
1415
+ style: allowHorizontalScroll ? {
1416
+ scrollbarWidth: "none",
1417
+ msOverflowStyle: "none"
1418
+ } : void 0,
1419
+ children: flexRender(
1420
+ cell.column.columnDef.cell,
1421
+ cell.getContext()
1422
+ )
1423
+ }
1424
+ )
1425
+ ]
1426
+ },
1427
+ cell.id
1428
+ );
1429
+ })
1430
+ ]
1431
+ },
1432
+ row.id
1433
+ );
1434
+ }) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
1435
+ TableCell,
1436
+ {
1437
+ colSpan: columns.length + (rowSelectionStyle === "hover" || enableRowDrag ? 1 : 0),
1438
+ className: "h-24",
1439
+ children: /* @__PURE__ */ jsx("div", { className: "sticky left-0 w-fit px-4 text-muted-foreground", children: "No results." })
1440
+ }
1441
+ ) })
1442
+ }
1443
+ ),
1444
+ summary && /* @__PURE__ */ jsx("tfoot", { children: summary })
1445
+ ]
1446
+ }
1447
+ )
1448
+ }
1449
+ ) }),
1450
+ actionBar && table.getFilteredSelectedRowModel().rows.length > 0 && actionBar,
1451
+ floatingBar && table.getFilteredSelectedRowModel().rows.length > 0 && floatingBar
1452
+ ]
1453
+ }
1454
+ ) });
1455
+ }
1456
+ var defaultColumnIcons = {
1457
+ text: Text,
1458
+ number: Hash,
1459
+ date: Calendar,
1460
+ boolean: CheckSquare,
1461
+ enum: List,
1462
+ custom: Text
1463
+ };
1464
+ function getColumnIcon(column, propIcon, propColumnType) {
1465
+ const meta = column.columnDef.meta;
1466
+ if (propIcon) return propIcon;
1467
+ if (meta?.icon) return meta.icon;
1468
+ const columnType = propColumnType ?? meta?.columnType;
1469
+ if (columnType) return defaultColumnIcons[columnType];
1470
+ return void 0;
1471
+ }
1472
+ function DataTableColumnHeader({
1473
+ column,
1474
+ title,
1475
+ className,
1476
+ columnType,
1477
+ icon,
1478
+ filterable,
1479
+ filterType,
1480
+ filterOptions,
1481
+ onFilterChange,
1482
+ isWrapped: isWrappedProp,
1483
+ onToggleWrap: onToggleWrapProp,
1484
+ customMenuItems
1485
+ }) {
1486
+ const meta = column.columnDef.meta;
1487
+ const dataTableContext = useDataTableContext();
1488
+ const isWrapped = isWrappedProp ?? dataTableContext?.columnWrapping[column.id] ?? false;
1489
+ const onToggleWrap = onToggleWrapProp ?? (() => dataTableContext?.toggleColumnWrapping(column.id));
1490
+ const canSort = column.getCanSort();
1491
+ const canHide = column.getCanHide();
1492
+ const isSorted = column.getIsSorted();
1493
+ const isFiltered = column.getIsFiltered();
1494
+ const Icon = getColumnIcon(column, icon, columnType);
1495
+ const showFilter = filterable ?? meta?.filterable ?? false;
1496
+ const resolvedFilterType = filterType ?? meta?.filterType ?? "text";
1497
+ const resolvedFilterOptions = filterOptions ?? meta?.filterOptions;
1498
+ const filterPlaceholder = meta?.filterPlaceholder;
1499
+ const [isOpen, setIsOpen] = React3.useState(false);
1500
+ const mouseDownPosRef = React3.useRef(null);
1501
+ const didDragRef = React3.useRef(false);
1502
+ const canDrag = dataTableContext?.enableColumnDrag ?? false;
1503
+ const handleMouseDown = (e) => {
1504
+ mouseDownPosRef.current = { x: e.clientX, y: e.clientY };
1505
+ didDragRef.current = false;
1506
+ };
1507
+ const handleDragStart = (e) => {
1508
+ didDragRef.current = true;
1509
+ mouseDownPosRef.current = null;
1510
+ dataTableContext?.onColumnDragStart(e, column.id);
1511
+ };
1512
+ const handleDragEnd = (e) => {
1513
+ dataTableContext?.onColumnDragEnd(e);
1514
+ };
1515
+ const handleClick = (e) => {
1516
+ if (!didDragRef.current && mouseDownPosRef.current) {
1517
+ const dx = Math.abs(e.clientX - mouseDownPosRef.current.x);
1518
+ const dy = Math.abs(e.clientY - mouseDownPosRef.current.y);
1519
+ if (dx < 5 && dy < 5) {
1520
+ setIsOpen(true);
1521
+ }
1522
+ }
1523
+ mouseDownPosRef.current = null;
1524
+ didDragRef.current = false;
1525
+ };
1526
+ const colAlign = meta?.align ?? "left";
1527
+ const justifyClass = colAlign === "right" ? "justify-end" : colAlign === "center" ? "justify-center" : "";
1528
+ if (!canSort && !canHide && !showFilter) {
1529
+ return /* @__PURE__ */ jsxs(
1530
+ "div",
1531
+ {
1532
+ className: cn(
1533
+ "flex items-center gap-1.5 px-2 h-full",
1534
+ justifyClass,
1535
+ canDrag && "cursor-grab",
1536
+ className
1537
+ ),
1538
+ draggable: canDrag,
1539
+ onDragStart: canDrag ? handleDragStart : void 0,
1540
+ onDragEnd: canDrag ? handleDragEnd : void 0,
1541
+ "aria-hidden": canDrag ? "true" : void 0,
1542
+ children: [
1543
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4 text-muted-foreground" }),
1544
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: title })
1545
+ ]
1546
+ }
1547
+ );
1548
+ }
1549
+ return /* @__PURE__ */ jsx("div", { className: cn("flex h-full items-center w-full", className), children: /* @__PURE__ */ jsxs(DropdownMenu, { open: isOpen, onOpenChange: setIsOpen, modal: false, children: [
1550
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: "w-full h-full", style: { pointerEvents: "none" }, children: /* @__PURE__ */ jsxs(
1551
+ "button",
1552
+ {
1553
+ type: "button",
1554
+ draggable: canDrag,
1555
+ onMouseDown: handleMouseDown,
1556
+ onDragStart: canDrag ? handleDragStart : void 0,
1557
+ onDragEnd: canDrag ? handleDragEnd : void 0,
1558
+ onClick: handleClick,
1559
+ style: { pointerEvents: "auto" },
1560
+ className: cn(
1561
+ "relative flex items-center gap-1.5 w-full h-full px-2",
1562
+ justifyClass || "text-left",
1563
+ "before:absolute before:-inset-[99px] before:-z-10 before:pointer-events-none before:transition-colors hover:before:bg-accent/50",
1564
+ isOpen && "before:bg-accent",
1565
+ "focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1566
+ canDrag ? "cursor-grab" : "cursor-pointer"
1567
+ ),
1568
+ children: [
1569
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "size-3.5 text-muted-foreground shrink-0" }),
1570
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: title }),
1571
+ isFiltered && /* @__PURE__ */ jsx(Filter, { className: "size-3 text-primary shrink-0" }),
1572
+ isSorted === "desc" && /* @__PURE__ */ jsx(ArrowDown, { className: "size-3.5 text-muted-foreground shrink-0" }),
1573
+ isSorted === "asc" && /* @__PURE__ */ jsx(ArrowUp, { className: "size-3.5 text-muted-foreground shrink-0" })
1574
+ ]
1575
+ }
1576
+ ) }) }),
1577
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "start", className: "w-36", children: [
1578
+ customMenuItems && /* @__PURE__ */ jsxs(Fragment, { children: [
1579
+ customMenuItems,
1580
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
1581
+ ] }),
1582
+ showFilter && /* @__PURE__ */ jsxs(Fragment, { children: [
1583
+ /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
1584
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { className: "text-xs", children: [
1585
+ /* @__PURE__ */ jsx(Filter, { className: "mr-2 size-3.5 text-muted-foreground" }),
1586
+ "Filter",
1587
+ isFiltered && /* @__PURE__ */ jsx("span", { className: "ml-auto text-[10px] text-muted-foreground", children: "Active" })
1588
+ ] }),
1589
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { alignOffset: -5, sideOffset: 5, children: /* @__PURE__ */ jsx(
1590
+ DataTableColumnFilterContent,
1591
+ {
1592
+ title,
1593
+ value: column.getFilterValue(),
1594
+ onChange: (v) => {
1595
+ column.setFilterValue(v);
1596
+ onFilterChange?.(column.id, v);
1597
+ },
1598
+ onClear: () => column.setFilterValue(void 0),
1599
+ filterType: resolvedFilterType,
1600
+ filterOptions: resolvedFilterOptions,
1601
+ placeholder: filterPlaceholder,
1602
+ showDeleteMenu: false
1603
+ }
1604
+ ) })
1605
+ ] }),
1606
+ isFiltered && /* @__PURE__ */ jsx(
1607
+ DropdownMenuItem,
1608
+ {
1609
+ onClick: () => column.setFilterValue(void 0),
1610
+ className: "text-xs",
1611
+ children: "Clear filter"
1612
+ }
1613
+ ),
1614
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
1615
+ ] }),
1616
+ canSort && /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
1617
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { className: "text-xs", children: [
1618
+ /* @__PURE__ */ jsx(ListOrdered, { className: "mr-2 size-3.5 text-muted-foreground" }),
1619
+ "Sort"
1620
+ ] }),
1621
+ /* @__PURE__ */ jsxs(DropdownMenuSubContent, { alignOffset: -5, sideOffset: 5, children: [
1622
+ /* @__PURE__ */ jsxs(
1623
+ DropdownMenuItem,
1624
+ {
1625
+ onClick: () => column.toggleSorting(false),
1626
+ className: cn("text-xs", isSorted === "asc" && "bg-accent"),
1627
+ children: [
1628
+ /* @__PURE__ */ jsx(ArrowUp, { className: "mr-2 size-3.5 text-muted-foreground" }),
1629
+ "Ascending"
1630
+ ]
1631
+ }
1632
+ ),
1633
+ /* @__PURE__ */ jsxs(
1634
+ DropdownMenuItem,
1635
+ {
1636
+ onClick: () => column.toggleSorting(true),
1637
+ className: cn("text-xs", isSorted === "desc" && "bg-accent"),
1638
+ children: [
1639
+ /* @__PURE__ */ jsx(ArrowDown, { className: "mr-2 size-3.5 text-muted-foreground" }),
1640
+ "Descending"
1641
+ ]
1642
+ }
1643
+ ),
1644
+ isSorted && /* @__PURE__ */ jsxs(Fragment, { children: [
1645
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1646
+ /* @__PURE__ */ jsx(
1647
+ DropdownMenuItem,
1648
+ {
1649
+ onClick: () => column.clearSorting(),
1650
+ className: "text-xs",
1651
+ children: "Clear sort"
1652
+ }
1653
+ )
1654
+ ] })
1655
+ ] })
1656
+ ] }),
1657
+ (dataTableContext || onToggleWrapProp) && /* @__PURE__ */ jsxs(Fragment, { children: [
1658
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1659
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: onToggleWrap, className: "text-xs", children: [
1660
+ /* @__PURE__ */ jsx(WrapText, { className: "mr-2 size-3.5 text-muted-foreground" }),
1661
+ isWrapped ? "Disable wrap" : "Wrap text"
1662
+ ] })
1663
+ ] }),
1664
+ canHide && /* @__PURE__ */ jsxs(Fragment, { children: [
1665
+ (canSort || dataTableContext || onToggleWrapProp) && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1666
+ /* @__PURE__ */ jsxs(
1667
+ DropdownMenuItem,
1668
+ {
1669
+ onClick: () => column.toggleVisibility(false),
1670
+ className: "text-xs",
1671
+ children: [
1672
+ /* @__PURE__ */ jsx(EyeOff, { className: "mr-2 size-3.5 text-muted-foreground" }),
1673
+ "Hide"
1674
+ ]
1675
+ }
1676
+ )
1677
+ ] })
1678
+ ] })
1679
+ ] }) });
1680
+ }
1681
+ var FinanceDisplayContext = React3.createContext({
1682
+ decimals: 0,
1683
+ displayUnits: "actuals",
1684
+ currencySignDisplay: "all"
1685
+ });
1686
+ var FinanceDecimalsContext = React3.createContext(0);
1687
+ function FinanceDisplayProvider({
1688
+ decimals,
1689
+ displayUnits = "actuals",
1690
+ currencySignDisplay = "all",
1691
+ children
1692
+ }) {
1693
+ const value = React3.useMemo(
1694
+ () => ({ decimals, displayUnits, currencySignDisplay }),
1695
+ [decimals, displayUnits, currencySignDisplay]
1696
+ );
1697
+ return /* @__PURE__ */ jsx(FinanceDisplayContext.Provider, { value, children: /* @__PURE__ */ jsx(FinanceDecimalsContext.Provider, { value: decimals, children }) });
1698
+ }
1699
+ function FinanceDecimalsProvider({
1700
+ decimals,
1701
+ children
1702
+ }) {
1703
+ return /* @__PURE__ */ jsx(FinanceDisplayProvider, { decimals, children });
1704
+ }
1705
+ function useFinanceDecimals() {
1706
+ return React3.useContext(FinanceDecimalsContext);
1707
+ }
1708
+ function useFinanceDisplay() {
1709
+ return React3.useContext(FinanceDisplayContext);
1710
+ }
1711
+ function getUnitDivisor(units) {
1712
+ switch (units) {
1713
+ case "thousands":
1714
+ return 1e3;
1715
+ case "millions":
1716
+ return 1e6;
1717
+ default:
1718
+ return 1;
1719
+ }
1720
+ }
1721
+ function getUnitLabel(units) {
1722
+ switch (units) {
1723
+ case "thousands":
1724
+ return "in thousands";
1725
+ case "millions":
1726
+ return "in millions";
1727
+ default:
1728
+ return null;
1729
+ }
1730
+ }
1731
+ function getUnitSuffix(units) {
1732
+ switch (units) {
1733
+ case "thousands":
1734
+ return "(000s)";
1735
+ case "millions":
1736
+ return "(M)";
1737
+ default:
1738
+ return null;
1739
+ }
1740
+ }
1741
+ function useFinanceDecimalsSetting(storageKey = "finance-decimal-places", defaultValue = 0) {
1742
+ const [decimals, setDecimalsState] = React3.useState(() => {
1743
+ if (typeof window === "undefined") return defaultValue;
1744
+ const stored = localStorage.getItem(storageKey);
1745
+ return stored !== null ? Number(stored) : defaultValue;
1746
+ });
1747
+ const setDecimals = React3.useCallback(
1748
+ (v) => {
1749
+ setDecimalsState(v);
1750
+ if (typeof window !== "undefined") {
1751
+ localStorage.setItem(storageKey, String(v));
1752
+ }
1753
+ },
1754
+ [storageKey]
1755
+ );
1756
+ return [decimals, setDecimals];
1757
+ }
1758
+ var formatterCache = /* @__PURE__ */ new Map();
1759
+ function getFormatter(locale, currencyCode, decimals) {
1760
+ const key = `${locale}:${currencyCode}:${decimals}`;
1761
+ let fmt = formatterCache.get(key);
1762
+ if (!fmt) {
1763
+ fmt = new Intl.NumberFormat(locale, {
1764
+ style: "currency",
1765
+ currency: currencyCode,
1766
+ minimumFractionDigits: decimals,
1767
+ maximumFractionDigits: decimals
1768
+ });
1769
+ formatterCache.set(key, fmt);
1770
+ }
1771
+ return fmt;
1772
+ }
1773
+ function formatCurrencyValue(value, options) {
1774
+ const raw = value ?? 0;
1775
+ const divisor = getUnitDivisor(options?.displayUnits ?? "actuals");
1776
+ const v = divisor === 1 ? raw : raw / divisor;
1777
+ const isZero = raw === 0;
1778
+ const isNegative = v < 0;
1779
+ const curr = options?.currency ?? "USD";
1780
+ const locale = options?.locale ?? "en-US";
1781
+ const dashZero = options?.dashZero ?? false;
1782
+ const decimals = options?.decimals ?? 2;
1783
+ const showSign = options?.showCurrencySign ?? true;
1784
+ if (isZero && dashZero) {
1785
+ return { text: "\u2014", isNegative: false, isZero: true };
1786
+ }
1787
+ const absV = Math.abs(v);
1788
+ let formatted;
1789
+ if (showSign) {
1790
+ const fmt = getFormatter(locale, curr, decimals);
1791
+ formatted = fmt.format(absV);
1792
+ } else {
1793
+ formatted = absV.toLocaleString(locale, {
1794
+ minimumFractionDigits: decimals,
1795
+ maximumFractionDigits: decimals
1796
+ });
1797
+ }
1798
+ if (isNegative) {
1799
+ return { text: `(${formatted})`, isNegative: true, isZero: false };
1800
+ }
1801
+ return { text: formatted, isNegative: false, isZero };
1802
+ }
1803
+ function DataTableCurrencyCell({
1804
+ value,
1805
+ currency: currencyCode = "USD",
1806
+ locale = "en-US",
1807
+ dashZero = false,
1808
+ decimals,
1809
+ className,
1810
+ /** Whether this cell is in a summary/total row */
1811
+ isSummaryRow
1812
+ }) {
1813
+ const ctx = useFinanceDisplay();
1814
+ const resolvedDecimals = decimals ?? ctx.decimals;
1815
+ const showCurrencySign = ctx.currencySignDisplay === "all" || ctx.currencySignDisplay === "summary" && isSummaryRow;
1816
+ const { text, isNegative, isZero } = formatCurrencyValue(value, {
1817
+ currency: currencyCode,
1818
+ locale,
1819
+ dashZero,
1820
+ decimals: resolvedDecimals,
1821
+ displayUnits: ctx.displayUnits,
1822
+ showCurrencySign
1823
+ });
1824
+ return /* @__PURE__ */ jsx(
1825
+ "span",
1826
+ {
1827
+ className: cn(
1828
+ "font-mono tabular-nums",
1829
+ isNegative && "text-red-600 dark:text-red-400",
1830
+ isZero && dashZero && "text-muted-foreground",
1831
+ className
1832
+ ),
1833
+ children: text
1834
+ }
1835
+ );
1836
+ }
1837
+
1838
+ // src/blocks/data-table/financialStatementRowImportance.ts
1839
+ var DEFAULT_ROW_IMPORTANCE_CONFIG = {
1840
+ "income-statement": {
1841
+ emphasis: ["Total Revenue", "Total Expenses", "Operating Income"],
1842
+ keySummary: ["Net Income"]
1843
+ },
1844
+ "balance-sheet": {
1845
+ emphasis: ["Total Liabilities", "Total Equity"],
1846
+ keySummary: ["Total Assets", "Total Liabilities & Equity"]
1847
+ },
1848
+ "cash-flow": {
1849
+ emphasis: [
1850
+ "Total Operating Activities",
1851
+ "Total Investing Activities",
1852
+ "Total Financing Activities",
1853
+ "Net Cash from Operating Activities",
1854
+ "Net Cash from Investing Activities",
1855
+ "Net Cash from Financing Activities"
1856
+ ],
1857
+ keySummary: [
1858
+ "Net Change in Cash",
1859
+ "Ending Cash",
1860
+ "Total Cash & Cash Equivalents"
1861
+ ]
1862
+ },
1863
+ "working-capital": {
1864
+ emphasis: ["Total Current Assets", "Total Current Liabilities"],
1865
+ keySummary: ["NWC", "Net Working Capital"]
1866
+ }
1867
+ };
1868
+ function normalizeLabel(value) {
1869
+ return value.trim().toLowerCase().replace(/\s+/g, " ");
1870
+ }
1871
+ function createFinancialStatementRowImportanceResolver(kind, overrides) {
1872
+ const base = DEFAULT_ROW_IMPORTANCE_CONFIG[kind];
1873
+ const emphasis = new Set(
1874
+ (overrides?.emphasis ?? base?.emphasis ?? []).map(normalizeLabel)
1875
+ );
1876
+ const keySummary = new Set(
1877
+ (overrides?.keySummary ?? base?.keySummary ?? []).map(normalizeLabel)
1878
+ );
1879
+ return (row) => {
1880
+ const label = normalizeLabel(row.name);
1881
+ if (keySummary.has(label)) return "key-summary";
1882
+ if (emphasis.has(label)) return "emphasis";
1883
+ return "normal";
1884
+ };
1885
+ }
1886
+ function getFinancialStatementRowImportanceClassName(importance) {
1887
+ switch (importance) {
1888
+ case "emphasis":
1889
+ return "bg-muted/25 border-t border-border/50 font-medium";
1890
+ case "key-summary":
1891
+ return "bg-muted/45 border-t-2 border-border/70 font-semibold";
1892
+ default:
1893
+ return void 0;
1894
+ }
1895
+ }
1896
+ var TOLERANCE = 0.01;
1897
+ function RecCheck({ computed, raw }) {
1898
+ const diff = Math.abs(computed - raw);
1899
+ const matches = diff <= TOLERANCE;
1900
+ const icon = matches ? /* @__PURE__ */ jsx(CheckCircle2, { className: "size-3.5 text-white fill-emerald-500" }) : /* @__PURE__ */ jsx(AlertTriangle, { className: "size-3.5 text-white fill-amber-500" });
1901
+ return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
1902
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { children: icon }) }),
1903
+ /* @__PURE__ */ jsx(TooltipContent, { side: "left", className: "text-[11px] max-w-none p-2", children: matches ? /* @__PURE__ */ jsx("span", { className: "text-emerald-400", children: "\u2713 Balanced \u2014 computed total matches source data" }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1904
+ /* @__PURE__ */ jsx("div", { className: "text-amber-400 font-medium", children: "\u26A0 Variance detected" }),
1905
+ /* @__PURE__ */ jsx("table", { className: "text-[11px]", children: /* @__PURE__ */ jsxs("tbody", { children: [
1906
+ /* @__PURE__ */ jsxs("tr", { children: [
1907
+ /* @__PURE__ */ jsx("td", { className: "pr-3 text-muted-foreground", children: "Computed:" }),
1908
+ /* @__PURE__ */ jsx("td", { className: "font-mono tabular-nums text-right", children: formatCurrencyValue(computed, { decimals: 2 }).text })
1909
+ ] }),
1910
+ /* @__PURE__ */ jsxs("tr", { children: [
1911
+ /* @__PURE__ */ jsx("td", { className: "pr-3 text-muted-foreground", children: "Source:" }),
1912
+ /* @__PURE__ */ jsx("td", { className: "font-mono tabular-nums text-right", children: formatCurrencyValue(raw, { decimals: 2 }).text })
1913
+ ] }),
1914
+ /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/40", children: [
1915
+ /* @__PURE__ */ jsx("td", { className: "pr-3 text-muted-foreground pt-0.5", children: "Variance:" }),
1916
+ /* @__PURE__ */ jsx("td", { className: "font-mono tabular-nums text-right pt-0.5 text-amber-400", children: formatCurrencyValue(computed - raw, { decimals: 2 }).text })
1917
+ ] })
1918
+ ] }) })
1919
+ ] }) })
1920
+ ] }) });
1921
+ }
1922
+ function splitMetricFormula(formula) {
1923
+ const lines = formula.split("\n").map((line) => line.trim()).filter(Boolean);
1924
+ return {
1925
+ equationLine: lines[0] ?? formula,
1926
+ detailLines: lines.slice(1)
1927
+ };
1928
+ }
1929
+ function MetricEquationTooltip({
1930
+ title,
1931
+ formula
1932
+ }) {
1933
+ const { equationLine } = splitMetricFormula(formula);
1934
+ const [lhs, rhs] = equationLine.split("=").map((part) => part.trim());
1935
+ if (!rhs) {
1936
+ return /* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed", children: formula });
1937
+ }
1938
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1939
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-muted-foreground/90", children: title }),
1940
+ /* @__PURE__ */ jsx("div", { className: "rounded-md border border-slate-500/15 bg-gradient-to-r from-slate-500/[0.05] via-sky-500/[0.04] to-emerald-500/[0.05] px-2.5 py-1.5", children: /* @__PURE__ */ jsxs("p", { className: "text-xs font-mono tracking-tight leading-relaxed text-foreground/85", children: [
1941
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-sky-700/85 dark:text-sky-200/85", children: lhs }),
1942
+ /* @__PURE__ */ jsx("span", { className: "px-1 text-muted-foreground/80", children: "=" }),
1943
+ /* @__PURE__ */ jsx("span", { className: "text-emerald-700/85 dark:text-emerald-200/85", children: rhs })
1944
+ ] }) })
1945
+ ] });
1946
+ }
1947
+ function toPeriodKey(date, unit) {
1948
+ const [y, m] = date.split("-").map(Number);
1949
+ if (unit === "year") return String(y);
1950
+ if (unit === "quarter") {
1951
+ const q = Math.floor((m - 1) / 3) + 1;
1952
+ return `${y}-Q${q}`;
1953
+ }
1954
+ return `${y}-${String(m).padStart(2, "0")}`;
1955
+ }
1956
+ function resolvePeriodColumns(periods, timeUnit, periodOrder, periodColumnOrder = "oldest-first") {
1957
+ const basePeriods = periodColumnOrder === "newest-first" ? [...periods].reverse() : periods;
1958
+ if (!periodOrder || periodOrder.length === 0) return basePeriods;
1959
+ const normalizedOrder = periodOrder.map((pk) => toPeriodKey(pk, timeUnit));
1960
+ const periodSet = new Set(basePeriods);
1961
+ const prioritized = Array.from(new Set(normalizedOrder)).filter(
1962
+ (pk) => periodSet.has(pk)
1963
+ );
1964
+ const remainder = basePeriods.filter((pk) => !prioritized.includes(pk));
1965
+ return [...prioritized, ...remainder];
1966
+ }
1967
+ function calculateDesktopFinancialColumnSizing({
1968
+ periods,
1969
+ rows,
1970
+ showRowTotals,
1971
+ showVariance
1972
+ }) {
1973
+ const MIN_ACCOUNT = 260;
1974
+ const MIN_DATA = 88;
1975
+ const MIN_TOTAL = 96;
1976
+ const VARIANCE_COL = showVariance ? 80 : 0;
1977
+ const TARGET_TABLE_WIDTH = 1180;
1978
+ let maxNumericChars = 1;
1979
+ for (const row of rows) {
1980
+ for (const pk of periods) {
1981
+ maxNumericChars = Math.max(
1982
+ maxNumericChars,
1983
+ Math.round(Math.abs(row.periodAmounts[pk] ?? 0)).toLocaleString().length
1984
+ );
1985
+ }
1986
+ maxNumericChars = Math.max(
1987
+ maxNumericChars,
1988
+ Math.round(Math.abs(row.total ?? 0)).toLocaleString().length
1989
+ );
1990
+ }
1991
+ let dataSize = Math.min(156, Math.max(104, maxNumericChars * 9 + 26));
1992
+ let totalSize = Math.max(MIN_TOTAL, dataSize + 8);
1993
+ let accountSize = Math.max(340, 620 - periods.length * 26);
1994
+ let tableWidth = accountSize + periods.length * dataSize + (showRowTotals ? totalSize : 0) + VARIANCE_COL;
1995
+ if (tableWidth > TARGET_TABLE_WIDTH) {
1996
+ const reducibleAccount = accountSize - MIN_ACCOUNT;
1997
+ const needed = tableWidth - TARGET_TABLE_WIDTH;
1998
+ const reduceAccount = Math.min(reducibleAccount, needed);
1999
+ accountSize -= reduceAccount;
2000
+ tableWidth -= reduceAccount;
2001
+ }
2002
+ if (tableWidth > TARGET_TABLE_WIDTH && periods.length > 0) {
2003
+ const reducibleDataPerCol = Math.max(0, dataSize - MIN_DATA);
2004
+ const totalReducible = reducibleDataPerCol * periods.length;
2005
+ const needed = tableWidth - TARGET_TABLE_WIDTH;
2006
+ const reduceTotal = Math.min(totalReducible, needed);
2007
+ const perCol = Math.floor(reduceTotal / periods.length);
2008
+ dataSize = Math.max(MIN_DATA, dataSize - perCol);
2009
+ if (showRowTotals) {
2010
+ totalSize = Math.max(MIN_TOTAL, totalSize - Math.ceil(perCol * 0.9));
2011
+ }
2012
+ }
2013
+ return {
2014
+ accountSize,
2015
+ accountMinSize: MIN_ACCOUNT,
2016
+ dataSize,
2017
+ dataMinSize: MIN_DATA,
2018
+ totalSize
2019
+ };
2020
+ }
2021
+ function toPeriodLabel(key, unit) {
2022
+ if (unit === "year") return key;
2023
+ if (unit === "quarter") return key.replace("-", " ");
2024
+ const [year, month] = key.split("-");
2025
+ const date = new Date(Number(year), Number(month) - 1);
2026
+ return date.toLocaleDateString("en-US", { month: "short", year: "2-digit" });
2027
+ }
2028
+ function sumChildPeriods(children) {
2029
+ const periodAmounts = {};
2030
+ let total = 0;
2031
+ for (const child of children) {
2032
+ total += child.total;
2033
+ for (const [pk, amt] of Object.entries(child.periodAmounts)) {
2034
+ periodAmounts[pk] = (periodAmounts[pk] || 0) + amt;
2035
+ }
2036
+ }
2037
+ return { periodAmounts, total };
2038
+ }
2039
+ function buildAccountTree(accounts, sign) {
2040
+ const byNumber = /* @__PURE__ */ new Map();
2041
+ for (const a of accounts) {
2042
+ if (a.number) byNumber.set(a.number, a);
2043
+ }
2044
+ const nameToNumber = /* @__PURE__ */ new Map();
2045
+ for (const a of accounts) {
2046
+ if (a.number && a.name) nameToNumber.set(a.name, a.number);
2047
+ }
2048
+ const synthetics = [];
2049
+ for (const a of accounts) {
2050
+ if (a.isSubAccount && a.parentNumber && a.parentNumber !== a.number && !byNumber.has(a.parentNumber)) {
2051
+ const parentName = a.parentName ?? a.name;
2052
+ const synthetic = {
2053
+ id: `synthetic-${a.parentNumber}`,
2054
+ name: parentName,
2055
+ secondaryLabel: null,
2056
+ number: a.parentNumber,
2057
+ type: a.type,
2058
+ subType: a.subType,
2059
+ isSubAccount: false,
2060
+ parentNumber: null,
2061
+ parentName: null,
2062
+ periods: {},
2063
+ total: 0
2064
+ };
2065
+ byNumber.set(a.parentNumber, synthetic);
2066
+ nameToNumber.set(parentName, a.parentNumber);
2067
+ synthetics.push(synthetic);
2068
+ }
2069
+ }
2070
+ for (const s of synthetics) {
2071
+ const lastColon = s.name.lastIndexOf(":");
2072
+ if (lastColon > 0) {
2073
+ const grandparentPath = s.name.substring(0, lastColon);
2074
+ const gpNumber = nameToNumber.get(grandparentPath);
2075
+ if (gpNumber && gpNumber !== s.number) {
2076
+ s.isSubAccount = true;
2077
+ s.parentNumber = gpNumber;
2078
+ s.parentName = grandparentPath;
2079
+ }
2080
+ }
2081
+ }
2082
+ for (const s of synthetics) {
2083
+ accounts.push(s);
2084
+ }
2085
+ const roots = [];
2086
+ const childrenOf = /* @__PURE__ */ new Map();
2087
+ for (const a of accounts) {
2088
+ if (a.isSubAccount && a.parentNumber && a.parentNumber !== a.number && byNumber.has(a.parentNumber)) {
2089
+ const existing = childrenOf.get(a.parentNumber) || [];
2090
+ existing.push(a);
2091
+ childrenOf.set(a.parentNumber, existing);
2092
+ } else {
2093
+ roots.push(a);
2094
+ }
2095
+ }
2096
+ function buildRow(acct, depth) {
2097
+ const kids = childrenOf.get(acct.number ?? "") || [];
2098
+ const childRows = kids.sort((a, b) => (a.number ?? "").localeCompare(b.number ?? "")).map((k) => buildRow(k, depth + 1));
2099
+ const periodAmounts = {};
2100
+ let total = 0;
2101
+ if (childRows.length > 0) {
2102
+ const childSums = sumChildPeriods(childRows);
2103
+ total = acct.total * sign + childSums.total;
2104
+ for (const pk of Object.keys({
2105
+ ...acct.periods,
2106
+ ...childSums.periodAmounts
2107
+ })) {
2108
+ periodAmounts[pk] = (acct.periods[pk] || 0) * sign + (childSums.periodAmounts[pk] || 0);
2109
+ }
2110
+ } else {
2111
+ total = acct.total * sign;
2112
+ for (const [pk, amt] of Object.entries(acct.periods)) {
2113
+ periodAmounts[pk] = amt * sign;
2114
+ }
2115
+ }
2116
+ return {
2117
+ _type: childRows.length > 0 ? "account-group" : "account",
2118
+ id: acct.id,
2119
+ name: acct.name.includes(":") ? acct.name.substring(acct.name.lastIndexOf(":") + 1) : acct.name,
2120
+ accountNumber: acct.number,
2121
+ secondaryLabel: acct.secondaryLabel,
2122
+ accountType: acct.type,
2123
+ accountSubType: acct.subType,
2124
+ periodAmounts,
2125
+ total,
2126
+ children: childRows,
2127
+ depth
2128
+ };
2129
+ }
2130
+ return roots.sort((a, b) => (a.number ?? "").localeCompare(b.number ?? "")).map((r) => buildRow(r, 0));
2131
+ }
2132
+ function applySubtotalRules(accountTree, rules, sectionId) {
2133
+ const sortedRules = [...rules].sort((a, b) => a.order - b.order);
2134
+ const compiled = sortedRules.map((r) => ({
2135
+ rule: r,
2136
+ regex: new RegExp(r.pattern)
2137
+ }));
2138
+ const groups = /* @__PURE__ */ new Map();
2139
+ const unmatched = [];
2140
+ for (const r of sortedRules) {
2141
+ groups.set(r.label, []);
2142
+ }
2143
+ function matchRow(row) {
2144
+ for (const { rule, regex } of compiled) {
2145
+ if (row.accountNumber && regex.test(row.accountNumber)) {
2146
+ return rule.label;
2147
+ }
2148
+ }
2149
+ return null;
2150
+ }
2151
+ let anyMatched = false;
2152
+ for (const row of accountTree) {
2153
+ const groupLabel = matchRow(row);
2154
+ if (groupLabel) {
2155
+ groups.get(groupLabel).push(row);
2156
+ anyMatched = true;
2157
+ } else {
2158
+ unmatched.push(row);
2159
+ }
2160
+ }
2161
+ if (!anyMatched) {
2162
+ return accountTree;
2163
+ }
2164
+ const result = [];
2165
+ for (const rule of sortedRules) {
2166
+ const groupRows = groups.get(rule.label) ?? [];
2167
+ if (groupRows.length === 0) continue;
2168
+ const sums = sumChildPeriods(groupRows);
2169
+ result.push({
2170
+ _type: "subtotal-group",
2171
+ id: `subtotal-${sectionId}-${rule.label.toLowerCase().replace(/\s+/g, "-")}`,
2172
+ name: rule.label,
2173
+ accountNumber: null,
2174
+ periodAmounts: sums.periodAmounts,
2175
+ total: sums.total,
2176
+ children: groupRows,
2177
+ depth: 0
2178
+ });
2179
+ }
2180
+ if (unmatched.length > 0) {
2181
+ const sums = sumChildPeriods(unmatched);
2182
+ result.push({
2183
+ _type: "subtotal-group",
2184
+ id: `subtotal-${sectionId}-other`,
2185
+ name: "Other",
2186
+ accountNumber: null,
2187
+ periodAmounts: sums.periodAmounts,
2188
+ total: sums.total,
2189
+ children: unmatched,
2190
+ depth: 0
2191
+ });
2192
+ }
2193
+ return result;
2194
+ }
2195
+ function filterZeroRows(rows) {
2196
+ return rows.reduce((acc, row) => {
2197
+ if (row._type === "section-header" || row._type === "section-total" || row._type === "grand-total") {
2198
+ if (row.children.length > 0) {
2199
+ acc.push({ ...row, children: filterZeroRows(row.children) });
2200
+ } else {
2201
+ acc.push(row);
2202
+ }
2203
+ return acc;
2204
+ }
2205
+ if (row.children.length > 0) {
2206
+ const filteredChildren = filterZeroRows(row.children);
2207
+ if (filteredChildren.length === 0) {
2208
+ const parentIsZero = row.total === 0 && Object.values(row.periodAmounts).every((v) => v === 0);
2209
+ if (parentIsZero) return acc;
2210
+ acc.push({ ...row, children: [] });
2211
+ return acc;
2212
+ }
2213
+ if (row._type === "subtotal-group") {
2214
+ const sums = sumChildPeriods(filteredChildren);
2215
+ acc.push({
2216
+ ...row,
2217
+ children: filteredChildren,
2218
+ periodAmounts: sums.periodAmounts,
2219
+ total: sums.total
2220
+ });
2221
+ } else {
2222
+ acc.push({ ...row, children: filteredChildren });
2223
+ }
2224
+ return acc;
2225
+ }
2226
+ const isZero = row.total === 0 && Object.values(row.periodAmounts).every((v) => v === 0);
2227
+ if (!isZero) {
2228
+ acc.push(row);
2229
+ }
2230
+ return acc;
2231
+ }, []);
2232
+ }
2233
+ function buildFinancialStatementData(data, config, timeUnit, subtotalRules, options) {
2234
+ const periodSet = /* @__PURE__ */ new Set();
2235
+ for (const entry of data) {
2236
+ periodSet.add(toPeriodKey(entry.period_first_day, timeUnit));
2237
+ }
2238
+ const periods = [...periodSet].sort();
2239
+ const sectionTotals = /* @__PURE__ */ new Map();
2240
+ const sectionRawTotals = /* @__PURE__ */ new Map();
2241
+ const rows = [];
2242
+ for (const section of config.sections) {
2243
+ const classSet = new Set(section.accountClasses);
2244
+ const sign = section.sign ?? 1;
2245
+ const accountMap = /* @__PURE__ */ new Map();
2246
+ for (const entry of data) {
2247
+ if (!classSet.has(entry.account_class)) continue;
2248
+ const pk = toPeriodKey(entry.period_first_day, timeUnit);
2249
+ if (!accountMap.has(entry.account_id)) {
2250
+ accountMap.set(entry.account_id, {
2251
+ id: entry.account_id,
2252
+ name: entry.account_name,
2253
+ secondaryLabel: entry.account_secondary_label ?? null,
2254
+ number: entry.account_number,
2255
+ type: entry.account_type ?? "",
2256
+ subType: entry.account_sub_type ?? null,
2257
+ isSubAccount: entry.is_sub_account ?? false,
2258
+ parentNumber: entry.parent_account_number ?? null,
2259
+ parentName: entry.parent_account_name ?? null,
2260
+ periods: {},
2261
+ total: 0
2262
+ });
2263
+ }
2264
+ const acct = accountMap.get(entry.account_id);
2265
+ acct.periods[pk] = (acct.periods[pk] || 0) + entry.amount;
2266
+ acct.total += entry.amount;
2267
+ }
2268
+ const rawPeriods = {};
2269
+ let rawTotal = 0;
2270
+ for (const entry of data) {
2271
+ if (!classSet.has(entry.account_class)) continue;
2272
+ const pk = toPeriodKey(entry.period_first_day, timeUnit);
2273
+ const val = entry.amount * sign;
2274
+ rawPeriods[pk] = (rawPeriods[pk] || 0) + val;
2275
+ rawTotal += val;
2276
+ }
2277
+ sectionRawTotals.set(section.id, {
2278
+ periodAmounts: rawPeriods,
2279
+ total: rawTotal
2280
+ });
2281
+ const accountList = [...accountMap.values()];
2282
+ const accountTree = buildAccountTree(accountList, sign);
2283
+ if (accountTree.length === 0 && !options?.preserveEmptySections) continue;
2284
+ const useSubtotals = subtotalRules?.enabled && subtotalRules.rules.length > 0;
2285
+ const sectionChildren = useSubtotals ? applySubtotalRules(accountTree, subtotalRules.rules, section.id) : accountTree;
2286
+ const sectionSums = sumChildPeriods(accountTree);
2287
+ sectionTotals.set(section.id, sectionSums);
2288
+ if (section.standalone) {
2289
+ const normalizeDepth = (items) => items.map((item) => ({
2290
+ ...item,
2291
+ depth: Math.max(0, item.depth - 1),
2292
+ children: normalizeDepth(item.children)
2293
+ }));
2294
+ rows.push(...normalizeDepth(sectionChildren));
2295
+ continue;
2296
+ }
2297
+ const sectionHeader = {
2298
+ _type: "section-header",
2299
+ id: `section-${section.id}`,
2300
+ name: section.label,
2301
+ accountNumber: null,
2302
+ periodAmounts: {},
2303
+ total: 0,
2304
+ children: sectionChildren,
2305
+ depth: 0
2306
+ };
2307
+ rows.push(sectionHeader);
2308
+ const rawSums = sectionRawTotals.get(section.id);
2309
+ rows.push({
2310
+ _type: "section-total",
2311
+ id: `total-${section.id}`,
2312
+ name: `Total ${section.label}`,
2313
+ accountNumber: null,
2314
+ periodAmounts: sectionSums.periodAmounts,
2315
+ total: sectionSums.total,
2316
+ children: [],
2317
+ depth: 0,
2318
+ _rawPeriodAmounts: rawSums?.periodAmounts,
2319
+ _rawTotal: rawSums?.total
2320
+ });
2321
+ }
2322
+ for (const total of config.totals) {
2323
+ const totalPeriods = {};
2324
+ let totalAmount = 0;
2325
+ const rawTotalPeriods = {};
2326
+ let rawTotalAmount = 0;
2327
+ for (const ref of total.formula) {
2328
+ const subtract = ref.startsWith("-");
2329
+ const sectionId = subtract ? ref.slice(1) : ref;
2330
+ const sums = sectionTotals.get(sectionId);
2331
+ const rawSums = sectionRawTotals.get(sectionId);
2332
+ if (!sums) continue;
2333
+ const mult = subtract ? -1 : 1;
2334
+ totalAmount += sums.total * mult;
2335
+ rawTotalAmount += (rawSums?.total ?? 0) * mult;
2336
+ for (const pk of periods) {
2337
+ totalPeriods[pk] = (totalPeriods[pk] || 0) + (sums.periodAmounts[pk] || 0) * mult;
2338
+ rawTotalPeriods[pk] = (rawTotalPeriods[pk] || 0) + (rawSums?.periodAmounts[pk] || 0) * mult;
2339
+ }
2340
+ }
2341
+ rows.push({
2342
+ _type: "grand-total",
2343
+ id: `grand-total-${total.label.toLowerCase().replace(/\s+/g, "-")}`,
2344
+ name: total.label,
2345
+ accountNumber: null,
2346
+ periodAmounts: totalPeriods,
2347
+ total: totalAmount,
2348
+ children: [],
2349
+ depth: 0,
2350
+ _rawPeriodAmounts: rawTotalPeriods,
2351
+ _rawTotal: rawTotalAmount
2352
+ });
2353
+ }
2354
+ return { rows, periods };
2355
+ }
2356
+ function buildColumns(periods, unit, rows, showAccountNumbers, renderAmountCell, showRowTotals = true, showVariance, currentPeriodCount, isMobile = false, mobileCompareEnabled = false, periodLabelsOverride, accountColumnLabel = "Account", accountColumnMenuItems) {
2357
+ const desktopSizing = calculateDesktopFinancialColumnSizing({
2358
+ periods,
2359
+ rows,
2360
+ showRowTotals,
2361
+ showVariance: Boolean(showVariance)
2362
+ });
2363
+ const compactMobileAccount = isMobile && periods.length <= 1;
2364
+ const cols = [
2365
+ {
2366
+ id: "account",
2367
+ accessorKey: "name",
2368
+ header: ({ column }) => /* @__PURE__ */ jsx(
2369
+ DataTableColumnHeader,
2370
+ {
2371
+ column,
2372
+ title: accountColumnLabel,
2373
+ customMenuItems: accountColumnMenuItems
2374
+ }
2375
+ ),
2376
+ meta: {
2377
+ displayName: accountColumnLabel,
2378
+ icon: BookOpen,
2379
+ allowHorizontalScroll: isMobile && mobileCompareEnabled
2380
+ },
2381
+ cell: ({ row }) => {
2382
+ const r = row.original;
2383
+ if (r._type === "section-header") {
2384
+ return /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: r.name });
2385
+ }
2386
+ if (r._type === "subtotal-group") {
2387
+ return /* @__PURE__ */ jsx("span", { className: "font-semibold", children: r.name });
2388
+ }
2389
+ if (r._type === "section-total" || r._type === "subtotal-total") {
2390
+ return /* @__PURE__ */ jsx("span", { className: "font-semibold", children: r.name });
2391
+ }
2392
+ if (r._type === "grand-total") {
2393
+ return /* @__PURE__ */ jsx("span", { className: "font-bold", children: r.name });
2394
+ }
2395
+ if (r._type === "account-group") {
2396
+ return /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
2397
+ showAccountNumbers && r.accountNumber && /* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground mr-1.5 text-xs", children: r.accountNumber }),
2398
+ r.name,
2399
+ !isMobile && r.secondaryLabel && /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-[11px] font-normal text-muted-foreground", children: r.secondaryLabel })
2400
+ ] });
2401
+ }
2402
+ const isPlaceholder = r.accountSubType === "placeholder";
2403
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
2404
+ /* @__PURE__ */ jsxs(
2405
+ "span",
2406
+ {
2407
+ className: isPlaceholder ? "italic text-muted-foreground" : "",
2408
+ children: [
2409
+ showAccountNumbers && r.accountNumber && /* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground mr-1.5 text-xs", children: r.accountNumber }),
2410
+ r.name,
2411
+ !isMobile && r.secondaryLabel && /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-[11px] font-normal text-muted-foreground", children: r.secondaryLabel })
2412
+ ]
2413
+ }
2414
+ ),
2415
+ r.accountType === "metric" && r.accountSubType && /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
2416
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
2417
+ "button",
2418
+ {
2419
+ type: "button",
2420
+ className: "text-muted-foreground/70 hover:text-foreground transition-colors",
2421
+ "aria-label": `${r.name} metric details`,
2422
+ children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5" })
2423
+ }
2424
+ ) }),
2425
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", className: "max-w-[340px] text-xs", children: /* @__PURE__ */ jsx(
2426
+ MetricEquationTooltip,
2427
+ {
2428
+ title: r.name,
2429
+ formula: r.accountSubType
2430
+ }
2431
+ ) })
2432
+ ] }) })
2433
+ ] });
2434
+ },
2435
+ enableSorting: false,
2436
+ // Mobile: shrink account column based on how many period columns need to fit.
2437
+ // 2 periods → moderate shrink, 3+ periods → aggressive shrink
2438
+ size: isMobile ? mobileCompareEnabled || compactMobileAccount ? 180 : periods.length >= 3 ? 120 : periods.length >= 2 ? 160 : 260 : desktopSizing.accountSize,
2439
+ minSize: isMobile ? mobileCompareEnabled || compactMobileAccount ? 120 : periods.length >= 2 ? 80 : 200 : desktopSizing.accountMinSize,
2440
+ maxSize: isMobile && !(mobileCompareEnabled || compactMobileAccount) ? periods.length >= 3 ? 160 : periods.length >= 2 ? 200 : 320 : void 0
2441
+ }
2442
+ ];
2443
+ for (const pk of periods) {
2444
+ const label = periodLabelsOverride?.[pk] ?? toPeriodLabel(pk, unit);
2445
+ cols.push({
2446
+ id: `period-${pk}`,
2447
+ accessorFn: (row) => row.periodAmounts[pk] ?? 0,
2448
+ header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, title: label }),
2449
+ meta: {
2450
+ displayName: label,
2451
+ icon: Calendar,
2452
+ align: "right"
2453
+ },
2454
+ cell: ({ row }) => {
2455
+ const r = row.original;
2456
+ const val = r.periodAmounts[pk] ?? 0;
2457
+ if (r._type === "section-header") return null;
2458
+ const isTotalRow = r._type === "section-total" || r._type === "grand-total";
2459
+ const bold = r._type === "grand-total" ? "font-bold" : r._type === "section-total" || r._type === "subtotal-total" || r._type === "account-group" || r._type === "subtotal-group" ? "font-semibold" : "";
2460
+ if (renderAmountCell) {
2461
+ const cellNode = renderAmountCell(r, pk, val);
2462
+ return /* @__PURE__ */ jsx(
2463
+ "span",
2464
+ {
2465
+ className: cn(
2466
+ bold,
2467
+ "inline-flex cursor-help border-b border-dotted border-muted-foreground/35 leading-none hover:border-muted-foreground/60"
2468
+ ),
2469
+ children: cellNode
2470
+ }
2471
+ );
2472
+ }
2473
+ const rawVal = isTotalRow ? r._rawPeriodAmounts?.[pk] : void 0;
2474
+ if (isTotalRow && rawVal !== void 0) {
2475
+ return /* @__PURE__ */ jsxs(
2476
+ "span",
2477
+ {
2478
+ className: cn(
2479
+ bold,
2480
+ "flex items-center justify-end gap-1.5 group/rec w-full h-full"
2481
+ ),
2482
+ children: [
2483
+ /* @__PURE__ */ jsx("span", { className: "opacity-0 group-hover/rec:opacity-100 transition-opacity flex-shrink-0 leading-[0]", children: /* @__PURE__ */ jsx(RecCheck, { computed: val, raw: rawVal }) }),
2484
+ /* @__PURE__ */ jsx("span", { className: "leading-none", children: /* @__PURE__ */ jsx(
2485
+ DataTableCurrencyCell,
2486
+ {
2487
+ value: val,
2488
+ dashZero: true,
2489
+ isSummaryRow: r._type === "section-total" || r._type === "grand-total" || r._type === "subtotal-total"
2490
+ }
2491
+ ) })
2492
+ ]
2493
+ }
2494
+ );
2495
+ }
2496
+ return /* @__PURE__ */ jsx("span", { className: bold, children: /* @__PURE__ */ jsx(
2497
+ DataTableCurrencyCell,
2498
+ {
2499
+ value: val,
2500
+ dashZero: true,
2501
+ isSummaryRow: r._type === "section-total" || r._type === "grand-total" || r._type === "subtotal-total"
2502
+ }
2503
+ ) });
2504
+ },
2505
+ enableSorting: false,
2506
+ // Mobile compare mode: data columns stay readable but can shrink/grow dynamically.
2507
+ size: isMobile ? mobileCompareEnabled ? 96 : 110 : desktopSizing.dataSize,
2508
+ minSize: isMobile ? mobileCompareEnabled ? 82 : 80 : desktopSizing.dataMinSize,
2509
+ maxSize: isMobile && mobileCompareEnabled ? 112 : void 0
2510
+ });
2511
+ }
2512
+ if (showRowTotals) {
2513
+ cols.push({
2514
+ id: "total",
2515
+ accessorFn: (row) => row.total,
2516
+ header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, title: "Total" }),
2517
+ meta: {
2518
+ displayName: "Total",
2519
+ icon: DollarSign,
2520
+ align: "right"
2521
+ },
2522
+ cell: ({ row }) => {
2523
+ const r = row.original;
2524
+ if (r._type === "section-header") return null;
2525
+ const isTotalRow = r._type === "section-total" || r._type === "grand-total";
2526
+ const bold = r._type === "grand-total" ? "font-bold" : r._type === "section-total" || r._type === "subtotal-total" || r._type === "account-group" || r._type === "subtotal-group" ? "font-semibold" : "";
2527
+ if (renderAmountCell) {
2528
+ const cellNode = renderAmountCell(r, null, r.total);
2529
+ return /* @__PURE__ */ jsx(
2530
+ "span",
2531
+ {
2532
+ className: cn(
2533
+ bold,
2534
+ "inline-flex cursor-help border-b border-dotted border-muted-foreground/35 leading-none hover:border-muted-foreground/60"
2535
+ ),
2536
+ children: cellNode
2537
+ }
2538
+ );
2539
+ }
2540
+ if (isTotalRow && r._rawTotal !== void 0) {
2541
+ return /* @__PURE__ */ jsxs(
2542
+ "span",
2543
+ {
2544
+ className: cn(
2545
+ bold,
2546
+ "flex items-center justify-end gap-1.5 group/rec w-full h-full"
2547
+ ),
2548
+ children: [
2549
+ /* @__PURE__ */ jsx("span", { className: "opacity-0 group-hover/rec:opacity-100 transition-opacity flex-shrink-0 leading-[0]", children: /* @__PURE__ */ jsx(RecCheck, { computed: r.total, raw: r._rawTotal }) }),
2550
+ /* @__PURE__ */ jsx("span", { className: "leading-none", children: /* @__PURE__ */ jsx(
2551
+ DataTableCurrencyCell,
2552
+ {
2553
+ value: r.total,
2554
+ dashZero: true,
2555
+ isSummaryRow: r._type === "section-total" || r._type === "grand-total" || r._type === "subtotal-total"
2556
+ }
2557
+ ) })
2558
+ ]
2559
+ }
2560
+ );
2561
+ }
2562
+ return /* @__PURE__ */ jsx("span", { className: bold, children: /* @__PURE__ */ jsx(
2563
+ DataTableCurrencyCell,
2564
+ {
2565
+ value: r.total,
2566
+ dashZero: true,
2567
+ isSummaryRow: r._type === "section-total" || r._type === "grand-total" || r._type === "subtotal-total"
2568
+ }
2569
+ ) });
2570
+ },
2571
+ enableSorting: false,
2572
+ size: isMobile ? 130 : desktopSizing.totalSize,
2573
+ minSize: isMobile ? 100 : 96
2574
+ });
2575
+ }
2576
+ if (showVariance && currentPeriodCount && currentPeriodCount > 0) {
2577
+ const priorPeriods = periods.slice(0, periods.length - currentPeriodCount);
2578
+ const currentPeriods = periods.slice(periods.length - currentPeriodCount);
2579
+ cols.push({
2580
+ id: "variance",
2581
+ accessorFn: (row) => {
2582
+ const currentSum = currentPeriods.reduce(
2583
+ (s, pk) => s + (row.periodAmounts[pk] ?? 0),
2584
+ 0
2585
+ );
2586
+ const priorSum = priorPeriods.reduce(
2587
+ (s, pk) => s + (row.periodAmounts[pk] ?? 0),
2588
+ 0
2589
+ );
2590
+ if (priorSum === 0) return currentSum === 0 ? 0 : Infinity;
2591
+ return (currentSum - priorSum) / Math.abs(priorSum) * 100;
2592
+ },
2593
+ header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, title: "\u0394%" }),
2594
+ meta: {
2595
+ displayName: "Variance %",
2596
+ icon: TrendingUp,
2597
+ align: "right"
2598
+ },
2599
+ cell: ({ row }) => {
2600
+ const r = row.original;
2601
+ if (r._type === "section-header") return null;
2602
+ const currentSum = currentPeriods.reduce(
2603
+ (s, pk) => s + (r.periodAmounts[pk] ?? 0),
2604
+ 0
2605
+ );
2606
+ const priorSum = priorPeriods.reduce(
2607
+ (s, pk) => s + (r.periodAmounts[pk] ?? 0),
2608
+ 0
2609
+ );
2610
+ if (priorSum === 0 && currentSum === 0)
2611
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" });
2612
+ if (priorSum === 0)
2613
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "n/a" });
2614
+ const pct = (currentSum - priorSum) / Math.abs(priorSum) * 100;
2615
+ const isPositive = pct > 0;
2616
+ const isNegative = pct < 0;
2617
+ const bold = r._type === "grand-total" ? "font-bold" : r._type === "section-total" || r._type === "subtotal-total" ? "font-semibold" : "";
2618
+ return /* @__PURE__ */ jsxs(
2619
+ "span",
2620
+ {
2621
+ className: cn(
2622
+ bold,
2623
+ "tabular-nums text-xs",
2624
+ isPositive && "text-emerald-500",
2625
+ isNegative && "text-red-500",
2626
+ !isPositive && !isNegative && "text-muted-foreground"
2627
+ ),
2628
+ children: [
2629
+ isPositive ? "+" : "",
2630
+ pct.toFixed(1),
2631
+ "%"
2632
+ ]
2633
+ }
2634
+ );
2635
+ },
2636
+ enableSorting: false,
2637
+ size: 80,
2638
+ minSize: 60
2639
+ });
2640
+ }
2641
+ return cols;
2642
+ }
2643
+ function getRowClassName(row, rowImportanceResolver) {
2644
+ const structuralClass = (() => {
2645
+ switch (row.original._type) {
2646
+ case "section-header":
2647
+ return "bg-muted/30 border-b-0";
2648
+ case "subtotal-group":
2649
+ return "bg-muted/20";
2650
+ case "section-total":
2651
+ case "subtotal-total":
2652
+ return "bg-muted/40 border-t border-border font-semibold";
2653
+ case "grand-total":
2654
+ return "bg-muted/60 border-t-2 border-double border-border";
2655
+ default:
2656
+ return void 0;
2657
+ }
2658
+ })();
2659
+ const importanceClass = rowImportanceResolver ? getFinancialStatementRowImportanceClassName(
2660
+ rowImportanceResolver(row.original)
2661
+ ) : void 0;
2662
+ return cn(structuralClass, importanceClass);
2663
+ }
2664
+ function FinancialStatementTable({
2665
+ data,
2666
+ timeUnit,
2667
+ config,
2668
+ showAccountNumbers = true,
2669
+ treeIndentPx = 20,
2670
+ subtotalRules,
2671
+ hideZeroRows = false,
2672
+ preserveEmptySections = false,
2673
+ showRowTotals = true,
2674
+ showVariance = false,
2675
+ currentPeriodCount,
2676
+ toolbar,
2677
+ renderAmountCell,
2678
+ isMobile = false,
2679
+ mobileCompareEnabled = false,
2680
+ periodOrder,
2681
+ periodColumnOrder = "oldest-first",
2682
+ periodLabels,
2683
+ accountColumnLabel,
2684
+ accountColumnMenuItems,
2685
+ className,
2686
+ rowImportanceResolver
2687
+ }) {
2688
+ const [expanded, setExpanded] = React3.useState({});
2689
+ const [columnVisibility, setColumnVisibility] = React3.useState({});
2690
+ const [viewportLayout, setViewportLayout] = React3.useState("unknown");
2691
+ const { rows: rawRows, periods } = React3.useMemo(
2692
+ () => buildFinancialStatementData(data, config, timeUnit, subtotalRules, {
2693
+ preserveEmptySections
2694
+ }),
2695
+ [data, config, timeUnit, subtotalRules, preserveEmptySections]
2696
+ );
2697
+ const rows = React3.useMemo(
2698
+ () => hideZeroRows ? filterZeroRows(rawRows) : rawRows,
2699
+ [rawRows, hideZeroRows]
2700
+ );
2701
+ const orderedPeriods = React3.useMemo(
2702
+ () => resolvePeriodColumns(periods, timeUnit, periodOrder, periodColumnOrder),
2703
+ [periods, timeUnit, periodOrder, periodColumnOrder]
2704
+ );
2705
+ const effectiveShowRowTotals = isMobile && mobileCompareEnabled ? false : showRowTotals;
2706
+ const columns = React3.useMemo(
2707
+ () => buildColumns(
2708
+ orderedPeriods,
2709
+ timeUnit,
2710
+ rows,
2711
+ showAccountNumbers,
2712
+ renderAmountCell,
2713
+ effectiveShowRowTotals,
2714
+ showVariance,
2715
+ currentPeriodCount,
2716
+ isMobile,
2717
+ mobileCompareEnabled,
2718
+ periodLabels,
2719
+ accountColumnLabel,
2720
+ accountColumnMenuItems
2721
+ ),
2722
+ [
2723
+ orderedPeriods,
2724
+ timeUnit,
2725
+ rows,
2726
+ showAccountNumbers,
2727
+ renderAmountCell,
2728
+ effectiveShowRowTotals,
2729
+ showVariance,
2730
+ currentPeriodCount,
2731
+ isMobile,
2732
+ mobileCompareEnabled,
2733
+ periodLabels,
2734
+ accountColumnLabel,
2735
+ accountColumnMenuItems
2736
+ ]
2737
+ );
2738
+ React3.useEffect(() => {
2739
+ if (typeof window === "undefined") return;
2740
+ const updateViewportLayout = () => {
2741
+ setViewportLayout(
2742
+ window.innerWidth > window.innerHeight ? "landscape" : "portrait"
2743
+ );
2744
+ };
2745
+ updateViewportLayout();
2746
+ window.addEventListener("resize", updateViewportLayout);
2747
+ window.addEventListener("orientationchange", updateViewportLayout);
2748
+ return () => {
2749
+ window.removeEventListener("resize", updateViewportLayout);
2750
+ window.removeEventListener("orientationchange", updateViewportLayout);
2751
+ };
2752
+ }, []);
2753
+ React3.useEffect(() => {
2754
+ if (rows.length === 0) return;
2755
+ const toExpand = {};
2756
+ for (const row of rows) {
2757
+ if (row._type === "section-header") {
2758
+ toExpand[row.id] = true;
2759
+ for (const child of row.children) {
2760
+ if (child._type === "account-group" || child._type === "subtotal-group") {
2761
+ toExpand[child.id] = true;
2762
+ if (child._type === "subtotal-group") {
2763
+ for (const grandchild of child.children) {
2764
+ if (grandchild._type === "account-group") {
2765
+ toExpand[grandchild.id] = true;
2766
+ }
2767
+ }
2768
+ }
2769
+ }
2770
+ }
2771
+ }
2772
+ }
2773
+ setExpanded(toExpand);
2774
+ }, [rows]);
2775
+ const table = useReactTable({
2776
+ data: rows,
2777
+ columns,
2778
+ getRowId: (row) => row.id,
2779
+ getCoreRowModel: getCoreRowModel(),
2780
+ getSubRows: (row) => row.children.length > 0 ? row.children : void 0,
2781
+ getExpandedRowModel: getExpandedRowModel(),
2782
+ state: { expanded, columnVisibility },
2783
+ onExpandedChange: setExpanded,
2784
+ onColumnVisibilityChange: setColumnVisibility
2785
+ });
2786
+ return /* @__PURE__ */ jsxs("div", { className, children: [
2787
+ toolbar && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-1 pb-1", children: toolbar }),
2788
+ /* @__PURE__ */ jsx(
2789
+ DataTable,
2790
+ {
2791
+ table,
2792
+ columns,
2793
+ enableTreeView: true,
2794
+ treeIndentPx,
2795
+ getRowClassName: ((row) => getRowClassName(row, rowImportanceResolver)),
2796
+ enableColumnResizing: true,
2797
+ enableRowDrag: false,
2798
+ rowSelectionStyle: "none",
2799
+ density: "compact",
2800
+ fontSize: "sm",
2801
+ stickyColumnIds: ["account"]
2802
+ },
2803
+ `fs-${isMobile ? "m" : "d"}-${mobileCompareEnabled ? "cmp" : "std"}-${viewportLayout}`
2804
+ )
2805
+ ] });
2806
+ }
2807
+ function flattenStatementForExport(data, config, timeUnit, subtotalRules, hideZeroRows) {
2808
+ let { rows: tree, periods } = buildFinancialStatementData(
2809
+ data,
2810
+ config,
2811
+ timeUnit,
2812
+ subtotalRules
2813
+ );
2814
+ if (hideZeroRows) tree = filterZeroRows(tree);
2815
+ const flat = [];
2816
+ function walk(rows, indent) {
2817
+ for (const row of rows) {
2818
+ const prefix = " ".repeat(indent);
2819
+ const exportRow = {
2820
+ type: row._type,
2821
+ account: `${prefix}${row.accountNumber ? `${row.accountNumber} ` : ""}${row.name}`,
2822
+ accountNumber: row.accountNumber ?? ""
2823
+ };
2824
+ for (const pk of periods) {
2825
+ exportRow[pk] = row.periodAmounts[pk] ?? 0;
2826
+ }
2827
+ exportRow.total = row.total;
2828
+ if (row._type !== "section-header" || row.children.length === 0) {
2829
+ flat.push(exportRow);
2830
+ } else {
2831
+ flat.push(exportRow);
2832
+ }
2833
+ if (row.children.length > 0) {
2834
+ walk(row.children, indent + 1);
2835
+ }
2836
+ }
2837
+ }
2838
+ walk(tree, 0);
2839
+ return { rows: flat, periodKeys: periods };
2840
+ }
2841
+ var INCOME_STATEMENT_CONFIG = {
2842
+ sections: [
2843
+ { id: "revenue", label: "Revenue", accountClasses: ["Revenue"] },
2844
+ { id: "expenses", label: "Expenses", accountClasses: ["Expense"] }
2845
+ ],
2846
+ totals: [{ label: "Net Income", formula: ["revenue", "-expenses"] }]
2847
+ };
2848
+ var BALANCE_SHEET_CONFIG = {
2849
+ sections: [
2850
+ { id: "assets", label: "Assets", accountClasses: ["Asset"] },
2851
+ { id: "liabilities", label: "Liabilities", accountClasses: ["Liability"] },
2852
+ { id: "equity", label: "Equity", accountClasses: ["Equity"] }
2853
+ ],
2854
+ totals: [
2855
+ {
2856
+ label: "Total Liabilities & Equity",
2857
+ formula: ["liabilities", "equity"]
2858
+ }
2859
+ ]
2860
+ };
2861
+
2862
+ // src/hooks/spreadsheetWorkbook.ts
2863
+ function escapeXml(value) {
2864
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2865
+ }
2866
+ function styleId(style) {
2867
+ if (!style) return void 0;
2868
+ const map = {
2869
+ header: "Header",
2870
+ text: "Text",
2871
+ number: "Number",
2872
+ currency: "Currency",
2873
+ percent: "Percent",
2874
+ totalLabel: "TotalLabel",
2875
+ totalNumber: "TotalNumber"
2876
+ };
2877
+ return map[style];
2878
+ }
2879
+ function excelColumnName(columnIndex) {
2880
+ let n = columnIndex + 1;
2881
+ let result = "";
2882
+ while (n > 0) {
2883
+ const rem = (n - 1) % 26;
2884
+ result = String.fromCharCode(65 + rem) + result;
2885
+ n = Math.floor((n - 1) / 26);
2886
+ }
2887
+ return result;
2888
+ }
2889
+ function buildSpreadsheetWorkbookXml({
2890
+ worksheetName,
2891
+ rows
2892
+ }) {
2893
+ const xmlRows = rows.map(
2894
+ (row) => `<Row>${row.map((cell) => {
2895
+ const inferredType = cell.type ?? (typeof cell.value === "number" || cell.formula ? "Number" : "String");
2896
+ const rawValue = cell.value ?? "";
2897
+ const value = inferredType === "Number" ? Number(rawValue || 0) : escapeXml(String(rawValue));
2898
+ const style = styleId(cell.style);
2899
+ const styleAttr = style ? ` ss:StyleID="${style}"` : "";
2900
+ const formulaAttr = cell.formula ? ` ss:Formula="=${escapeXml(cell.formula)}"` : "";
2901
+ return `<Cell${styleAttr}${formulaAttr}><Data ss:Type="${inferredType}">${value}</Data></Cell>`;
2902
+ }).join("")}</Row>`
2903
+ ).join("\n");
2904
+ return `<?xml version="1.0" encoding="UTF-8"?>
2905
+ <?mso-application progid="Excel.Sheet"?>
2906
+ <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
2907
+ <Styles>
2908
+ <Style ss:ID="Header"><Font ss:Bold="1"/><Interior ss:Color="#F3F4F6" ss:Pattern="Solid"/></Style>
2909
+ <Style ss:ID="Text"><NumberFormat ss:Format="@"/></Style>
2910
+ <Style ss:ID="Number"><NumberFormat ss:Format="#,##0.00"/></Style>
2911
+ <Style ss:ID="Currency"><NumberFormat ss:Format="$#,##0.00;[Red]($#,##0.00)"/></Style>
2912
+ <Style ss:ID="Percent"><NumberFormat ss:Format="0.00%"/></Style>
2913
+ <Style ss:ID="TotalLabel"><Font ss:Bold="1"/><Interior ss:Color="#F9FAFB" ss:Pattern="Solid"/></Style>
2914
+ <Style ss:ID="TotalNumber"><Font ss:Bold="1"/><NumberFormat ss:Format="$#,##0.00;[Red]($#,##0.00)"/><Interior ss:Color="#F9FAFB" ss:Pattern="Solid"/></Style>
2915
+ </Styles>
2916
+ <Worksheet ss:Name="${escapeXml(worksheetName)}"><Table>
2917
+ ${xmlRows}
2918
+ </Table></Worksheet></Workbook>`;
2919
+ }
2920
+ function downloadExcelXml(xml, filename) {
2921
+ const blob = new Blob([xml], {
2922
+ type: "application/vnd.ms-excel;charset=utf-8;"
2923
+ });
2924
+ const url = URL.createObjectURL(blob);
2925
+ const link = document.createElement("a");
2926
+ link.href = url;
2927
+ link.download = `${filename}.xls`;
2928
+ link.click();
2929
+ URL.revokeObjectURL(url);
2930
+ }
2931
+
2932
+ export { BALANCE_SHEET_CONFIG, DataTable, DataTableColumnFilter, DataTableColumnFilterContent, DataTableColumnHeader, DataTableContext, DataTableCurrencyCell, DataTableFilterBadges, FinanceDecimalsProvider, FinanceDisplayProvider, FinancialStatementTable, INCOME_STATEMENT_CONFIG, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, buildFinancialStatementData, buildSpreadsheetWorkbookXml, calculateDesktopFinancialColumnSizing, createFinancialStatementRowImportanceResolver, downloadExcelXml, excelColumnName, flattenStatementForExport, formatCurrencyValue, getFinancialStatementRowImportanceClassName, getUnitDivisor, getUnitLabel, getUnitSuffix, resolvePeriodColumns, useDataTableContext, useFinanceDecimals, useFinanceDecimalsSetting, useFinanceDisplay };