@bmsuisse/ui 0.4.5

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.js ADDED
@@ -0,0 +1,1336 @@
1
+ // src/primitives/button.tsx
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva } from "class-variance-authority";
4
+ import { forwardRef } from "react";
5
+
6
+ // src/lib/utils.ts
7
+ import { clsx } from "clsx";
8
+ import { twMerge } from "tailwind-merge";
9
+ function cn(...inputs) {
10
+ return twMerge(clsx(inputs));
11
+ }
12
+
13
+ // src/primitives/button.tsx
14
+ import { jsx } from "react/jsx-runtime";
15
+ var buttonVariants = cva(
16
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
17
+ {
18
+ variants: {
19
+ variant: {
20
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
21
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
22
+ ghost: "hover:bg-accent hover:text-accent-foreground",
23
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
24
+ secondary: "bg-muted text-muted-foreground hover:bg-muted/80",
25
+ link: "text-primary underline-offset-4 hover:underline",
26
+ // "Swiss design" red CTA, ported from akeneo_editor's old .btn-primary-swiss
27
+ // naive-ui override. Relies on --color-swiss-primary/-hover and
28
+ // --color-swiss-primary-foreground tokens defined by the consuming app's own
29
+ // Tailwind theme (this package doesn't define them) — same pattern as AlertBox's
30
+ // warning/info/success colors, which assume tokens the shared theme doesn't have.
31
+ "swiss-primary": "bg-swiss-primary text-swiss-primary-foreground font-semibold shadow-sm hover:bg-swiss-primary-hover hover:-translate-y-px active:translate-y-px",
32
+ "swiss-secondary": "border border-input bg-background font-semibold text-foreground shadow-sm hover:bg-accent hover:-translate-y-px active:translate-y-px"
33
+ },
34
+ size: {
35
+ default: "h-9 px-3",
36
+ xs: "h-7 px-2 text-xs",
37
+ sm: "h-8 px-2 text-xs",
38
+ lg: "h-10 px-4",
39
+ icon: "h-9 w-9",
40
+ "icon-xs": "h-7 w-7",
41
+ "icon-sm": "h-8 w-8",
42
+ "icon-lg": "h-10 w-10"
43
+ }
44
+ },
45
+ defaultVariants: {
46
+ variant: "default",
47
+ size: "default"
48
+ }
49
+ }
50
+ );
51
+ var Button = forwardRef(
52
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
53
+ const Comp = asChild ? Slot : "button";
54
+ return /* @__PURE__ */ jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
55
+ }
56
+ );
57
+ Button.displayName = "Button";
58
+
59
+ // src/primitives/input.tsx
60
+ import { forwardRef as forwardRef2 } from "react";
61
+ import { jsx as jsx2 } from "react/jsx-runtime";
62
+ var Input = forwardRef2(({ className, type, ...props }, ref) => {
63
+ return /* @__PURE__ */ jsx2(
64
+ "input",
65
+ {
66
+ type,
67
+ className: cn(
68
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
69
+ className
70
+ ),
71
+ ref,
72
+ ...props
73
+ }
74
+ );
75
+ });
76
+ Input.displayName = "Input";
77
+
78
+ // src/primitives/label.tsx
79
+ import { forwardRef as forwardRef3 } from "react";
80
+ import { jsx as jsx3 } from "react/jsx-runtime";
81
+ var Label = forwardRef3(
82
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
83
+ "label",
84
+ {
85
+ ref,
86
+ className: cn(
87
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
88
+ className
89
+ ),
90
+ ...props
91
+ }
92
+ )
93
+ );
94
+ Label.displayName = "Label";
95
+
96
+ // src/primitives/textarea.tsx
97
+ import { forwardRef as forwardRef4 } from "react";
98
+ import { jsx as jsx4 } from "react/jsx-runtime";
99
+ var Textarea = forwardRef4(
100
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
101
+ "textarea",
102
+ {
103
+ className: cn(
104
+ "flex min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
105
+ className
106
+ ),
107
+ ref,
108
+ ...props
109
+ }
110
+ )
111
+ );
112
+ Textarea.displayName = "Textarea";
113
+
114
+ // src/primitives/card.tsx
115
+ import { jsx as jsx5 } from "react/jsx-runtime";
116
+ var Card = ({ className, ...props }) => /* @__PURE__ */ jsx5("div", { className: cn("rounded-lg border bg-background text-foreground shadow-sm", className), ...props });
117
+ var CardHeader = ({
118
+ className,
119
+ ...props
120
+ }) => /* @__PURE__ */ jsx5("div", { className: cn("flex flex-col gap-1.5 p-6", className), ...props });
121
+ var CardTitle = ({ className, ...props }) => /* @__PURE__ */ jsx5("div", { className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props });
122
+ var CardDescription = ({
123
+ className,
124
+ ...props
125
+ }) => /* @__PURE__ */ jsx5("div", { className: cn("text-sm text-muted-foreground", className), ...props });
126
+ var CardContent = ({
127
+ className,
128
+ ...props
129
+ }) => /* @__PURE__ */ jsx5("div", { className: cn("p-6 pt-0", className), ...props });
130
+ var CardFooter = ({
131
+ className,
132
+ ...props
133
+ }) => /* @__PURE__ */ jsx5("div", { className: cn("flex items-center gap-2 p-6 pt-0", className), ...props });
134
+
135
+ // src/primitives/badge.tsx
136
+ import { cva as cva2 } from "class-variance-authority";
137
+ import { jsx as jsx6 } from "react/jsx-runtime";
138
+ var badgeVariants = cva2(
139
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring",
140
+ {
141
+ variants: {
142
+ variant: {
143
+ default: "border-transparent bg-primary text-primary-foreground",
144
+ secondary: "border-transparent bg-muted text-muted-foreground",
145
+ destructive: "border-transparent bg-destructive text-destructive-foreground",
146
+ outline: "border-input text-foreground",
147
+ // Amber warning tone — same fixed-palette choice as AlertBox/StatusBadge's
148
+ // warning tone, since the shared theme has no warning token yet.
149
+ warning: "border-transparent bg-amber-100 text-amber-900 dark:bg-amber-900/40 dark:text-amber-200"
150
+ }
151
+ },
152
+ defaultVariants: {
153
+ variant: "default"
154
+ }
155
+ }
156
+ );
157
+ var Badge = ({ className, variant, ...props }) => /* @__PURE__ */ jsx6("div", { className: cn(badgeVariants({ variant }), className), ...props });
158
+
159
+ // src/primitives/dialog.tsx
160
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
161
+ import { X } from "lucide-react";
162
+ import { forwardRef as forwardRef5 } from "react";
163
+ import { jsx as jsx7, jsxs } from "react/jsx-runtime";
164
+ var Dialog = DialogPrimitive.Root;
165
+ var DialogTrigger = DialogPrimitive.Trigger;
166
+ var DialogOverlay = forwardRef5(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
167
+ DialogPrimitive.Overlay,
168
+ {
169
+ ref,
170
+ className: cn("fixed inset-0 z-50 bg-black/50", className),
171
+ ...props
172
+ }
173
+ ));
174
+ DialogOverlay.displayName = "DialogOverlay";
175
+ var DialogContent = forwardRef5(({ className, children, showCloseButton = true, closeButtonTestId, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [
176
+ /* @__PURE__ */ jsx7(DialogOverlay, {}),
177
+ /* @__PURE__ */ jsxs(
178
+ DialogPrimitive.Content,
179
+ {
180
+ ref,
181
+ className: cn(
182
+ "fixed left-1/2 top-1/2 z-50 max-h-[85vh] w-full max-w-md -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-lg border bg-background p-6 shadow-lg",
183
+ className
184
+ ),
185
+ ...props,
186
+ children: [
187
+ children,
188
+ showCloseButton ? /* @__PURE__ */ jsxs(
189
+ DialogPrimitive.Close,
190
+ {
191
+ "data-testid": closeButtonTestId,
192
+ className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100",
193
+ children: [
194
+ /* @__PURE__ */ jsx7(X, { className: "h-4 w-4" }),
195
+ /* @__PURE__ */ jsx7("span", { className: "sr-only", children: "Close" })
196
+ ]
197
+ }
198
+ ) : null
199
+ ]
200
+ }
201
+ )
202
+ ] }));
203
+ DialogContent.displayName = "DialogContent";
204
+ var DialogHeader = ({
205
+ className,
206
+ ...props
207
+ }) => /* @__PURE__ */ jsx7("div", { className: cn("flex flex-col gap-1.5", className), ...props });
208
+ var DialogTitle = forwardRef5(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
209
+ DialogTitle.displayName = "DialogTitle";
210
+ var DialogDescription = forwardRef5(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
211
+ DialogPrimitive.Description,
212
+ {
213
+ ref,
214
+ className: cn("text-sm text-muted-foreground", className),
215
+ ...props
216
+ }
217
+ ));
218
+ DialogDescription.displayName = "DialogDescription";
219
+ var DialogFooter = ({
220
+ className,
221
+ ...props
222
+ }) => /* @__PURE__ */ jsx7("div", { className: cn("flex justify-end gap-2", className), ...props });
223
+
224
+ // src/primitives/popover.tsx
225
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
226
+ import { forwardRef as forwardRef6 } from "react";
227
+ import { jsx as jsx8 } from "react/jsx-runtime";
228
+ var Popover = PopoverPrimitive.Root;
229
+ var PopoverTrigger = PopoverPrimitive.Trigger;
230
+ var PopoverAnchor = PopoverPrimitive.Anchor;
231
+ var PopoverContent = forwardRef6(({ className, align = "start", sideOffset = 4, container, ...props }, ref) => /* @__PURE__ */ jsx8(PopoverPrimitive.Portal, { container, children: /* @__PURE__ */ jsx8(
232
+ PopoverPrimitive.Content,
233
+ {
234
+ ref,
235
+ align,
236
+ sideOffset,
237
+ className: cn(
238
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
239
+ className
240
+ ),
241
+ ...props
242
+ }
243
+ ) }));
244
+ PopoverContent.displayName = "PopoverContent";
245
+
246
+ // src/primitives/select.tsx
247
+ import * as SelectPrimitive from "@radix-ui/react-select";
248
+ import { Check, ChevronDown } from "lucide-react";
249
+ import { forwardRef as forwardRef7 } from "react";
250
+ import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
251
+ var Select = SelectPrimitive.Root;
252
+ var SelectGroup = SelectPrimitive.Group;
253
+ var SelectValue = SelectPrimitive.Value;
254
+ var SelectTrigger = forwardRef7(
255
+ ({ className, children, size = "default", ...props }, ref) => /* @__PURE__ */ jsxs2(
256
+ SelectPrimitive.Trigger,
257
+ {
258
+ ref,
259
+ className: cn(
260
+ "flex w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
261
+ size === "sm" ? "h-8" : "h-9",
262
+ className
263
+ ),
264
+ ...props,
265
+ children: [
266
+ children,
267
+ /* @__PURE__ */ jsx9(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx9(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
268
+ ]
269
+ }
270
+ )
271
+ );
272
+ SelectTrigger.displayName = "SelectTrigger";
273
+ var SelectContent = forwardRef7(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx9(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsx9(
274
+ SelectPrimitive.Content,
275
+ {
276
+ ref,
277
+ position,
278
+ className: cn(
279
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
280
+ position === "popper" && "translate-y-1",
281
+ className
282
+ ),
283
+ ...props,
284
+ children: /* @__PURE__ */ jsx9(SelectPrimitive.Viewport, { className: "p-1", children })
285
+ }
286
+ ) }));
287
+ SelectContent.displayName = "SelectContent";
288
+ var SelectItem = forwardRef7(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs2(
289
+ SelectPrimitive.Item,
290
+ {
291
+ ref,
292
+ className: cn(
293
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
294
+ className
295
+ ),
296
+ ...props,
297
+ children: [
298
+ /* @__PURE__ */ jsx9("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(Check, { className: "h-4 w-4" }) }) }),
299
+ /* @__PURE__ */ jsx9(SelectPrimitive.ItemText, { children })
300
+ ]
301
+ }
302
+ ));
303
+ SelectItem.displayName = "SelectItem";
304
+
305
+ // src/primitives/skeleton.tsx
306
+ import { jsx as jsx10 } from "react/jsx-runtime";
307
+ var Skeleton = ({ className, ...props }) => /* @__PURE__ */ jsx10("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
308
+
309
+ // src/primitives/checkbox.tsx
310
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
311
+ import { Check as Check2 } from "lucide-react";
312
+ import { forwardRef as forwardRef8 } from "react";
313
+ import { jsx as jsx11 } from "react/jsx-runtime";
314
+ var Checkbox = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
315
+ CheckboxPrimitive.Root,
316
+ {
317
+ ref,
318
+ className: cn(
319
+ "peer h-4 w-4 shrink-0 rounded-sm border border-input shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary",
320
+ className
321
+ ),
322
+ ...props,
323
+ children: /* @__PURE__ */ jsx11(CheckboxPrimitive.Indicator, { className: "flex items-center justify-center text-current", children: /* @__PURE__ */ jsx11(Check2, { className: "h-3.5 w-3.5" }) })
324
+ }
325
+ ));
326
+ Checkbox.displayName = "Checkbox";
327
+
328
+ // src/primitives/switch.tsx
329
+ import * as SwitchPrimitive from "@radix-ui/react-switch";
330
+ import { forwardRef as forwardRef9 } from "react";
331
+ import { jsx as jsx12 } from "react/jsx-runtime";
332
+ var Switch = forwardRef9(
333
+ ({ className, size = "default", ...props }, ref) => /* @__PURE__ */ jsx12(
334
+ SwitchPrimitive.Root,
335
+ {
336
+ ref,
337
+ className: cn(
338
+ "peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
339
+ size === "sm" ? "h-3.5 w-6" : "h-5 w-9",
340
+ className
341
+ ),
342
+ ...props,
343
+ children: /* @__PURE__ */ jsx12(
344
+ SwitchPrimitive.Thumb,
345
+ {
346
+ className: cn(
347
+ "pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=unchecked]:translate-x-0",
348
+ size === "sm" ? "h-3 w-3 data-[state=checked]:translate-x-2.5" : "h-4 w-4 data-[state=checked]:translate-x-4"
349
+ )
350
+ }
351
+ )
352
+ }
353
+ )
354
+ );
355
+ Switch.displayName = "Switch";
356
+
357
+ // src/primitives/tabs.tsx
358
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
359
+ import { forwardRef as forwardRef10 } from "react";
360
+ import { jsx as jsx13 } from "react/jsx-runtime";
361
+ var Tabs = TabsPrimitive.Root;
362
+ var TabsList = forwardRef10(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
363
+ TabsPrimitive.List,
364
+ {
365
+ ref,
366
+ className: cn(
367
+ "inline-flex h-9 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground",
368
+ className
369
+ ),
370
+ ...props
371
+ }
372
+ ));
373
+ TabsList.displayName = "TabsList";
374
+ var TabsTrigger = forwardRef10(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
375
+ TabsPrimitive.Trigger,
376
+ {
377
+ ref,
378
+ className: cn(
379
+ "inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-2 py-1 text-sm font-medium text-foreground/60 transition-all hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
380
+ className
381
+ ),
382
+ ...props
383
+ }
384
+ ));
385
+ TabsTrigger.displayName = "TabsTrigger";
386
+ var TabsContent = forwardRef10(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(TabsPrimitive.Content, { ref, className: cn("flex-1 outline-none", className), ...props }));
387
+ TabsContent.displayName = "TabsContent";
388
+
389
+ // src/primitives/separator.tsx
390
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
391
+ import { forwardRef as forwardRef11 } from "react";
392
+ import { jsx as jsx14 } from "react/jsx-runtime";
393
+ var Separator = forwardRef11(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx14(
394
+ SeparatorPrimitive.Root,
395
+ {
396
+ ref,
397
+ decorative,
398
+ orientation,
399
+ className: cn(
400
+ "shrink-0 bg-border",
401
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
402
+ className
403
+ ),
404
+ ...props
405
+ }
406
+ ));
407
+ Separator.displayName = "Separator";
408
+
409
+ // src/primitives/scroll-area.tsx
410
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
411
+ import { forwardRef as forwardRef12 } from "react";
412
+ import { jsx as jsx15, jsxs as jsxs3 } from "react/jsx-runtime";
413
+ var ScrollBar = forwardRef12(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx15(
414
+ ScrollAreaPrimitive.ScrollAreaScrollbar,
415
+ {
416
+ ref,
417
+ orientation,
418
+ className: cn(
419
+ "flex touch-none select-none transition-colors",
420
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
421
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
422
+ className
423
+ ),
424
+ ...props,
425
+ children: /* @__PURE__ */ jsx15(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
426
+ }
427
+ ));
428
+ ScrollBar.displayName = "ScrollBar";
429
+ var ScrollArea = forwardRef12(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs3(ScrollAreaPrimitive.Root, { ref, className: cn("relative overflow-hidden", className), ...props, children: [
430
+ /* @__PURE__ */ jsx15(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
431
+ /* @__PURE__ */ jsx15(ScrollBar, {}),
432
+ /* @__PURE__ */ jsx15(ScrollAreaPrimitive.Corner, {})
433
+ ] }));
434
+ ScrollArea.displayName = "ScrollArea";
435
+
436
+ // src/primitives/table.tsx
437
+ import { forwardRef as forwardRef13 } from "react";
438
+ import { jsx as jsx16 } from "react/jsx-runtime";
439
+ var Table = forwardRef13(
440
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16("div", { className: "relative w-full overflow-x-auto", children: /* @__PURE__ */ jsx16("table", { ref, className: cn("w-full caption-bottom text-sm", className), ...props }) })
441
+ );
442
+ Table.displayName = "Table";
443
+ var TableHeader = forwardRef13(
444
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16("thead", { ref, className: cn("[&_tr]:border-b", className), ...props })
445
+ );
446
+ TableHeader.displayName = "TableHeader";
447
+ var TableBody = forwardRef13(
448
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props })
449
+ );
450
+ TableBody.displayName = "TableBody";
451
+ var TableFooter = forwardRef13(
452
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
453
+ "tfoot",
454
+ {
455
+ ref,
456
+ className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className),
457
+ ...props
458
+ }
459
+ )
460
+ );
461
+ TableFooter.displayName = "TableFooter";
462
+ var TableRow = forwardRef13(
463
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
464
+ "tr",
465
+ {
466
+ ref,
467
+ className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
468
+ ...props
469
+ }
470
+ )
471
+ );
472
+ TableRow.displayName = "TableRow";
473
+ var TableHead = forwardRef13(
474
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
475
+ "th",
476
+ {
477
+ ref,
478
+ className: cn(
479
+ "h-10 whitespace-nowrap px-2 text-left align-middle font-medium text-foreground [&:has([role=checkbox])]:pr-0",
480
+ className
481
+ ),
482
+ ...props
483
+ }
484
+ )
485
+ );
486
+ TableHead.displayName = "TableHead";
487
+ var TableCell = forwardRef13(
488
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16(
489
+ "td",
490
+ {
491
+ ref,
492
+ className: cn("whitespace-nowrap p-2 align-middle [&:has([role=checkbox])]:pr-0", className),
493
+ ...props
494
+ }
495
+ )
496
+ );
497
+ TableCell.displayName = "TableCell";
498
+ var TableCaption = forwardRef13(
499
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx16("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })
500
+ );
501
+ TableCaption.displayName = "TableCaption";
502
+
503
+ // src/primitives/dropdown-menu.tsx
504
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
505
+ import { Check as Check3, ChevronRight, Circle } from "lucide-react";
506
+ import { forwardRef as forwardRef14 } from "react";
507
+ import { jsx as jsx17, jsxs as jsxs4 } from "react/jsx-runtime";
508
+ var DropdownMenu = DropdownMenuPrimitive.Root;
509
+ var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
510
+ var DropdownMenuGroup = DropdownMenuPrimitive.Group;
511
+ var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
512
+ var DropdownMenuSub = DropdownMenuPrimitive.Sub;
513
+ var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
514
+ var DropdownMenuContent = forwardRef14(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx17(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx17(
515
+ DropdownMenuPrimitive.Content,
516
+ {
517
+ ref,
518
+ sideOffset,
519
+ className: cn(
520
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
521
+ className
522
+ ),
523
+ ...props
524
+ }
525
+ ) }));
526
+ DropdownMenuContent.displayName = "DropdownMenuContent";
527
+ var DropdownMenuItem = forwardRef14(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx17(
528
+ DropdownMenuPrimitive.Item,
529
+ {
530
+ ref,
531
+ className: cn(
532
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
533
+ danger && "text-destructive focus:bg-destructive/10 focus:text-destructive",
534
+ className
535
+ ),
536
+ ...props
537
+ }
538
+ ));
539
+ DropdownMenuItem.displayName = "DropdownMenuItem";
540
+ var DropdownMenuCheckboxItem = forwardRef14(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs4(
541
+ DropdownMenuPrimitive.CheckboxItem,
542
+ {
543
+ ref,
544
+ checked,
545
+ className: cn(
546
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
547
+ className
548
+ ),
549
+ ...props,
550
+ children: [
551
+ /* @__PURE__ */ jsx17("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx17(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17(Check3, { className: "h-4 w-4" }) }) }),
552
+ children
553
+ ]
554
+ }
555
+ ));
556
+ DropdownMenuCheckboxItem.displayName = "DropdownMenuCheckboxItem";
557
+ var DropdownMenuRadioItem = forwardRef14(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
558
+ DropdownMenuPrimitive.RadioItem,
559
+ {
560
+ ref,
561
+ className: cn(
562
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
563
+ className
564
+ ),
565
+ ...props,
566
+ children: [
567
+ /* @__PURE__ */ jsx17("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx17(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17(Circle, { className: "h-2 w-2 fill-current" }) }) }),
568
+ children
569
+ ]
570
+ }
571
+ ));
572
+ DropdownMenuRadioItem.displayName = "DropdownMenuRadioItem";
573
+ var DropdownMenuSeparator = forwardRef14(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
574
+ DropdownMenuPrimitive.Separator,
575
+ {
576
+ ref,
577
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
578
+ ...props
579
+ }
580
+ ));
581
+ DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
582
+ var DropdownMenuLabel = forwardRef14(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
583
+ DropdownMenuPrimitive.Label,
584
+ {
585
+ ref,
586
+ className: cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", className),
587
+ ...props
588
+ }
589
+ ));
590
+ DropdownMenuLabel.displayName = "DropdownMenuLabel";
591
+ var DropdownMenuShortcut = ({
592
+ className,
593
+ ...props
594
+ }) => /* @__PURE__ */ jsx17("span", { className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className), ...props });
595
+ var DropdownMenuSubTrigger = forwardRef14(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
596
+ DropdownMenuPrimitive.SubTrigger,
597
+ {
598
+ ref,
599
+ className: cn(
600
+ "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
601
+ className
602
+ ),
603
+ ...props,
604
+ children: [
605
+ children,
606
+ /* @__PURE__ */ jsx17(ChevronRight, { className: "ml-auto h-4 w-4" })
607
+ ]
608
+ }
609
+ ));
610
+ DropdownMenuSubTrigger.displayName = "DropdownMenuSubTrigger";
611
+ var DropdownMenuSubContent = forwardRef14(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
612
+ DropdownMenuPrimitive.SubContent,
613
+ {
614
+ ref,
615
+ className: cn(
616
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
617
+ className
618
+ ),
619
+ ...props
620
+ }
621
+ ));
622
+ DropdownMenuSubContent.displayName = "DropdownMenuSubContent";
623
+
624
+ // src/primitives/sheet.tsx
625
+ import { cva as cva3 } from "class-variance-authority";
626
+ import * as DialogPrimitive2 from "@radix-ui/react-dialog";
627
+ import { X as X2 } from "lucide-react";
628
+ import { forwardRef as forwardRef15 } from "react";
629
+ import { jsx as jsx18, jsxs as jsxs5 } from "react/jsx-runtime";
630
+ var Sheet = DialogPrimitive2.Root;
631
+ var SheetTrigger = DialogPrimitive2.Trigger;
632
+ var SheetClose = DialogPrimitive2.Close;
633
+ var SheetPortal = DialogPrimitive2.Portal;
634
+ var SheetOverlay = forwardRef15(({ className, ...props }, ref) => /* @__PURE__ */ jsx18(
635
+ DialogPrimitive2.Overlay,
636
+ {
637
+ ref,
638
+ className: cn("fixed inset-0 z-50 bg-black/50", className),
639
+ ...props
640
+ }
641
+ ));
642
+ SheetOverlay.displayName = "SheetOverlay";
643
+ var sheetVariants = cva3(
644
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition-transform duration-300 ease-in-out",
645
+ {
646
+ variants: {
647
+ side: {
648
+ top: "inset-x-0 top-0 border-b data-[state=closed]:-translate-y-full data-[state=open]:translate-y-0",
649
+ bottom: "inset-x-0 bottom-0 border-t data-[state=closed]:translate-y-full data-[state=open]:translate-y-0",
650
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:-translate-x-full data-[state=open]:translate-x-0 sm:max-w-sm",
651
+ right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:translate-x-full data-[state=open]:translate-x-0 sm:max-w-sm"
652
+ }
653
+ },
654
+ defaultVariants: {
655
+ side: "right"
656
+ }
657
+ }
658
+ );
659
+ var SheetContent = forwardRef15(
660
+ ({ side = "right", className, children, ...props }, ref) => /* @__PURE__ */ jsxs5(SheetPortal, { children: [
661
+ /* @__PURE__ */ jsx18(SheetOverlay, {}),
662
+ /* @__PURE__ */ jsxs5(DialogPrimitive2.Content, { ref, className: cn(sheetVariants({ side }), className), ...props, children: [
663
+ children,
664
+ /* @__PURE__ */ jsxs5(DialogPrimitive2.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
665
+ /* @__PURE__ */ jsx18(X2, { className: "h-4 w-4" }),
666
+ /* @__PURE__ */ jsx18("span", { className: "sr-only", children: "Close" })
667
+ ] })
668
+ ] })
669
+ ] })
670
+ );
671
+ SheetContent.displayName = "SheetContent";
672
+ var SheetHeader = ({
673
+ className,
674
+ ...props
675
+ }) => /* @__PURE__ */ jsx18("div", { className: cn("flex flex-col gap-1.5", className), ...props });
676
+ var SheetFooter = ({
677
+ className,
678
+ ...props
679
+ }) => /* @__PURE__ */ jsx18("div", { className: cn("flex justify-end gap-2", className), ...props });
680
+ var SheetTitle = forwardRef15(({ className, ...props }, ref) => /* @__PURE__ */ jsx18(DialogPrimitive2.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
681
+ SheetTitle.displayName = "SheetTitle";
682
+ var SheetDescription = forwardRef15(({ className, ...props }, ref) => /* @__PURE__ */ jsx18(
683
+ DialogPrimitive2.Description,
684
+ {
685
+ ref,
686
+ className: cn("text-sm text-muted-foreground", className),
687
+ ...props
688
+ }
689
+ ));
690
+ SheetDescription.displayName = "SheetDescription";
691
+
692
+ // src/primitives/tooltip.tsx
693
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
694
+ import { forwardRef as forwardRef16 } from "react";
695
+ import { jsx as jsx19 } from "react/jsx-runtime";
696
+ var TooltipProvider = TooltipPrimitive.Provider;
697
+ var Tooltip = TooltipPrimitive.Root;
698
+ var TooltipTrigger = TooltipPrimitive.Trigger;
699
+ var TooltipContent = forwardRef16(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx19(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx19(
700
+ TooltipPrimitive.Content,
701
+ {
702
+ ref,
703
+ sideOffset,
704
+ className: cn(
705
+ "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md",
706
+ className
707
+ ),
708
+ ...props
709
+ }
710
+ ) }));
711
+ TooltipContent.displayName = "TooltipContent";
712
+
713
+ // src/patterns/modal/Modal.tsx
714
+ import { jsx as jsx20, jsxs as jsxs6 } from "react/jsx-runtime";
715
+ var Modal = ({
716
+ open,
717
+ onOpenChange,
718
+ title,
719
+ description,
720
+ children,
721
+ footer,
722
+ className
723
+ }) => /* @__PURE__ */ jsx20(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs6(DialogContent, { className, children: [
724
+ /* @__PURE__ */ jsxs6(DialogHeader, { children: [
725
+ /* @__PURE__ */ jsx20(DialogTitle, { children: title }),
726
+ description ? /* @__PURE__ */ jsx20(DialogDescription, { children: description }) : null
727
+ ] }),
728
+ children,
729
+ footer ? /* @__PURE__ */ jsx20(DialogFooter, { children: footer }) : null
730
+ ] }) });
731
+
732
+ // src/patterns/modal/ConfirmDialog.tsx
733
+ import { useState } from "react";
734
+ import { Fragment, jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
735
+ var ConfirmDialog = ({
736
+ open,
737
+ onOpenChange,
738
+ title,
739
+ description,
740
+ confirmLabel = "Confirm",
741
+ cancelLabel = "Cancel",
742
+ onConfirm,
743
+ variant = "default",
744
+ confirmTestId,
745
+ cancelTestId
746
+ }) => {
747
+ const [pending, setPending] = useState(false);
748
+ const handleCancel = () => {
749
+ onOpenChange(false);
750
+ };
751
+ const handleConfirm = async () => {
752
+ setPending(true);
753
+ try {
754
+ await onConfirm();
755
+ onOpenChange(false);
756
+ } catch (error) {
757
+ console.error(error);
758
+ } finally {
759
+ setPending(false);
760
+ }
761
+ };
762
+ return /* @__PURE__ */ jsx21(
763
+ Modal,
764
+ {
765
+ open,
766
+ onOpenChange,
767
+ title,
768
+ description,
769
+ footer: /* @__PURE__ */ jsxs7(Fragment, { children: [
770
+ /* @__PURE__ */ jsx21(Button, { variant: "outline", onClick: handleCancel, disabled: pending, "data-testid": cancelTestId, children: cancelLabel }),
771
+ /* @__PURE__ */ jsx21(
772
+ Button,
773
+ {
774
+ variant: variant === "destructive" ? "destructive" : "default",
775
+ onClick: handleConfirm,
776
+ disabled: pending,
777
+ "data-testid": confirmTestId,
778
+ children: pending ? `${confirmLabel}\u2026` : confirmLabel
779
+ }
780
+ )
781
+ ] }),
782
+ children: null
783
+ }
784
+ );
785
+ };
786
+
787
+ // src/patterns/modal/FormModal.tsx
788
+ import { useState as useState2 } from "react";
789
+ import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
790
+ var FormModal = ({
791
+ open,
792
+ onOpenChange,
793
+ title,
794
+ description,
795
+ children,
796
+ onSubmit,
797
+ submitLabel = "Save",
798
+ cancelLabel = "Cancel",
799
+ submitDisabled = false,
800
+ className,
801
+ submitTestId,
802
+ cancelTestId
803
+ }) => {
804
+ const [pending, setPending] = useState2(false);
805
+ const handleSubmit = async (event) => {
806
+ event.preventDefault();
807
+ setPending(true);
808
+ try {
809
+ await onSubmit();
810
+ } finally {
811
+ setPending(false);
812
+ }
813
+ };
814
+ const handleCancel = () => {
815
+ onOpenChange(false);
816
+ };
817
+ return /* @__PURE__ */ jsx22(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx22(DialogContent, { className, children: /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, children: [
818
+ /* @__PURE__ */ jsxs8(DialogHeader, { children: [
819
+ /* @__PURE__ */ jsx22(DialogTitle, { children: title }),
820
+ description ? /* @__PURE__ */ jsx22(DialogDescription, { children: description }) : null
821
+ ] }),
822
+ children,
823
+ /* @__PURE__ */ jsxs8(DialogFooter, { children: [
824
+ /* @__PURE__ */ jsx22(
825
+ Button,
826
+ {
827
+ type: "button",
828
+ variant: "outline",
829
+ onClick: handleCancel,
830
+ disabled: pending,
831
+ "data-testid": cancelTestId,
832
+ children: cancelLabel
833
+ }
834
+ ),
835
+ /* @__PURE__ */ jsx22(Button, { type: "submit", disabled: pending || submitDisabled, "data-testid": submitTestId, children: pending ? `${submitLabel}\u2026` : submitLabel })
836
+ ] })
837
+ ] }) }) });
838
+ };
839
+
840
+ // src/patterns/form-field/FormField.tsx
841
+ import { cloneElement, isValidElement, useId } from "react";
842
+ import { jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
843
+ function FormField({
844
+ label,
845
+ htmlFor,
846
+ error,
847
+ description,
848
+ required,
849
+ children,
850
+ className
851
+ }) {
852
+ const generatedId = useId();
853
+ const resolvedId = htmlFor ?? generatedId;
854
+ const isSingleElement = isValidElement(children);
855
+ const existingChildId = isSingleElement ? children.props.id : void 0;
856
+ const effectiveId = existingChildId ?? resolvedId;
857
+ const message = error ?? description;
858
+ const messageId = message ? `${effectiveId}-message` : void 0;
859
+ const child = isSingleElement ? cloneElement(
860
+ children,
861
+ {
862
+ id: effectiveId,
863
+ ...error ? { "aria-invalid": true } : {},
864
+ ...messageId ? { "aria-describedby": messageId } : {}
865
+ }
866
+ ) : children;
867
+ return /* @__PURE__ */ jsxs9("div", { className: cn("flex flex-col gap-1.5", className), children: [
868
+ /* @__PURE__ */ jsxs9(Label, { htmlFor: effectiveId, children: [
869
+ label,
870
+ required ? /* @__PURE__ */ jsxs9("span", { className: "text-destructive", "aria-hidden": "true", children: [
871
+ " ",
872
+ "*"
873
+ ] }) : null
874
+ ] }),
875
+ child,
876
+ error ? /* @__PURE__ */ jsx23("p", { id: messageId, className: "text-sm text-destructive", children: error }) : description ? /* @__PURE__ */ jsx23("p", { id: messageId, className: "text-sm text-muted-foreground", children: description }) : null
877
+ ] });
878
+ }
879
+
880
+ // src/patterns/combobox/Combobox.tsx
881
+ import { Check as Check4, ChevronsUpDown, X as X3 } from "lucide-react";
882
+ import { useEffect, useMemo, useRef, useState as useState3 } from "react";
883
+ import { jsx as jsx24, jsxs as jsxs10 } from "react/jsx-runtime";
884
+ function groupMembers(options, group) {
885
+ return options.filter((o) => o.group === group && !o.disabled).map((o) => o.value);
886
+ }
887
+ function groupCheckState(options, selectedValues, group) {
888
+ const members = groupMembers(options, group);
889
+ const selectedCount = members.filter((v) => selectedValues.includes(v)).length;
890
+ if (selectedCount === 0) return "unchecked";
891
+ return selectedCount === members.length ? "checked" : "indeterminate";
892
+ }
893
+ function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
894
+ const selectedSet = new Set(selectedValues);
895
+ const groupsPresent = Array.from(
896
+ new Set(options.filter((o) => selectedSet.has(o.value) && o.group).map((o) => o.group))
897
+ );
898
+ if (groupsPresent.length === 0) return null;
899
+ const coveredValues = /* @__PURE__ */ new Set();
900
+ const fullyCoveredGroups = [];
901
+ for (const group of groupsPresent) {
902
+ const members = groupMembers(options, group);
903
+ if (members.length > 0 && members.every((v) => selectedSet.has(v))) {
904
+ fullyCoveredGroups.push(group);
905
+ for (const v of members) coveredValues.add(v);
906
+ }
907
+ }
908
+ const hasLeftover = selectedValues.some((v) => !coveredValues.has(v));
909
+ if (fullyCoveredGroups.length === 0 || hasLeftover) return null;
910
+ return fullyCoveredGroups.map(groupLabel).join(", ");
911
+ }
912
+ function buildOptionRows(visibleOptions) {
913
+ let lastGroup;
914
+ return visibleOptions.map((option, index) => {
915
+ const headerGroup = option.group && option.group !== lastGroup ? option.group : void 0;
916
+ lastGroup = option.group ?? lastGroup;
917
+ return { option, index, headerGroup };
918
+ });
919
+ }
920
+ function Combobox(props) {
921
+ const {
922
+ options,
923
+ placeholder = "Select\u2026",
924
+ searchPlaceholder = "Search\u2026",
925
+ emptyMessage = "No matches.",
926
+ disabled,
927
+ loading = false,
928
+ loadingMessage = "Loading\u2026",
929
+ clearable = true,
930
+ className,
931
+ id,
932
+ groupLabels,
933
+ container,
934
+ onSearchChange,
935
+ selectedLabel
936
+ } = props;
937
+ const testId = props["data-testid"];
938
+ const [open, setOpen] = useState3(false);
939
+ const [search, setSearch] = useState3("");
940
+ const [activeIndex, setActiveIndex] = useState3(0);
941
+ const inputRef = useRef(null);
942
+ const selectedValues = useMemo(
943
+ () => props.multiple ? props.value : props.value ? [props.value] : [],
944
+ [props.multiple, props.value]
945
+ );
946
+ const selectedOptions = useMemo(
947
+ () => options.filter((option) => selectedValues.includes(option.value)),
948
+ [options, selectedValues]
949
+ );
950
+ const visibleOptions = useMemo(() => {
951
+ if (onSearchChange) return options;
952
+ const term = search.trim().toLowerCase();
953
+ if (!term) return options;
954
+ return options.filter((option) => option.label.toLowerCase().includes(term));
955
+ }, [options, search, onSearchChange]);
956
+ useEffect(() => {
957
+ if (!open) return;
958
+ setSearch("");
959
+ onSearchChange?.("");
960
+ const firstSelected = selectedValues[0];
961
+ const selectedIdx = firstSelected ? options.findIndex((option) => option.value === firstSelected) : -1;
962
+ setActiveIndex(selectedIdx >= 0 ? selectedIdx : 0);
963
+ inputRef.current?.focus();
964
+ }, [open]);
965
+ useEffect(() => {
966
+ setActiveIndex((i) => Math.min(i, Math.max(visibleOptions.length - 1, 0)));
967
+ }, [visibleOptions.length]);
968
+ function selectOption(option) {
969
+ if (option.disabled) return;
970
+ if (props.multiple) {
971
+ const next = props.value.includes(option.value) ? props.value.filter((v) => v !== option.value) : [...props.value, option.value];
972
+ props.onChange(next);
973
+ return;
974
+ }
975
+ props.onChange(option.value);
976
+ setOpen(false);
977
+ }
978
+ function handleKeyDown(event) {
979
+ if (event.key === "ArrowDown") {
980
+ event.preventDefault();
981
+ setActiveIndex((i) => Math.min(i + 1, visibleOptions.length - 1));
982
+ } else if (event.key === "ArrowUp") {
983
+ event.preventDefault();
984
+ setActiveIndex((i) => Math.max(i - 1, 0));
985
+ } else if (event.key === "Enter") {
986
+ event.preventDefault();
987
+ const option = visibleOptions[activeIndex];
988
+ if (option) selectOption(option);
989
+ }
990
+ }
991
+ const groupLabel = (group) => groupLabels?.[group] ?? group;
992
+ function toggleGroup(group) {
993
+ if (!props.multiple) return;
994
+ const members = groupMembers(options, group);
995
+ const next = groupCheckState(options, selectedValues, group) === "checked" ? props.value.filter((v) => !members.includes(v)) : Array.from(/* @__PURE__ */ new Set([...props.value, ...members]));
996
+ props.onChange(next);
997
+ }
998
+ const triggerLabel = loading ? loadingMessage : selectedOptions.length === 0 ? selectedValues.length > 0 ? selectedLabel ?? placeholder : placeholder : selectedOptions.length === 1 ? selectedOptions[0]?.label ?? placeholder : (props.multiple ? resolveGroupTriggerLabel(options, selectedValues, groupLabel) : null) ?? `${selectedOptions.length} selected`;
999
+ const optionRows = useMemo(() => buildOptionRows(visibleOptions), [visibleOptions]);
1000
+ return /* @__PURE__ */ jsxs10(Popover, { open, onOpenChange: setOpen, children: [
1001
+ /* @__PURE__ */ jsx24(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs10(
1002
+ Button,
1003
+ {
1004
+ id,
1005
+ "data-testid": testId,
1006
+ type: "button",
1007
+ variant: "outline",
1008
+ role: "combobox",
1009
+ "aria-expanded": open,
1010
+ disabled: disabled || loading,
1011
+ className: cn(
1012
+ "w-full justify-between font-normal",
1013
+ (selectedOptions.length === 0 || loading) && "text-muted-foreground",
1014
+ className
1015
+ ),
1016
+ children: [
1017
+ /* @__PURE__ */ jsx24("span", { className: "truncate", children: triggerLabel }),
1018
+ /* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-1", children: [
1019
+ clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */ jsx24(
1020
+ "span",
1021
+ {
1022
+ role: "button",
1023
+ "aria-label": "Clear selection",
1024
+ tabIndex: -1,
1025
+ className: "rounded-sm p-0.5 opacity-60 hover:bg-accent hover:opacity-100",
1026
+ onClick: (event) => {
1027
+ event.stopPropagation();
1028
+ if (props.multiple) {
1029
+ props.onChange([]);
1030
+ } else {
1031
+ props.onChange(null);
1032
+ }
1033
+ },
1034
+ children: /* @__PURE__ */ jsx24(X3, { className: "h-3.5 w-3.5" })
1035
+ }
1036
+ ) : null,
1037
+ /* @__PURE__ */ jsx24(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": "true" })
1038
+ ] })
1039
+ ]
1040
+ }
1041
+ ) }),
1042
+ /* @__PURE__ */ jsx24(
1043
+ PopoverContent,
1044
+ {
1045
+ align: "start",
1046
+ container,
1047
+ className: "w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
1048
+ onOpenAutoFocus: (event) => event.preventDefault(),
1049
+ children: /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
1050
+ /* @__PURE__ */ jsx24(
1051
+ Input,
1052
+ {
1053
+ ref: inputRef,
1054
+ value: search,
1055
+ onChange: (event) => {
1056
+ setSearch(event.target.value);
1057
+ onSearchChange?.(event.target.value);
1058
+ },
1059
+ onKeyDown: handleKeyDown,
1060
+ placeholder: searchPlaceholder,
1061
+ "aria-label": searchPlaceholder
1062
+ }
1063
+ ),
1064
+ /* @__PURE__ */ jsx24("div", { role: "listbox", className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: optionRows.length === 0 ? /* @__PURE__ */ jsx24("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : optionRows.map(({ option, index, headerGroup }) => {
1065
+ const isSelected = selectedValues.includes(option.value);
1066
+ const isActive = index === activeIndex;
1067
+ const headerState = headerGroup ? groupCheckState(options, selectedValues, headerGroup) : void 0;
1068
+ return /* @__PURE__ */ jsxs10("div", { children: [
1069
+ headerGroup ? /* @__PURE__ */ jsxs10(
1070
+ "div",
1071
+ {
1072
+ "data-group-header": true,
1073
+ className: "mt-1 flex items-center gap-2 rounded-sm bg-muted/50 px-2 py-1.5 text-sm font-semibold text-foreground first:mt-0",
1074
+ children: [
1075
+ props.multiple ? /* @__PURE__ */ jsx24(
1076
+ "input",
1077
+ {
1078
+ type: "checkbox",
1079
+ checked: headerState === "checked",
1080
+ ref: (el) => {
1081
+ if (el) el.indeterminate = headerState === "indeterminate";
1082
+ },
1083
+ onChange: () => toggleGroup(headerGroup),
1084
+ onClick: (event) => event.stopPropagation(),
1085
+ className: "h-3.5 w-3.5 shrink-0 accent-primary",
1086
+ "aria-label": `Select all of ${groupLabel(headerGroup)}`
1087
+ }
1088
+ ) : null,
1089
+ /* @__PURE__ */ jsx24("span", { className: "truncate", children: groupLabel(headerGroup) })
1090
+ ]
1091
+ }
1092
+ ) : null,
1093
+ /* @__PURE__ */ jsxs10(
1094
+ "button",
1095
+ {
1096
+ type: "button",
1097
+ role: "option",
1098
+ "aria-selected": isSelected,
1099
+ disabled: option.disabled,
1100
+ "data-testid": testId ? `${testId}-option` : void 0,
1101
+ "data-option-value": option.value,
1102
+ onMouseEnter: () => setActiveIndex(index),
1103
+ onClick: () => selectOption(option),
1104
+ className: cn(
1105
+ "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none disabled:pointer-events-none disabled:opacity-50",
1106
+ isActive && "bg-accent text-accent-foreground"
1107
+ ),
1108
+ children: [
1109
+ props.multiple ? /* @__PURE__ */ jsx24(
1110
+ "input",
1111
+ {
1112
+ type: "checkbox",
1113
+ checked: isSelected,
1114
+ readOnly: true,
1115
+ tabIndex: -1,
1116
+ className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
1117
+ }
1118
+ ) : /* @__PURE__ */ jsx24(Check4, { className: cn("h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0") }),
1119
+ /* @__PURE__ */ jsx24("span", { className: "truncate", children: option.label })
1120
+ ]
1121
+ }
1122
+ )
1123
+ ] }, option.value);
1124
+ }) })
1125
+ ] })
1126
+ }
1127
+ )
1128
+ ] });
1129
+ }
1130
+
1131
+ // src/patterns/alert-box/AlertBox.tsx
1132
+ import { AlertCircle, AlertTriangle, CheckCircle2, Info } from "lucide-react";
1133
+ import { jsx as jsx25, jsxs as jsxs11 } from "react/jsx-runtime";
1134
+ var variantStyles = {
1135
+ error: "border-destructive bg-destructive/10 text-destructive",
1136
+ warning: "border-amber-500 bg-amber-500/10 text-amber-700 dark:text-amber-300",
1137
+ info: "border-sky-500 bg-sky-500/10 text-sky-700 dark:text-sky-300",
1138
+ success: "border-emerald-500 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
1139
+ };
1140
+ var defaultIcons = {
1141
+ error: /* @__PURE__ */ jsx25(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1142
+ warning: /* @__PURE__ */ jsx25(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1143
+ info: /* @__PURE__ */ jsx25(Info, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1144
+ success: /* @__PURE__ */ jsx25(CheckCircle2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
1145
+ };
1146
+ var AlertBox = ({ variant, title, children, className, icon }) => {
1147
+ const resolvedIcon = icon === void 0 ? defaultIcons[variant] : icon;
1148
+ return /* @__PURE__ */ jsx25("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-row items-start gap-2", children: [
1149
+ resolvedIcon != null && /* @__PURE__ */ jsx25("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
1150
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-1", children: [
1151
+ title && /* @__PURE__ */ jsx25("p", { className: "font-bold", children: title }),
1152
+ /* @__PURE__ */ jsx25("div", { children })
1153
+ ] })
1154
+ ] }) });
1155
+ };
1156
+
1157
+ // src/patterns/status-badge/StatusBadge.tsx
1158
+ import { jsx as jsx26 } from "react/jsx-runtime";
1159
+ var TONE_CLASSES = {
1160
+ success: "border-transparent bg-emerald-500/15 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300",
1161
+ warning: "border-transparent bg-amber-500/15 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300",
1162
+ info: "border-transparent bg-sky-500/15 text-sky-700 dark:bg-sky-500/20 dark:text-sky-300",
1163
+ error: "border-transparent bg-destructive/15 text-destructive",
1164
+ neutral: "border-transparent bg-muted text-muted-foreground"
1165
+ };
1166
+ var DEFAULT_TONE_MAP = {
1167
+ active: "success",
1168
+ approved: "success",
1169
+ completed: "success",
1170
+ paid: "success",
1171
+ done: "success",
1172
+ pending: "warning",
1173
+ draft: "warning",
1174
+ in_review: "warning",
1175
+ inreview: "warning",
1176
+ rejected: "error",
1177
+ failed: "error",
1178
+ cancelled: "error",
1179
+ canceled: "error",
1180
+ overdue: "error",
1181
+ inactive: "neutral",
1182
+ archived: "neutral",
1183
+ new: "info"
1184
+ };
1185
+ function deriveLabel(status) {
1186
+ return status.replace(/[_-]+/g, " ").trim().split(/\s+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1187
+ }
1188
+ function resolveTone(status, tone, toneMap) {
1189
+ if (tone) {
1190
+ return tone;
1191
+ }
1192
+ const key = status.toLowerCase();
1193
+ if (toneMap) {
1194
+ for (const [candidate, candidateTone] of Object.entries(toneMap)) {
1195
+ if (candidate.toLowerCase() === key) {
1196
+ return candidateTone;
1197
+ }
1198
+ }
1199
+ }
1200
+ return DEFAULT_TONE_MAP[key] ?? "neutral";
1201
+ }
1202
+ var StatusBadge = ({ status, label, tone, toneMap, className, ...props }) => {
1203
+ const resolvedTone = resolveTone(status, tone, toneMap);
1204
+ const displayLabel = label ?? deriveLabel(status);
1205
+ return /* @__PURE__ */ jsx26(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
1206
+ };
1207
+
1208
+ // src/patterns/loading-spinner/LoadingSpinner.tsx
1209
+ import { cva as cva4 } from "class-variance-authority";
1210
+ import { Loader2 } from "lucide-react";
1211
+ import { jsx as jsx27, jsxs as jsxs12 } from "react/jsx-runtime";
1212
+ var loadingSpinnerIconVariants = cva4("animate-spin text-muted-foreground", {
1213
+ variants: {
1214
+ size: {
1215
+ sm: "h-3.5 w-3.5",
1216
+ default: "h-4 w-4",
1217
+ lg: "h-6 w-6"
1218
+ }
1219
+ },
1220
+ defaultVariants: {
1221
+ size: "default"
1222
+ }
1223
+ });
1224
+ var LoadingSpinner = ({
1225
+ size,
1226
+ label,
1227
+ className,
1228
+ ...props
1229
+ }) => /* @__PURE__ */ jsxs12("span", { role: "status", className: cn("inline-flex items-center gap-2", className), ...props, children: [
1230
+ /* @__PURE__ */ jsx27(Loader2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
1231
+ label ? /* @__PURE__ */ jsx27("span", { children: label }) : null
1232
+ ] });
1233
+ var LoadingOverlay = ({
1234
+ size = "lg",
1235
+ label,
1236
+ className,
1237
+ ...props
1238
+ }) => /* @__PURE__ */ jsx27(
1239
+ "div",
1240
+ {
1241
+ className: cn("flex h-full min-h-32 w-full items-center justify-center", className),
1242
+ ...props,
1243
+ children: /* @__PURE__ */ jsx27(LoadingSpinner, { size, label })
1244
+ }
1245
+ );
1246
+ export {
1247
+ AlertBox,
1248
+ Badge,
1249
+ Button,
1250
+ Card,
1251
+ CardContent,
1252
+ CardDescription,
1253
+ CardFooter,
1254
+ CardHeader,
1255
+ CardTitle,
1256
+ Checkbox,
1257
+ Combobox,
1258
+ ConfirmDialog,
1259
+ Dialog,
1260
+ DialogContent,
1261
+ DialogDescription,
1262
+ DialogFooter,
1263
+ DialogHeader,
1264
+ DialogTitle,
1265
+ DialogTrigger,
1266
+ DropdownMenu,
1267
+ DropdownMenuCheckboxItem,
1268
+ DropdownMenuContent,
1269
+ DropdownMenuGroup,
1270
+ DropdownMenuItem,
1271
+ DropdownMenuLabel,
1272
+ DropdownMenuPortal,
1273
+ DropdownMenuRadioGroup,
1274
+ DropdownMenuRadioItem,
1275
+ DropdownMenuSeparator,
1276
+ DropdownMenuShortcut,
1277
+ DropdownMenuSub,
1278
+ DropdownMenuSubContent,
1279
+ DropdownMenuSubTrigger,
1280
+ DropdownMenuTrigger,
1281
+ FormField,
1282
+ FormModal,
1283
+ Input,
1284
+ Label,
1285
+ LoadingOverlay,
1286
+ LoadingSpinner,
1287
+ Modal,
1288
+ Popover,
1289
+ PopoverAnchor,
1290
+ PopoverContent,
1291
+ PopoverTrigger,
1292
+ ScrollArea,
1293
+ ScrollBar,
1294
+ Select,
1295
+ SelectContent,
1296
+ SelectGroup,
1297
+ SelectItem,
1298
+ SelectTrigger,
1299
+ SelectValue,
1300
+ Separator,
1301
+ Sheet,
1302
+ SheetClose,
1303
+ SheetContent,
1304
+ SheetDescription,
1305
+ SheetFooter,
1306
+ SheetHeader,
1307
+ SheetPortal,
1308
+ SheetTitle,
1309
+ SheetTrigger,
1310
+ Skeleton,
1311
+ StatusBadge,
1312
+ Switch,
1313
+ Table,
1314
+ TableBody,
1315
+ TableCaption,
1316
+ TableCell,
1317
+ TableFooter,
1318
+ TableHead,
1319
+ TableHeader,
1320
+ TableRow,
1321
+ Tabs,
1322
+ TabsContent,
1323
+ TabsList,
1324
+ TabsTrigger,
1325
+ Textarea,
1326
+ Tooltip,
1327
+ TooltipContent,
1328
+ TooltipProvider,
1329
+ TooltipTrigger,
1330
+ badgeVariants,
1331
+ buttonVariants,
1332
+ cn,
1333
+ loadingSpinnerIconVariants,
1334
+ sheetVariants
1335
+ };
1336
+ //# sourceMappingURL=index.js.map