@carefully-built/ui 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,3489 @@
1
+ 'use client';
2
+
3
+ import * as React from "react";
4
+ import { createElement, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
5
+ import { Avatar as Avatar$1, Dialog as Dialog$1, DropdownMenu as DropdownMenu$1, Label as Label$1, Popover as Popover$1, Select as Select$1, Slot, Switch as Switch$1, Tabs as Tabs$1, Tooltip as Tooltip$1 } from "radix-ui";
6
+ import { clsx } from "clsx";
7
+ import { twMerge } from "tailwind-merge";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { cva } from "class-variance-authority";
10
+ import { ArrowDown, ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, ChevronsLeft, ChevronsRight, ChevronsUpDown, CircleHelp, Command, CornerDownLeft, Eye, FileText, Filter, Inbox, Pencil, Search, SearchX, Trash2, Upload, X } from "lucide-react";
11
+ import { DayPicker, getDefaultClassNames } from "react-day-picker";
12
+ import { Drawer as Drawer$1 } from "vaul";
13
+ import { createPortal } from "react-dom";
14
+
15
+ //#region src/utils/cn.ts
16
+ function cn(...inputs) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+
20
+ //#endregion
21
+ //#region src/primitives/avatar.tsx
22
+ function Avatar({ className, size = "default", ...props }) {
23
+ return /* @__PURE__ */ jsx(Avatar$1.Root, {
24
+ "data-slot": "avatar",
25
+ "data-size": size,
26
+ className: cn("size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten", className),
27
+ ...props
28
+ });
29
+ }
30
+ function AvatarImage({ className, ...props }) {
31
+ return /* @__PURE__ */ jsx(Avatar$1.Image, {
32
+ "data-slot": "avatar-image",
33
+ className: cn("rounded-full aspect-square size-full object-cover", className),
34
+ ...props
35
+ });
36
+ }
37
+ function AvatarFallback({ className, ...props }) {
38
+ return /* @__PURE__ */ jsx(Avatar$1.Fallback, {
39
+ "data-slot": "avatar-fallback",
40
+ className: cn("bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs", className),
41
+ ...props
42
+ });
43
+ }
44
+
45
+ //#endregion
46
+ //#region src/primitives/button.tsx
47
+ const buttonVariants = cva("group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
48
+ variants: {
49
+ variant: {
50
+ default: "bg-primary text-primary-foreground hover:brightness-90 [a]:hover:bg-primary/80",
51
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
52
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
53
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
54
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
55
+ link: "text-primary underline-offset-4 hover:underline"
56
+ },
57
+ size: {
58
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
59
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
60
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
61
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
62
+ icon: "size-8",
63
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
64
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
65
+ "icon-lg": "size-9"
66
+ }
67
+ },
68
+ defaultVariants: {
69
+ variant: "default",
70
+ size: "default"
71
+ }
72
+ });
73
+ function Button({ className, variant = "default", size = "default", asChild = false, ...props }) {
74
+ return /* @__PURE__ */ jsx(asChild ? Slot.Root : "button", {
75
+ "data-slot": "button",
76
+ "data-variant": variant,
77
+ "data-size": size,
78
+ className: cn(buttonVariants({
79
+ variant,
80
+ size,
81
+ className
82
+ })),
83
+ ...props
84
+ });
85
+ }
86
+
87
+ //#endregion
88
+ //#region src/primitives/calendar.tsx
89
+ function Calendar({ className, classNames, showOutsideDays = true, ...props }) {
90
+ const defaultClassNames = getDefaultClassNames();
91
+ return /* @__PURE__ */ jsx(DayPicker, {
92
+ showOutsideDays,
93
+ className: cn("p-3", className),
94
+ classNames: {
95
+ root: cn("w-fit", defaultClassNames.root),
96
+ months: "flex flex-col gap-4",
97
+ month: "flex flex-col gap-4",
98
+ month_caption: "relative flex items-center justify-center pt-1",
99
+ caption_label: "text-sm font-medium",
100
+ nav: "flex items-center gap-1",
101
+ button_previous: cn(buttonVariants({
102
+ variant: "ghost",
103
+ size: "icon-sm"
104
+ }), "absolute left-1 size-7 bg-transparent p-0 opacity-70 hover:opacity-100"),
105
+ button_next: cn(buttonVariants({
106
+ variant: "ghost",
107
+ size: "icon-sm"
108
+ }), "absolute right-1 size-7 bg-transparent p-0 opacity-70 hover:opacity-100"),
109
+ month_grid: "w-full border-collapse space-y-1",
110
+ weekdays: "flex",
111
+ weekday: "w-9 text-[0.78rem] font-medium text-muted-foreground",
112
+ week: "mt-2 flex w-full",
113
+ day: "size-9 p-0 text-sm",
114
+ day_button: cn(buttonVariants({
115
+ variant: "ghost",
116
+ size: "icon-sm"
117
+ }), "size-9 p-0 font-normal aria-selected:opacity-100"),
118
+ selected: "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
119
+ today: "bg-muted text-foreground",
120
+ outside: "text-muted-foreground opacity-50 aria-selected:bg-primary/10 aria-selected:text-muted-foreground",
121
+ disabled: "text-muted-foreground opacity-50",
122
+ hidden: "invisible",
123
+ ...classNames
124
+ },
125
+ components: { Chevron: ({ orientation, className: iconClassName, ...iconProps }) => orientation === "left" ? /* @__PURE__ */ jsx(ChevronLeft, {
126
+ className: cn("size-4", iconClassName),
127
+ ...iconProps
128
+ }) : /* @__PURE__ */ jsx(ChevronRight, {
129
+ className: cn("size-4", iconClassName),
130
+ ...iconProps
131
+ }) },
132
+ ...props
133
+ });
134
+ }
135
+
136
+ //#endregion
137
+ //#region src/primitives/card.tsx
138
+ function Card({ className, size = "default", ...props }) {
139
+ return /* @__PURE__ */ jsx("div", {
140
+ "data-slot": "card",
141
+ "data-size": size,
142
+ className: cn("ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className),
143
+ ...props
144
+ });
145
+ }
146
+ function CardHeader({ className, ...props }) {
147
+ return /* @__PURE__ */ jsx("div", {
148
+ "data-slot": "card-header",
149
+ className: cn("gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]", className),
150
+ ...props
151
+ });
152
+ }
153
+ function CardTitle({ className, ...props }) {
154
+ return /* @__PURE__ */ jsx("div", {
155
+ "data-slot": "card-title",
156
+ className: cn("text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className),
157
+ ...props
158
+ });
159
+ }
160
+ function CardDescription({ className, ...props }) {
161
+ return /* @__PURE__ */ jsx("div", {
162
+ "data-slot": "card-description",
163
+ className: cn("text-muted-foreground text-sm", className),
164
+ ...props
165
+ });
166
+ }
167
+ function CardContent({ className, ...props }) {
168
+ return /* @__PURE__ */ jsx("div", {
169
+ "data-slot": "card-content",
170
+ className: cn("px-4 group-data-[size=sm]/card:px-3", className),
171
+ ...props
172
+ });
173
+ }
174
+ function CardFooter({ className, ...props }) {
175
+ return /* @__PURE__ */ jsx("div", {
176
+ "data-slot": "card-footer",
177
+ className: cn("bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center", className),
178
+ ...props
179
+ });
180
+ }
181
+
182
+ //#endregion
183
+ //#region src/primitives/chip-utils.ts
184
+ const CHIP_CLASS_NAMES = {
185
+ default: "inline-flex w-fit max-w-full flex-none items-center gap-1 rounded-md px-1.5 py-[2px] text-xs font-medium leading-4",
186
+ compact: "inline-flex w-fit max-w-full flex-none items-center gap-1 rounded-[4px] px-1.5 py-px text-[10px] font-medium leading-3"
187
+ };
188
+ function getChipClassName(size = "default") {
189
+ return CHIP_CLASS_NAMES[size];
190
+ }
191
+
192
+ //#endregion
193
+ //#region src/primitives/chip.tsx
194
+ function Chip({ children, className, leading, size = "default", trailing, ...props }) {
195
+ return /* @__PURE__ */ jsxs("span", {
196
+ ...props,
197
+ className: cn(getChipClassName(size), className),
198
+ children: [
199
+ leading ? /* @__PURE__ */ jsx("span", {
200
+ className: "shrink-0",
201
+ children: leading
202
+ }) : null,
203
+ /* @__PURE__ */ jsx("span", {
204
+ className: "truncate",
205
+ children
206
+ }),
207
+ trailing ? /* @__PURE__ */ jsx("span", {
208
+ className: "shrink-0",
209
+ children: trailing
210
+ }) : null
211
+ ]
212
+ });
213
+ }
214
+ function ChipButton({ children, className, leading, selected = false, size = "default", trailing, ...props }) {
215
+ return /* @__PURE__ */ jsxs("button", {
216
+ type: "button",
217
+ "aria-pressed": selected,
218
+ ...props,
219
+ className: cn(getChipClassName(size), "border text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", selected ? "border-[#713dff] bg-[#f6f1ff] text-[#1f1f23] shadow-[0_0_0_1px_rgba(113,61,255,0.08)] hover:bg-[#f6f1ff]" : "border-[#e7e8eb] bg-white text-[#5f6368] hover:bg-[#f7f7f9]", className),
220
+ children: [
221
+ leading ? /* @__PURE__ */ jsx("span", {
222
+ className: "shrink-0",
223
+ children: leading
224
+ }) : null,
225
+ /* @__PURE__ */ jsx("span", {
226
+ className: "truncate",
227
+ children
228
+ }),
229
+ trailing ? /* @__PURE__ */ jsx("span", {
230
+ className: "shrink-0",
231
+ children: trailing
232
+ }) : null
233
+ ]
234
+ });
235
+ }
236
+
237
+ //#endregion
238
+ //#region src/primitives/dialog.tsx
239
+ function Dialog({ ...props }) {
240
+ return /* @__PURE__ */ jsx(Dialog$1.Root, {
241
+ "data-slot": "dialog",
242
+ ...props
243
+ });
244
+ }
245
+ function DialogTrigger({ ...props }) {
246
+ return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
247
+ "data-slot": "dialog-trigger",
248
+ ...props
249
+ });
250
+ }
251
+ function DialogPortal({ ...props }) {
252
+ return /* @__PURE__ */ jsx(Dialog$1.Portal, {
253
+ "data-slot": "dialog-portal",
254
+ ...props
255
+ });
256
+ }
257
+ function DialogOverlay({ className, ...props }) {
258
+ return /* @__PURE__ */ jsx(Dialog$1.Overlay, {
259
+ "data-slot": "dialog-overlay",
260
+ className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className),
261
+ ...props
262
+ });
263
+ }
264
+ function DialogContent({ className, children, showCloseButton = true, ...props }) {
265
+ return /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog$1.Content, {
266
+ "data-slot": "dialog-content",
267
+ className: cn("bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-sm ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none", className),
268
+ ...props,
269
+ children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, {
270
+ "data-slot": "dialog-close",
271
+ asChild: true,
272
+ children: /* @__PURE__ */ jsxs(Button, {
273
+ variant: "ghost",
274
+ className: "absolute top-2 right-2",
275
+ size: "icon-sm",
276
+ children: [/* @__PURE__ */ jsx(X, {}), /* @__PURE__ */ jsx("span", {
277
+ className: "sr-only",
278
+ children: "Close"
279
+ })]
280
+ })
281
+ })]
282
+ })] });
283
+ }
284
+ function DialogHeader({ className, ...props }) {
285
+ return /* @__PURE__ */ jsx("div", {
286
+ "data-slot": "dialog-header",
287
+ className: cn("gap-2 flex flex-col", className),
288
+ ...props
289
+ });
290
+ }
291
+ function DialogFooter({ className, showCloseButton = false, children, ...props }) {
292
+ return /* @__PURE__ */ jsxs("div", {
293
+ "data-slot": "dialog-footer",
294
+ className: cn("bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
295
+ ...props,
296
+ children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, {
297
+ asChild: true,
298
+ children: /* @__PURE__ */ jsx(Button, {
299
+ variant: "outline",
300
+ children: "Close"
301
+ })
302
+ })]
303
+ });
304
+ }
305
+ function DialogTitle({ className, ...props }) {
306
+ return /* @__PURE__ */ jsx(Dialog$1.Title, {
307
+ "data-slot": "dialog-title",
308
+ className: cn("text-base leading-none font-medium", className),
309
+ ...props
310
+ });
311
+ }
312
+ function DialogDescription({ className, ...props }) {
313
+ return /* @__PURE__ */ jsx(Dialog$1.Description, {
314
+ "data-slot": "dialog-description",
315
+ className: cn("text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3", className),
316
+ ...props
317
+ });
318
+ }
319
+
320
+ //#endregion
321
+ //#region src/primitives/dropdown-menu.tsx
322
+ function DropdownMenu({ modal = false, ...props }) {
323
+ return /* @__PURE__ */ jsx(DropdownMenu$1.Root, {
324
+ "data-slot": "dropdown-menu",
325
+ modal,
326
+ ...props
327
+ });
328
+ }
329
+ function DropdownMenuTrigger({ ...props }) {
330
+ return /* @__PURE__ */ jsx(DropdownMenu$1.Trigger, {
331
+ "data-slot": "dropdown-menu-trigger",
332
+ ...props
333
+ });
334
+ }
335
+ function DropdownMenuContent({ className, align = "start", sideOffset = 4, ...props }) {
336
+ return /* @__PURE__ */ jsx(DropdownMenu$1.Portal, { children: /* @__PURE__ */ jsx(DropdownMenu$1.Content, {
337
+ "data-slot": "dropdown-menu-content",
338
+ sideOffset,
339
+ align,
340
+ className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-lg p-1 shadow-md ring-1 duration-100 z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto data-[state=closed]:overflow-hidden", className),
341
+ ...props
342
+ }) });
343
+ }
344
+ function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
345
+ return /* @__PURE__ */ jsx(DropdownMenu$1.Item, {
346
+ "data-slot": "dropdown-menu-item",
347
+ "data-inset": inset,
348
+ "data-variant": variant,
349
+ className: cn("focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-pointer items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
350
+ ...props
351
+ });
352
+ }
353
+ function DropdownMenuCheckboxItem({ className, children, checked, inset, ...props }) {
354
+ return /* @__PURE__ */ jsxs(DropdownMenu$1.CheckboxItem, {
355
+ "data-slot": "dropdown-menu-checkbox-item",
356
+ "data-inset": inset,
357
+ className: cn("focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
358
+ checked,
359
+ ...props,
360
+ children: [/* @__PURE__ */ jsx("span", {
361
+ className: "absolute right-2 flex items-center justify-center pointer-events-none",
362
+ "data-slot": "dropdown-menu-checkbox-item-indicator",
363
+ children: /* @__PURE__ */ jsx(DropdownMenu$1.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "size-4" }) })
364
+ }), children]
365
+ });
366
+ }
367
+ function DropdownMenuLabel({ className, inset, ...props }) {
368
+ return /* @__PURE__ */ jsx(DropdownMenu$1.Label, {
369
+ "data-slot": "dropdown-menu-label",
370
+ "data-inset": inset,
371
+ className: cn("text-muted-foreground px-1.5 py-1 text-xs font-medium data-inset:pl-7", className),
372
+ ...props
373
+ });
374
+ }
375
+ function DropdownMenuSeparator({ className, ...props }) {
376
+ return /* @__PURE__ */ jsx(DropdownMenu$1.Separator, {
377
+ "data-slot": "dropdown-menu-separator",
378
+ className: cn("bg-border -mx-1 my-1 h-px", className),
379
+ ...props
380
+ });
381
+ }
382
+
383
+ //#endregion
384
+ //#region src/primitives/drawer.tsx
385
+ function Drawer({ ...props }) {
386
+ return /* @__PURE__ */ jsx(Drawer$1.Root, {
387
+ "data-slot": "drawer",
388
+ ...props
389
+ });
390
+ }
391
+ function DrawerPortal({ ...props }) {
392
+ return /* @__PURE__ */ jsx(Drawer$1.Portal, {
393
+ "data-slot": "drawer-portal",
394
+ ...props
395
+ });
396
+ }
397
+ function DrawerOverlay({ className, ...props }) {
398
+ return /* @__PURE__ */ jsx(Drawer$1.Overlay, {
399
+ "data-slot": "drawer-overlay",
400
+ className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/10 backdrop-blur-xs", className),
401
+ ...props
402
+ });
403
+ }
404
+ function DrawerContent({ className, children, ...props }) {
405
+ return /* @__PURE__ */ jsxs(DrawerPortal, {
406
+ "data-slot": "drawer-portal",
407
+ children: [/* @__PURE__ */ jsx(DrawerOverlay, {}), /* @__PURE__ */ jsxs(Drawer$1.Content, {
408
+ "data-slot": "drawer-content",
409
+ className: cn("bg-background group/drawer-content fixed z-50 flex h-auto flex-col overflow-visible text-sm data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", className),
410
+ ...props,
411
+ children: [/* @__PURE__ */ jsx("div", { className: "bg-muted mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }), children]
412
+ })]
413
+ });
414
+ }
415
+ function DrawerHeader({ className, ...props }) {
416
+ return /* @__PURE__ */ jsx("div", {
417
+ "data-slot": "drawer-header",
418
+ className: cn("flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-0.5 md:text-left", className),
419
+ ...props
420
+ });
421
+ }
422
+ function DrawerTitle({ className, ...props }) {
423
+ return /* @__PURE__ */ jsx(Drawer$1.Title, {
424
+ "data-slot": "drawer-title",
425
+ className: cn("text-foreground text-base font-medium", className),
426
+ ...props
427
+ });
428
+ }
429
+ function DrawerDescription({ className, ...props }) {
430
+ return /* @__PURE__ */ jsx(Drawer$1.Description, {
431
+ "data-slot": "drawer-description",
432
+ className: cn("text-muted-foreground text-sm", className),
433
+ ...props
434
+ });
435
+ }
436
+
437
+ //#endregion
438
+ //#region src/utils/date-display.ts
439
+ const DEFAULT_DATE_DISPLAY_LABELS = {
440
+ today: "Today",
441
+ yesterday: "Yesterday",
442
+ daysAgo: (dayCount) => `${String(dayCount)} days ago`
443
+ };
444
+ function getDate(value) {
445
+ return value instanceof Date ? value : new Date(value);
446
+ }
447
+ function startOfDay(date) {
448
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
449
+ }
450
+ function getDayDifference(from, to) {
451
+ return Math.round((startOfDay(to).getTime() - startOfDay(from).getTime()) / (1440 * 60 * 1e3));
452
+ }
453
+ function capitalizeMonthLabel(value) {
454
+ return value.split(" ").map((part, index) => {
455
+ if (index !== 1 || part.length === 0) return part;
456
+ const [firstCharacter = "", ...restCharacters] = part;
457
+ return `${firstCharacter.toUpperCase()}${restCharacters.join("")}`;
458
+ }).join(" ");
459
+ }
460
+ function formatAbsoluteDate(value, options = {}) {
461
+ const date = getDate(value);
462
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
463
+ const includesYear = date.getFullYear() !== currentYear;
464
+ const locale = options.locale ?? "en-US";
465
+ return capitalizeMonthLabel(new Intl.DateTimeFormat(locale, {
466
+ day: "numeric",
467
+ month: "short",
468
+ ...includesYear ? { year: "numeric" } : {}
469
+ }).format(date));
470
+ }
471
+ function formatDisplayDate(value, options = {}) {
472
+ const date = getDate(value);
473
+ const dayDifference = getDayDifference(date, /* @__PURE__ */ new Date());
474
+ const labels = {
475
+ ...DEFAULT_DATE_DISPLAY_LABELS,
476
+ ...options.labels
477
+ };
478
+ if (dayDifference === 0) return labels.today;
479
+ if (dayDifference === 1) return labels.yesterday;
480
+ if (dayDifference >= 2 && dayDifference <= 10) return labels.daysAgo(dayDifference);
481
+ return formatAbsoluteDate(date, options);
482
+ }
483
+
484
+ //#endregion
485
+ //#region src/primitives/display-date.tsx
486
+ function DisplayDate({ value, formatOptions, className }) {
487
+ const date = value instanceof Date ? value : new Date(value);
488
+ return /* @__PURE__ */ jsx("time", {
489
+ dateTime: date.toISOString(),
490
+ title: formatAbsoluteDate(date, formatOptions),
491
+ className,
492
+ children: formatDisplayDate(date, formatOptions)
493
+ });
494
+ }
495
+
496
+ //#endregion
497
+ //#region src/primitives/field-detail-row.tsx
498
+ function FieldDetailRow({ icon: Icon, label, value, labelColumnClassName = "grid-cols-[110px_minmax(0,1fr)]" }) {
499
+ return /* @__PURE__ */ jsxs("div", {
500
+ className: `grid ${labelColumnClassName} items-start gap-2 py-1.5`,
501
+ children: [/* @__PURE__ */ jsxs("div", {
502
+ className: "flex items-center gap-2 text-sm text-muted-foreground",
503
+ children: [/* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0" }), /* @__PURE__ */ jsx("span", {
504
+ className: "text-[12px]",
505
+ children: label
506
+ })]
507
+ }), /* @__PURE__ */ jsx("div", {
508
+ className: "min-w-0 text-[14px] leading-5 tracking-[-0.28px] text-foreground",
509
+ children: value
510
+ })]
511
+ });
512
+ }
513
+
514
+ //#endregion
515
+ //#region src/primitives/file-dropzone.tsx
516
+ function formatFileSize(size) {
517
+ if (size < 1024) return `${size} B`;
518
+ if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`;
519
+ return `${(size / (1024 * 1024)).toFixed(1)} MB`;
520
+ }
521
+ function getFileTypeLabel(file) {
522
+ return file.type || file.name.split(".").pop()?.toUpperCase() || "File";
523
+ }
524
+ function FileDropzone({ accept, browseLabel = "Browse", browseButtonClassName, className, currentPreviewUrl = null, disabled = false, emptyIcon, helperText, footerText = "Drag a file here or click the box to select one.", inputClassName, multiple = false, onFileSelect, onFilesSelect, onRemove, previewAlt, title = "Drop a file here or browse" }) {
525
+ const inputRef = useRef(null);
526
+ const [isDragging, setIsDragging] = useState(false);
527
+ const [selectedFiles, setSelectedFiles] = useState([]);
528
+ const hasSelectedFiles = selectedFiles.length > 0;
529
+ const openFilePicker = () => {
530
+ if (disabled) return;
531
+ inputRef.current?.click();
532
+ };
533
+ const handleFileSelection = (files) => {
534
+ const selectedFiles$1 = Array.from(files ?? []);
535
+ if (selectedFiles$1.length === 0 || disabled) return;
536
+ setSelectedFiles(selectedFiles$1);
537
+ if (multiple && onFilesSelect) {
538
+ onFilesSelect(selectedFiles$1);
539
+ return;
540
+ }
541
+ const firstFile = selectedFiles$1[0];
542
+ if (firstFile) onFileSelect(firstFile);
543
+ };
544
+ const handleInputChange = (event) => {
545
+ handleFileSelection(event.target.files);
546
+ event.target.value = "";
547
+ };
548
+ const handleDrop = (event) => {
549
+ event.preventDefault();
550
+ event.stopPropagation();
551
+ setIsDragging(false);
552
+ handleFileSelection(event.dataTransfer.files);
553
+ };
554
+ const handleRemove = (event) => {
555
+ event.stopPropagation();
556
+ setSelectedFiles([]);
557
+ onRemove?.();
558
+ };
559
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
560
+ /* @__PURE__ */ jsx("input", {
561
+ ref: inputRef,
562
+ type: "file",
563
+ accept,
564
+ multiple,
565
+ className: "hidden",
566
+ onChange: handleInputChange,
567
+ disabled
568
+ }),
569
+ /* @__PURE__ */ jsx("div", {
570
+ role: "button",
571
+ tabIndex: disabled ? -1 : 0,
572
+ "aria-disabled": disabled,
573
+ onClick: openFilePicker,
574
+ onKeyDown: (event) => {
575
+ if (event.key === "Enter" || event.key === " ") {
576
+ event.preventDefault();
577
+ openFilePicker();
578
+ }
579
+ },
580
+ onDragEnter: (event) => {
581
+ event.preventDefault();
582
+ event.stopPropagation();
583
+ if (!disabled) setIsDragging(true);
584
+ },
585
+ onDragOver: (event) => {
586
+ event.preventDefault();
587
+ event.stopPropagation();
588
+ if (!disabled) setIsDragging(true);
589
+ },
590
+ onDragLeave: (event) => {
591
+ event.preventDefault();
592
+ event.stopPropagation();
593
+ setIsDragging(false);
594
+ },
595
+ onDrop: handleDrop,
596
+ className: cn("bg-muted/30 relative flex w-full cursor-pointer flex-col items-center justify-center rounded-lg border border-dashed px-6 py-5 text-center transition-colors outline-none", "border-primary/60 hover:bg-muted/50 focus-visible:ring-primary/20 focus-visible:ring-2", isDragging && "bg-muted/60", disabled && "cursor-not-allowed opacity-60", className),
597
+ children: currentPreviewUrl ? /* @__PURE__ */ jsxs("div", {
598
+ className: "bg-background relative flex min-h-40 w-full items-center justify-center overflow-hidden rounded-md",
599
+ children: [
600
+ /* @__PURE__ */ jsx("img", {
601
+ src: currentPreviewUrl,
602
+ alt: previewAlt,
603
+ className: "size-full object-contain"
604
+ }),
605
+ onRemove ? /* @__PURE__ */ jsx(Button, {
606
+ type: "button",
607
+ variant: "destructive",
608
+ size: "icon-xs",
609
+ className: "absolute top-2 right-2 z-10",
610
+ onClick: handleRemove,
611
+ disabled,
612
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
613
+ }) : null,
614
+ hasSelectedFiles ? /* @__PURE__ */ jsxs("div", {
615
+ className: "bg-background/95 absolute right-2 bottom-2 left-2 rounded-md border px-3 py-2 text-left shadow-sm backdrop-blur",
616
+ children: [/* @__PURE__ */ jsx("p", {
617
+ className: "truncate text-sm font-medium",
618
+ children: selectedFiles[0]?.name
619
+ }), selectedFiles[0] ? /* @__PURE__ */ jsxs("p", {
620
+ className: "text-muted-foreground text-xs",
621
+ children: [
622
+ getFileTypeLabel(selectedFiles[0]),
623
+ " | ",
624
+ formatFileSize(selectedFiles[0].size)
625
+ ]
626
+ }) : null]
627
+ }) : null
628
+ ]
629
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
630
+ /* @__PURE__ */ jsx("div", {
631
+ className: "text-primary mb-2 flex items-center justify-center",
632
+ children: emptyIcon ?? /* @__PURE__ */ jsx(Upload, { className: "size-5" })
633
+ }),
634
+ /* @__PURE__ */ jsxs("div", {
635
+ className: "space-y-0.5",
636
+ children: [/* @__PURE__ */ jsx("p", {
637
+ className: "text-foreground text-base font-medium",
638
+ children: title
639
+ }), helperText ? /* @__PURE__ */ jsx("p", {
640
+ className: "text-muted-foreground text-sm",
641
+ children: helperText
642
+ }) : null]
643
+ }),
644
+ /* @__PURE__ */ jsx(Button, {
645
+ type: "button",
646
+ size: "sm",
647
+ className: cn("mt-4 shadow-[0px_1px_1px_rgba(5,32,81,0.05)]", browseButtonClassName),
648
+ onClick: (event) => {
649
+ event.stopPropagation();
650
+ openFilePicker();
651
+ },
652
+ disabled,
653
+ children: browseLabel
654
+ }),
655
+ hasSelectedFiles ? /* @__PURE__ */ jsx("div", {
656
+ className: "mt-4 w-full space-y-2",
657
+ children: selectedFiles.map((file) => /* @__PURE__ */ jsxs("div", {
658
+ className: "bg-background flex min-w-0 items-center gap-3 rounded-md border px-3 py-2 text-left",
659
+ children: [
660
+ /* @__PURE__ */ jsx(FileText, { className: "text-muted-foreground size-4 shrink-0" }),
661
+ /* @__PURE__ */ jsxs("div", {
662
+ className: "min-w-0 flex-1",
663
+ children: [/* @__PURE__ */ jsx("p", {
664
+ className: "truncate text-sm font-medium",
665
+ children: file.name
666
+ }), /* @__PURE__ */ jsxs("p", {
667
+ className: "text-muted-foreground text-xs",
668
+ children: [
669
+ getFileTypeLabel(file),
670
+ " | ",
671
+ formatFileSize(file.size)
672
+ ]
673
+ })]
674
+ }),
675
+ /* @__PURE__ */ jsx(Check, { className: "text-primary size-4 shrink-0" })
676
+ ]
677
+ }, `${file.name}-${file.size}-${file.lastModified}`))
678
+ }) : null
679
+ ] })
680
+ }),
681
+ footerText ? /* @__PURE__ */ jsx("p", {
682
+ className: cn("text-muted-foreground mt-2 text-xs", inputClassName),
683
+ children: footerText
684
+ }) : null
685
+ ] });
686
+ }
687
+
688
+ //#endregion
689
+ //#region src/primitives/keyboard-shortcut-hint.tsx
690
+ function KeyboardKeycap({ children, className }) {
691
+ return /* @__PURE__ */ jsx("span", {
692
+ className: cn("inline-flex h-4 min-w-4 items-center justify-center rounded-[4px] border px-1 text-[9px] font-semibold leading-none", className),
693
+ children
694
+ });
695
+ }
696
+ function ShortcutModifierKeycap({ modifierLabel, className }) {
697
+ return /* @__PURE__ */ jsx(KeyboardKeycap, {
698
+ className,
699
+ children: modifierLabel === "Cmd" ? /* @__PURE__ */ jsx(Command, { className: "size-[10px]" }) : "Ctrl"
700
+ });
701
+ }
702
+
703
+ //#endregion
704
+ //#region src/overlays/responsive-sheet.footer.tsx
705
+ function DesktopConfirmShortcutHint({ desktopModifierLabel }) {
706
+ const keycapClassName = "border-primary-foreground/20 bg-primary-foreground/10";
707
+ return /* @__PURE__ */ jsxs("span", {
708
+ className: "text-primary-foreground/70 inline-flex items-center gap-1",
709
+ children: [/* @__PURE__ */ jsx(ShortcutModifierKeycap, {
710
+ modifierLabel: desktopModifierLabel,
711
+ className: keycapClassName
712
+ }), /* @__PURE__ */ jsx(KeyboardKeycap, {
713
+ className: keycapClassName,
714
+ children: /* @__PURE__ */ jsx(CornerDownLeft, { className: "size-[10px]" })
715
+ })]
716
+ });
717
+ }
718
+ function SheetActionFooter({ footer, onCancel, cancelLabel, onConfirm, confirmLabel, confirmDisabled, confirmLoading, desktopConfirmShortcutEnabled = false, desktopModifierLabel = null }) {
719
+ if (footer) return footer;
720
+ if (!onCancel && !onConfirm) return null;
721
+ const footerButtonClassName = Number(Boolean(onCancel)) + Number(Boolean(onConfirm)) === 1 ? "w-full" : "min-w-0 flex-1";
722
+ return /* @__PURE__ */ jsxs("div", {
723
+ className: "flex w-full flex-nowrap items-center gap-2",
724
+ children: [onCancel ? /* @__PURE__ */ jsx(Button, {
725
+ type: "button",
726
+ variant: "outline",
727
+ onClick: onCancel,
728
+ className: footerButtonClassName,
729
+ children: cancelLabel
730
+ }) : null, onConfirm ? /* @__PURE__ */ jsx(Button, {
731
+ type: "button",
732
+ onClick: onConfirm,
733
+ disabled: confirmDisabled,
734
+ className: `${footerButtonClassName} relative`,
735
+ children: /* @__PURE__ */ jsxs("span", {
736
+ className: "inline-flex w-full items-center justify-center",
737
+ children: [/* @__PURE__ */ jsx("span", { children: confirmLoading ? "Saving..." : confirmLabel }), desktopConfirmShortcutEnabled && desktopModifierLabel ? /* @__PURE__ */ jsx("span", {
738
+ className: "absolute top-1/2 right-2 -translate-y-1/2",
739
+ children: /* @__PURE__ */ jsx(DesktopConfirmShortcutHint, { desktopModifierLabel })
740
+ }) : null]
741
+ })
742
+ }) : null]
743
+ });
744
+ }
745
+
746
+ //#endregion
747
+ //#region src/primitives/sheet.tsx
748
+ function Sheet({ ...props }) {
749
+ return /* @__PURE__ */ jsx(Dialog$1.Root, {
750
+ "data-slot": "sheet",
751
+ ...props
752
+ });
753
+ }
754
+ function SheetPortal({ ...props }) {
755
+ return /* @__PURE__ */ jsx(Dialog$1.Portal, {
756
+ "data-slot": "sheet-portal",
757
+ ...props
758
+ });
759
+ }
760
+ function SheetOverlay({ className, ...props }) {
761
+ return /* @__PURE__ */ jsx(Dialog$1.Overlay, {
762
+ "data-slot": "sheet-overlay",
763
+ className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/10 backdrop-blur-xs duration-100 data-ending-style:opacity-0 data-starting-style:opacity-0", className),
764
+ ...props
765
+ });
766
+ }
767
+ function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }) {
768
+ return /* @__PURE__ */ jsxs(SheetPortal, { children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(Dialog$1.Content, {
769
+ "data-slot": "sheet-content",
770
+ "data-side": side,
771
+ className: cn("bg-background data-open:animate-in data-closed:animate-out data-[side=right]:data-closed:slide-out-to-right-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=top]:data-closed:slide-out-to-top-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:fade-out-0 data-open:fade-in-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=bottom]:data-open:slide-in-from-bottom-10 fixed z-50 flex flex-col gap-4 overflow-visible bg-clip-padding text-sm shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:rounded-r-xl data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:rounded-l-xl data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm", className),
772
+ ...props,
773
+ children: [children, showCloseButton ? /* @__PURE__ */ jsx(Dialog$1.Close, {
774
+ "data-slot": "sheet-close",
775
+ asChild: true,
776
+ children: /* @__PURE__ */ jsxs(Button, {
777
+ variant: "ghost",
778
+ className: "absolute top-3 right-3",
779
+ size: "icon-sm",
780
+ children: [/* @__PURE__ */ jsx(X, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
781
+ className: "sr-only",
782
+ children: "Close"
783
+ })]
784
+ })
785
+ }) : null]
786
+ })] });
787
+ }
788
+ function SheetHeader({ className, ...props }) {
789
+ return /* @__PURE__ */ jsx("div", {
790
+ "data-slot": "sheet-header",
791
+ className: cn("flex flex-col gap-0.5 p-4", className),
792
+ ...props
793
+ });
794
+ }
795
+ function SheetTitle({ className, ...props }) {
796
+ return /* @__PURE__ */ jsx(Dialog$1.Title, {
797
+ "data-slot": "sheet-title",
798
+ className: cn("text-foreground text-base font-medium", className),
799
+ ...props
800
+ });
801
+ }
802
+ function SheetDescription({ className, ...props }) {
803
+ return /* @__PURE__ */ jsx(Dialog$1.Description, {
804
+ "data-slot": "sheet-description",
805
+ className: cn("text-muted-foreground text-sm", className),
806
+ ...props
807
+ });
808
+ }
809
+
810
+ //#endregion
811
+ //#region src/overlays/responsive-sheet.layouts.tsx
812
+ function SheetDescriptionBlock({ title, description, classes }) {
813
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SheetTitle, {
814
+ className: classes?.title,
815
+ children: title
816
+ }), description ? /* @__PURE__ */ jsx(SheetDescription, {
817
+ className: classes?.description,
818
+ children: description
819
+ }) : null] });
820
+ }
821
+ function shouldPreventOutsideInteraction(target, guard) {
822
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
823
+ if (!element) return false;
824
+ if (element.closest("[data-searchable-select-content]")) return true;
825
+ return guard?.selectors.some((selector) => element.closest(selector)) ?? false;
826
+ }
827
+ function MobileSheetLayout({ open, onOpenChange, modal, outsideInteractionGuard, title, description, children, footer, mobileDrawerContentClassName, contentClassName, footerClassName, classes }) {
828
+ return /* @__PURE__ */ jsx(Drawer, {
829
+ open,
830
+ onOpenChange,
831
+ modal,
832
+ children: /* @__PURE__ */ jsxs(DrawerContent, {
833
+ "aria-describedby": description ? void 0 : "responsive-sheet-description-empty",
834
+ className: cn("px-4 pb-[calc(env(safe-area-inset-bottom)+20px)]", mobileDrawerContentClassName, classes?.mobileContent),
835
+ onInteractOutside: (event) => {
836
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) event.preventDefault();
837
+ },
838
+ onPointerDownOutside: (event) => {
839
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) event.preventDefault();
840
+ },
841
+ children: [/* @__PURE__ */ jsxs(DrawerHeader, {
842
+ className: cn("px-0 pb-4", classes?.header),
843
+ children: [/* @__PURE__ */ jsx(DrawerTitle, {
844
+ className: classes?.title,
845
+ children: title
846
+ }), description ? /* @__PURE__ */ jsx(DrawerDescription, {
847
+ className: classes?.description,
848
+ children: description
849
+ }) : /* @__PURE__ */ jsx(DrawerDescription, {
850
+ id: "responsive-sheet-description-empty",
851
+ className: "sr-only",
852
+ children: "Dialog"
853
+ })]
854
+ }), /* @__PURE__ */ jsxs("div", {
855
+ className: "flex min-h-0 flex-1 flex-col gap-4",
856
+ children: [/* @__PURE__ */ jsx("div", {
857
+ className: cn("-mx-1 flex-1 overflow-y-auto px-1 pb-3 [scrollbar-gutter:stable]", contentClassName, classes?.body),
858
+ children
859
+ }), footer ? /* @__PURE__ */ jsx("div", {
860
+ className: cn("shrink-0 border-t pt-4 pb-3", footerClassName, classes?.footer),
861
+ children: footer
862
+ }) : null]
863
+ })]
864
+ })
865
+ });
866
+ }
867
+ function DesktopSheetLayout({ open, onOpenChange, modal, outsideInteractionGuard, title, description, children, footer, width, contentClassName, footerClassName, classes }) {
868
+ return /* @__PURE__ */ jsx(Sheet, {
869
+ open,
870
+ onOpenChange,
871
+ modal,
872
+ children: /* @__PURE__ */ jsxs(SheetContent, {
873
+ "aria-describedby": description ? void 0 : "responsive-sheet-description-empty",
874
+ style: {
875
+ width: `${String(width)}px`,
876
+ maxWidth: "85vw"
877
+ },
878
+ className: cn("flex flex-col gap-0 p-0", classes?.desktopContent),
879
+ onInteractOutside: (event) => {
880
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) event.preventDefault();
881
+ },
882
+ onPointerDownOutside: (event) => {
883
+ if (shouldPreventOutsideInteraction(event.target, outsideInteractionGuard)) event.preventDefault();
884
+ },
885
+ children: [
886
+ /* @__PURE__ */ jsx(SheetHeader, {
887
+ className: cn("border-b px-4 py-4", classes?.header),
888
+ children: /* @__PURE__ */ jsx(SheetDescriptionBlock, {
889
+ title,
890
+ description,
891
+ classes
892
+ })
893
+ }),
894
+ description ? null : /* @__PURE__ */ jsx(SheetDescription, {
895
+ id: "responsive-sheet-description-empty",
896
+ className: "sr-only",
897
+ children: "Dialog"
898
+ }),
899
+ /* @__PURE__ */ jsxs("div", {
900
+ className: "flex min-h-0 flex-1 flex-col",
901
+ children: [/* @__PURE__ */ jsx("div", {
902
+ className: cn("flex-1 overflow-x-visible overflow-y-auto px-4 pt-4 pb-6 [scrollbar-gutter:stable]", contentClassName, classes?.body),
903
+ children
904
+ }), footer ? /* @__PURE__ */ jsx("div", {
905
+ className: cn("border-t px-4 py-4", footerClassName, classes?.footer),
906
+ children: footer
907
+ }) : null]
908
+ })
909
+ ]
910
+ })
911
+ });
912
+ }
913
+
914
+ //#endregion
915
+ //#region src/overlays/responsive-sheet.shortcuts.ts
916
+ function getDesktopShortcutModifierLabel() {
917
+ if (typeof navigator === "undefined") return "Ctrl";
918
+ const platform = navigator.userAgentData?.platform ?? navigator.userAgent;
919
+ return /Mac|iPhone|iPad|iPod/i.test(platform) ? "Cmd" : "Ctrl";
920
+ }
921
+ function isAllowedConfirmShortcutEvent(event, desktopModifierLabel) {
922
+ if (event.key !== "Enter" || event.repeat || event.isComposing) return false;
923
+ const expectsMetaKey = desktopModifierLabel === "Cmd";
924
+ const usedExpectedModifier = expectsMetaKey ? event.metaKey : event.ctrlKey;
925
+ const usedOtherModifier = expectsMetaKey ? event.ctrlKey : event.metaKey;
926
+ const usedShiftModifier = event.shiftKey;
927
+ const usedAltModifier = event.altKey;
928
+ if (!usedExpectedModifier || usedOtherModifier || usedShiftModifier || usedAltModifier) return false;
929
+ return true;
930
+ }
931
+ function useDesktopShortcutModifierLabel(enabled) {
932
+ const [desktopModifierLabel, setDesktopModifierLabel] = useState(null);
933
+ useEffect(() => {
934
+ if (!enabled) {
935
+ setDesktopModifierLabel(null);
936
+ return;
937
+ }
938
+ setDesktopModifierLabel(getDesktopShortcutModifierLabel());
939
+ }, [enabled]);
940
+ return desktopModifierLabel;
941
+ }
942
+ function useDesktopConfirmShortcut({ open, enabled, confirmDisabled, confirmLoading, onConfirm }) {
943
+ useEffect(() => {
944
+ if (!open || !enabled || !onConfirm || confirmDisabled || confirmLoading) return;
945
+ const desktopModifierLabel = getDesktopShortcutModifierLabel();
946
+ const handleKeyDown = (event) => {
947
+ if (!isAllowedConfirmShortcutEvent(event, desktopModifierLabel)) return;
948
+ event.preventDefault();
949
+ onConfirm();
950
+ };
951
+ window.addEventListener("keydown", handleKeyDown);
952
+ return () => {
953
+ window.removeEventListener("keydown", handleKeyDown);
954
+ };
955
+ }, [
956
+ confirmDisabled,
957
+ confirmLoading,
958
+ enabled,
959
+ onConfirm,
960
+ open
961
+ ]);
962
+ }
963
+
964
+ //#endregion
965
+ //#region src/utils/use-media-query.ts
966
+ function useMediaQuery(query, defaultValue = false) {
967
+ const [matches, setMatches] = useState(defaultValue);
968
+ useEffect(() => {
969
+ const mediaQuery = window.matchMedia(query);
970
+ const handleChange = () => {
971
+ setMatches(mediaQuery.matches);
972
+ };
973
+ handleChange();
974
+ mediaQuery.addEventListener("change", handleChange);
975
+ return () => {
976
+ mediaQuery.removeEventListener("change", handleChange);
977
+ };
978
+ }, [query]);
979
+ return matches;
980
+ }
981
+ function useIsMobile(maxWidth = 767) {
982
+ return useMediaQuery(`(max-width: ${String(maxWidth)}px)`);
983
+ }
984
+
985
+ //#endregion
986
+ //#region src/overlays/responsive-sheet.tsx
987
+ function ResponsiveSheet({ open, onOpenChange, title, description, children, footer, onCancel, cancelLabel = "Cancel", onConfirm, confirmLabel = "Save", confirmDisabled = false, confirmLoading = false, width = 550, modal = true, outsideInteractionGuard, enableDesktopConfirmShortcut = true, mobileDrawerContentClassName, className, contentClassName, footerClassName, classes }) {
988
+ const isMobile = useIsMobile();
989
+ const desktopConfirmShortcutEnabled = !isMobile && enableDesktopConfirmShortcut && Boolean(onConfirm);
990
+ const desktopModifierLabel = useDesktopShortcutModifierLabel(desktopConfirmShortcutEnabled);
991
+ useDesktopConfirmShortcut({
992
+ open,
993
+ enabled: desktopConfirmShortcutEnabled,
994
+ confirmDisabled,
995
+ confirmLoading,
996
+ onConfirm: onConfirm ? () => {
997
+ onConfirm();
998
+ } : void 0
999
+ });
1000
+ const resolvedFooter = /* @__PURE__ */ jsx(SheetActionFooter, {
1001
+ footer,
1002
+ onCancel,
1003
+ cancelLabel,
1004
+ onConfirm,
1005
+ confirmLabel,
1006
+ confirmDisabled,
1007
+ confirmLoading,
1008
+ desktopConfirmShortcutEnabled,
1009
+ desktopModifierLabel
1010
+ });
1011
+ const sharedLayoutProps = {
1012
+ open,
1013
+ onOpenChange,
1014
+ modal,
1015
+ outsideInteractionGuard,
1016
+ title,
1017
+ description,
1018
+ footer: [
1019
+ footer,
1020
+ onCancel,
1021
+ onConfirm
1022
+ ].some((value) => value !== null && value !== void 0) ? resolvedFooter : null,
1023
+ children,
1024
+ contentClassName,
1025
+ footerClassName,
1026
+ mobileDrawerContentClassName,
1027
+ classes: {
1028
+ ...classes,
1029
+ desktopContent: cn(className, classes?.desktopContent),
1030
+ mobileContent: cn(className, mobileDrawerContentClassName, classes?.mobileContent)
1031
+ }
1032
+ };
1033
+ return isMobile ? /* @__PURE__ */ jsx(MobileSheetLayout, { ...sharedLayoutProps }) : /* @__PURE__ */ jsx(DesktopSheetLayout, {
1034
+ ...sharedLayoutProps,
1035
+ width
1036
+ });
1037
+ }
1038
+
1039
+ //#endregion
1040
+ //#region src/primitives/tooltip.tsx
1041
+ function TooltipProvider({ delayDuration = 0, ...props }) {
1042
+ return /* @__PURE__ */ jsx(Tooltip$1.Provider, {
1043
+ "data-slot": "tooltip-provider",
1044
+ delayDuration,
1045
+ ...props
1046
+ });
1047
+ }
1048
+ function Tooltip({ ...props }) {
1049
+ return /* @__PURE__ */ jsx(Tooltip$1.Root, {
1050
+ "data-slot": "tooltip",
1051
+ ...props
1052
+ });
1053
+ }
1054
+ function TooltipTrigger({ ...props }) {
1055
+ return /* @__PURE__ */ jsx(Tooltip$1.Trigger, {
1056
+ "data-slot": "tooltip-trigger",
1057
+ ...props
1058
+ });
1059
+ }
1060
+ function TooltipContent({ className, sideOffset = 0, children, ...props }) {
1061
+ return /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsxs(Tooltip$1.Content, {
1062
+ "data-slot": "tooltip-content",
1063
+ sideOffset,
1064
+ className: cn("data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-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 rounded-md bg-foreground px-3 py-1.5 text-xs text-background z-50 w-fit max-w-[min(20rem,calc(100vw-1.5rem))] whitespace-normal break-words origin-(--radix-tooltip-content-transform-origin)", className),
1065
+ ...props,
1066
+ children: [children, /* @__PURE__ */ jsx(Tooltip$1.Arrow, { className: "size-2.5 rotate-45 rounded-[2px] bg-foreground fill-foreground z-50 translate-y-[calc(-50%_-_2px)]" })]
1067
+ }) });
1068
+ }
1069
+
1070
+ //#endregion
1071
+ //#region src/primitives/help-info-button.tsx
1072
+ function HelpInfoButton({ ariaLabel, tooltip, title, description, children, width = 620 }) {
1073
+ const [open, setOpen] = useState(false);
1074
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1075
+ asChild: true,
1076
+ children: /* @__PURE__ */ jsx(Button, {
1077
+ type: "button",
1078
+ variant: "ghost",
1079
+ size: "icon",
1080
+ className: "size-6 text-muted-foreground hover:text-foreground",
1081
+ "aria-label": ariaLabel,
1082
+ onClick: () => {
1083
+ setOpen(true);
1084
+ },
1085
+ children: /* @__PURE__ */ jsx(CircleHelp, { className: "size-3.5" })
1086
+ })
1087
+ }), /* @__PURE__ */ jsx(TooltipContent, { children: tooltip })] }), /* @__PURE__ */ jsx(ResponsiveSheet, {
1088
+ open,
1089
+ onOpenChange: setOpen,
1090
+ title,
1091
+ description,
1092
+ cancelLabel: "Close",
1093
+ onCancel: () => {
1094
+ setOpen(false);
1095
+ },
1096
+ width,
1097
+ children
1098
+ })] });
1099
+ }
1100
+
1101
+ //#endregion
1102
+ //#region src/primitives/input.tsx
1103
+ function Input({ className, type, ...props }) {
1104
+ return /* @__PURE__ */ jsx("input", {
1105
+ type,
1106
+ "data-slot": "input",
1107
+ className: cn("dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", className),
1108
+ ...props
1109
+ });
1110
+ }
1111
+
1112
+ //#endregion
1113
+ //#region src/primitives/label.tsx
1114
+ function Label({ className, ...props }) {
1115
+ return /* @__PURE__ */ jsx(Label$1.Root, {
1116
+ "data-slot": "label",
1117
+ className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className),
1118
+ ...props
1119
+ });
1120
+ }
1121
+
1122
+ //#endregion
1123
+ //#region src/primitives/pagination.tsx
1124
+ function Pagination({ currentPage, totalPages, totalItems, pageSize, startIndex, endIndex, onPageChange, pageSizeOptions, onPageSizeChange, className }) {
1125
+ if (totalPages <= 1) return null;
1126
+ const hasPrevPage = currentPage > 1;
1127
+ const hasNextPage = currentPage < totalPages;
1128
+ return /* @__PURE__ */ jsxs("div", {
1129
+ className: cn("flex items-center justify-between gap-3 py-3", className),
1130
+ children: [/* @__PURE__ */ jsx("div", {
1131
+ className: "text-xs text-muted-foreground",
1132
+ children: totalItems === 0 ? "No items" : /* @__PURE__ */ jsxs(Fragment, { children: [
1133
+ "Showing ",
1134
+ /* @__PURE__ */ jsx("span", {
1135
+ className: "font-medium",
1136
+ children: startIndex + 1
1137
+ }),
1138
+ " -",
1139
+ " ",
1140
+ /* @__PURE__ */ jsx("span", {
1141
+ className: "font-medium",
1142
+ children: endIndex
1143
+ }),
1144
+ " of",
1145
+ " ",
1146
+ /* @__PURE__ */ jsx("span", {
1147
+ className: "font-medium",
1148
+ children: totalItems
1149
+ })
1150
+ ] })
1151
+ }), /* @__PURE__ */ jsxs("div", {
1152
+ className: "flex items-center gap-1.5",
1153
+ children: [
1154
+ pageSizeOptions && onPageSizeChange && /* @__PURE__ */ jsxs("div", {
1155
+ className: "hidden items-center gap-1.5 sm:flex",
1156
+ children: [/* @__PURE__ */ jsx("span", {
1157
+ className: "text-xs text-muted-foreground",
1158
+ children: "Rows:"
1159
+ }), /* @__PURE__ */ jsx("select", {
1160
+ value: String(pageSize),
1161
+ onChange: (event) => {
1162
+ onPageSizeChange(Number(event.target.value));
1163
+ },
1164
+ className: "border-input bg-background h-7 w-[64px] rounded-md border px-2 text-xs",
1165
+ children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx("option", {
1166
+ value: String(size),
1167
+ children: size
1168
+ }, size))
1169
+ })]
1170
+ }),
1171
+ /* @__PURE__ */ jsxs("div", {
1172
+ className: "hidden text-xs text-muted-foreground sm:block",
1173
+ children: [
1174
+ "Page ",
1175
+ currentPage,
1176
+ " of ",
1177
+ totalPages
1178
+ ]
1179
+ }),
1180
+ /* @__PURE__ */ jsxs("div", {
1181
+ className: "flex items-center gap-0.5",
1182
+ children: [
1183
+ /* @__PURE__ */ jsxs(Button, {
1184
+ variant: "outline",
1185
+ size: "icon",
1186
+ className: "size-7",
1187
+ onClick: () => {
1188
+ onPageChange(1);
1189
+ },
1190
+ disabled: !hasPrevPage,
1191
+ children: [/* @__PURE__ */ jsx(ChevronsLeft, { className: "size-3.5" }), /* @__PURE__ */ jsx("span", {
1192
+ className: "sr-only",
1193
+ children: "First page"
1194
+ })]
1195
+ }),
1196
+ /* @__PURE__ */ jsxs(Button, {
1197
+ variant: "outline",
1198
+ size: "icon",
1199
+ className: "size-7",
1200
+ onClick: () => {
1201
+ onPageChange(currentPage - 1);
1202
+ },
1203
+ disabled: !hasPrevPage,
1204
+ children: [/* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5" }), /* @__PURE__ */ jsx("span", {
1205
+ className: "sr-only",
1206
+ children: "Previous page"
1207
+ })]
1208
+ }),
1209
+ /* @__PURE__ */ jsxs("span", {
1210
+ className: "px-1.5 text-xs sm:hidden",
1211
+ children: [
1212
+ currentPage,
1213
+ "/",
1214
+ totalPages
1215
+ ]
1216
+ }),
1217
+ /* @__PURE__ */ jsxs(Button, {
1218
+ variant: "outline",
1219
+ size: "icon",
1220
+ className: "size-7",
1221
+ onClick: () => {
1222
+ onPageChange(currentPage + 1);
1223
+ },
1224
+ disabled: !hasNextPage,
1225
+ children: [/* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5" }), /* @__PURE__ */ jsx("span", {
1226
+ className: "sr-only",
1227
+ children: "Next page"
1228
+ })]
1229
+ }),
1230
+ /* @__PURE__ */ jsxs(Button, {
1231
+ variant: "outline",
1232
+ size: "icon",
1233
+ className: "size-7",
1234
+ onClick: () => {
1235
+ onPageChange(totalPages);
1236
+ },
1237
+ disabled: !hasNextPage,
1238
+ children: [/* @__PURE__ */ jsx(ChevronsRight, { className: "size-3.5" }), /* @__PURE__ */ jsx("span", {
1239
+ className: "sr-only",
1240
+ children: "Last page"
1241
+ })]
1242
+ })
1243
+ ]
1244
+ })
1245
+ ]
1246
+ })]
1247
+ });
1248
+ }
1249
+
1250
+ //#endregion
1251
+ //#region src/primitives/popover.tsx
1252
+ function Popover({ ...props }) {
1253
+ return /* @__PURE__ */ jsx(Popover$1.Root, {
1254
+ "data-slot": "popover",
1255
+ ...props
1256
+ });
1257
+ }
1258
+ function PopoverTrigger({ ...props }) {
1259
+ return /* @__PURE__ */ jsx(Popover$1.Trigger, {
1260
+ "data-slot": "popover-trigger",
1261
+ ...props
1262
+ });
1263
+ }
1264
+ function PopoverContent({ className, align = "center", sideOffset = 4, ...props }) {
1265
+ return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Content, {
1266
+ "data-slot": "popover-content",
1267
+ align,
1268
+ sideOffset,
1269
+ className: cn("z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
1270
+ ...props
1271
+ }) });
1272
+ }
1273
+
1274
+ //#endregion
1275
+ //#region src/primitives/scroll-fade-area.tsx
1276
+ const MIN_THUMB_SIZE = 40;
1277
+ const SCROLLBAR_INSET = 6;
1278
+ function getScrollFadeMask({ canScrollEnd, canScrollStart, orientation }) {
1279
+ const direction = orientation === "horizontal" ? "right" : "bottom";
1280
+ if (canScrollStart && canScrollEnd) return `linear-gradient(to ${direction}, transparent, black var(--scroll-fade-size), black calc(100% - var(--scroll-fade-size)), transparent)`;
1281
+ if (canScrollStart) return `linear-gradient(to ${direction}, transparent, black var(--scroll-fade-size))`;
1282
+ if (canScrollEnd) return `linear-gradient(to ${direction}, black calc(100% - var(--scroll-fade-size)), transparent)`;
1283
+ return "none";
1284
+ }
1285
+ function getScrollState(scrollArea, orientation) {
1286
+ if (orientation === "horizontal") return {
1287
+ canScrollStart: scrollArea.scrollLeft > 1,
1288
+ canScrollEnd: scrollArea.scrollLeft + scrollArea.clientWidth < scrollArea.scrollWidth - 1
1289
+ };
1290
+ return {
1291
+ canScrollStart: scrollArea.scrollTop > 1,
1292
+ canScrollEnd: scrollArea.scrollTop + scrollArea.clientHeight < scrollArea.scrollHeight - 1
1293
+ };
1294
+ }
1295
+ function getVerticalScrollbarState(scrollArea) {
1296
+ const trackHeight = scrollArea.clientHeight - SCROLLBAR_INSET * 2;
1297
+ const maxScrollTop = scrollArea.scrollHeight - scrollArea.clientHeight;
1298
+ if (maxScrollTop <= 0 || trackHeight <= 0) return {
1299
+ isScrollable: false,
1300
+ thumbHeight: 0,
1301
+ thumbTop: 0
1302
+ };
1303
+ const thumbHeight = Math.max(scrollArea.clientHeight / scrollArea.scrollHeight * trackHeight, MIN_THUMB_SIZE);
1304
+ return {
1305
+ isScrollable: true,
1306
+ thumbHeight,
1307
+ thumbTop: (trackHeight - thumbHeight) * (scrollArea.scrollTop / maxScrollTop)
1308
+ };
1309
+ }
1310
+ function ScrollFadeArea({ children, className, fadeSize = 24, onPointerEnter, onPointerLeave, onScroll, orientation = "vertical", scrollbarVisibility = "hidden", style, viewportClassName, ...props }) {
1311
+ const scrollAreaRef = React.useRef(null);
1312
+ const dragOffsetRef = React.useRef(0);
1313
+ const [scrollState, setScrollState] = React.useState({
1314
+ canScrollStart: false,
1315
+ canScrollEnd: false
1316
+ });
1317
+ const [verticalScrollbarState, setVerticalScrollbarState] = React.useState({
1318
+ isScrollable: false,
1319
+ thumbHeight: 0,
1320
+ thumbTop: 0
1321
+ });
1322
+ const [isSectionActive, setIsSectionActive] = React.useState(false);
1323
+ const [isDraggingScrollbar, setIsDraggingScrollbar] = React.useState(false);
1324
+ const shouldRenderSectionScrollbar = orientation === "vertical" && scrollbarVisibility === "section-hover";
1325
+ const isSectionScrollbarVisible = isSectionActive || isDraggingScrollbar;
1326
+ const updateScrollState = React.useCallback(() => {
1327
+ const scrollArea = scrollAreaRef.current;
1328
+ if (!scrollArea) return;
1329
+ const nextScrollState = getScrollState(scrollArea, orientation);
1330
+ setScrollState((currentScrollState) => {
1331
+ if (currentScrollState.canScrollStart === nextScrollState.canScrollStart && currentScrollState.canScrollEnd === nextScrollState.canScrollEnd) return currentScrollState;
1332
+ return nextScrollState;
1333
+ });
1334
+ if (shouldRenderSectionScrollbar) setVerticalScrollbarState(getVerticalScrollbarState(scrollArea));
1335
+ }, [orientation, shouldRenderSectionScrollbar]);
1336
+ const scrollToThumbPosition = React.useCallback((thumbTop) => {
1337
+ const scrollArea = scrollAreaRef.current;
1338
+ if (!scrollArea) return;
1339
+ const maxThumbTop = scrollArea.clientHeight - SCROLLBAR_INSET * 2 - verticalScrollbarState.thumbHeight;
1340
+ scrollArea.scrollTop = (scrollArea.scrollHeight - scrollArea.clientHeight) * (maxThumbTop > 0 ? Math.min(Math.max(thumbTop, 0), maxThumbTop) / maxThumbTop : 0);
1341
+ updateScrollState();
1342
+ }, [updateScrollState, verticalScrollbarState.thumbHeight]);
1343
+ function handleTrackPointerDown(event) {
1344
+ if (event.target !== event.currentTarget) return;
1345
+ scrollToThumbPosition(event.nativeEvent.offsetY - verticalScrollbarState.thumbHeight / 2);
1346
+ }
1347
+ function handleThumbPointerDown(event) {
1348
+ event.preventDefault();
1349
+ event.currentTarget.setPointerCapture(event.pointerId);
1350
+ setIsDraggingScrollbar(true);
1351
+ dragOffsetRef.current = event.clientY - SCROLLBAR_INSET - verticalScrollbarState.thumbTop;
1352
+ }
1353
+ function handleThumbPointerMove(event) {
1354
+ if (!event.currentTarget.hasPointerCapture(event.pointerId)) return;
1355
+ scrollToThumbPosition(event.clientY - SCROLLBAR_INSET - dragOffsetRef.current);
1356
+ }
1357
+ function handleThumbPointerUp(event) {
1358
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
1359
+ setIsDraggingScrollbar(false);
1360
+ }
1361
+ React.useEffect(() => {
1362
+ const scrollArea = scrollAreaRef.current;
1363
+ if (!scrollArea) return;
1364
+ updateScrollState();
1365
+ const resizeObserver = new ResizeObserver(updateScrollState);
1366
+ resizeObserver.observe(scrollArea);
1367
+ if (scrollArea.firstElementChild) resizeObserver.observe(scrollArea.firstElementChild);
1368
+ return () => {
1369
+ resizeObserver.disconnect();
1370
+ };
1371
+ }, [children, updateScrollState]);
1372
+ const maskImage = getScrollFadeMask({
1373
+ canScrollEnd: scrollState.canScrollEnd,
1374
+ canScrollStart: scrollState.canScrollStart,
1375
+ orientation
1376
+ });
1377
+ const maskStyle = {
1378
+ "--scroll-fade-size": `${String(fadeSize)}px`,
1379
+ WebkitMaskImage: maskImage,
1380
+ maskImage
1381
+ };
1382
+ return /* @__PURE__ */ jsxs("div", {
1383
+ className: cn("group/scroll-fade relative min-h-0 min-w-0", className),
1384
+ style,
1385
+ onPointerEnter: (event) => {
1386
+ setIsSectionActive(true);
1387
+ onPointerEnter?.(event);
1388
+ },
1389
+ onPointerLeave: (event) => {
1390
+ setIsSectionActive(false);
1391
+ onPointerLeave?.(event);
1392
+ },
1393
+ ...props,
1394
+ children: [/* @__PURE__ */ jsx("div", {
1395
+ ref: scrollAreaRef,
1396
+ className: cn("min-h-0 min-w-0", orientation === "horizontal" ? "overflow-x-auto" : "h-full overflow-y-auto", "[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", viewportClassName),
1397
+ style: maskStyle,
1398
+ onScroll: (event) => {
1399
+ updateScrollState();
1400
+ onScroll?.(event);
1401
+ },
1402
+ children
1403
+ }), shouldRenderSectionScrollbar && verticalScrollbarState.isScrollable ? /* @__PURE__ */ jsx("div", {
1404
+ "aria-hidden": "true",
1405
+ className: cn("absolute top-1.5 right-0 bottom-1.5 z-10 w-3 transition-opacity duration-150", isSectionScrollbarVisible ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"),
1406
+ "data-section-scrollbar": "true",
1407
+ onPointerDown: handleTrackPointerDown,
1408
+ children: /* @__PURE__ */ jsx("div", {
1409
+ className: "absolute right-1 w-1.5 rounded-full bg-black/15 transition-colors hover:bg-black/25",
1410
+ onPointerDown: handleThumbPointerDown,
1411
+ onPointerMove: handleThumbPointerMove,
1412
+ onPointerUp: handleThumbPointerUp,
1413
+ onPointerCancel: handleThumbPointerUp,
1414
+ style: {
1415
+ height: `${String(verticalScrollbarState.thumbHeight)}px`,
1416
+ transform: `translateY(${String(verticalScrollbarState.thumbTop)}px)`
1417
+ }
1418
+ })
1419
+ }) : null]
1420
+ });
1421
+ }
1422
+
1423
+ //#endregion
1424
+ //#region src/primitives/tabs.tsx
1425
+ function Tabs({ className, orientation = "horizontal", ...props }) {
1426
+ return /* @__PURE__ */ jsx(Tabs$1.Root, {
1427
+ "data-slot": "tabs",
1428
+ "data-orientation": orientation,
1429
+ className: cn("group/tabs flex gap-2 data-horizontal:flex-col", className),
1430
+ ...props
1431
+ });
1432
+ }
1433
+ const tabsListVariants = cva("gap-2 rounded-lg p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col", {
1434
+ variants: { variant: {
1435
+ default: "bg-muted",
1436
+ line: "gap-2 bg-transparent"
1437
+ } },
1438
+ defaultVariants: { variant: "default" }
1439
+ });
1440
+ function TabsList({ className, variant = "default", ...props }) {
1441
+ return /* @__PURE__ */ jsx(Tabs$1.List, {
1442
+ "data-slot": "tabs-list",
1443
+ "data-variant": variant,
1444
+ className: cn(tabsListVariants({ variant }), className),
1445
+ ...props
1446
+ });
1447
+ }
1448
+ function TabsScrollArea({ className, viewportClassName, ...props }) {
1449
+ return /* @__PURE__ */ jsx(ScrollFadeArea, {
1450
+ ...props,
1451
+ "data-slot": "tabs-scroll-area",
1452
+ orientation: "horizontal",
1453
+ className: cn("w-full min-w-0", className),
1454
+ viewportClassName: cn("min-w-0", viewportClassName)
1455
+ });
1456
+ }
1457
+ function TabsTrigger({ className, ...props }) {
1458
+ return /* @__PURE__ */ jsx(Tabs$1.Trigger, {
1459
+ "data-slot": "tabs-trigger",
1460
+ className: cn("focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent", "data-active:bg-background dark:data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 data-active:text-foreground", "after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100", className),
1461
+ ...props
1462
+ });
1463
+ }
1464
+ function TabsContent({ className, ...props }) {
1465
+ return /* @__PURE__ */ jsx(Tabs$1.Content, {
1466
+ "data-slot": "tabs-content",
1467
+ className: cn("flex-1 text-sm outline-none", className),
1468
+ ...props
1469
+ });
1470
+ }
1471
+
1472
+ //#endregion
1473
+ //#region src/primitives/segmented-toggle.tsx
1474
+ function SegmentedToggle({ value, onChange, options, disabled = false, className, variant = "default", scrollable = false }) {
1475
+ const isPrimaryVariant = variant === "primary";
1476
+ return /* @__PURE__ */ jsx(Tabs, {
1477
+ value,
1478
+ onValueChange: (nextValue) => {
1479
+ if (nextValue) onChange(nextValue);
1480
+ },
1481
+ className: cn("w-full", disabled && "opacity-50", className),
1482
+ children: scrollable ? /* @__PURE__ */ jsx(TabsScrollArea, { children: /* @__PURE__ */ jsx(TabsList, {
1483
+ className: cn("w-full min-w-max", isPrimaryVariant && "border-border bg-background h-8 gap-0.5 rounded-lg border p-0.5 shadow-none"),
1484
+ children: options.map((option) => /* @__PURE__ */ jsxs(TabsTrigger, {
1485
+ value: option.value,
1486
+ disabled,
1487
+ className: cn("shrink-0", isPrimaryVariant && [
1488
+ "text-foreground h-full gap-1.5 rounded-md px-2 py-0 text-[13px] leading-5 font-medium tracking-[-0.28px] shadow-none",
1489
+ "hover:text-foreground",
1490
+ "data-active:border-border data-active:bg-accent data-active:text-accent-foreground data-active:shadow-none",
1491
+ "[&_svg:not([class*=size-])]:size-4"
1492
+ ]),
1493
+ children: [option.icon, option.label]
1494
+ }, option.value))
1495
+ }) }) : /* @__PURE__ */ jsx(TabsList, {
1496
+ className: cn("w-full", isPrimaryVariant && "border-border bg-background h-8 gap-0.5 rounded-lg border p-0.5 shadow-none"),
1497
+ children: options.map((option) => /* @__PURE__ */ jsxs(TabsTrigger, {
1498
+ value: option.value,
1499
+ disabled,
1500
+ className: cn("flex-1", isPrimaryVariant && [
1501
+ "text-foreground h-full gap-1.5 rounded-md px-2 py-0 text-[13px] leading-5 font-medium tracking-[-0.28px] shadow-none",
1502
+ "hover:text-foreground",
1503
+ "data-active:border-border data-active:bg-accent data-active:text-accent-foreground data-active:shadow-none",
1504
+ "[&_svg:not([class*=size-])]:size-4"
1505
+ ]),
1506
+ children: [option.icon, option.label]
1507
+ }, option.value))
1508
+ })
1509
+ });
1510
+ }
1511
+
1512
+ //#endregion
1513
+ //#region src/primitives/select.tsx
1514
+ function Select({ ...props }) {
1515
+ return /* @__PURE__ */ jsx(Select$1.Root, {
1516
+ "data-slot": "select",
1517
+ ...props
1518
+ });
1519
+ }
1520
+ function SelectValue({ ...props }) {
1521
+ return /* @__PURE__ */ jsx(Select$1.Value, {
1522
+ "data-slot": "select-value",
1523
+ ...props
1524
+ });
1525
+ }
1526
+ function SelectTrigger({ className, size = "default", children, ...props }) {
1527
+ return /* @__PURE__ */ jsxs(Select$1.Trigger, {
1528
+ "data-slot": "select-trigger",
1529
+ "data-size": size,
1530
+ className: cn("border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-lg border bg-transparent py-1 pr-2 pl-2 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
1531
+ ...props,
1532
+ children: [children, /* @__PURE__ */ jsx(Select$1.Icon, {
1533
+ asChild: true,
1534
+ children: /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-muted-foreground size-4 pointer-events-none" })
1535
+ })]
1536
+ });
1537
+ }
1538
+ function SelectContent({ className, children, position = "item-aligned", align = "center", ...props }) {
1539
+ return /* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsxs(Select$1.Content, {
1540
+ "data-slot": "select-content",
1541
+ "data-align-trigger": position === "item-aligned",
1542
+ className: cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 min-w-[250px] rounded-lg shadow-md ring-1 duration-100 relative z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className),
1543
+ position,
1544
+ align,
1545
+ ...props,
1546
+ children: [
1547
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
1548
+ /* @__PURE__ */ jsx(Select$1.Viewport, {
1549
+ "data-position": position,
1550
+ className: cn("data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)", position === "popper" && ""),
1551
+ children
1552
+ }),
1553
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
1554
+ ]
1555
+ }) });
1556
+ }
1557
+ function SelectItem({ className, children, ...props }) {
1558
+ return /* @__PURE__ */ jsxs(Select$1.Item, {
1559
+ "data-slot": "select-item",
1560
+ className: cn("focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
1561
+ ...props,
1562
+ children: [/* @__PURE__ */ jsx("span", {
1563
+ className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center",
1564
+ children: /* @__PURE__ */ jsx(Select$1.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "pointer-events-none size-4" }) })
1565
+ }), /* @__PURE__ */ jsx(Select$1.ItemText, { children })]
1566
+ });
1567
+ }
1568
+ function SelectScrollUpButton({ className, ...props }) {
1569
+ return /* @__PURE__ */ jsx(Select$1.ScrollUpButton, {
1570
+ "data-slot": "select-scroll-up-button",
1571
+ className: cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className),
1572
+ ...props,
1573
+ children: /* @__PURE__ */ jsx(ChevronUp, {})
1574
+ });
1575
+ }
1576
+ function SelectScrollDownButton({ className, ...props }) {
1577
+ return /* @__PURE__ */ jsx(Select$1.ScrollDownButton, {
1578
+ "data-slot": "select-scroll-down-button",
1579
+ className: cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className),
1580
+ ...props,
1581
+ children: /* @__PURE__ */ jsx(ChevronDown, {})
1582
+ });
1583
+ }
1584
+
1585
+ //#endregion
1586
+ //#region src/primitives/skeleton.tsx
1587
+ function Skeleton({ className, ...props }) {
1588
+ return /* @__PURE__ */ jsx("div", {
1589
+ "data-slot": "skeleton",
1590
+ className: cn("bg-muted rounded-md animate-pulse", className),
1591
+ ...props
1592
+ });
1593
+ }
1594
+
1595
+ //#endregion
1596
+ //#region src/primitives/switch.tsx
1597
+ function Switch({ className, ...props }) {
1598
+ return /* @__PURE__ */ jsx(Switch$1.Root, {
1599
+ "data-slot": "switch",
1600
+ className: cn("peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input inline-flex h-6 w-11 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50", className),
1601
+ ...props,
1602
+ children: /* @__PURE__ */ jsx(Switch$1.Thumb, {
1603
+ "data-slot": "switch-thumb",
1604
+ className: cn("bg-background pointer-events-none block size-5 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[22px] data-[state=unchecked]:translate-x-0.5")
1605
+ })
1606
+ });
1607
+ }
1608
+
1609
+ //#endregion
1610
+ //#region src/primitives/table.tsx
1611
+ function Table({ className, ...props }) {
1612
+ return /* @__PURE__ */ jsx("div", {
1613
+ "data-slot": "table-container",
1614
+ className: "relative w-full min-w-0 overflow-x-auto",
1615
+ children: /* @__PURE__ */ jsx("table", {
1616
+ "data-slot": "table",
1617
+ className: cn("w-full min-w-0 caption-bottom text-sm", className),
1618
+ ...props
1619
+ })
1620
+ });
1621
+ }
1622
+ function TableHeader({ className, ...props }) {
1623
+ return /* @__PURE__ */ jsx("thead", {
1624
+ "data-slot": "table-header",
1625
+ className: cn("[&_tr]:border-b", className),
1626
+ ...props
1627
+ });
1628
+ }
1629
+ function TableBody({ className, ...props }) {
1630
+ return /* @__PURE__ */ jsx("tbody", {
1631
+ "data-slot": "table-body",
1632
+ className: cn("[&_tr:last-child]:border-0", className),
1633
+ ...props
1634
+ });
1635
+ }
1636
+ function TableFooter({ className, ...props }) {
1637
+ return /* @__PURE__ */ jsx("tfoot", {
1638
+ "data-slot": "table-footer",
1639
+ className: cn("bg-muted/50 border-t font-medium [&>tr]:last:border-b-0", className),
1640
+ ...props
1641
+ });
1642
+ }
1643
+ function TableRow({ className, ...props }) {
1644
+ return /* @__PURE__ */ jsx("tr", {
1645
+ "data-slot": "table-row",
1646
+ className: cn("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", className),
1647
+ ...props
1648
+ });
1649
+ }
1650
+ function TableHead({ className, ...props }) {
1651
+ return /* @__PURE__ */ jsx("th", {
1652
+ "data-slot": "table-head",
1653
+ className: cn("text-foreground h-10 overflow-hidden px-2 text-left align-middle font-medium text-ellipsis whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
1654
+ ...props
1655
+ });
1656
+ }
1657
+ function TableCell({ className, ...props }) {
1658
+ return /* @__PURE__ */ jsx("td", {
1659
+ "data-slot": "table-cell",
1660
+ className: cn("overflow-hidden p-2 align-middle text-ellipsis whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
1661
+ ...props
1662
+ });
1663
+ }
1664
+ function TableCaption({ className, ...props }) {
1665
+ return /* @__PURE__ */ jsx("caption", {
1666
+ "data-slot": "table-caption",
1667
+ className: cn("text-muted-foreground mt-4 text-sm", className),
1668
+ ...props
1669
+ });
1670
+ }
1671
+
1672
+ //#endregion
1673
+ //#region src/primitives/textarea.tsx
1674
+ function Textarea({ className, ...props }) {
1675
+ return /* @__PURE__ */ jsx("textarea", {
1676
+ "data-slot": "textarea",
1677
+ className: cn("dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 min-h-24 rounded-lg border bg-transparent px-3 py-2 text-base transition-colors placeholder:text-muted-foreground w-full min-w-0 resize-y outline-none focus-visible:ring-3 aria-invalid:ring-3 md:text-sm disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", className),
1678
+ ...props
1679
+ });
1680
+ }
1681
+
1682
+ //#endregion
1683
+ //#region src/primitives/user-picker-utils.ts
1684
+ function formatUserDisplayName(user, fallbackName = "User") {
1685
+ const label = user.label?.trim();
1686
+ if (label) return label;
1687
+ const email = user.email?.trim();
1688
+ if (email) return email;
1689
+ return fallbackName;
1690
+ }
1691
+ function formatSelectedUserSummary(selectedUsers, copy = {}) {
1692
+ const selectedNames = selectedUsers.slice(0, 2).map((user) => formatUserDisplayName(user, copy.fallbackName));
1693
+ const remainingCount = selectedUsers.length - selectedNames.length;
1694
+ if (remainingCount <= 0) return selectedNames.join(", ");
1695
+ const remainingLabel = remainingCount === 1 ? copy.selectedSingleLabel ?? "person" : copy.selectedPluralLabel ?? "people";
1696
+ return `${selectedNames.join(", ")} +${String(remainingCount)} ${remainingLabel}`;
1697
+ }
1698
+ function buildUserInitials(user, fallbackName) {
1699
+ const displayName = formatUserDisplayName(user, fallbackName);
1700
+ const nameParts = displayName.split(/\s+/).filter(Boolean).slice(0, 2);
1701
+ if (nameParts.length > 1) return nameParts.map((part) => part[0]?.toUpperCase()).join("");
1702
+ return displayName[0]?.toUpperCase() ?? "U";
1703
+ }
1704
+ function filterSelectableUsers(users, selectedValues) {
1705
+ const selectedValueSet = new Set(selectedValues);
1706
+ return users.filter((user) => !user.archived || selectedValueSet.has(user.value));
1707
+ }
1708
+ function filterUsersBySearch(users, search, fallbackName) {
1709
+ const normalizedSearch = search.trim().toLowerCase();
1710
+ if (!normalizedSearch) return [...users];
1711
+ return users.filter((user) => {
1712
+ return [
1713
+ formatUserDisplayName(user, fallbackName),
1714
+ user.email,
1715
+ user.searchText
1716
+ ].filter(Boolean).join(" ").toLowerCase().includes(normalizedSearch);
1717
+ });
1718
+ }
1719
+ function toggleUserSelection(selectedValues, toggledValue) {
1720
+ if (selectedValues.includes(toggledValue)) return selectedValues.filter((value) => value !== toggledValue);
1721
+ return [...selectedValues, toggledValue];
1722
+ }
1723
+
1724
+ //#endregion
1725
+ //#region src/primitives/user-picker.tsx
1726
+ function UserAvatar({ user, className, fallbackName }) {
1727
+ return /* @__PURE__ */ jsxs(Avatar, {
1728
+ size: "sm",
1729
+ className,
1730
+ children: [user.imageUrl ? /* @__PURE__ */ jsx(AvatarImage, {
1731
+ src: user.imageUrl,
1732
+ alt: formatUserDisplayName(user, fallbackName)
1733
+ }) : null, /* @__PURE__ */ jsx(AvatarFallback, { children: buildUserInitials(user, fallbackName) })]
1734
+ });
1735
+ }
1736
+ function UserRow({ user, fallbackName }) {
1737
+ return /* @__PURE__ */ jsxs("span", {
1738
+ className: "flex min-w-0 items-center gap-2",
1739
+ children: [/* @__PURE__ */ jsx(UserAvatar, {
1740
+ user,
1741
+ fallbackName
1742
+ }), /* @__PURE__ */ jsxs("span", {
1743
+ className: "min-w-0",
1744
+ children: [/* @__PURE__ */ jsx("span", {
1745
+ className: "block truncate font-medium",
1746
+ children: formatUserDisplayName(user, fallbackName)
1747
+ }), user.email ? /* @__PURE__ */ jsx("span", {
1748
+ className: "block truncate text-xs text-muted-foreground",
1749
+ children: user.email
1750
+ }) : null]
1751
+ })]
1752
+ });
1753
+ }
1754
+ function MultipleUserValue({ selectedUsers, placeholder, copy }) {
1755
+ if (selectedUsers.length === 0) return /* @__PURE__ */ jsx("span", {
1756
+ className: "text-muted-foreground",
1757
+ children: placeholder
1758
+ });
1759
+ return /* @__PURE__ */ jsxs("span", {
1760
+ className: "flex min-w-0 items-center gap-2",
1761
+ children: [/* @__PURE__ */ jsx("span", {
1762
+ className: "flex -space-x-2",
1763
+ children: selectedUsers.slice(0, 3).map((user) => /* @__PURE__ */ jsx(UserAvatar, {
1764
+ user,
1765
+ className: "ring-2 ring-background",
1766
+ fallbackName: copy.fallbackName
1767
+ }, user.value))
1768
+ }), /* @__PURE__ */ jsx("span", {
1769
+ className: "truncate",
1770
+ children: copy.formatSelectedCount ? copy.formatSelectedCount(selectedUsers.length) : formatSelectedUserSummary(selectedUsers, copy)
1771
+ })]
1772
+ });
1773
+ }
1774
+ function UserPicker(props) {
1775
+ const { options, copy = {}, placeholder = props.mode === "single" ? copy.singlePlaceholder ?? "Select person" : copy.multiplePlaceholder ?? "Select people", searchPlaceholder = copy.searchPlaceholder ?? "Search people...", emptyMessage = copy.emptyMessage ?? "No people available", disabled = false, className, triggerClassName } = props;
1776
+ const [open, setOpen] = useState(false);
1777
+ const [search, setSearch] = useState("");
1778
+ const singleValue = props.mode === "single" ? props.value : void 0;
1779
+ const multipleValue = props.mode === "multiple" ? props.value : void 0;
1780
+ const selectedValues = useMemo(() => props.mode === "single" ? singleValue ? [singleValue] : [] : [...multipleValue ?? []], [
1781
+ multipleValue,
1782
+ props.mode,
1783
+ singleValue
1784
+ ]);
1785
+ const selectedValueSet = useMemo(() => new Set(selectedValues), [selectedValues]);
1786
+ const selectableUsers = useMemo(() => filterSelectableUsers(options, selectedValues), [options, selectedValues]);
1787
+ const filteredUsers = useMemo(() => filterUsersBySearch(selectableUsers, search, copy.fallbackName), [
1788
+ copy.fallbackName,
1789
+ search,
1790
+ selectableUsers
1791
+ ]);
1792
+ const selectedUsers = selectedValues.map((selectedValue) => options.find((option) => option.value === selectedValue)).filter((user) => Boolean(user));
1793
+ const selectedUser = selectedUsers[0];
1794
+ function selectUser(userValue) {
1795
+ if (props.mode === "single") {
1796
+ props.onValueChange(userValue);
1797
+ setOpen(false);
1798
+ return;
1799
+ }
1800
+ props.onValueChange(toggleUserSelection(props.value, userValue));
1801
+ }
1802
+ return /* @__PURE__ */ jsx(Popover, {
1803
+ open,
1804
+ onOpenChange: setOpen,
1805
+ children: /* @__PURE__ */ jsxs("div", {
1806
+ className: cn("relative", className),
1807
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
1808
+ asChild: true,
1809
+ children: /* @__PURE__ */ jsxs(Button, {
1810
+ type: "button",
1811
+ variant: "outline",
1812
+ disabled,
1813
+ className: cn("h-9 w-full justify-between gap-2 px-2 text-left font-normal", selectedValues.length === 0 && "text-muted-foreground", triggerClassName),
1814
+ children: [/* @__PURE__ */ jsx("span", {
1815
+ className: "min-w-0 flex-1",
1816
+ children: props.mode === "single" ? selectedUser ? /* @__PURE__ */ jsx(UserRow, {
1817
+ user: selectedUser,
1818
+ fallbackName: copy.fallbackName
1819
+ }) : /* @__PURE__ */ jsx("span", { children: placeholder }) : /* @__PURE__ */ jsx(MultipleUserValue, {
1820
+ selectedUsers,
1821
+ placeholder,
1822
+ copy
1823
+ })
1824
+ }), /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 shrink-0 text-muted-foreground" })]
1825
+ })
1826
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
1827
+ align: "start",
1828
+ "data-searchable-select-content": "",
1829
+ className: "w-[var(--radix-popover-trigger-width)] min-w-72 p-2",
1830
+ children: [/* @__PURE__ */ jsxs("div", {
1831
+ className: "relative",
1832
+ children: [/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ jsx(Input, {
1833
+ value: search,
1834
+ onChange: (event) => {
1835
+ setSearch(event.target.value);
1836
+ },
1837
+ placeholder: searchPlaceholder,
1838
+ className: "h-8 pl-8"
1839
+ })]
1840
+ }), /* @__PURE__ */ jsxs("div", {
1841
+ className: "max-h-72 space-y-1 overflow-y-auto",
1842
+ children: [filteredUsers.map((user) => {
1843
+ const selected = selectedValueSet.has(user.value);
1844
+ return /* @__PURE__ */ jsxs("button", {
1845
+ type: "button",
1846
+ className: cn("flex w-full items-center justify-between gap-3 rounded-md px-2 py-2 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground", selected && "bg-muted/70"),
1847
+ onClick: () => {
1848
+ selectUser(user.value);
1849
+ },
1850
+ children: [/* @__PURE__ */ jsx(UserRow, {
1851
+ user,
1852
+ fallbackName: copy.fallbackName
1853
+ }), selected ? /* @__PURE__ */ jsx(Check, { className: "size-4 shrink-0" }) : null]
1854
+ }, user.value);
1855
+ }), filteredUsers.length === 0 ? /* @__PURE__ */ jsx("p", {
1856
+ className: "px-2 py-4 text-sm text-muted-foreground",
1857
+ children: emptyMessage
1858
+ }) : null]
1859
+ })]
1860
+ })]
1861
+ })
1862
+ });
1863
+ }
1864
+
1865
+ //#endregion
1866
+ //#region src/empty-state/empty-state-card.tsx
1867
+ function EmptyStateCard({ icon, title, subtitle, actionLabel, onAction, actionHref, actionTarget = "_self", actionRel, actionDisabled = false, actionIcon, className }) {
1868
+ const hasLinkAction = actionLabel !== void 0 && actionHref !== void 0;
1869
+ const hasButtonAction = actionLabel !== void 0 && onAction !== void 0;
1870
+ const hasDisabledAction = actionLabel !== void 0 && actionDisabled;
1871
+ const hasAction = hasLinkAction || hasButtonAction || hasDisabledAction;
1872
+ return /* @__PURE__ */ jsx(Card, {
1873
+ className: cn("w-full border border-dashed border-border shadow-none ring-0", className),
1874
+ children: /* @__PURE__ */ jsxs(CardContent, {
1875
+ className: "flex flex-col items-center justify-start px-6 py-10 text-center",
1876
+ children: [
1877
+ /* @__PURE__ */ jsx("div", {
1878
+ className: "mb-4 flex size-14 items-center justify-center rounded-full bg-muted text-muted-foreground",
1879
+ children: icon
1880
+ }),
1881
+ /* @__PURE__ */ jsxs("div", {
1882
+ className: "space-y-1.5",
1883
+ children: [/* @__PURE__ */ jsx("h3", {
1884
+ className: "text-lg font-medium tracking-tight",
1885
+ children: title
1886
+ }), /* @__PURE__ */ jsx("p", {
1887
+ className: "max-w-2xl text-sm text-muted-foreground",
1888
+ children: subtitle
1889
+ })]
1890
+ }),
1891
+ hasAction ? hasLinkAction ? /* @__PURE__ */ jsx(Button, {
1892
+ asChild: true,
1893
+ className: "mt-5",
1894
+ disabled: actionDisabled,
1895
+ children: /* @__PURE__ */ jsxs("a", {
1896
+ href: actionHref,
1897
+ target: actionTarget,
1898
+ rel: actionRel,
1899
+ children: [actionIcon ? /* @__PURE__ */ jsx("span", {
1900
+ className: "mr-2 inline-flex items-center",
1901
+ children: actionIcon
1902
+ }) : null, actionLabel]
1903
+ })
1904
+ }) : hasDisabledAction ? /* @__PURE__ */ jsxs(Button, {
1905
+ className: "mt-5",
1906
+ disabled: true,
1907
+ children: [actionIcon ? /* @__PURE__ */ jsx("span", {
1908
+ className: "mr-2 inline-flex items-center",
1909
+ children: actionIcon
1910
+ }) : null, actionLabel]
1911
+ }) : /* @__PURE__ */ jsxs(Button, {
1912
+ className: "mt-5",
1913
+ onClick: onAction,
1914
+ disabled: actionDisabled,
1915
+ children: [actionIcon ? /* @__PURE__ */ jsx("span", {
1916
+ className: "mr-2 inline-flex items-center",
1917
+ children: actionIcon
1918
+ }) : null, actionLabel]
1919
+ }) : null
1920
+ ]
1921
+ })
1922
+ });
1923
+ }
1924
+
1925
+ //#endregion
1926
+ //#region src/empty-state/initial-empty-state.tsx
1927
+ function InitialEmptyState({ icon = /* @__PURE__ */ jsx(Inbox, { className: "size-7" }), title, subtitle, actionLabel, onAction, actionIcon, className }) {
1928
+ return /* @__PURE__ */ jsx(EmptyStateCard, {
1929
+ icon,
1930
+ title,
1931
+ subtitle,
1932
+ actionLabel,
1933
+ onAction,
1934
+ actionIcon,
1935
+ className
1936
+ });
1937
+ }
1938
+
1939
+ //#endregion
1940
+ //#region src/empty-state/no-results-state.tsx
1941
+ function NoResultsState({ icon = /* @__PURE__ */ jsx(SearchX, { className: "size-7" }), title = "No results", subtitle = "Try changing your search or filters.", className }) {
1942
+ return /* @__PURE__ */ jsx(EmptyStateCard, {
1943
+ icon,
1944
+ title,
1945
+ subtitle,
1946
+ className
1947
+ });
1948
+ }
1949
+
1950
+ //#endregion
1951
+ //#region src/empty-state/collection-empty-state.ts
1952
+ function resolveCollectionEmptyState({ totalCount, filteredCount, hasSearch = false, hasFilters = false }) {
1953
+ if (filteredCount > 0) return "none";
1954
+ if (totalCount === 0) return "initial";
1955
+ if (hasSearch || hasFilters) return "no-results";
1956
+ return "initial";
1957
+ }
1958
+
1959
+ //#endregion
1960
+ //#region src/utils/search.ts
1961
+ function normalizeSearchValue(value) {
1962
+ return value.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLocaleLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim();
1963
+ }
1964
+ function tokenize(value) {
1965
+ return normalizeSearchValue(value).split(" ").filter((token) => token.length > 0);
1966
+ }
1967
+ function flattenSearchPart(part, fragments) {
1968
+ if (typeof part === "string") {
1969
+ const normalized = part.trim();
1970
+ if (normalized.length > 0) fragments.push(normalized);
1971
+ return;
1972
+ }
1973
+ if (!part) return;
1974
+ for (const nestedPart of part) flattenSearchPart(nestedPart, fragments);
1975
+ }
1976
+ function getMaxDistance(term) {
1977
+ if (term.length <= 4) return 1;
1978
+ if (term.length <= 8) return 2;
1979
+ return 3;
1980
+ }
1981
+ function damerauLevenshtein(left, right) {
1982
+ const rows = left.length + 1;
1983
+ const columns = right.length + 1;
1984
+ const matrix = Array.from({ length: rows }, () => Array(columns).fill(0));
1985
+ for (let row = 0; row < rows; row += 1) {
1986
+ const matrixRow = matrix[row];
1987
+ if (!matrixRow) continue;
1988
+ matrixRow[0] = row;
1989
+ }
1990
+ for (let column = 0; column < columns; column += 1) {
1991
+ const firstRow = matrix[0];
1992
+ if (!firstRow) return 0;
1993
+ firstRow[column] = column;
1994
+ }
1995
+ for (let row = 1; row < rows; row += 1) {
1996
+ const currentRow = matrix[row];
1997
+ const previousRow = matrix[row - 1];
1998
+ if (!currentRow || !previousRow) continue;
1999
+ for (let column = 1; column < columns; column += 1) {
2000
+ const substitutionCost = left[row - 1] === right[column - 1] ? 0 : 1;
2001
+ const leftCost = currentRow[column - 1];
2002
+ const topCost = previousRow[column];
2003
+ const diagonalCost = previousRow[column - 1];
2004
+ if (leftCost === void 0 || topCost === void 0 || diagonalCost === void 0) continue;
2005
+ currentRow[column] = Math.min(topCost + 1, leftCost + 1, diagonalCost + substitutionCost);
2006
+ if (row > 1 && column > 1 && left[row - 1] === right[column - 2] && left[row - 2] === right[column - 1]) {
2007
+ const transpositionCost = matrix[row - 2]?.[column - 2];
2008
+ if (transpositionCost !== void 0) currentRow[column] = Math.min(currentRow[column] ?? Number.POSITIVE_INFINITY, transpositionCost + 1);
2009
+ }
2010
+ }
2011
+ }
2012
+ return matrix[left.length]?.[right.length] ?? 0;
2013
+ }
2014
+ function scoreTokenMatch(searchTerm, candidateToken, isLastTerm) {
2015
+ if (candidateToken === searchTerm) return 120;
2016
+ if (isLastTerm && candidateToken.startsWith(searchTerm)) return 90 - Math.max(candidateToken.length - searchTerm.length, 0);
2017
+ const distance = damerauLevenshtein(searchTerm, candidateToken);
2018
+ if (distance > getMaxDistance(searchTerm)) return null;
2019
+ return 70 - distance * 10 - Math.abs(candidateToken.length - searchTerm.length);
2020
+ }
2021
+ function buildSearchText(...parts) {
2022
+ const fragments = [];
2023
+ for (const part of parts) flattenSearchPart(part, fragments);
2024
+ return fragments.join(" ").replace(/\s+/g, " ").trim();
2025
+ }
2026
+ function scoreFuzzyMatch(query, candidate) {
2027
+ const normalizedQuery = normalizeSearchValue(query);
2028
+ if (normalizedQuery.length === 0) return 0;
2029
+ const queryTerms = tokenize(normalizedQuery);
2030
+ const candidateTerms = tokenize(candidate);
2031
+ const normalizedCandidate = normalizeSearchValue(candidate);
2032
+ if (queryTerms.length === 0 || candidateTerms.length === 0) return null;
2033
+ let score = normalizedCandidate.includes(normalizedQuery) ? 40 : 0;
2034
+ for (const [index, queryTerm] of queryTerms.entries()) {
2035
+ let bestScore = null;
2036
+ for (const candidateTerm of candidateTerms) {
2037
+ const tokenScore = scoreTokenMatch(queryTerm, candidateTerm, index === queryTerms.length - 1);
2038
+ if (tokenScore !== null && (bestScore === null || tokenScore > bestScore)) bestScore = tokenScore;
2039
+ }
2040
+ if (bestScore === null) return null;
2041
+ score += bestScore;
2042
+ }
2043
+ return score;
2044
+ }
2045
+ function filterAndRankBySearch(items, query) {
2046
+ const normalizedQuery = normalizeSearchValue(query);
2047
+ if (normalizedQuery.length === 0) return [...items];
2048
+ return items.map((item) => ({
2049
+ item,
2050
+ score: scoreFuzzyMatch(normalizedQuery, item.searchText)
2051
+ })).filter((result) => result.score !== null).sort((left, right) => right.score - left.score).map((result) => result.item);
2052
+ }
2053
+ function rankBySearch(items, query, getSearchText) {
2054
+ return filterAndRankBySearch(items.map((item) => ({
2055
+ item,
2056
+ searchText: getSearchText(item)
2057
+ })), query).map((result) => result.item);
2058
+ }
2059
+
2060
+ //#endregion
2061
+ //#region src/search/searchable-select-position.ts
2062
+ function clamp(value, min, max) {
2063
+ if (max < min) return min;
2064
+ return Math.min(Math.max(value, min), max);
2065
+ }
2066
+ function resolveSearchableSelectDropdownPosition({ triggerRect, boundaryRect, portalRect, contentWidth, contentHeight, viewportWidth, viewportHeight, offset = 8, padding = 8 }) {
2067
+ const boundaryLeftEdge = boundaryRect?.left ?? 0;
2068
+ const boundaryRightEdge = boundaryRect?.right ?? viewportWidth;
2069
+ const boundaryTopEdge = boundaryRect?.top ?? 0;
2070
+ const boundaryBottomEdge = boundaryRect?.bottom ?? viewportHeight;
2071
+ const boundaryLeft = Math.max(padding, boundaryLeftEdge + padding);
2072
+ const boundaryRight = Math.min(viewportWidth - padding, boundaryRightEdge - padding);
2073
+ const boundaryTop = Math.max(padding, boundaryTopEdge + padding);
2074
+ const boundaryBottom = Math.min(viewportHeight - padding, boundaryBottomEdge - padding);
2075
+ const availableWidth = Math.max(0, boundaryRight - boundaryLeft);
2076
+ const width = Math.min(contentWidth, availableWidth);
2077
+ const alignedRightLeft = triggerRect.right - width;
2078
+ const defaultLeft = triggerRect.left;
2079
+ const left = clamp(defaultLeft + width > boundaryRight && alignedRightLeft >= boundaryLeft ? alignedRightLeft : defaultLeft, boundaryLeft, boundaryRight - width);
2080
+ const spaceAbove = triggerRect.top - boundaryTop;
2081
+ const spaceBelow = boundaryBottom - triggerRect.bottom;
2082
+ const shouldOpenUp = spaceBelow < contentHeight + offset && spaceAbove > spaceBelow;
2083
+ const direction = shouldOpenUp ? "up" : "down";
2084
+ const maxHeight = Math.max(120, Math.floor((shouldOpenUp ? spaceAbove : spaceBelow) - offset));
2085
+ const renderedHeight = Math.min(contentHeight, maxHeight);
2086
+ return {
2087
+ top: (shouldOpenUp ? Math.max(boundaryTop, triggerRect.top - offset - renderedHeight) : Math.min(boundaryBottom - renderedHeight, triggerRect.bottom + offset)) - (portalRect?.top ?? 0),
2088
+ left: left - (portalRect?.left ?? 0),
2089
+ width,
2090
+ maxHeight,
2091
+ direction
2092
+ };
2093
+ }
2094
+
2095
+ //#endregion
2096
+ //#region src/search/searchable-select.tsx
2097
+ const AUTO_SEARCHABLE_SELECT_THRESHOLD = 5;
2098
+ const SEARCHABLE_SELECT_MODAL_CONTENT_SELECTOR = "[data-slot=\"sheet-content\"], [data-slot=\"drawer-content\"]";
2099
+ function getSearchableSelectBoundaryRect(element) {
2100
+ return element.closest(SEARCHABLE_SELECT_MODAL_CONTENT_SELECTOR)?.getBoundingClientRect();
2101
+ }
2102
+ function isSearchableSelectPointerInside(target, triggerContainer, dropdownContent) {
2103
+ return Boolean(triggerContainer?.contains(target) || dropdownContent?.contains(target));
2104
+ }
2105
+ function getSearchableSelectPortalContainer(trigger) {
2106
+ return trigger?.closest(SEARCHABLE_SELECT_MODAL_CONTENT_SELECTOR) ?? null;
2107
+ }
2108
+ function SearchableSelect({ value, onValueChange, options, placeholder = "Select...", disabled = false, className, triggerClassName, contentClassName, ariaLabel, searchPlaceholder = "Search...", emptyMessage = "No results found", leadingIcon: LeadingIcon, searchThreshold = AUTO_SEARCHABLE_SELECT_THRESHOLD, size = "default", renderOption, renderValue }) {
2109
+ const selectId = useId();
2110
+ const [isOpen, setIsOpen] = useState(false);
2111
+ const [isMounted, setIsMounted] = useState(false);
2112
+ const [search, setSearch] = useState("");
2113
+ const [highlightedOptionIndex, setHighlightedOptionIndex] = useState(0);
2114
+ const containerRef = useRef(null);
2115
+ const triggerRef = useRef(null);
2116
+ const contentRef = useRef(null);
2117
+ const searchInputRef = useRef(null);
2118
+ const optionRefs = useRef([]);
2119
+ const [portalContainer, setPortalContainer] = useState(null);
2120
+ const [dropdownPosition, setDropdownPosition] = useState(null);
2121
+ const selectedOption = options.find((option) => option.value === value);
2122
+ const showSearchInput = options.length > searchThreshold;
2123
+ useEffect(() => {
2124
+ setIsMounted(true);
2125
+ }, []);
2126
+ useEffect(() => {
2127
+ function closeOtherSearchableSelects(event) {
2128
+ if (event instanceof CustomEvent && event.detail !== selectId) setIsOpen(false);
2129
+ }
2130
+ window.addEventListener("searchable-select:open", closeOtherSearchableSelects);
2131
+ return () => {
2132
+ window.removeEventListener("searchable-select:open", closeOtherSearchableSelects);
2133
+ };
2134
+ }, [selectId]);
2135
+ useEffect(() => {
2136
+ if (!isOpen) return;
2137
+ window.dispatchEvent(new CustomEvent("searchable-select:open", { detail: selectId }));
2138
+ }, [isOpen, selectId]);
2139
+ useEffect(() => {
2140
+ if (!showSearchInput || !isOpen) {
2141
+ setSearch("");
2142
+ return;
2143
+ }
2144
+ requestAnimationFrame(() => {
2145
+ searchInputRef.current?.focus();
2146
+ });
2147
+ }, [isOpen, showSearchInput]);
2148
+ useEffect(() => {
2149
+ function handlePointerDown(event) {
2150
+ if (isSearchableSelectPointerInside(event.target, containerRef.current, contentRef.current)) return;
2151
+ setIsOpen(false);
2152
+ }
2153
+ if (!isOpen) return;
2154
+ document.addEventListener("pointerdown", handlePointerDown);
2155
+ return () => {
2156
+ document.removeEventListener("pointerdown", handlePointerDown);
2157
+ };
2158
+ }, [isOpen]);
2159
+ const filteredOptions = useMemo(() => rankBySearch(options, search, (option) => buildSearchText(option.label, option.searchText)), [options, search]);
2160
+ useEffect(() => {
2161
+ if (!isOpen) return;
2162
+ const selectedIndex = filteredOptions.findIndex((option) => option.value === value);
2163
+ if (selectedIndex >= 0) {
2164
+ setHighlightedOptionIndex(selectedIndex);
2165
+ return;
2166
+ }
2167
+ setHighlightedOptionIndex(0);
2168
+ }, [
2169
+ filteredOptions,
2170
+ isOpen,
2171
+ value
2172
+ ]);
2173
+ useEffect(() => {
2174
+ optionRefs.current = optionRefs.current.slice(0, filteredOptions.length);
2175
+ if (filteredOptions.length === 0) {
2176
+ setHighlightedOptionIndex(0);
2177
+ return;
2178
+ }
2179
+ setHighlightedOptionIndex((currentValue) => Math.min(currentValue, filteredOptions.length - 1));
2180
+ }, [filteredOptions]);
2181
+ useEffect(() => {
2182
+ if (!isOpen || filteredOptions.length === 0) return;
2183
+ optionRefs.current[highlightedOptionIndex]?.scrollIntoView({ block: "nearest" });
2184
+ }, [
2185
+ filteredOptions.length,
2186
+ highlightedOptionIndex,
2187
+ isOpen
2188
+ ]);
2189
+ useLayoutEffect(() => {
2190
+ if (!isOpen) {
2191
+ setDropdownPosition(null);
2192
+ setPortalContainer(null);
2193
+ return;
2194
+ }
2195
+ function updateDropdownPosition() {
2196
+ const trigger = triggerRef.current;
2197
+ const content = contentRef.current;
2198
+ if (!trigger || !content) return;
2199
+ const nextPortalContainer = getSearchableSelectPortalContainer(trigger);
2200
+ setPortalContainer(nextPortalContainer);
2201
+ const triggerRect = trigger.getBoundingClientRect();
2202
+ const contentRect = content.getBoundingClientRect();
2203
+ const nextPosition = resolveSearchableSelectDropdownPosition({
2204
+ triggerRect,
2205
+ boundaryRect: getSearchableSelectBoundaryRect(trigger),
2206
+ portalRect: nextPortalContainer?.getBoundingClientRect(),
2207
+ contentWidth: Math.max(triggerRect.width, contentRect.width, 250),
2208
+ contentHeight: contentRect.height,
2209
+ viewportWidth: window.innerWidth,
2210
+ viewportHeight: window.innerHeight
2211
+ });
2212
+ setDropdownPosition((currentValue) => {
2213
+ if (currentValue?.top === nextPosition.top && currentValue?.left === nextPosition.left && currentValue?.width === nextPosition.width && currentValue?.maxHeight === nextPosition.maxHeight) return currentValue;
2214
+ return nextPosition;
2215
+ });
2216
+ }
2217
+ updateDropdownPosition();
2218
+ const animationFrameId = window.requestAnimationFrame(updateDropdownPosition);
2219
+ window.addEventListener("resize", updateDropdownPosition);
2220
+ window.addEventListener("scroll", updateDropdownPosition, true);
2221
+ return () => {
2222
+ window.cancelAnimationFrame(animationFrameId);
2223
+ window.removeEventListener("resize", updateDropdownPosition);
2224
+ window.removeEventListener("scroll", updateDropdownPosition, true);
2225
+ };
2226
+ }, [
2227
+ filteredOptions.length,
2228
+ isOpen,
2229
+ search,
2230
+ showSearchInput
2231
+ ]);
2232
+ function selectHighlightedOption() {
2233
+ const highlightedOption = filteredOptions[highlightedOptionIndex];
2234
+ if (!highlightedOption) return;
2235
+ onValueChange(highlightedOption.value);
2236
+ setIsOpen(false);
2237
+ }
2238
+ function selectOption(value$1) {
2239
+ onValueChange(value$1);
2240
+ setIsOpen(false);
2241
+ }
2242
+ function moveHighlightedOption(direction) {
2243
+ if (filteredOptions.length === 0) return;
2244
+ setHighlightedOptionIndex((currentValue) => {
2245
+ if (direction === "down") return (currentValue + 1) % filteredOptions.length;
2246
+ return (currentValue - 1 + filteredOptions.length) % filteredOptions.length;
2247
+ });
2248
+ }
2249
+ return /* @__PURE__ */ jsxs("div", {
2250
+ ref: containerRef,
2251
+ className: cn("relative", className),
2252
+ children: [/* @__PURE__ */ jsxs("button", {
2253
+ ref: triggerRef,
2254
+ type: "button",
2255
+ "aria-label": ariaLabel,
2256
+ "aria-expanded": isOpen,
2257
+ disabled,
2258
+ onClick: () => setIsOpen((currentValue) => !currentValue),
2259
+ onKeyDown: (event) => {
2260
+ if (event.key === "Escape") setIsOpen(false);
2261
+ },
2262
+ className: cn("border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 flex w-full items-center justify-between gap-1.5 rounded-lg border bg-transparent py-1 pr-2 pl-2 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3", size === "sm" ? "h-7 rounded-[min(var(--radius-md),10px)]" : "h-8", triggerClassName),
2263
+ children: [
2264
+ LeadingIcon ? /* @__PURE__ */ jsx(LeadingIcon, {
2265
+ className: "text-muted-foreground size-4 shrink-0",
2266
+ strokeWidth: 1.8
2267
+ }) : null,
2268
+ /* @__PURE__ */ jsx("span", {
2269
+ className: "min-w-0 flex-1 truncate text-left",
2270
+ children: selectedOption ? renderValue?.(selectedOption) ?? selectedOption.label : /* @__PURE__ */ jsx("span", {
2271
+ className: "text-muted-foreground",
2272
+ children: placeholder
2273
+ })
2274
+ }),
2275
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("text-muted-foreground size-4 shrink-0 transition-transform", isOpen && "rotate-180") })
2276
+ ]
2277
+ }), isOpen && isMounted ? createPortal(/* @__PURE__ */ jsxs("div", {
2278
+ ref: contentRef,
2279
+ "data-searchable-select-content": "",
2280
+ className: cn("bg-popover text-popover-foreground pointer-events-auto z-[60] min-w-0 rounded-xl border p-2 shadow-lg", portalContainer ? "absolute" : "fixed", contentClassName),
2281
+ style: {
2282
+ top: dropdownPosition?.top ?? 0,
2283
+ left: dropdownPosition?.left ?? 0,
2284
+ width: dropdownPosition?.width ?? triggerRef.current?.getBoundingClientRect().width,
2285
+ visibility: dropdownPosition ? "visible" : "hidden"
2286
+ },
2287
+ children: [showSearchInput ? /* @__PURE__ */ jsxs("div", {
2288
+ className: "relative mb-2",
2289
+ children: [/* @__PURE__ */ jsx(Search, { className: "text-muted-foreground absolute top-1/2 left-2.5 size-4 -translate-y-1/2" }), /* @__PURE__ */ jsx(Input, {
2290
+ ref: searchInputRef,
2291
+ value: search,
2292
+ onChange: (event) => setSearch(event.target.value),
2293
+ onKeyDown: (event) => {
2294
+ if (event.key === "ArrowDown") {
2295
+ event.preventDefault();
2296
+ moveHighlightedOption("down");
2297
+ }
2298
+ if (event.key === "ArrowUp") {
2299
+ event.preventDefault();
2300
+ moveHighlightedOption("up");
2301
+ }
2302
+ if (event.key === "Enter") {
2303
+ event.preventDefault();
2304
+ selectHighlightedOption();
2305
+ }
2306
+ if (event.key === "Escape") {
2307
+ event.preventDefault();
2308
+ setIsOpen(false);
2309
+ }
2310
+ },
2311
+ placeholder: searchPlaceholder,
2312
+ className: "pl-9"
2313
+ })]
2314
+ }) : null, /* @__PURE__ */ jsxs("div", {
2315
+ className: "space-y-1 overflow-y-auto",
2316
+ style: { maxHeight: Math.min(256, dropdownPosition?.maxHeight ?? 256) },
2317
+ children: [filteredOptions.map((option, index) => {
2318
+ const isSelected = option.value === value;
2319
+ return /* @__PURE__ */ jsxs("button", {
2320
+ ref: (element) => {
2321
+ optionRefs.current[index] = element;
2322
+ },
2323
+ type: "button",
2324
+ className: cn("hover:bg-accent hover:text-accent-foreground flex h-auto w-full items-center justify-between gap-3 rounded-md px-2 py-1.5 text-left text-sm transition-colors", isSelected && "bg-muted/60", highlightedOptionIndex === index && "bg-accent text-accent-foreground"),
2325
+ onMouseEnter: () => setHighlightedOptionIndex(index),
2326
+ onPointerDown: (event) => {
2327
+ event.preventDefault();
2328
+ event.stopPropagation();
2329
+ selectOption(option.value);
2330
+ },
2331
+ onClick: (event) => {
2332
+ if (event.detail === 0) selectOption(option.value);
2333
+ },
2334
+ children: [/* @__PURE__ */ jsx("span", {
2335
+ className: "min-w-0 flex-1 truncate",
2336
+ children: renderOption?.(option) ?? option.label
2337
+ }), isSelected ? /* @__PURE__ */ jsx(Check, { className: "size-4 shrink-0" }) : null]
2338
+ }, option.value);
2339
+ }), filteredOptions.length === 0 ? /* @__PURE__ */ jsx("p", {
2340
+ className: "text-muted-foreground px-2 py-4 text-sm",
2341
+ children: emptyMessage
2342
+ }) : null]
2343
+ })]
2344
+ }), portalContainer ?? document.body) : null]
2345
+ });
2346
+ }
2347
+
2348
+ //#endregion
2349
+ //#region src/smart-table/SmartTableActions.tsx
2350
+ const SMART_TABLE_ACTIONS_CONTAINER_CLASS = "flex w-full items-center justify-end gap-1";
2351
+ const actionIcons = {
2352
+ view: Eye,
2353
+ edit: Pencil,
2354
+ delete: Trash2
2355
+ };
2356
+ const actionLabels = {
2357
+ view: "Visualizza",
2358
+ edit: "Edit",
2359
+ delete: "Delete"
2360
+ };
2361
+ function SmartTableActions({ item, actions, actionHandlers, renderActions }) {
2362
+ if (renderActions) return /* @__PURE__ */ jsx("div", {
2363
+ className: SMART_TABLE_ACTIONS_CONTAINER_CLASS,
2364
+ children: renderActions(item)
2365
+ });
2366
+ if (!actions || !actionHandlers) return null;
2367
+ return /* @__PURE__ */ jsx("div", {
2368
+ className: SMART_TABLE_ACTIONS_CONTAINER_CLASS,
2369
+ children: actions.map((action) => {
2370
+ const Icon = actionIcons[action];
2371
+ const handler = actionHandlers[`on${action.charAt(0).toUpperCase()}${action.slice(1)}`];
2372
+ return /* @__PURE__ */ jsx(Button, {
2373
+ variant: "ghost",
2374
+ size: "icon",
2375
+ className: "size-8",
2376
+ "aria-label": actionLabels[action],
2377
+ onClick: (event) => {
2378
+ event.stopPropagation();
2379
+ handler?.(item);
2380
+ },
2381
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-4" })
2382
+ }, action);
2383
+ })
2384
+ });
2385
+ }
2386
+
2387
+ //#endregion
2388
+ //#region src/smart-table/utils.ts
2389
+ function formatListEntry(value) {
2390
+ const formatted = formatValue(value);
2391
+ if (typeof formatted === "string" || typeof formatted === "number") return String(formatted);
2392
+ return "—";
2393
+ }
2394
+ /**
2395
+ * Get nested value from object using dot notation
2396
+ * e.g., getNestedValue(obj, 'user.profile.name')
2397
+ */
2398
+ function getNestedValue(obj, path) {
2399
+ return path.split(".").reduce((acc, key) => {
2400
+ if (acc && typeof acc === "object" && key in acc) return acc[key];
2401
+ }, obj);
2402
+ }
2403
+ /**
2404
+ * Format a value for display
2405
+ */
2406
+ function formatValue(value) {
2407
+ if (value === null || value === void 0) return "—";
2408
+ if (typeof value === "boolean") return value ? "Yes" : "No";
2409
+ if (value instanceof Date) return createElement(DisplayDate, { value });
2410
+ if (typeof value === "number") return value.toLocaleString();
2411
+ if (typeof value === "string") return value;
2412
+ if (Array.isArray(value)) {
2413
+ if (value.length === 0) return "—";
2414
+ return value.map((entry) => formatListEntry(entry)).join(", ");
2415
+ }
2416
+ if (typeof value === "object") {
2417
+ const record = value;
2418
+ for (const key of [
2419
+ "label",
2420
+ "name",
2421
+ "title"
2422
+ ]) {
2423
+ const nestedValue = record[key];
2424
+ if (typeof nestedValue === "string" || typeof nestedValue === "number") return String(nestedValue);
2425
+ }
2426
+ }
2427
+ return "[Object]";
2428
+ }
2429
+ function formatValueAsText(value) {
2430
+ if (value === null || value === void 0) return "—";
2431
+ if (typeof value === "boolean") return value ? "Yes" : "No";
2432
+ if (value instanceof Date) return formatDisplayDate(value);
2433
+ if (typeof value === "number") return value.toLocaleString();
2434
+ if (typeof value === "string") return value;
2435
+ if (Array.isArray(value)) {
2436
+ if (value.length === 0) return "—";
2437
+ return value.map((entry) => formatValueAsText(entry) ?? "—").join(", ");
2438
+ }
2439
+ if (typeof value === "object") {
2440
+ const record = value;
2441
+ for (const key of [
2442
+ "label",
2443
+ "name",
2444
+ "title"
2445
+ ]) {
2446
+ const nestedValue = record[key];
2447
+ if (typeof nestedValue === "string" || typeof nestedValue === "number") return String(nestedValue);
2448
+ }
2449
+ }
2450
+ return null;
2451
+ }
2452
+ function getColumnValue(column, item) {
2453
+ if (!column.accessor) return null;
2454
+ if (typeof column.accessor === "string" && column.accessor.includes(".")) return getNestedValue(item, column.accessor);
2455
+ return item[column.accessor];
2456
+ }
2457
+ function renderColumnValue(column, item) {
2458
+ const value = getColumnValue(column, item);
2459
+ if (column.render) return column.render(value, item);
2460
+ return formatValue(value);
2461
+ }
2462
+ function getColumnTooltipText(column, item) {
2463
+ if (column.render) return null;
2464
+ return formatValueAsText(getColumnValue(column, item));
2465
+ }
2466
+
2467
+ //#endregion
2468
+ //#region src/smart-table/sorting.ts
2469
+ function getColumnSortKey(column) {
2470
+ if (column.sortable === false) return null;
2471
+ if (column.sortKey) return column.sortKey;
2472
+ return typeof column.accessor === "string" ? column.accessor : null;
2473
+ }
2474
+ function getNextSortState(currentState, key) {
2475
+ if (currentState?.key !== key) return {
2476
+ key,
2477
+ direction: "asc"
2478
+ };
2479
+ if (currentState.direction === "asc") return {
2480
+ key,
2481
+ direction: "desc"
2482
+ };
2483
+ return null;
2484
+ }
2485
+ function normalizeSortValue(value) {
2486
+ if (value === null || value === void 0 || value === "") return null;
2487
+ if (value instanceof Date) return value.getTime();
2488
+ return value;
2489
+ }
2490
+ function compareSortValues(left, right) {
2491
+ const leftValue = normalizeSortValue(left);
2492
+ const rightValue = normalizeSortValue(right);
2493
+ if (leftValue === null && rightValue === null) return 0;
2494
+ if (leftValue === null) return 1;
2495
+ if (rightValue === null) return -1;
2496
+ if (typeof leftValue === "number" && typeof rightValue === "number") return leftValue - rightValue;
2497
+ if (typeof leftValue === "boolean" && typeof rightValue === "boolean") return Number(leftValue) - Number(rightValue);
2498
+ return String(leftValue).localeCompare(String(rightValue), "it", {
2499
+ numeric: true,
2500
+ sensitivity: "base"
2501
+ });
2502
+ }
2503
+ function getSortValue(column, row) {
2504
+ if (column.sortAccessor) return column.sortAccessor(row);
2505
+ return getColumnValue(column, row);
2506
+ }
2507
+ function getSortColumn(columns, sortState) {
2508
+ if (!sortState) return null;
2509
+ return columns.find((column) => getColumnSortKey(column) === sortState.key) ?? null;
2510
+ }
2511
+ function applyDirection(value, direction) {
2512
+ return direction === "asc" ? value : -value;
2513
+ }
2514
+ function sortTableData(data, columns, sortState) {
2515
+ const sortColumn = getSortColumn(columns, sortState);
2516
+ if (!sortColumn || !sortState) return [...data];
2517
+ return data.map((row, index) => ({
2518
+ row,
2519
+ index
2520
+ })).sort((left, right) => {
2521
+ const compared = compareSortValues(getSortValue(sortColumn, left.row), getSortValue(sortColumn, right.row));
2522
+ return compared === 0 ? left.index - right.index : applyDirection(compared, sortState.direction);
2523
+ }).map(({ row }) => row);
2524
+ }
2525
+ function useTableSorting({ data, columns, initialSortState = null }) {
2526
+ const [sortState, setSortState] = useState(initialSortState);
2527
+ return {
2528
+ sortedData: useMemo(() => sortTableData(data, columns, sortState), [
2529
+ columns,
2530
+ data,
2531
+ sortState
2532
+ ]),
2533
+ sortState,
2534
+ setSortState
2535
+ };
2536
+ }
2537
+
2538
+ //#endregion
2539
+ //#region src/smart-table/truncated-content.utils.ts
2540
+ function getTruncatedContentAlignmentClass(align) {
2541
+ if (align === "right") return "text-right";
2542
+ if (align === "center") return "text-center";
2543
+ return "text-left";
2544
+ }
2545
+ function shouldRenderTooltipTrigger(options) {
2546
+ return Boolean(options.tooltip) && options.isOverflowing;
2547
+ }
2548
+
2549
+ //#endregion
2550
+ //#region src/smart-table/TruncatedContent.tsx
2551
+ function TruncatedContent({ children, tooltip, align = "left", className = "" }) {
2552
+ const contentRef = useRef(null);
2553
+ const [isOverflowing, setIsOverflowing] = useState(false);
2554
+ const [isOpen, setIsOpen] = useState(false);
2555
+ const updateOverflowState = () => {
2556
+ const element = contentRef.current;
2557
+ if (!element) {
2558
+ setIsOverflowing(false);
2559
+ return false;
2560
+ }
2561
+ const overflowing = element.scrollWidth > element.clientWidth + 1;
2562
+ setIsOverflowing(overflowing);
2563
+ return overflowing;
2564
+ };
2565
+ useEffect(() => {
2566
+ const element = contentRef.current;
2567
+ if (!element) return;
2568
+ updateOverflowState();
2569
+ const resizeObserver = new ResizeObserver(() => {
2570
+ updateOverflowState();
2571
+ });
2572
+ resizeObserver.observe(element);
2573
+ window.addEventListener("resize", updateOverflowState);
2574
+ return () => {
2575
+ resizeObserver.disconnect();
2576
+ window.removeEventListener("resize", updateOverflowState);
2577
+ };
2578
+ }, [
2579
+ children,
2580
+ className,
2581
+ align,
2582
+ tooltip
2583
+ ]);
2584
+ useEffect(() => {
2585
+ if (!isOverflowing && isOpen) setIsOpen(false);
2586
+ }, [isOpen, isOverflowing]);
2587
+ const alignmentClass = getTruncatedContentAlignmentClass(align);
2588
+ const showTooltipTrigger = shouldRenderTooltipTrigger({
2589
+ tooltip,
2590
+ isOverflowing
2591
+ });
2592
+ const content = /* @__PURE__ */ jsx("div", {
2593
+ ref: contentRef,
2594
+ className: [
2595
+ "block min-w-0 w-full truncate",
2596
+ alignmentClass,
2597
+ className
2598
+ ].filter(Boolean).join(" "),
2599
+ children
2600
+ });
2601
+ if (!showTooltipTrigger) return content;
2602
+ return /* @__PURE__ */ jsxs(Tooltip, {
2603
+ delayDuration: 100,
2604
+ open: isOverflowing ? isOpen : false,
2605
+ onOpenChange: (nextOpen) => {
2606
+ if (!nextOpen) {
2607
+ setIsOpen(false);
2608
+ return;
2609
+ }
2610
+ setIsOpen(updateOverflowState());
2611
+ },
2612
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, {
2613
+ asChild: true,
2614
+ children: /* @__PURE__ */ jsx("button", {
2615
+ type: "button",
2616
+ className: [
2617
+ "block min-w-0 w-full overflow-hidden bg-transparent p-0 text-inherit outline-none",
2618
+ alignmentClass,
2619
+ isOverflowing ? "cursor-help" : "cursor-default"
2620
+ ].filter(Boolean).join(" "),
2621
+ onMouseEnter: () => {
2622
+ updateOverflowState();
2623
+ },
2624
+ onFocus: () => {
2625
+ updateOverflowState();
2626
+ },
2627
+ onClick: (event) => {
2628
+ event.stopPropagation();
2629
+ if (updateOverflowState()) setIsOpen((previous) => !previous);
2630
+ },
2631
+ children: content
2632
+ })
2633
+ }), /* @__PURE__ */ jsx(TooltipContent, {
2634
+ side: "top",
2635
+ align: align === "right" ? "end" : align === "center" ? "center" : "start",
2636
+ sideOffset: 6,
2637
+ collisionPadding: 12,
2638
+ avoidCollisions: true,
2639
+ className: "w-[min(20rem,calc(100vw-1.5rem))] max-w-[calc(100vw-1.5rem)] whitespace-normal break-words text-left",
2640
+ children: tooltip
2641
+ })]
2642
+ });
2643
+ }
2644
+
2645
+ //#endregion
2646
+ //#region src/smart-table/DesktopView.tsx
2647
+ const ACTION_COLUMN_WIDTH_PX = 112;
2648
+ const ACTION_CELL_CLASS_NAME = "w-28 min-w-28 overflow-visible whitespace-nowrap text-right";
2649
+ function SortIcon({ activeDirection }) {
2650
+ if (activeDirection === "asc") return /* @__PURE__ */ jsx(ArrowUp, {
2651
+ className: "size-3.5",
2652
+ "aria-hidden": "true"
2653
+ });
2654
+ if (activeDirection === "desc") return /* @__PURE__ */ jsx(ArrowDown, {
2655
+ className: "size-3.5",
2656
+ "aria-hidden": "true"
2657
+ });
2658
+ return /* @__PURE__ */ jsx(ChevronsUpDown, {
2659
+ className: "size-3.5 opacity-45",
2660
+ "aria-hidden": "true"
2661
+ });
2662
+ }
2663
+ function SortableHeaderContent({ column, sortState, onSortChange }) {
2664
+ const sortKey = getColumnSortKey(column);
2665
+ const activeDirection = sortState?.key === sortKey ? sortState.direction : void 0;
2666
+ if (!sortKey || !onSortChange) return /* @__PURE__ */ jsx(Fragment, { children: column.header });
2667
+ return /* @__PURE__ */ jsxs("button", {
2668
+ type: "button",
2669
+ className: cn("hover:text-foreground focus-visible:ring-ring inline-flex max-w-full items-center gap-1.5 rounded-sm text-left font-medium transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none", column.align === "right" && "ml-auto", column.align === "center" && "mx-auto"),
2670
+ "aria-label": `Sort by ${column.header}`,
2671
+ onClick: () => {
2672
+ onSortChange(getNextSortState(sortState ?? null, sortKey));
2673
+ },
2674
+ children: [/* @__PURE__ */ jsx("span", {
2675
+ className: "truncate",
2676
+ children: column.header
2677
+ }), /* @__PURE__ */ jsx(SortIcon, { activeDirection })]
2678
+ });
2679
+ }
2680
+ function DesktopView({ data, columns, isLoading, skeletonRows, actions, actionHandlers, renderActions, noDataMessage, noDataContent, getRowKey, onRowClick, pagination, stickyHeader = false, maxHeight = "calc(100vh - 300px)", fullHeight = false, sortState, onSortChange }) {
2681
+ const resolvedNoDataContent = noDataContent ?? noDataMessage;
2682
+ const hasActions = (actions?.length ?? 0) > 0 || renderActions !== void 0;
2683
+ const actionColumnWidth = hasActions ? `${String(ACTION_COLUMN_WIDTH_PX)}px` : void 0;
2684
+ const specifiedPercentageWidth = columns.reduce((total, column) => {
2685
+ if (typeof column.width === "string" && column.width.endsWith("%")) {
2686
+ const parsedWidth = Number.parseFloat(column.width);
2687
+ return Number.isNaN(parsedWidth) ? total : total + parsedWidth;
2688
+ }
2689
+ return total;
2690
+ }, 0);
2691
+ const remainingPercentageForDataColumns = Math.max(0, 100 - specifiedPercentageWidth - 0);
2692
+ const columnsWithoutWidth = columns.filter((column) => column.width === void 0).length;
2693
+ const defaultPercentageWidth = columnsWithoutWidth > 0 && remainingPercentageForDataColumns > 0 ? `${String(remainingPercentageForDataColumns / columnsWithoutWidth)}%` : void 0;
2694
+ const columnGroup = /* @__PURE__ */ jsxs("colgroup", { children: [columns.map((column) => /* @__PURE__ */ jsx("col", { style: { width: column.width ?? defaultPercentageWidth } }, column.header)), hasActions && actionColumnWidth ? /* @__PURE__ */ jsx("col", { style: {
2695
+ width: actionColumnWidth,
2696
+ minWidth: actionColumnWidth
2697
+ } }) : null] });
2698
+ const tableHeader = /* @__PURE__ */ jsx(TableHeader, {
2699
+ className: cn(stickyHeader && "bg-muted/50 sticky top-0 z-10 backdrop-blur-sm"),
2700
+ children: /* @__PURE__ */ jsxs(TableRow, { children: [columns.map((col) => /* @__PURE__ */ jsx(TableHead, {
2701
+ style: { width: col.width },
2702
+ "aria-sort": sortState?.key === getColumnSortKey(col) ? sortState.direction === "asc" ? "ascending" : "descending" : void 0,
2703
+ className: col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "",
2704
+ children: /* @__PURE__ */ jsx(SortableHeaderContent, {
2705
+ column: col,
2706
+ sortState,
2707
+ onSortChange
2708
+ })
2709
+ }, col.header)), hasActions ? /* @__PURE__ */ jsx(TableHead, {
2710
+ style: actionColumnWidth ? {
2711
+ width: actionColumnWidth,
2712
+ minWidth: actionColumnWidth
2713
+ } : void 0,
2714
+ className: "w-28 min-w-28 overflow-visible text-right whitespace-nowrap",
2715
+ children: "Actions"
2716
+ }) : null] })
2717
+ });
2718
+ const scrollContainerClass = cn("min-w-0 rounded-lg border overflow-x-auto", fullHeight && "flex-1 min-h-0 overflow-y-auto", !fullHeight && stickyHeader && "overflow-y-auto");
2719
+ const scrollContainerStyle = !fullHeight && stickyHeader ? { maxHeight } : void 0;
2720
+ if (isLoading) return /* @__PURE__ */ jsxs("div", {
2721
+ className: cn("flex flex-col", fullHeight && "min-h-0 flex-1"),
2722
+ children: [/* @__PURE__ */ jsx("div", {
2723
+ className: scrollContainerClass,
2724
+ style: scrollContainerStyle,
2725
+ children: /* @__PURE__ */ jsxs(Table, {
2726
+ className: "table-fixed",
2727
+ children: [
2728
+ columnGroup,
2729
+ tableHeader,
2730
+ /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: skeletonRows }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { children: [columns.map((col) => /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-full" }) }, col.header)), hasActions ? /* @__PURE__ */ jsx(TableCell, {
2731
+ className: ACTION_CELL_CLASS_NAME,
2732
+ children: /* @__PURE__ */ jsx("div", {
2733
+ className: "flex w-full justify-end",
2734
+ children: /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-20" })
2735
+ })
2736
+ }) : null] }, i)) })
2737
+ ]
2738
+ })
2739
+ }), pagination && /* @__PURE__ */ jsx(Pagination, {
2740
+ currentPage: pagination.currentPage,
2741
+ totalPages: pagination.totalPages,
2742
+ totalItems: pagination.totalItems,
2743
+ pageSize: pagination.pageSize,
2744
+ startIndex: pagination.startIndex,
2745
+ endIndex: pagination.endIndex,
2746
+ onPageChange: pagination.onPageChange
2747
+ })]
2748
+ });
2749
+ if (data.length === 0) return /* @__PURE__ */ jsxs("div", {
2750
+ className: cn("flex flex-col", fullHeight && "min-h-0 flex-1"),
2751
+ children: [/* @__PURE__ */ jsx("div", {
2752
+ className: "w-full py-0",
2753
+ children: typeof resolvedNoDataContent === "string" ? /* @__PURE__ */ jsx("div", {
2754
+ className: "text-muted-foreground",
2755
+ children: resolvedNoDataContent
2756
+ }) : resolvedNoDataContent
2757
+ }), pagination && pagination.totalItems > 0 && /* @__PURE__ */ jsx(Pagination, {
2758
+ currentPage: pagination.currentPage,
2759
+ totalPages: pagination.totalPages,
2760
+ totalItems: pagination.totalItems,
2761
+ pageSize: pagination.pageSize,
2762
+ startIndex: pagination.startIndex,
2763
+ endIndex: pagination.endIndex,
2764
+ onPageChange: pagination.onPageChange
2765
+ })]
2766
+ });
2767
+ return /* @__PURE__ */ jsxs("div", {
2768
+ className: cn("flex flex-col", fullHeight && "min-h-0 flex-1"),
2769
+ children: [/* @__PURE__ */ jsx("div", {
2770
+ className: scrollContainerClass,
2771
+ style: scrollContainerStyle,
2772
+ children: /* @__PURE__ */ jsxs(Table, {
2773
+ className: "table-fixed",
2774
+ children: [
2775
+ columnGroup,
2776
+ tableHeader,
2777
+ /* @__PURE__ */ jsx(TableBody, { children: data.map((item) => /* @__PURE__ */ jsxs(TableRow, {
2778
+ className: onRowClick ? "hover:bg-muted/50 cursor-pointer" : "",
2779
+ onClick: () => onRowClick?.(item),
2780
+ children: [columns.map((col) => /* @__PURE__ */ jsx(TableCell, {
2781
+ className: cn("max-w-0", col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : ""),
2782
+ children: col.truncate === false ? /* @__PURE__ */ jsx("div", {
2783
+ className: cn("block w-full min-w-0", col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left"),
2784
+ children: renderColumnValue(col, item)
2785
+ }) : /* @__PURE__ */ jsx(TruncatedContent, {
2786
+ align: col.align,
2787
+ tooltip: getColumnTooltipText(col, item),
2788
+ children: renderColumnValue(col, item)
2789
+ })
2790
+ }, col.header)), hasActions ? /* @__PURE__ */ jsx(TableCell, {
2791
+ className: ACTION_CELL_CLASS_NAME,
2792
+ children: /* @__PURE__ */ jsx(SmartTableActions, {
2793
+ item,
2794
+ actions,
2795
+ actionHandlers,
2796
+ renderActions
2797
+ })
2798
+ }) : null]
2799
+ }, getRowKey(item))) })
2800
+ ]
2801
+ })
2802
+ }), pagination && /* @__PURE__ */ jsx(Pagination, {
2803
+ currentPage: pagination.currentPage,
2804
+ totalPages: pagination.totalPages,
2805
+ totalItems: pagination.totalItems,
2806
+ pageSize: pagination.pageSize,
2807
+ startIndex: pagination.startIndex,
2808
+ endIndex: pagination.endIndex,
2809
+ onPageChange: pagination.onPageChange
2810
+ })]
2811
+ });
2812
+ }
2813
+
2814
+ //#endregion
2815
+ //#region src/smart-table/MobileView.tsx
2816
+ function MobileView({ data, columns, isLoading, skeletonRows, actions, actionHandlers, renderActions, noDataMessage, noDataContent, getRowKey, onRowClick, renderMobileCard, pagination, fullHeight = false }) {
2817
+ const resolvedNoDataContent = noDataContent ?? noDataMessage;
2818
+ const visibleColumns = columns.filter((col) => !col.hideOnMobile);
2819
+ const hasActions = (actions?.length ?? 0) > 0 || renderActions !== void 0;
2820
+ const paginationComponent = pagination && /* @__PURE__ */ jsx(Pagination, {
2821
+ currentPage: pagination.currentPage,
2822
+ totalPages: pagination.totalPages,
2823
+ totalItems: pagination.totalItems,
2824
+ pageSize: pagination.pageSize,
2825
+ startIndex: pagination.startIndex,
2826
+ endIndex: pagination.endIndex,
2827
+ onPageChange: pagination.onPageChange
2828
+ });
2829
+ if (isLoading) return /* @__PURE__ */ jsxs("div", {
2830
+ className: cn("flex flex-col", fullHeight && "flex-1 min-h-0"),
2831
+ children: [/* @__PURE__ */ jsx("div", {
2832
+ className: cn("space-y-2", fullHeight && "flex-1 min-h-0 overflow-auto px-px"),
2833
+ children: Array.from({ length: skeletonRows }).map((_, i) => /* @__PURE__ */ jsx(Card, {
2834
+ size: "sm",
2835
+ className: "border border-border/80 shadow-none ring-0",
2836
+ children: /* @__PURE__ */ jsx(CardContent, {
2837
+ className: "px-3",
2838
+ children: /* @__PURE__ */ jsxs("div", {
2839
+ className: "space-y-2",
2840
+ children: [visibleColumns.map((column) => /* @__PURE__ */ jsxs("div", {
2841
+ className: "flex items-center gap-3",
2842
+ children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-20 shrink-0" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" })]
2843
+ }, column.header)), hasActions ? /* @__PURE__ */ jsx("div", {
2844
+ className: "flex justify-end border-t pt-3",
2845
+ children: /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-24" })
2846
+ }) : null]
2847
+ })
2848
+ })
2849
+ }, i))
2850
+ }), paginationComponent]
2851
+ });
2852
+ if (data.length === 0) return /* @__PURE__ */ jsxs("div", {
2853
+ className: cn("flex flex-col", fullHeight && "flex-1 min-h-0"),
2854
+ children: [/* @__PURE__ */ jsx("div", {
2855
+ className: "w-full py-0",
2856
+ children: typeof resolvedNoDataContent === "string" ? /* @__PURE__ */ jsx("div", {
2857
+ className: "text-muted-foreground",
2858
+ children: resolvedNoDataContent
2859
+ }) : resolvedNoDataContent
2860
+ }), pagination && pagination.totalItems > 0 && paginationComponent]
2861
+ });
2862
+ return /* @__PURE__ */ jsxs("div", {
2863
+ className: cn("flex flex-col", fullHeight && "flex-1 min-h-0"),
2864
+ children: [/* @__PURE__ */ jsx("div", {
2865
+ className: cn("space-y-2", fullHeight && "flex-1 min-h-0 overflow-auto px-px"),
2866
+ children: data.map((item) => /* @__PURE__ */ jsx(Card, {
2867
+ size: "sm",
2868
+ className: cn("border border-border/80 shadow-none ring-0", onRowClick ? "cursor-pointer hover:bg-muted/50" : ""),
2869
+ onClick: () => onRowClick?.(item),
2870
+ children: /* @__PURE__ */ jsx(CardContent, {
2871
+ className: "px-3",
2872
+ children: renderMobileCard ? renderMobileCard(item) : /* @__PURE__ */ jsxs("div", {
2873
+ className: "space-y-2.5",
2874
+ children: [visibleColumns.map((col) => /* @__PURE__ */ jsxs("div", {
2875
+ className: "flex items-center gap-3 overflow-hidden",
2876
+ children: [/* @__PURE__ */ jsx("span", {
2877
+ className: "w-20 shrink-0 truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground",
2878
+ children: col.mobileLabel ?? col.header
2879
+ }), /* @__PURE__ */ jsx("div", {
2880
+ className: "min-w-0 flex-1 text-sm",
2881
+ children: col.truncate === false ? /* @__PURE__ */ jsx("div", {
2882
+ className: "block min-w-0 w-full text-right text-sm",
2883
+ children: renderColumnValue(col, item)
2884
+ }) : /* @__PURE__ */ jsx(TruncatedContent, {
2885
+ align: "right",
2886
+ tooltip: getColumnTooltipText(col, item),
2887
+ className: "text-sm",
2888
+ children: renderColumnValue(col, item)
2889
+ })
2890
+ })]
2891
+ }, col.header)), hasActions ? /* @__PURE__ */ jsx("div", {
2892
+ className: "mt-3 flex justify-end border-t pt-2.5",
2893
+ children: /* @__PURE__ */ jsx(SmartTableActions, {
2894
+ item,
2895
+ actions,
2896
+ actionHandlers,
2897
+ renderActions
2898
+ })
2899
+ }) : null]
2900
+ })
2901
+ })
2902
+ }, getRowKey(item)))
2903
+ }), paginationComponent]
2904
+ });
2905
+ }
2906
+
2907
+ //#endregion
2908
+ //#region src/smart-table/SmartTable.tsx
2909
+ function SmartTable({ data, columns, isLoading, skeletonRows = 5, actions, actionHandlers, renderActions, noDataMessage = "No data available", noDataContent, getRowKey = (item) => {
2910
+ const record = item;
2911
+ if ("_id" in record) return String(record._id);
2912
+ if ("id" in record) return String(record.id);
2913
+ return data.indexOf(item);
2914
+ }, onRowClick, renderMobileCard, pagination, stickyHeader, maxHeight, fullHeight, sortState, onSortChange }) {
2915
+ if (useIsMobile()) return /* @__PURE__ */ jsx("div", {
2916
+ className: cn("min-w-0", fullHeight && "flex min-h-0 flex-1 flex-col"),
2917
+ children: /* @__PURE__ */ jsx(MobileView, {
2918
+ data,
2919
+ columns,
2920
+ isLoading,
2921
+ skeletonRows,
2922
+ actions,
2923
+ actionHandlers,
2924
+ renderActions,
2925
+ noDataMessage,
2926
+ noDataContent,
2927
+ getRowKey,
2928
+ onRowClick,
2929
+ renderMobileCard,
2930
+ pagination,
2931
+ fullHeight
2932
+ })
2933
+ });
2934
+ return /* @__PURE__ */ jsx("div", {
2935
+ className: cn("min-w-0", fullHeight && "flex min-h-0 flex-1 flex-col"),
2936
+ children: /* @__PURE__ */ jsx(DesktopView, {
2937
+ data,
2938
+ columns,
2939
+ isLoading,
2940
+ skeletonRows,
2941
+ actions,
2942
+ actionHandlers,
2943
+ renderActions,
2944
+ noDataMessage,
2945
+ noDataContent,
2946
+ getRowKey,
2947
+ onRowClick,
2948
+ pagination,
2949
+ stickyHeader,
2950
+ maxHeight,
2951
+ fullHeight,
2952
+ sortState,
2953
+ onSortChange
2954
+ })
2955
+ });
2956
+ }
2957
+
2958
+ //#endregion
2959
+ //#region src/table-toolbar/table-toolbar.tsx
2960
+ function buildAllFilterOptionLabel(label, allOptionLabel) {
2961
+ return allOptionLabel ?? `All: ${label}`;
2962
+ }
2963
+ function FilterDropdown({ label, value, options, onChange, className, allowAll = true, allOptionLabel }) {
2964
+ return /* @__PURE__ */ jsx(SearchableSelect, {
2965
+ value,
2966
+ onValueChange: (nextValue) => {
2967
+ onChange(nextValue);
2968
+ },
2969
+ placeholder: label,
2970
+ className: className ?? "w-full sm:w-[140px]",
2971
+ searchPlaceholder: `Search ${label.toLocaleLowerCase()}...`,
2972
+ options: [...allowAll ? [{
2973
+ value: "all",
2974
+ label: buildAllFilterOptionLabel(label, allOptionLabel)
2975
+ }] : [], ...options]
2976
+ });
2977
+ }
2978
+ function SearchInput({ value, onChange, placeholder = "Search...", className }) {
2979
+ return /* @__PURE__ */ jsxs("div", {
2980
+ className: cn("relative", className),
2981
+ children: [
2982
+ /* @__PURE__ */ jsx(Search, { className: "text-muted-foreground absolute top-1/2 left-2.5 size-4 -translate-y-1/2" }),
2983
+ /* @__PURE__ */ jsx(Input, {
2984
+ type: "text",
2985
+ placeholder,
2986
+ value,
2987
+ onChange: (event) => {
2988
+ onChange(event.target.value);
2989
+ },
2990
+ className: "pr-9 pl-9"
2991
+ }),
2992
+ value ? /* @__PURE__ */ jsx("button", {
2993
+ type: "button",
2994
+ onClick: () => {
2995
+ onChange("");
2996
+ },
2997
+ className: "text-muted-foreground hover:text-foreground absolute top-1/2 right-2.5 -translate-y-1/2",
2998
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
2999
+ }) : null
3000
+ ]
3001
+ });
3002
+ }
3003
+ function resolveTableToolbarLabels(labels = {}) {
3004
+ return {
3005
+ filtersButtonLabel: labels.filtersButtonLabel ?? "Filters",
3006
+ filtersTitle: labels.filtersTitle ?? "Filters",
3007
+ filtersDescription: labels.filtersDescription ?? "Narrow the list using the available criteria.",
3008
+ clearFiltersLabel: labels.clearFiltersLabel ?? "Clear",
3009
+ showResultsLabel: labels.showResultsLabel ?? ((resultCount) => typeof resultCount === "number" ? `Show ${resultCount.toLocaleString("en-US")} ${resultCount === 1 ? "result" : "results"}` : "Show results"),
3010
+ rangeMinPlaceholder: labels.rangeMinPlaceholder ?? "Min",
3011
+ rangeMaxPlaceholder: labels.rangeMaxPlaceholder ?? "Max"
3012
+ };
3013
+ }
3014
+ function TableToolbar({ search, filters, textFilters, customFilters, rangeFilters, renderRangeInput, inlineControls, onClearAll, getDraftResultCount, labels, children }) {
3015
+ const resolvedLabels = resolveTableToolbarLabels(labels);
3016
+ const isMobile = useIsMobile();
3017
+ const [filtersOpen, setFiltersOpen] = useState(false);
3018
+ const [draftFilterValues, setDraftFilterValues] = useState({});
3019
+ const hasFilters = Boolean((filters?.length ?? 0) + (textFilters?.length ?? 0) + (customFilters?.length ?? 0) + (rangeFilters?.length ?? 0));
3020
+ const desktopConfirmShortcutEnabled = filtersOpen && !isMobile;
3021
+ const desktopModifierLabel = useDesktopShortcutModifierLabel(desktopConfirmShortcutEnabled);
3022
+ const clearableFilters = filters?.filter((filter) => filter.clearable !== false) ?? [];
3023
+ const clearableCustomFilters = customFilters?.filter((filter) => filter.clearable !== false) ?? [];
3024
+ const filterValues = useMemo(() => ({
3025
+ ...Object.fromEntries((filters ?? []).map((filter) => [filter.config.key, filter.value])),
3026
+ ...Object.fromEntries((textFilters ?? []).map((filter) => [filter.key, filter.value])),
3027
+ ...Object.fromEntries((customFilters ?? []).map((filter) => [filter.key, filter.value])),
3028
+ ...Object.fromEntries((rangeFilters ?? []).flatMap((filter) => [[`${filter.key}Min`, filter.minValue], [`${filter.key}Max`, filter.maxValue]]))
3029
+ }), [
3030
+ customFilters,
3031
+ filters,
3032
+ rangeFilters,
3033
+ textFilters
3034
+ ]);
3035
+ const activeFilterCount = clearableFilters.filter((filter) => filter.value !== "all").length;
3036
+ const activeCustomFilterCount = clearableCustomFilters.filter((filter) => filter.value !== (filter.clearValue ?? "all")).length;
3037
+ const activeRangeFilterCount = rangeFilters?.filter((filter) => filter.minValue.trim() || filter.maxValue.trim()).length ?? 0;
3038
+ const activeTextFilterCount = textFilters?.filter((filter) => filter.value.trim().length > 0).length ?? 0;
3039
+ const hasDraftFilters = clearableFilters.some((filter) => {
3040
+ return (draftFilterValues[filter.config.key] ?? filter.value) !== "all";
3041
+ }) || clearableCustomFilters.some((filter) => {
3042
+ return (draftFilterValues[filter.key] ?? filter.value) !== (filter.clearValue ?? "all");
3043
+ }) || (rangeFilters ?? []).some((filter) => {
3044
+ const minValue = draftFilterValues[`${filter.key}Min`] ?? filter.minValue;
3045
+ const maxValue = draftFilterValues[`${filter.key}Max`] ?? filter.maxValue;
3046
+ return Boolean(minValue.trim() || maxValue.trim());
3047
+ }) || (textFilters ?? []).some((filter) => {
3048
+ return (draftFilterValues[filter.key] ?? filter.value).trim().length > 0;
3049
+ });
3050
+ const draftResultCount = useMemo(() => filtersOpen ? getDraftResultCount?.(draftFilterValues) : void 0, [
3051
+ draftFilterValues,
3052
+ filtersOpen,
3053
+ getDraftResultCount
3054
+ ]);
3055
+ const applyButtonLabel = resolvedLabels.showResultsLabel(draftResultCount);
3056
+ function openFiltersSheet() {
3057
+ setDraftFilterValues(filterValues);
3058
+ setFiltersOpen(true);
3059
+ }
3060
+ const activeFilterTotal = activeFilterCount + activeCustomFilterCount + activeRangeFilterCount + activeTextFilterCount;
3061
+ function updateDraftFilterValue(key, value) {
3062
+ setDraftFilterValues((currentValues) => ({
3063
+ ...currentValues,
3064
+ [key]: value
3065
+ }));
3066
+ }
3067
+ function clearAndApplyFilters() {
3068
+ const clearedValues = Object.fromEntries(clearableFilters.map((filter) => [filter.config.key, "all"]));
3069
+ const clearedRangeValues = Object.fromEntries((rangeFilters ?? []).flatMap((filter) => [[`${filter.key}Min`, ""], [`${filter.key}Max`, ""]]));
3070
+ const clearedTextValues = Object.fromEntries((textFilters ?? []).map((filter) => [filter.key, ""]));
3071
+ const clearedCustomValues = Object.fromEntries(clearableCustomFilters.map((filter) => [filter.key, filter.clearValue ?? "all"]));
3072
+ setDraftFilterValues((currentValues) => ({
3073
+ ...currentValues,
3074
+ ...clearedValues,
3075
+ ...clearedRangeValues,
3076
+ ...clearedTextValues,
3077
+ ...clearedCustomValues
3078
+ }));
3079
+ filters?.forEach((filter) => {
3080
+ const nextValue = filter.clearable === false ? filter.value : "all";
3081
+ if (nextValue !== filter.value) filter.onChange(nextValue);
3082
+ });
3083
+ rangeFilters?.forEach((filter) => {
3084
+ if (filter.minValue) filter.onMinChange("");
3085
+ if (filter.maxValue) filter.onMaxChange("");
3086
+ });
3087
+ textFilters?.forEach((filter) => {
3088
+ if (filter.value) filter.onChange("");
3089
+ });
3090
+ customFilters?.forEach((filter) => {
3091
+ const nextValue = filter.clearable === false ? filter.value : filter.clearValue ?? "all";
3092
+ if (nextValue !== filter.value) filter.onChange(nextValue);
3093
+ });
3094
+ setFiltersOpen(false);
3095
+ }
3096
+ function applyDraftFilters() {
3097
+ filters?.forEach((filter) => {
3098
+ const nextValue = draftFilterValues[filter.config.key] ?? filter.value;
3099
+ if (nextValue !== filter.value) filter.onChange(nextValue);
3100
+ });
3101
+ rangeFilters?.forEach((filter) => {
3102
+ const nextMinValue = draftFilterValues[`${filter.key}Min`] ?? filter.minValue;
3103
+ const nextMaxValue = draftFilterValues[`${filter.key}Max`] ?? filter.maxValue;
3104
+ if (nextMinValue !== filter.minValue) filter.onMinChange(nextMinValue);
3105
+ if (nextMaxValue !== filter.maxValue) filter.onMaxChange(nextMaxValue);
3106
+ });
3107
+ textFilters?.forEach((filter) => {
3108
+ const nextValue = draftFilterValues[filter.key] ?? filter.value;
3109
+ if (nextValue !== filter.value) filter.onChange(nextValue);
3110
+ });
3111
+ customFilters?.forEach((filter) => {
3112
+ const nextValue = draftFilterValues[filter.key] ?? filter.value;
3113
+ if (nextValue !== filter.value) filter.onChange(nextValue);
3114
+ });
3115
+ setFiltersOpen(false);
3116
+ }
3117
+ return /* @__PURE__ */ jsxs("div", {
3118
+ className: "space-y-3",
3119
+ children: [/* @__PURE__ */ jsxs("div", {
3120
+ className: "flex flex-wrap items-center gap-2",
3121
+ children: [
3122
+ search ? /* @__PURE__ */ jsx(SearchInput, {
3123
+ value: search.value,
3124
+ onChange: search.onChange,
3125
+ placeholder: search.placeholder,
3126
+ className: "min-w-0 flex-1 sm:w-64 sm:flex-initial"
3127
+ }) : null,
3128
+ inlineControls,
3129
+ hasFilters ? /* @__PURE__ */ jsxs(Button, {
3130
+ variant: "outline",
3131
+ className: "relative gap-2",
3132
+ onClick: openFiltersSheet,
3133
+ children: [
3134
+ /* @__PURE__ */ jsx(Filter, { className: "size-4" }),
3135
+ resolvedLabels.filtersButtonLabel,
3136
+ activeFilterTotal > 0 ? /* @__PURE__ */ jsx("span", {
3137
+ className: "bg-primary text-primary-foreground absolute -top-1 -right-1 flex size-4 items-center justify-center rounded-full text-[10px]",
3138
+ children: activeFilterTotal
3139
+ }) : null
3140
+ ]
3141
+ }) : null,
3142
+ children ? /* @__PURE__ */ jsx("div", {
3143
+ className: "hidden sm:ml-auto sm:flex sm:items-center sm:gap-2",
3144
+ children
3145
+ }) : null
3146
+ ]
3147
+ }), hasFilters ? /* @__PURE__ */ jsx(ResponsiveSheet, {
3148
+ open: filtersOpen,
3149
+ onOpenChange: setFiltersOpen,
3150
+ title: resolvedLabels.filtersTitle,
3151
+ description: resolvedLabels.filtersDescription,
3152
+ onConfirm: applyDraftFilters,
3153
+ footer: /* @__PURE__ */ jsxs("div", {
3154
+ className: "flex w-full gap-2",
3155
+ children: [onClearAll ? /* @__PURE__ */ jsxs(Button, {
3156
+ type: "button",
3157
+ variant: "outline",
3158
+ disabled: !hasDraftFilters,
3159
+ onClick: clearAndApplyFilters,
3160
+ className: "min-w-0 flex-1",
3161
+ children: [/* @__PURE__ */ jsx(X, { className: "mr-1 size-4" }), resolvedLabels.clearFiltersLabel]
3162
+ }) : null, /* @__PURE__ */ jsx(Button, {
3163
+ type: "button",
3164
+ onClick: applyDraftFilters,
3165
+ className: cn("relative min-w-0 flex-1", desktopConfirmShortcutEnabled ? "pr-12" : null),
3166
+ children: /* @__PURE__ */ jsxs("span", {
3167
+ className: "inline-flex w-full items-center justify-center",
3168
+ children: [/* @__PURE__ */ jsx("span", {
3169
+ className: "min-w-0 truncate",
3170
+ children: applyButtonLabel
3171
+ }), desktopConfirmShortcutEnabled && desktopModifierLabel ? /* @__PURE__ */ jsx("span", {
3172
+ className: "absolute top-1/2 right-2 -translate-y-1/2",
3173
+ children: /* @__PURE__ */ jsx(DesktopConfirmShortcutHint, { desktopModifierLabel })
3174
+ }) : null]
3175
+ })
3176
+ })]
3177
+ }),
3178
+ width: 420,
3179
+ children: /* @__PURE__ */ jsxs("div", {
3180
+ className: "space-y-4",
3181
+ children: [
3182
+ filters?.map((filter) => {
3183
+ const Icon = filter.config.icon;
3184
+ return /* @__PURE__ */ jsxs("div", {
3185
+ className: "flex flex-col gap-1.5",
3186
+ children: [/* @__PURE__ */ jsxs("span", {
3187
+ className: "text-foreground flex items-center gap-2 text-sm font-medium",
3188
+ children: [Icon ? /* @__PURE__ */ jsx(Icon, {
3189
+ className: "text-foreground/70 size-3.5 shrink-0",
3190
+ strokeWidth: 1.8
3191
+ }) : null, filter.config.label]
3192
+ }), /* @__PURE__ */ jsx(FilterDropdown, {
3193
+ label: filter.config.label,
3194
+ value: draftFilterValues[filter.config.key] ?? filter.value,
3195
+ options: filter.config.options,
3196
+ onChange: (value) => {
3197
+ updateDraftFilterValue(filter.config.key, value);
3198
+ },
3199
+ className: "w-full",
3200
+ allowAll: filter.allowAll,
3201
+ allOptionLabel: filter.allOptionLabel
3202
+ })]
3203
+ }, filter.config.key);
3204
+ }),
3205
+ customFilters?.map((filter) => {
3206
+ const Icon = filter.icon;
3207
+ return /* @__PURE__ */ jsxs("div", {
3208
+ className: "flex flex-col gap-1.5",
3209
+ children: [/* @__PURE__ */ jsxs("span", {
3210
+ className: "text-foreground flex items-center gap-2 text-sm font-medium",
3211
+ children: [Icon ? /* @__PURE__ */ jsx(Icon, {
3212
+ className: "text-foreground/70 size-3.5 shrink-0",
3213
+ strokeWidth: 1.8
3214
+ }) : null, filter.label]
3215
+ }), filter.render({
3216
+ value: draftFilterValues[filter.key] ?? filter.value,
3217
+ setValue: (value) => {
3218
+ updateDraftFilterValue(filter.key, value);
3219
+ }
3220
+ })]
3221
+ }, filter.key);
3222
+ }),
3223
+ textFilters?.map((filter) => {
3224
+ const Icon = filter.icon;
3225
+ return /* @__PURE__ */ jsxs("div", {
3226
+ className: "flex flex-col gap-1.5",
3227
+ children: [/* @__PURE__ */ jsxs("span", {
3228
+ className: "text-foreground flex items-center gap-2 text-sm font-medium",
3229
+ children: [Icon ? /* @__PURE__ */ jsx(Icon, {
3230
+ className: "text-foreground/70 size-3.5 shrink-0",
3231
+ strokeWidth: 1.8
3232
+ }) : null, filter.label]
3233
+ }), /* @__PURE__ */ jsx(Input, {
3234
+ type: "text",
3235
+ value: draftFilterValues[filter.key] ?? filter.value,
3236
+ onChange: (event) => {
3237
+ updateDraftFilterValue(filter.key, event.target.value);
3238
+ },
3239
+ placeholder: filter.placeholder ?? filter.label
3240
+ })]
3241
+ }, filter.key);
3242
+ }),
3243
+ rangeFilters?.map((filter) => {
3244
+ const Icon = filter.icon;
3245
+ const minKey = `${filter.key}Min`;
3246
+ const maxKey = `${filter.key}Max`;
3247
+ return /* @__PURE__ */ jsxs("div", {
3248
+ className: "flex flex-col gap-1.5",
3249
+ children: [/* @__PURE__ */ jsxs("span", {
3250
+ className: "text-foreground flex items-center gap-2 text-sm font-medium",
3251
+ children: [Icon ? /* @__PURE__ */ jsx(Icon, {
3252
+ className: "text-foreground/70 size-3.5 shrink-0",
3253
+ strokeWidth: 1.8
3254
+ }) : null, filter.label]
3255
+ }), /* @__PURE__ */ jsx("div", {
3256
+ className: "grid grid-cols-2 gap-2",
3257
+ children: renderRangeInput ? /* @__PURE__ */ jsxs(Fragment, { children: [renderRangeInput({
3258
+ filter,
3259
+ input: "min",
3260
+ value: draftFilterValues[minKey] ?? filter.minValue,
3261
+ onChange: (value) => {
3262
+ updateDraftFilterValue(minKey, value);
3263
+ },
3264
+ placeholder: filter.minPlaceholder ?? resolvedLabels.rangeMinPlaceholder
3265
+ }), renderRangeInput({
3266
+ filter,
3267
+ input: "max",
3268
+ value: draftFilterValues[maxKey] ?? filter.maxValue,
3269
+ onChange: (value) => {
3270
+ updateDraftFilterValue(maxKey, value);
3271
+ },
3272
+ placeholder: filter.maxPlaceholder ?? resolvedLabels.rangeMaxPlaceholder
3273
+ })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Input, {
3274
+ type: filter.inputType ?? "text",
3275
+ inputMode: filter.inputMode ?? "decimal",
3276
+ value: draftFilterValues[minKey] ?? filter.minValue,
3277
+ onChange: (event) => {
3278
+ updateDraftFilterValue(minKey, event.target.value);
3279
+ },
3280
+ placeholder: filter.minPlaceholder ?? "Min"
3281
+ }), /* @__PURE__ */ jsx(Input, {
3282
+ type: filter.inputType ?? "text",
3283
+ inputMode: filter.inputMode ?? "decimal",
3284
+ value: draftFilterValues[maxKey] ?? filter.maxValue,
3285
+ onChange: (event) => {
3286
+ updateDraftFilterValue(maxKey, event.target.value);
3287
+ },
3288
+ placeholder: filter.maxPlaceholder ?? "Max"
3289
+ })] })
3290
+ })]
3291
+ }, filter.key);
3292
+ })
3293
+ ]
3294
+ })
3295
+ }) : null]
3296
+ });
3297
+ }
3298
+
3299
+ //#endregion
3300
+ //#region src/error-page/error-code.tsx
3301
+ function ErrorCode({ code, reference, className }) {
3302
+ return /* @__PURE__ */ jsxs("p", {
3303
+ className: cn("text-xs text-muted-foreground/55", className),
3304
+ children: [
3305
+ "Codice ",
3306
+ code,
3307
+ reference ? /* @__PURE__ */ jsxs("span", {
3308
+ className: "ml-2",
3309
+ children: ["Rif. ", reference]
3310
+ }) : null
3311
+ ]
3312
+ });
3313
+ }
3314
+
3315
+ //#endregion
3316
+ //#region src/error-page/error-page-content.ts
3317
+ const NOT_FOUND_STATUS = 404;
3318
+ function getErrorText(error) {
3319
+ if (error instanceof Error) {
3320
+ const digest = "digest" in error ? String(error.digest) : "";
3321
+ return `${error.message} ${digest}`;
3322
+ }
3323
+ if (typeof error === "object" && error !== null) {
3324
+ const record = error;
3325
+ return `${typeof record.message === "string" ? record.message : ""} ${typeof record.digest === "string" ? record.digest : ""}`;
3326
+ }
3327
+ return typeof error === "string" ? error : "";
3328
+ }
3329
+ function getErrorStatusCode(error) {
3330
+ const text = getErrorText(error);
3331
+ const fallbackMatch = /NEXT_HTTP_ERROR_FALLBACK;(\d{3})/.exec(text);
3332
+ if (fallbackMatch) return Number(fallbackMatch[1]);
3333
+ const statusMatch = /\bstatus(?:Code)?[=:]\s*(\d{3})\b/i.exec(text);
3334
+ return statusMatch ? Number(statusMatch[1]) : null;
3335
+ }
3336
+ function resolveErrorPageContent(error, options = {}) {
3337
+ if (getErrorStatusCode(error) === NOT_FOUND_STATUS) return {
3338
+ code: "404",
3339
+ title: "Page not found",
3340
+ description: "The page you are looking for does not exist or has been moved.",
3341
+ shouldCapture: false
3342
+ };
3343
+ return {
3344
+ code: options.fallbackCode ?? "500",
3345
+ title: options.fallbackTitle ?? "An error occurred",
3346
+ description: options.fallbackDescription ?? "An unexpected error occurred. Please try again later.",
3347
+ shouldCapture: true
3348
+ };
3349
+ }
3350
+ function getNotFoundPageContent() {
3351
+ return {
3352
+ code: "404",
3353
+ title: "Page not found",
3354
+ description: "The page you are looking for does not exist or has been moved.",
3355
+ shouldCapture: false
3356
+ };
3357
+ }
3358
+
3359
+ //#endregion
3360
+ //#region src/error-page/posthog-error-capture.ts
3361
+ function getPostHogClient() {
3362
+ if (typeof window === "undefined") return null;
3363
+ const client = window.posthog;
3364
+ return typeof client?.capture === "function" ? client : null;
3365
+ }
3366
+ function normalizeError(error) {
3367
+ if (error instanceof Error) {
3368
+ const digest = "digest" in error && typeof error.digest === "string" ? error.digest : void 0;
3369
+ return {
3370
+ name: error.name,
3371
+ message: error.message,
3372
+ stack: error.stack,
3373
+ digest
3374
+ };
3375
+ }
3376
+ return {
3377
+ name: "UnknownError",
3378
+ message: typeof error === "string" ? error : "Unknown error"
3379
+ };
3380
+ }
3381
+ function createErrorReference(error) {
3382
+ if (error instanceof Error && "digest" in error && typeof error.digest === "string") return error.digest;
3383
+ return `err_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
3384
+ }
3385
+ function captureErrorToPostHog({ error, code, reference, source = "error-page", metadata }) {
3386
+ const posthog = getPostHogClient();
3387
+ if (!posthog) return;
3388
+ const normalizedError = normalizeError(error);
3389
+ try {
3390
+ posthog.capture("$exception", {
3391
+ $exception_type: normalizedError.name,
3392
+ $exception_message: normalizedError.message,
3393
+ $exception_stack_trace: normalizedError.stack,
3394
+ code,
3395
+ digest: normalizedError.digest,
3396
+ error_reference: reference,
3397
+ pathname: window.location.pathname,
3398
+ source,
3399
+ ...metadata
3400
+ });
3401
+ } catch {}
3402
+ }
3403
+
3404
+ //#endregion
3405
+ //#region src/error-page/saas-error-page.tsx
3406
+ function resolveContent(error, content) {
3407
+ const resolvedContent = error ? resolveErrorPageContent(error) : getNotFoundPageContent();
3408
+ return {
3409
+ ...resolvedContent,
3410
+ ...content,
3411
+ shouldCapture: content?.shouldCapture ?? resolvedContent.shouldCapture
3412
+ };
3413
+ }
3414
+ function SaasErrorPage({ error, reset, homeHref = "/", homeLabel = "Torna alla home", retryLabel = "Riprova", source, metadata, content, className, classes }) {
3415
+ const resolvedContent = useMemo(() => resolveContent(error, content), [content, error]);
3416
+ const [reference] = useState(() => error ? createErrorReference(error) : null);
3417
+ useEffect(() => {
3418
+ if (!error || !resolvedContent.shouldCapture || !reference) return;
3419
+ captureErrorToPostHog({
3420
+ error,
3421
+ code: resolvedContent.code,
3422
+ reference,
3423
+ source,
3424
+ metadata
3425
+ });
3426
+ }, [
3427
+ error,
3428
+ metadata,
3429
+ reference,
3430
+ resolvedContent.code,
3431
+ resolvedContent.shouldCapture,
3432
+ source
3433
+ ]);
3434
+ return /* @__PURE__ */ jsxs("main", {
3435
+ className: cn("bg-background text-foreground flex min-h-screen flex-col items-center justify-center gap-4 px-6 text-center", className, classes?.root),
3436
+ children: [
3437
+ /* @__PURE__ */ jsxs("div", {
3438
+ className: cn("space-y-3", classes?.content),
3439
+ children: [/* @__PURE__ */ jsx("p", {
3440
+ className: cn("text-8xl font-semibold leading-none text-muted-foreground/25 sm:text-9xl", classes?.code),
3441
+ children: resolvedContent.code
3442
+ }), /* @__PURE__ */ jsxs("div", {
3443
+ className: cn("space-y-2", classes?.text),
3444
+ children: [/* @__PURE__ */ jsx("h1", {
3445
+ className: cn("text-2xl font-semibold tracking-tight", classes?.title),
3446
+ children: resolvedContent.title
3447
+ }), /* @__PURE__ */ jsx("p", {
3448
+ className: cn("mx-auto max-w-md text-sm text-muted-foreground", classes?.description),
3449
+ children: resolvedContent.description
3450
+ })]
3451
+ })]
3452
+ }),
3453
+ /* @__PURE__ */ jsx(ErrorCode, {
3454
+ code: resolvedContent.code,
3455
+ reference
3456
+ }),
3457
+ /* @__PURE__ */ jsxs("div", {
3458
+ className: cn("mt-2 flex flex-wrap justify-center gap-3", classes?.actions),
3459
+ children: [reset ? /* @__PURE__ */ jsx(Button, {
3460
+ type: "button",
3461
+ variant: "outline",
3462
+ onClick: reset,
3463
+ children: retryLabel
3464
+ }) : null, /* @__PURE__ */ jsx(Button, {
3465
+ asChild: true,
3466
+ children: /* @__PURE__ */ jsx("a", {
3467
+ href: homeHref,
3468
+ children: homeLabel
3469
+ })
3470
+ })]
3471
+ })
3472
+ ]
3473
+ });
3474
+ }
3475
+ function SaasNotFoundPage({ title, description, ...props }) {
3476
+ return /* @__PURE__ */ jsx(SaasErrorPage, {
3477
+ ...props,
3478
+ content: {
3479
+ code: "404",
3480
+ title: title ?? "Page not found",
3481
+ description: description ?? "The page you are looking for does not exist or has been moved.",
3482
+ shouldCapture: false
3483
+ }
3484
+ });
3485
+ }
3486
+
3487
+ //#endregion
3488
+ export { AUTO_SEARCHABLE_SELECT_THRESHOLD, Avatar, AvatarFallback, AvatarImage, Button, CHIP_CLASS_NAMES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Chip, ChipButton, DesktopConfirmShortcutHint, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DisplayDate, Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, EmptyStateCard, ErrorCode, FieldDetailRow, FileDropzone, FilterDropdown, HelpInfoButton, InitialEmptyState, Input, KeyboardKeycap, Label, NoResultsState, Pagination, Popover, PopoverContent, PopoverTrigger, ResponsiveSheet, SaasErrorPage, SaasNotFoundPage, ScrollFadeArea, SearchInput, SearchableSelect, SegmentedToggle, Select, SelectContent, SelectItem, SelectScrollDownButton, SelectScrollUpButton, SelectTrigger, SelectValue, Sheet, SheetActionFooter, SheetContent, SheetDescription, SheetHeader, SheetTitle, ShortcutModifierKeycap, Skeleton, SmartTable, SmartTableActions, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, TableToolbar, Tabs, TabsContent, TabsList, TabsScrollArea, TabsTrigger, Textarea, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TruncatedContent, UserPicker, buildAllFilterOptionLabel, buildSearchText, buildUserInitials, buttonVariants, captureErrorToPostHog, cn, createErrorReference, filterAndRankBySearch, filterSelectableUsers, filterUsersBySearch, formatAbsoluteDate, formatDisplayDate, formatSelectedUserSummary, formatUserDisplayName, getChipClassName, getDesktopShortcutModifierLabel, getNotFoundPageContent, getSearchableSelectPortalContainer, isAllowedConfirmShortcutEvent, isSearchableSelectPointerInside, rankBySearch, resolveCollectionEmptyState, resolveErrorPageContent, resolveSearchableSelectDropdownPosition, scoreFuzzyMatch, tabsListVariants, toggleUserSelection, useDesktopConfirmShortcut, useDesktopShortcutModifierLabel, useIsMobile, useMediaQuery, useTableSorting };
3489
+ //# sourceMappingURL=index.mjs.map