@kjaniec-dev/ui 0.8.0 → 0.9.1

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 CHANGED
@@ -1,6 +1,7 @@
1
+ "use client";
1
2
  import { clsx } from 'clsx';
2
3
  import { twMerge } from 'tailwind-merge';
3
- import * as React18 from 'react';
4
+ import * as React52 from 'react';
4
5
  import { cva } from 'class-variance-authority';
5
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
7
 
@@ -38,7 +39,7 @@ var buttonVariants = cva(
38
39
  defaultVariants: { variant: "primary", size: "md" }
39
40
  }
40
41
  );
41
- var Button = React18.forwardRef(
42
+ var Button = React52.forwardRef(
42
43
  ({ className, variant, size, loading, leadingIcon, trailingIcon, children, disabled, ...props }, ref) => {
43
44
  return /* @__PURE__ */ jsxs(
44
45
  "button",
@@ -85,7 +86,7 @@ var badgeVariants = cva(
85
86
  defaultVariants: { variant: "neutral" }
86
87
  }
87
88
  );
88
- var Badge = React18.forwardRef(
89
+ var Badge = React52.forwardRef(
89
90
  ({ className, variant, dot, children, ...props }, ref) => /* @__PURE__ */ jsxs("span", { ref, className: cn(badgeVariants({ variant }), className), ...props, children: [
90
91
  dot && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-current", "aria-hidden": true }),
91
92
  children
@@ -112,7 +113,7 @@ var iconColor = {
112
113
  warning: "text-warning",
113
114
  danger: "text-danger"
114
115
  };
115
- var Alert = React18.forwardRef(
116
+ var Alert = React52.forwardRef(
116
117
  ({ className, variant = "info", icon, title, children, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, role: "alert", className: cn(alertVariants({ variant }), className), ...props, children: [
117
118
  icon && /* @__PURE__ */ jsx("span", { className: cn("shrink-0 mt-0.5 [&_svg]:h-[1.15rem] [&_svg]:w-[1.15rem]", iconColor[variant ?? "info"]), children: icon }),
118
119
  /* @__PURE__ */ jsxs("div", { className: "text-foreground", children: [
@@ -122,7 +123,7 @@ var Alert = React18.forwardRef(
122
123
  ] })
123
124
  );
124
125
  Alert.displayName = "Alert";
125
- var Spinner = React18.forwardRef(
126
+ var Spinner = React52.forwardRef(
126
127
  ({ className, size = 24, style, ...props }, ref) => /* @__PURE__ */ jsx(
127
128
  "span",
128
129
  {
@@ -139,7 +140,7 @@ var Spinner = React18.forwardRef(
139
140
  )
140
141
  );
141
142
  Spinner.displayName = "Spinner";
142
- var Progress = React18.forwardRef(
143
+ var Progress = React52.forwardRef(
143
144
  ({ className, value = 0, tone = "primary", barClassName, ...props }, ref) => {
144
145
  const pct = Math.max(0, Math.min(100, value));
145
146
  return /* @__PURE__ */ jsx(
@@ -169,14 +170,14 @@ var Progress = React18.forwardRef(
169
170
  }
170
171
  );
171
172
  Progress.displayName = "Progress";
172
- var Label = React18.forwardRef(
173
+ var Label = React52.forwardRef(
173
174
  ({ className, required, children, ...props }, ref) => /* @__PURE__ */ jsxs("label", { ref, className: cn("text-[0.8rem] font-semibold text-foreground", className), ...props, children: [
174
175
  children,
175
176
  required && /* @__PURE__ */ jsx("span", { className: "text-danger", children: " *" })
176
177
  ] })
177
178
  );
178
179
  Label.displayName = "Label";
179
- var Hint = React18.forwardRef(
180
+ var Hint = React52.forwardRef(
180
181
  ({ className, error, ...props }, ref) => /* @__PURE__ */ jsx(
181
182
  "span",
182
183
  {
@@ -187,17 +188,17 @@ var Hint = React18.forwardRef(
187
188
  )
188
189
  );
189
190
  Hint.displayName = "Hint";
190
- var Field = React18.forwardRef(
191
+ var Field = React52.forwardRef(
191
192
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-1.5", className), ...props })
192
193
  );
193
194
  Field.displayName = "Field";
194
- var FormField = React18.forwardRef(
195
+ var FormField = React52.forwardRef(
195
196
  ({ className, label, hint, error, required, children, ...props }, ref) => {
196
- const id = React18.useId();
197
+ const id = React52.useId();
197
198
  const hintId = `${id}-hint`;
198
199
  const errorId = `${id}-error`;
199
- const child = React18.Children.only(children);
200
- const clonedChild = React18.cloneElement(child, {
200
+ const child = React52.Children.only(children);
201
+ const clonedChild = React52.cloneElement(child, {
201
202
  id: child.props.id ?? id,
202
203
  "aria-describedby": cn(
203
204
  hint && hintId,
@@ -216,9 +217,136 @@ var FormField = React18.forwardRef(
216
217
  }
217
218
  );
218
219
  FormField.displayName = "FormField";
220
+ var sizeDimensions = {
221
+ sm: 36,
222
+ md: 64,
223
+ lg: 96,
224
+ xl: 128
225
+ };
226
+ var defaultStrokeWidths = {
227
+ sm: 3.5,
228
+ md: 6,
229
+ lg: 8,
230
+ xl: 10
231
+ };
232
+ var textSizeClasses = {
233
+ sm: "text-[0.65rem] font-semibold leading-none",
234
+ md: "text-xs font-semibold leading-none",
235
+ lg: "text-base font-bold leading-none",
236
+ xl: "text-xl font-bold leading-none"
237
+ };
238
+ var toneClasses = {
239
+ primary: "text-primary stroke-current",
240
+ secondary: "text-secondary stroke-current",
241
+ success: "text-success stroke-current",
242
+ warning: "text-warning stroke-current",
243
+ danger: "text-danger stroke-current",
244
+ info: "text-info stroke-current"
245
+ };
246
+ var ProgressRing = React52.forwardRef(
247
+ ({
248
+ className,
249
+ style,
250
+ value = 0,
251
+ min = 0,
252
+ max = 100,
253
+ size = "md",
254
+ strokeWidth,
255
+ tone = "primary",
256
+ showValue = false,
257
+ formatValue,
258
+ trackClassName,
259
+ indicatorClassName,
260
+ children,
261
+ "aria-label": ariaLabel,
262
+ ...props
263
+ }, ref) => {
264
+ const range2 = max > min ? max - min : 100;
265
+ const clampedValue = Math.max(min, Math.min(max, value));
266
+ const percentage = Math.max(0, Math.min(100, (clampedValue - min) / range2 * 100));
267
+ const dimension = sizeDimensions[size];
268
+ const strokeWidthVal = strokeWidth ?? defaultStrokeWidths[size];
269
+ const radius = Math.max(0, (dimension - strokeWidthVal) / 2);
270
+ const circumference = 2 * Math.PI * radius;
271
+ const strokeDashoffset = circumference - percentage / 100 * circumference;
272
+ const computedAriaLabel = ariaLabel ?? (props["aria-labelledby"] ? void 0 : "Progress");
273
+ return /* @__PURE__ */ jsxs(
274
+ "div",
275
+ {
276
+ ref,
277
+ role: "progressbar",
278
+ "aria-label": computedAriaLabel,
279
+ "aria-valuenow": clampedValue,
280
+ "aria-valuemin": min,
281
+ "aria-valuemax": max,
282
+ className: cn("relative inline-flex items-center justify-center shrink-0", className),
283
+ style: { width: dimension, height: dimension, ...style },
284
+ ...props,
285
+ children: [
286
+ /* @__PURE__ */ jsxs(
287
+ "svg",
288
+ {
289
+ width: dimension,
290
+ height: dimension,
291
+ viewBox: `0 0 ${dimension} ${dimension}`,
292
+ className: "-rotate-90 transform",
293
+ children: [
294
+ /* @__PURE__ */ jsx(
295
+ "circle",
296
+ {
297
+ cx: dimension / 2,
298
+ cy: dimension / 2,
299
+ r: radius,
300
+ strokeWidth: strokeWidthVal,
301
+ className: cn("stroke-muted/30 fill-none", trackClassName)
302
+ }
303
+ ),
304
+ /* @__PURE__ */ jsx(
305
+ "circle",
306
+ {
307
+ cx: dimension / 2,
308
+ cy: dimension / 2,
309
+ r: radius,
310
+ strokeWidth: strokeWidthVal,
311
+ strokeDasharray: circumference,
312
+ strokeDashoffset,
313
+ strokeLinecap: percentage === 0 ? "butt" : "round",
314
+ className: cn(
315
+ "fill-none transition-[stroke-dashoffset,opacity] duration-500 ease-in-out",
316
+ percentage === 0 && "opacity-0",
317
+ toneClasses[tone],
318
+ indicatorClassName
319
+ )
320
+ }
321
+ )
322
+ ]
323
+ }
324
+ ),
325
+ (children || showValue) && /* @__PURE__ */ jsx(
326
+ "div",
327
+ {
328
+ className: cn(
329
+ "absolute inset-0 flex flex-col items-center justify-center text-center text-foreground p-1",
330
+ textSizeClasses[size]
331
+ ),
332
+ children: children ?? (formatValue ? formatValue(clampedValue, Math.round(percentage)) : `${Math.round(percentage)}%`)
333
+ }
334
+ )
335
+ ]
336
+ }
337
+ );
338
+ }
339
+ );
340
+ ProgressRing.displayName = "ProgressRing";
341
+ var ProgressRingField = React52.forwardRef(
342
+ ({ label, hint, error, required, fieldClassName, ...ringProps }, ref) => {
343
+ return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className: fieldClassName, children: /* @__PURE__ */ jsx(ProgressRing, { ref, "aria-label": ringProps["aria-label"] ?? label, ...ringProps }) });
344
+ }
345
+ );
346
+ ProgressRingField.displayName = "ProgressRingField";
219
347
  var baseField = "w-full font-sans text-sm text-foreground bg-surface border rounded-kj-md px-[0.85rem] transition-[border-color,box-shadow] duration-150 placeholder:text-muted-foreground focus:outline-none focus:border-ring focus:ring-[3px] focus:ring-ring/30 disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed";
220
- var Input = React18.forwardRef(
221
- ({ className, error, leadingIcon, style, ...props }, ref) => {
348
+ var Input = React52.forwardRef(
349
+ ({ className, error, leadingIcon, ...props }, ref) => {
222
350
  const field = /* @__PURE__ */ jsx(
223
351
  "input",
224
352
  {
@@ -242,7 +370,7 @@ var Input = React18.forwardRef(
242
370
  }
243
371
  );
244
372
  Input.displayName = "Input";
245
- var Textarea = React18.forwardRef(
373
+ var Textarea = React52.forwardRef(
246
374
  ({ className, error, ...props }, ref) => /* @__PURE__ */ jsx(
247
375
  "textarea",
248
376
  {
@@ -259,14 +387,14 @@ var Textarea = React18.forwardRef(
259
387
  )
260
388
  );
261
389
  Textarea.displayName = "Textarea";
262
- var TextField = React18.forwardRef(
390
+ var TextField = React52.forwardRef(
263
391
  ({ label, hint, error, required, className, ...props }, ref) => {
264
392
  return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(Input, { ref, error: !!error, ...props }) });
265
393
  }
266
394
  );
267
395
  TextField.displayName = "TextField";
268
396
  var chevron = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E";
269
- var Select = React18.forwardRef(
397
+ var Select = React52.forwardRef(
270
398
  ({ className, error, style, children, ...props }, ref) => /* @__PURE__ */ jsx(
271
399
  "select",
272
400
  {
@@ -294,15 +422,15 @@ var Select = React18.forwardRef(
294
422
  )
295
423
  );
296
424
  Select.displayName = "Select";
297
- var SelectField = React18.forwardRef(
425
+ var SelectField = React52.forwardRef(
298
426
  ({ label, hint, error, required, className, children, ...props }, ref) => {
299
427
  return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(Select, { ref, error: !!error, ...props, children }) });
300
428
  }
301
429
  );
302
430
  SelectField.displayName = "SelectField";
303
- var Checkbox = React18.forwardRef(
431
+ var Checkbox = React52.forwardRef(
304
432
  ({ className, label, id, ...props }, ref) => {
305
- const autoId = React18.useId();
433
+ const autoId = React52.useId();
306
434
  const inputId = id ?? autoId;
307
435
  return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
308
436
  /* @__PURE__ */ jsx("input", { ref, id: inputId, type: "checkbox", className: "peer sr-only", ...props }),
@@ -312,9 +440,9 @@ var Checkbox = React18.forwardRef(
312
440
  }
313
441
  );
314
442
  Checkbox.displayName = "Checkbox";
315
- var Radio = React18.forwardRef(
443
+ var Radio = React52.forwardRef(
316
444
  ({ className, label, id, ...props }, ref) => {
317
- const autoId = React18.useId();
445
+ const autoId = React52.useId();
318
446
  const inputId = id ?? autoId;
319
447
  return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
320
448
  /* @__PURE__ */ jsx("input", { ref, id: inputId, type: "radio", className: "peer sr-only", ...props }),
@@ -324,9 +452,9 @@ var Radio = React18.forwardRef(
324
452
  }
325
453
  );
326
454
  Radio.displayName = "Radio";
327
- var CheckboxField = React18.forwardRef(
455
+ var CheckboxField = React52.forwardRef(
328
456
  ({ label, hint, error, className, ...props }, ref) => {
329
- const id = React18.useId();
457
+ const id = React52.useId();
330
458
  const hintId = `${id}-hint`;
331
459
  const errorId = `${id}-error`;
332
460
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 w-full", className), children: [
@@ -347,9 +475,9 @@ var CheckboxField = React18.forwardRef(
347
475
  }
348
476
  );
349
477
  CheckboxField.displayName = "CheckboxField";
350
- var Switch = React18.forwardRef(
478
+ var Switch = React52.forwardRef(
351
479
  ({ className, label, id, ...props }, ref) => {
352
- const autoId = React18.useId();
480
+ const autoId = React52.useId();
353
481
  const inputId = id ?? autoId;
354
482
  return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
355
483
  /* @__PURE__ */ jsx("input", { ref, id: inputId, type: "checkbox", role: "switch", className: "peer sr-only", ...props }),
@@ -359,19 +487,47 @@ var Switch = React18.forwardRef(
359
487
  }
360
488
  );
361
489
  Switch.displayName = "Switch";
362
- var Slider = React18.forwardRef(
363
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
364
- "input",
365
- {
366
- ref,
367
- type: "range",
368
- className: cn(
369
- "kj-slider w-full h-1.5 rounded-full bg-input appearance-none cursor-pointer",
370
- className
371
- ),
372
- ...props
373
- }
374
- )
490
+ var Slider = React52.forwardRef(
491
+ ({ className, style, value, defaultValue, min = 0, max = 100, onChange, ...props }, ref) => {
492
+ const [localValue, setLocalValue] = React52.useState(
493
+ Number(defaultValue ?? value ?? 50)
494
+ );
495
+ const isControlled = value !== void 0;
496
+ const currentVal = isControlled ? Number(value) : localValue;
497
+ const numMin = Number(min);
498
+ const numMax = Number(max);
499
+ const percentage = Math.min(
500
+ 100,
501
+ Math.max(0, (currentVal - numMin) / (numMax - numMin) * 100)
502
+ );
503
+ const handleChange = (e) => {
504
+ if (!isControlled) {
505
+ setLocalValue(Number(e.target.value));
506
+ }
507
+ onChange?.(e);
508
+ };
509
+ return /* @__PURE__ */ jsx(
510
+ "input",
511
+ {
512
+ ref,
513
+ type: "range",
514
+ value,
515
+ defaultValue,
516
+ min,
517
+ max,
518
+ onChange: handleChange,
519
+ style: {
520
+ background: `linear-gradient(to right, var(--kj-primary) ${percentage}%, var(--kj-border, rgba(120, 120, 128, 0.2)) ${percentage}%)`,
521
+ ...style
522
+ },
523
+ className: cn(
524
+ "kj-slider w-full h-1.5 rounded-full appearance-none cursor-pointer accent-primary",
525
+ className
526
+ ),
527
+ ...props
528
+ }
529
+ );
530
+ }
375
531
  );
376
532
  Slider.displayName = "Slider";
377
533
  var sliderThumbCSS = `
@@ -412,7 +568,47 @@ function Segmented({
412
568
  }
413
569
  );
414
570
  }
415
- var Card = React18.forwardRef(
571
+ function ToggleGroup({
572
+ options,
573
+ value,
574
+ onChange,
575
+ disabled,
576
+ className,
577
+ ...props
578
+ }) {
579
+ const toggle = (optionValue) => {
580
+ if (disabled) return;
581
+ const next = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue];
582
+ onChange(next);
583
+ };
584
+ return /* @__PURE__ */ jsx(
585
+ "div",
586
+ {
587
+ role: "group",
588
+ className: cn("inline-flex gap-0.5 p-[3px] rounded-kj-md bg-muted", className),
589
+ ...props,
590
+ children: options.map((opt) => {
591
+ const active = value.includes(opt.value);
592
+ return /* @__PURE__ */ jsx(
593
+ "button",
594
+ {
595
+ type: "button",
596
+ "aria-pressed": active,
597
+ disabled,
598
+ onClick: () => toggle(opt.value),
599
+ className: cn(
600
+ "px-3.5 py-1.5 text-[0.82rem] font-semibold rounded-[calc(var(--kj-radius-md)-3px)] cursor-pointer transition-all duration-150 disabled:cursor-not-allowed disabled:opacity-50",
601
+ active ? "bg-surface text-foreground shadow-kj-xs" : "bg-transparent text-muted-foreground hover:text-foreground"
602
+ ),
603
+ children: opt.label
604
+ },
605
+ opt.value
606
+ );
607
+ })
608
+ }
609
+ );
610
+ }
611
+ var Card = React52.forwardRef(
416
612
  ({ as: Tag = "div", className, elevated, interactive, ...props }, ref) => /* @__PURE__ */ jsx(
417
613
  Tag,
418
614
  {
@@ -428,23 +624,23 @@ var Card = React18.forwardRef(
428
624
  )
429
625
  );
430
626
  Card.displayName = "Card";
431
- var CardHeader = React18.forwardRef(
627
+ var CardHeader = React52.forwardRef(
432
628
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-[1.35rem] flex flex-col gap-1", className), ...props })
433
629
  );
434
630
  CardHeader.displayName = "CardHeader";
435
- var CardTitle = React18.forwardRef(
436
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("m-0 text-base font-bold tracking-[-0.01em]", className), ...props })
631
+ var CardTitle = React52.forwardRef(
632
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("m-0 text-base font-bold tracking-[-0.01em]", className), ...props, children })
437
633
  );
438
634
  CardTitle.displayName = "CardTitle";
439
- var CardDescription = React18.forwardRef(
635
+ var CardDescription = React52.forwardRef(
440
636
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("m-0 text-[0.85rem] text-muted-foreground", className), ...props })
441
637
  );
442
638
  CardDescription.displayName = "CardDescription";
443
- var CardContent = React18.forwardRef(
639
+ var CardContent = React52.forwardRef(
444
640
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("px-[1.35rem] pb-[1.35rem]", className), ...props })
445
641
  );
446
642
  CardContent.displayName = "CardContent";
447
- var CardFooter = React18.forwardRef(
643
+ var CardFooter = React52.forwardRef(
448
644
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
449
645
  "div",
450
646
  {
@@ -455,7 +651,7 @@ var CardFooter = React18.forwardRef(
455
651
  )
456
652
  );
457
653
  CardFooter.displayName = "CardFooter";
458
- var Stat = React18.forwardRef(
654
+ var Stat = React52.forwardRef(
459
655
  ({ className, label, value, delta, trend, ...props }, ref) => /* @__PURE__ */ jsxs(Card, { ref, className: cn("p-[1.35rem] flex flex-col gap-2", className), ...props, children: [
460
656
  /* @__PURE__ */ jsx("span", { className: "text-[0.78rem] font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: label }),
461
657
  /* @__PURE__ */ jsx("span", { className: "text-[2rem] font-bold tracking-[-0.03em] tabular-nums leading-none", children: value }),
@@ -495,7 +691,7 @@ var avatarVariants = cva(
495
691
  defaultVariants: { size: "md", tone: "secondary" }
496
692
  }
497
693
  );
498
- var Avatar = React18.forwardRef(
694
+ var Avatar = React52.forwardRef(
499
695
  ({ className, size, tone, src, alt, status, children, ...props }, ref) => {
500
696
  const node = /* @__PURE__ */ jsx("span", { ref, className: cn(avatarVariants({ size, tone }), "overflow-hidden", className), ...props, children: src ? /* @__PURE__ */ jsx("img", { src, alt, className: "h-full w-full object-cover" }) : children });
501
697
  if (!status) return node;
@@ -506,21 +702,21 @@ var Avatar = React18.forwardRef(
506
702
  }
507
703
  );
508
704
  Avatar.displayName = "Avatar";
509
- var AvatarGroup = React18.forwardRef(
705
+ var AvatarGroup = React52.forwardRef(
510
706
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex [&>*:not(:first-child)]:-ml-2.5", className), ...props })
511
707
  );
512
708
  AvatarGroup.displayName = "AvatarGroup";
513
- var TabsContext = React18.createContext(null);
709
+ var TabsContext = React52.createContext(null);
514
710
  function useTabs() {
515
- const ctx = React18.useContext(TabsContext);
711
+ const ctx = React52.useContext(TabsContext);
516
712
  if (!ctx) throw new Error("Tabs.* must be used within <Tabs>");
517
713
  return ctx;
518
714
  }
519
715
  function Tabs({ value, defaultValue, onValueChange, className, children, ...props }) {
520
- const [internal, setInternal] = React18.useState(defaultValue ?? "");
521
- const uid = React18.useId();
716
+ const [internal, setInternal] = React52.useState(defaultValue ?? "");
717
+ const uid = React52.useId();
522
718
  const current = value ?? internal;
523
- const setValue = React18.useCallback(
719
+ const setValue = React52.useCallback(
524
720
  (v) => {
525
721
  if (value === void 0) setInternal(v);
526
722
  onValueChange?.(v);
@@ -529,7 +725,7 @@ function Tabs({ value, defaultValue, onValueChange, className, children, ...prop
529
725
  );
530
726
  return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { value: current, setValue, uid }, children: /* @__PURE__ */ jsx("div", { className, ...props, children }) });
531
727
  }
532
- var TabsList = React18.forwardRef(
728
+ var TabsList = React52.forwardRef(
533
729
  ({ className, onKeyDown, ...props }, ref) => {
534
730
  const handleKeyDown = (e) => {
535
731
  const container = e.currentTarget;
@@ -576,7 +772,7 @@ var TabsList = React18.forwardRef(
576
772
  }
577
773
  );
578
774
  TabsList.displayName = "TabsList";
579
- var TabsTrigger = React18.forwardRef(
775
+ var TabsTrigger = React52.forwardRef(
580
776
  ({ className, value, children, ...props }, ref) => {
581
777
  const { value: current, setValue, uid } = useTabs();
582
778
  const active = current === value;
@@ -605,7 +801,7 @@ var TabsTrigger = React18.forwardRef(
605
801
  }
606
802
  );
607
803
  TabsTrigger.displayName = "TabsTrigger";
608
- var TabsContent = React18.forwardRef(
804
+ var TabsContent = React52.forwardRef(
609
805
  ({ className, value, ...props }, ref) => {
610
806
  const { value: current, uid } = useTabs();
611
807
  const active = current === value;
@@ -625,10 +821,10 @@ var TabsContent = React18.forwardRef(
625
821
  }
626
822
  );
627
823
  TabsContent.displayName = "TabsContent";
628
- var AccordionContext = React18.createContext(null);
824
+ var AccordionContext = React52.createContext(null);
629
825
  function Accordion({ type = "single", defaultValue = [], className, children, ...props }) {
630
- const [open, setOpen] = React18.useState(defaultValue);
631
- const toggle = React18.useCallback(
826
+ const [open, setOpen] = React52.useState(defaultValue);
827
+ const toggle = React52.useCallback(
632
828
  (v) => {
633
829
  setOpen((prev) => {
634
830
  const isOpen = prev.includes(v);
@@ -640,15 +836,15 @@ function Accordion({ type = "single", defaultValue = [], className, children, ..
640
836
  );
641
837
  return /* @__PURE__ */ jsx(AccordionContext.Provider, { value: { open, toggle }, children: /* @__PURE__ */ jsx("div", { className: cn("border border-border rounded-kj-lg overflow-hidden", className), ...props, children }) });
642
838
  }
643
- var ItemContext = React18.createContext("");
644
- var AccordionItem = React18.forwardRef(
839
+ var ItemContext = React52.createContext("");
840
+ var AccordionItem = React52.forwardRef(
645
841
  ({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(ItemContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { ref, className: cn("border-t border-border first:border-t-0", className), ...props }) })
646
842
  );
647
843
  AccordionItem.displayName = "AccordionItem";
648
- var AccordionTrigger = React18.forwardRef(
844
+ var AccordionTrigger = React52.forwardRef(
649
845
  ({ className, children, ...props }, ref) => {
650
- const ctx = React18.useContext(AccordionContext);
651
- const value = React18.useContext(ItemContext);
846
+ const ctx = React52.useContext(AccordionContext);
847
+ const value = React52.useContext(ItemContext);
652
848
  const isOpen = ctx.open.includes(value);
653
849
  return /* @__PURE__ */ jsxs(
654
850
  "button",
@@ -685,10 +881,10 @@ var AccordionTrigger = React18.forwardRef(
685
881
  }
686
882
  );
687
883
  AccordionTrigger.displayName = "AccordionTrigger";
688
- var AccordionContent = React18.forwardRef(
884
+ var AccordionContent = React52.forwardRef(
689
885
  ({ className, children, ...props }, ref) => {
690
- const ctx = React18.useContext(AccordionContext);
691
- const value = React18.useContext(ItemContext);
886
+ const ctx = React52.useContext(AccordionContext);
887
+ const value = React52.useContext(ItemContext);
692
888
  const isOpen = ctx.open.includes(value);
693
889
  return /* @__PURE__ */ jsx(
694
890
  "div",
@@ -702,11 +898,11 @@ var AccordionContent = React18.forwardRef(
702
898
  }
703
899
  );
704
900
  AccordionContent.displayName = "AccordionContent";
705
- var MenuContext = React18.createContext(null);
901
+ var MenuContext = React52.createContext(null);
706
902
  function DropdownMenu({ children, className }) {
707
- const [open, setOpen] = React18.useState(false);
708
- const ref = React18.useRef(null);
709
- React18.useEffect(() => {
903
+ const [open, setOpen] = React52.useState(false);
904
+ const ref = React52.useRef(null);
905
+ React52.useEffect(() => {
710
906
  if (!open) return;
711
907
  const onDoc = (e) => {
712
908
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -721,11 +917,11 @@ function DropdownMenu({ children, className }) {
721
917
  }, [open]);
722
918
  return /* @__PURE__ */ jsx(MenuContext.Provider, { value: { open, setOpen }, children: /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block", className), children }) });
723
919
  }
724
- var DropdownMenuTrigger = React18.forwardRef(({ onClick, asChild, ...props }, ref) => {
725
- const ctx = React18.useContext(MenuContext);
920
+ var DropdownMenuTrigger = React52.forwardRef(({ onClick, asChild, ...props }, ref) => {
921
+ const ctx = React52.useContext(MenuContext);
726
922
  if (asChild) {
727
- const child = React18.Children.only(props.children);
728
- return React18.cloneElement(child, {
923
+ const child = React52.Children.only(props.children);
924
+ return React52.cloneElement(child, {
729
925
  ref,
730
926
  ...props,
731
927
  ...child.props,
@@ -755,9 +951,9 @@ var DropdownMenuTrigger = React18.forwardRef(({ onClick, asChild, ...props }, re
755
951
  });
756
952
  DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
757
953
  function DropdownMenuContent({ className, align = "start", children, ...props }) {
758
- const ctx = React18.useContext(MenuContext);
759
- const ref = React18.useRef(null);
760
- React18.useEffect(() => {
954
+ const ctx = React52.useContext(MenuContext);
955
+ const ref = React52.useRef(null);
956
+ React52.useEffect(() => {
761
957
  if (!ctx.open) return;
762
958
  const container = ref.current;
763
959
  if (container) {
@@ -804,9 +1000,9 @@ function DropdownMenuContent({ className, align = "start", children, ...props })
804
1000
  }
805
1001
  );
806
1002
  }
807
- var DropdownMenuItem = React18.forwardRef(
1003
+ var DropdownMenuItem = React52.forwardRef(
808
1004
  ({ className, danger, icon, children, onClick, ...props }, ref) => {
809
- const ctx = React18.useContext(MenuContext);
1005
+ const ctx = React52.useContext(MenuContext);
810
1006
  return /* @__PURE__ */ jsxs(
811
1007
  "button",
812
1008
  {
@@ -836,11 +1032,11 @@ DropdownMenuItem.displayName = "DropdownMenuItem";
836
1032
  function DropdownMenuSeparator({ className }) {
837
1033
  return /* @__PURE__ */ jsx("div", { className: cn("h-px bg-border my-1.5", className), role: "separator" });
838
1034
  }
839
- var Breadcrumb = React18.forwardRef(
1035
+ var Breadcrumb = React52.forwardRef(
840
1036
  ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx("nav", { ref, "aria-label": "Breadcrumb", className: cn("flex items-center gap-1.5 text-[0.85rem] text-muted-foreground", className), ...props, children })
841
1037
  );
842
1038
  Breadcrumb.displayName = "Breadcrumb";
843
- var BreadcrumbItem = React18.forwardRef(
1039
+ var BreadcrumbItem = React52.forwardRef(
844
1040
  ({ className, current, children, ...props }, ref) => {
845
1041
  if (current) {
846
1042
  return /* @__PURE__ */ jsx("span", { "aria-current": "page", className: cn("text-foreground font-semibold", className), children });
@@ -887,7 +1083,7 @@ function Pagination({ page, pageCount, onPageChange, className }) {
887
1083
  /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Next", disabled: page >= pageCount, onClick: () => onPageChange(page + 1), children: /* @__PURE__ */ jsx(Chevron, { dir: "right" }) })
888
1084
  ] });
889
1085
  }
890
- var BottomNavigation = React18.forwardRef(
1086
+ var BottomNavigation = React52.forwardRef(
891
1087
  ({ className, items, showLabels = "always", fixed = true, ...props }, ref) => {
892
1088
  return /* @__PURE__ */ jsx(
893
1089
  "nav",
@@ -959,27 +1155,27 @@ var BottomNavigation = React18.forwardRef(
959
1155
  }
960
1156
  );
961
1157
  BottomNavigation.displayName = "BottomNavigation";
962
- var TableWrap = React18.forwardRef(
1158
+ var TableWrap = React52.forwardRef(
963
1159
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("border border-border rounded-kj-lg overflow-x-auto w-full", className), ...props })
964
1160
  );
965
1161
  TableWrap.displayName = "TableWrap";
966
- var Table = React18.forwardRef(
1162
+ var Table = React52.forwardRef(
967
1163
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("table", { ref, className: cn("w-full border-collapse text-[0.85rem]", className), ...props })
968
1164
  );
969
1165
  Table.displayName = "Table";
970
- var TableHeader = React18.forwardRef(
1166
+ var TableHeader = React52.forwardRef(
971
1167
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className, ...props })
972
1168
  );
973
1169
  TableHeader.displayName = "TableHeader";
974
- var TableBody = React18.forwardRef(
1170
+ var TableBody = React52.forwardRef(
975
1171
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className, ...props })
976
1172
  );
977
1173
  TableBody.displayName = "TableBody";
978
- var TableRow = React18.forwardRef(
1174
+ var TableRow = React52.forwardRef(
979
1175
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx("tr", { ref, className: cn("transition-colors hover:bg-muted/50", className), ...props })
980
1176
  );
981
1177
  TableRow.displayName = "TableRow";
982
- var TableHead = React18.forwardRef(
1178
+ var TableHead = React52.forwardRef(
983
1179
  ({ className, numeric, ...props }, ref) => /* @__PURE__ */ jsx(
984
1180
  "th",
985
1181
  {
@@ -994,7 +1190,7 @@ var TableHead = React18.forwardRef(
994
1190
  )
995
1191
  );
996
1192
  TableHead.displayName = "TableHead";
997
- var TableCell = React18.forwardRef(
1193
+ var TableCell = React52.forwardRef(
998
1194
  ({ className, numeric, ...props }, ref) => /* @__PURE__ */ jsx(
999
1195
  "td",
1000
1196
  {
@@ -1010,30 +1206,33 @@ var TableCell = React18.forwardRef(
1010
1206
  );
1011
1207
  TableCell.displayName = "TableCell";
1012
1208
  function Tooltip({ content, children, className }) {
1013
- return /* @__PURE__ */ jsxs("span", { className: cn("relative inline-flex group", className), tabIndex: 0, children: [
1014
- children,
1015
- /* @__PURE__ */ jsx(
1016
- "span",
1017
- {
1018
- role: "tooltip",
1019
- className: cn(
1020
- "pointer-events-none absolute bottom-[calc(100%+8px)] left-1/2 -translate-x-1/2 translate-y-1 whitespace-nowrap",
1021
- "rounded-kj-sm bg-foreground text-background text-xs font-medium px-2.5 py-1.5 shadow-kj-md",
1022
- "opacity-0 transition-all duration-150 group-hover:opacity-100 group-hover:translate-y-0 group-focus-visible:opacity-100 group-focus-visible:translate-y-0",
1023
- "after:content-[''] after:absolute after:top-full after:left-1/2 after:-translate-x-1/2 after:border-[5px] after:border-transparent after:border-t-foreground"
1024
- ),
1025
- children: content
1026
- }
1027
- )
1028
- ] });
1209
+ return (
1210
+ /* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
1211
+ /* @__PURE__ */ jsxs("span", { className: cn("relative inline-flex group", className), tabIndex: 0, children: [
1212
+ children,
1213
+ /* @__PURE__ */ jsx(
1214
+ "span",
1215
+ {
1216
+ role: "tooltip",
1217
+ className: cn(
1218
+ "pointer-events-none absolute bottom-[calc(100%+8px)] left-1/2 -translate-x-1/2 translate-y-1 whitespace-nowrap",
1219
+ "rounded-kj-sm bg-foreground text-background text-xs font-medium px-2.5 py-1.5 shadow-kj-md",
1220
+ "opacity-0 transition-all duration-150 group-hover:opacity-100 group-hover:translate-y-0 group-focus-visible:opacity-100 group-focus-visible:translate-y-0",
1221
+ "after:content-[''] after:absolute after:top-full after:left-1/2 after:-translate-x-1/2 after:border-[5px] after:border-transparent after:border-t-foreground"
1222
+ ),
1223
+ children: content
1224
+ }
1225
+ )
1226
+ ] })
1227
+ );
1029
1228
  }
1030
- var ModalContext = React18.createContext(null);
1229
+ var ModalContext = React52.createContext(null);
1031
1230
  function Modal({ open, onClose, children, width = 440, className, showClose = true }) {
1032
- const titleId = React18.useId();
1033
- const descId = React18.useId();
1034
- const containerRef = React18.useRef(null);
1035
- const lastActiveElement = React18.useRef(null);
1036
- React18.useEffect(() => {
1231
+ const titleId = React52.useId();
1232
+ const descId = React52.useId();
1233
+ const containerRef = React52.useRef(null);
1234
+ const lastActiveElement = React52.useRef(null);
1235
+ React52.useEffect(() => {
1037
1236
  if (!open) {
1038
1237
  if (lastActiveElement.current) {
1039
1238
  lastActiveElement.current.focus();
@@ -1134,20 +1333,20 @@ function Modal({ open, onClose, children, width = 440, className, showClose = tr
1134
1333
  }
1135
1334
  );
1136
1335
  }
1137
- function ModalTitle({ className, id, ...props }) {
1138
- const ctx = React18.useContext(ModalContext);
1139
- return /* @__PURE__ */ jsx("h3", { id: id ?? ctx?.titleId, className: cn("m-0 mb-2 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props });
1336
+ function ModalTitle({ className, id, children, ...props }) {
1337
+ const ctx = React52.useContext(ModalContext);
1338
+ return /* @__PURE__ */ jsx("h3", { id: id ?? ctx?.titleId, className: cn("m-0 mb-2 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props, children });
1140
1339
  }
1141
1340
  function ModalDescription({ className, id, ...props }) {
1142
- const ctx = React18.useContext(ModalContext);
1341
+ const ctx = React52.useContext(ModalContext);
1143
1342
  return /* @__PURE__ */ jsx("p", { id: id ?? ctx?.descId, className: cn("m-0 text-[0.9rem] text-muted-foreground", className), ...props });
1144
1343
  }
1145
1344
  function ModalActions({ className, ...props }) {
1146
1345
  return /* @__PURE__ */ jsx("div", { className: cn("flex gap-2.5 justify-end mt-6", className), ...props });
1147
1346
  }
1148
- var ToastContext = React18.createContext(null);
1347
+ var ToastContext = React52.createContext(null);
1149
1348
  function useToast() {
1150
- const ctx = React18.useContext(ToastContext);
1349
+ const ctx = React52.useContext(ToastContext);
1151
1350
  if (!ctx) throw new Error("useToast must be used within <ToastProvider>");
1152
1351
  return ctx;
1153
1352
  }
@@ -1168,13 +1367,13 @@ var toneBorder = {
1168
1367
  danger: "border-l-danger"
1169
1368
  };
1170
1369
  function ToastProvider({ children }) {
1171
- const [items, setItems] = React18.useState([]);
1172
- const idRef = React18.useRef(0);
1173
- const remove = React18.useCallback((id) => {
1370
+ const [items, setItems] = React52.useState([]);
1371
+ const idRef = React52.useRef(0);
1372
+ const remove = React52.useCallback((id) => {
1174
1373
  setItems((prev) => prev.map((t) => t.id === id ? { ...t, leaving: true } : t));
1175
1374
  setTimeout(() => setItems((prev) => prev.filter((t) => t.id !== id)), 250);
1176
1375
  }, []);
1177
- const toast = React18.useCallback(
1376
+ const toast = React52.useCallback(
1178
1377
  ({ message, tone = "default", duration = 3200 }) => {
1179
1378
  const id = ++idRef.current;
1180
1379
  setItems((prev) => [...prev, { id, message, tone, duration }]);
@@ -1204,7 +1403,7 @@ function ToastProvider({ children }) {
1204
1403
  )) })
1205
1404
  ] });
1206
1405
  }
1207
- var PageHeader = React18.forwardRef(
1406
+ var PageHeader = React52.forwardRef(
1208
1407
  ({ eyebrow, title, description, actions }, ref) => /* @__PURE__ */ jsx("header", { ref, className: "py-12 md:py-16", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 md:flex-row md:items-end md:justify-between", children: [
1209
1408
  /* @__PURE__ */ jsxs("div", { className: "space-y-3 max-w-2xl", children: [
1210
1409
  eyebrow && /* @__PURE__ */ jsx("p", { className: "font-mono text-xs font-bold uppercase tracking-[0.2em] text-primary", children: eyebrow }),
@@ -1215,7 +1414,111 @@ var PageHeader = React18.forwardRef(
1215
1414
  ] }) })
1216
1415
  );
1217
1416
  PageHeader.displayName = "PageHeader";
1218
- var EmptyState = React18.forwardRef(
1417
+ var SectionHeaderKicker = React52.forwardRef(
1418
+ ({ className, children, ...props }, ref) => {
1419
+ if (!children) return null;
1420
+ return /* @__PURE__ */ jsx(
1421
+ "p",
1422
+ {
1423
+ ref,
1424
+ className: cn(
1425
+ "font-mono text-xs font-bold uppercase tracking-[0.2em] text-primary",
1426
+ className
1427
+ ),
1428
+ ...props,
1429
+ children
1430
+ }
1431
+ );
1432
+ }
1433
+ );
1434
+ SectionHeaderKicker.displayName = "SectionHeaderKicker";
1435
+ var SectionHeaderTitle = React52.forwardRef(
1436
+ ({ className, as: Component = "h2", children, ...props }, ref) => {
1437
+ if (!children) return null;
1438
+ return /* @__PURE__ */ jsx(
1439
+ Component,
1440
+ {
1441
+ ref,
1442
+ className: cn("text-2xl font-bold tracking-tight text-foreground sm:text-3xl", className),
1443
+ ...props,
1444
+ children
1445
+ }
1446
+ );
1447
+ }
1448
+ );
1449
+ SectionHeaderTitle.displayName = "SectionHeaderTitle";
1450
+ var SectionHeaderDescription = React52.forwardRef(({ className, children, ...props }, ref) => {
1451
+ if (!children) return null;
1452
+ return /* @__PURE__ */ jsx(
1453
+ "p",
1454
+ {
1455
+ ref,
1456
+ className: cn("text-base leading-relaxed text-muted-foreground max-w-2xl", className),
1457
+ ...props,
1458
+ children
1459
+ }
1460
+ );
1461
+ });
1462
+ SectionHeaderDescription.displayName = "SectionHeaderDescription";
1463
+ var SectionHeaderActions = React52.forwardRef(
1464
+ ({ className, children, ...props }, ref) => {
1465
+ if (!children) return null;
1466
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-wrap items-center gap-3", className), ...props, children });
1467
+ }
1468
+ );
1469
+ SectionHeaderActions.displayName = "SectionHeaderActions";
1470
+ var SectionHeader = React52.forwardRef(
1471
+ ({
1472
+ kicker,
1473
+ title,
1474
+ description,
1475
+ actions,
1476
+ align = "left",
1477
+ headingLevel = "h2",
1478
+ divider = false,
1479
+ className,
1480
+ children,
1481
+ ...props
1482
+ }, ref) => {
1483
+ const isCentered = align === "center";
1484
+ return /* @__PURE__ */ jsx(
1485
+ "div",
1486
+ {
1487
+ ref,
1488
+ className: cn(
1489
+ "space-y-4",
1490
+ isCentered ? "text-center items-center mx-auto max-w-3xl" : "md:flex md:items-end md:justify-between md:space-y-0",
1491
+ divider && "border-b border-border pb-6",
1492
+ className
1493
+ ),
1494
+ ...props,
1495
+ children: children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
1496
+ /* @__PURE__ */ jsxs(
1497
+ "div",
1498
+ {
1499
+ className: cn(
1500
+ "space-y-2",
1501
+ isCentered && "flex flex-col items-center"
1502
+ ),
1503
+ children: [
1504
+ kicker && /* @__PURE__ */ jsx(SectionHeaderKicker, { children: kicker }),
1505
+ title && /* @__PURE__ */ jsx(SectionHeaderTitle, { as: headingLevel, children: title }),
1506
+ description && /* @__PURE__ */ jsx(SectionHeaderDescription, { children: description })
1507
+ ]
1508
+ }
1509
+ ),
1510
+ actions && /* @__PURE__ */ jsx(SectionHeaderActions, { className: cn(isCentered && "justify-center mt-4"), children: actions })
1511
+ ] })
1512
+ }
1513
+ );
1514
+ }
1515
+ );
1516
+ SectionHeader.displayName = "SectionHeader";
1517
+ SectionHeader.Kicker = SectionHeaderKicker;
1518
+ SectionHeader.Title = SectionHeaderTitle;
1519
+ SectionHeader.Description = SectionHeaderDescription;
1520
+ SectionHeader.Actions = SectionHeaderActions;
1521
+ var EmptyState = React52.forwardRef(
1219
1522
  ({ className, icon, title, description, action, ...props }, ref) => /* @__PURE__ */ jsxs(
1220
1523
  "div",
1221
1524
  {
@@ -1235,7 +1538,7 @@ var EmptyState = React18.forwardRef(
1235
1538
  )
1236
1539
  );
1237
1540
  EmptyState.displayName = "EmptyState";
1238
- var MetricCard = React18.forwardRef(
1541
+ var MetricCard = React52.forwardRef(
1239
1542
  ({ className, title, value, trend, trendDirection = "neutral", description, icon, ...props }, ref) => {
1240
1543
  const trendColor = {
1241
1544
  up: "text-success bg-success-surface border-success/20",
@@ -1276,22 +1579,22 @@ function DataTable({
1276
1579
  onSort,
1277
1580
  ...props
1278
1581
  }) {
1279
- const getRowId = React18.useCallback(
1582
+ const getRowId = React52.useCallback(
1280
1583
  (row, index) => getRowKey ? getRowKey(row, index) : index,
1281
1584
  [getRowKey]
1282
1585
  );
1283
1586
  const isSelectionActive = selectedRows !== void 0 && onSelectionChange !== void 0;
1284
- const allSelected = React18.useMemo(() => {
1587
+ const allSelected = React52.useMemo(() => {
1285
1588
  if (!isSelectionActive || data.length === 0) return false;
1286
1589
  return data.every((row, idx) => selectedRows.has(getRowId(row, idx)));
1287
1590
  }, [isSelectionActive, data, selectedRows, getRowId]);
1288
- const someSelected = React18.useMemo(() => {
1591
+ const someSelected = React52.useMemo(() => {
1289
1592
  if (!isSelectionActive || data.length === 0) return false;
1290
1593
  const selectedCount = data.filter((row, idx) => selectedRows.has(getRowId(row, idx))).length;
1291
1594
  return selectedCount > 0 && selectedCount < data.length;
1292
1595
  }, [isSelectionActive, data, selectedRows, getRowId]);
1293
- const headerCheckboxRef = React18.useRef(null);
1294
- React18.useEffect(() => {
1596
+ const headerCheckboxRef = React52.useRef(null);
1597
+ React52.useEffect(() => {
1295
1598
  if (headerCheckboxRef.current) {
1296
1599
  headerCheckboxRef.current.indeterminate = someSelected;
1297
1600
  }
@@ -1445,7 +1748,7 @@ function DataTable({
1445
1748
  ] });
1446
1749
  }
1447
1750
  DataTable.displayName = "DataTable";
1448
- var ErrorState = React18.forwardRef(
1751
+ var ErrorState = React52.forwardRef(
1449
1752
  ({ className, title = "Wyst\u0105pi\u0142 b\u0142\u0105d", message, onRetry, retryLabel = "Spr\xF3buj ponownie", ...props }, ref) => /* @__PURE__ */ jsxs(
1450
1753
  "div",
1451
1754
  {
@@ -1465,7 +1768,7 @@ var ErrorState = React18.forwardRef(
1465
1768
  )
1466
1769
  );
1467
1770
  ErrorState.displayName = "ErrorState";
1468
- var Skeleton = React18.forwardRef(
1771
+ var Skeleton = React52.forwardRef(
1469
1772
  ({ className, variant = "rectangular", width, height, style, ...props }, ref) => /* @__PURE__ */ jsx(
1470
1773
  "div",
1471
1774
  {
@@ -1487,7 +1790,7 @@ var Skeleton = React18.forwardRef(
1487
1790
  )
1488
1791
  );
1489
1792
  Skeleton.displayName = "Skeleton";
1490
- var DashboardShell = React18.forwardRef(
1793
+ var DashboardShell = React52.forwardRef(
1491
1794
  ({
1492
1795
  className,
1493
1796
  sidebar,
@@ -1499,7 +1802,7 @@ var DashboardShell = React18.forwardRef(
1499
1802
  mobileSidebar,
1500
1803
  ...props
1501
1804
  }, ref) => {
1502
- const [mobileOpen, setMobileOpen] = React18.useState(false);
1805
+ const [mobileOpen, setMobileOpen] = React52.useState(false);
1503
1806
  const contentWidthClass = {
1504
1807
  default: "max-w-7xl w-full mx-auto",
1505
1808
  wide: "max-w-screen-2xl w-full mx-auto",
@@ -1590,7 +1893,7 @@ var DashboardShell = React18.forwardRef(
1590
1893
  }
1591
1894
  );
1592
1895
  DashboardShell.displayName = "DashboardShell";
1593
- var SettingsLayout = React18.forwardRef(
1896
+ var SettingsLayout = React52.forwardRef(
1594
1897
  ({ className, sidebar, children, title, description, ...props }, ref) => {
1595
1898
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-6 w-full", className), ...props, children: [
1596
1899
  (title || description) && /* @__PURE__ */ jsxs("div", { className: "border-b border-border pb-4", children: [
@@ -1605,7 +1908,7 @@ var SettingsLayout = React18.forwardRef(
1605
1908
  }
1606
1909
  );
1607
1910
  SettingsLayout.displayName = "SettingsLayout";
1608
- var DetailPageLayout = React18.forwardRef(
1911
+ var DetailPageLayout = React52.forwardRef(
1609
1912
  ({ className, title, description, backHref, backLabel = "Back", onBackClick, actions, aside, children, ...props }, ref) => {
1610
1913
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-6 w-full", className), ...props, children: [
1611
1914
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
@@ -1651,7 +1954,7 @@ var DetailPageLayout = React18.forwardRef(
1651
1954
  }
1652
1955
  );
1653
1956
  DetailPageLayout.displayName = "DetailPageLayout";
1654
- var TableToolbar = React18.forwardRef(
1957
+ var TableToolbar = React52.forwardRef(
1655
1958
  ({ className, searchQuery, onSearchChange, searchPlaceholder = "Search...", actions, filters, ...props }, ref) => {
1656
1959
  return /* @__PURE__ */ jsxs(
1657
1960
  "div",
@@ -1720,9 +2023,9 @@ function Drawer({
1720
2023
  width = "max-w-md",
1721
2024
  side = "right"
1722
2025
  }) {
1723
- const containerRef = React18.useRef(null);
1724
- const lastActiveElement = React18.useRef(null);
1725
- React18.useEffect(() => {
2026
+ const containerRef = React52.useRef(null);
2027
+ const lastActiveElement = React52.useRef(null);
2028
+ React52.useEffect(() => {
1726
2029
  if (!open) {
1727
2030
  if (lastActiveElement.current) {
1728
2031
  lastActiveElement.current.focus();
@@ -1841,11 +2144,11 @@ function Drawer({
1841
2144
  }
1842
2145
  Drawer.displayName = "Drawer";
1843
2146
  function CommandPalette({ open, onClose, items, placeholder = "Type a command or search..." }) {
1844
- const [search, setSearch] = React18.useState("");
1845
- const [activeIndex, setActiveIndex] = React18.useState(0);
1846
- const containerRef = React18.useRef(null);
1847
- const listRef = React18.useRef(null);
1848
- React18.useEffect(() => {
2147
+ const [search, setSearch] = React52.useState("");
2148
+ const [activeIndex, setActiveIndex] = React52.useState(0);
2149
+ const containerRef = React52.useRef(null);
2150
+ const listRef = React52.useRef(null);
2151
+ React52.useEffect(() => {
1849
2152
  if (open) {
1850
2153
  setSearch("");
1851
2154
  setActiveIndex(0);
@@ -1854,17 +2157,17 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
1854
2157
  document.body.style.overflow = "";
1855
2158
  }
1856
2159
  }, [open]);
1857
- const filteredItems = React18.useMemo(() => {
2160
+ const filteredItems = React52.useMemo(() => {
1858
2161
  if (!search) return items;
1859
2162
  const cleanSearch = search.toLowerCase();
1860
2163
  return items.filter(
1861
2164
  (item) => item.title.toLowerCase().includes(cleanSearch) || item.subtitle?.toLowerCase().includes(cleanSearch) || item.category?.toLowerCase().includes(cleanSearch)
1862
2165
  );
1863
2166
  }, [search, items]);
1864
- React18.useEffect(() => {
2167
+ React52.useEffect(() => {
1865
2168
  setActiveIndex(0);
1866
2169
  }, [filteredItems]);
1867
- React18.useEffect(() => {
2170
+ React52.useEffect(() => {
1868
2171
  if (!open) return;
1869
2172
  const handleKeyDown = (e) => {
1870
2173
  if (e.key === "Escape") {
@@ -1888,7 +2191,7 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
1888
2191
  document.addEventListener("keydown", handleKeyDown);
1889
2192
  return () => document.removeEventListener("keydown", handleKeyDown);
1890
2193
  }, [open, filteredItems, activeIndex, onClose]);
1891
- React18.useEffect(() => {
2194
+ React52.useEffect(() => {
1892
2195
  if (listRef.current) {
1893
2196
  const activeEl = listRef.current.querySelector('[aria-selected="true"]');
1894
2197
  if (activeEl) {
@@ -2091,7 +2394,7 @@ var fabVariants = cva(
2091
2394
  defaultVariants: { variant: "primary", size: "md", position: "bottom-right" }
2092
2395
  }
2093
2396
  );
2094
- var Fab = React18.forwardRef(
2397
+ var Fab = React52.forwardRef(
2095
2398
  ({ className, variant, size, position, mobileOnly, icon, label, loading, disabled, ...props }, ref) => {
2096
2399
  return /* @__PURE__ */ jsx(
2097
2400
  "button",
@@ -2118,7 +2421,7 @@ var Fab = React18.forwardRef(
2118
2421
  }
2119
2422
  );
2120
2423
  Fab.displayName = "Fab";
2121
- var BottomSheetContext = React18.createContext(null);
2424
+ var BottomSheetContext = React52.createContext(null);
2122
2425
  function BottomSheet({
2123
2426
  open,
2124
2427
  onClose,
@@ -2127,12 +2430,12 @@ function BottomSheet({
2127
2430
  className,
2128
2431
  showClose = true
2129
2432
  }) {
2130
- const titleId = React18.useId();
2131
- const descId = React18.useId();
2132
- const containerRef = React18.useRef(null);
2133
- const lastActiveElement = React18.useRef(null);
2134
- const [mounted, setMounted] = React18.useState(open);
2135
- React18.useEffect(() => {
2433
+ const titleId = React52.useId();
2434
+ const descId = React52.useId();
2435
+ const containerRef = React52.useRef(null);
2436
+ const lastActiveElement = React52.useRef(null);
2437
+ const [mounted, setMounted] = React52.useState(open);
2438
+ React52.useEffect(() => {
2136
2439
  if (open) {
2137
2440
  setMounted(true);
2138
2441
  } else {
@@ -2140,7 +2443,7 @@ function BottomSheet({
2140
2443
  return () => clearTimeout(timer);
2141
2444
  }
2142
2445
  }, [open]);
2143
- React18.useEffect(() => {
2446
+ React52.useEffect(() => {
2144
2447
  if (!open) return;
2145
2448
  lastActiveElement.current = document.activeElement;
2146
2449
  const container = containerRef.current;
@@ -2262,12 +2565,12 @@ function BottomSheet({
2262
2565
  function BottomSheetHeader({ className, ...props }) {
2263
2566
  return /* @__PURE__ */ jsx("div", { className: cn("p-6 flex flex-col gap-1.5 border-b border-border", className), ...props });
2264
2567
  }
2265
- function BottomSheetTitle({ className, id, ...props }) {
2266
- const ctx = React18.useContext(BottomSheetContext);
2267
- return /* @__PURE__ */ jsx("h2", { id: id ?? ctx?.titleId, className: cn("m-0 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props });
2568
+ function BottomSheetTitle({ className, id, children, ...props }) {
2569
+ const ctx = React52.useContext(BottomSheetContext);
2570
+ return /* @__PURE__ */ jsx("h2", { id: id ?? ctx?.titleId, className: cn("m-0 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props, children });
2268
2571
  }
2269
2572
  function BottomSheetDescription({ className, id, ...props }) {
2270
- const ctx = React18.useContext(BottomSheetContext);
2573
+ const ctx = React52.useContext(BottomSheetContext);
2271
2574
  return /* @__PURE__ */ jsx("p", { id: id ?? ctx?.descId, className: cn("m-0 text-[0.9rem] text-muted-foreground", className), ...props });
2272
2575
  }
2273
2576
  function BottomSheetContent({ className, ...props }) {
@@ -2288,7 +2591,7 @@ var kbdVariants = cva(
2288
2591
  defaultVariants: { size: "sm" }
2289
2592
  }
2290
2593
  );
2291
- var Kbd = React18.forwardRef(
2594
+ var Kbd = React52.forwardRef(
2292
2595
  ({ className, size, keys, children, ...props }, ref) => {
2293
2596
  if (keys && keys.length > 0) {
2294
2597
  return /* @__PURE__ */ jsx(
@@ -2305,9 +2608,9 @@ var Kbd = React18.forwardRef(
2305
2608
  }
2306
2609
  );
2307
2610
  Kbd.displayName = "Kbd";
2308
- var CodeBlock = React18.forwardRef(
2611
+ var CodeBlock = React52.forwardRef(
2309
2612
  ({ className, code, language, filename, copyable = true, maxHeight, ...props }, ref) => {
2310
- const [copied, setCopied] = React18.useState(false);
2613
+ const [copied, setCopied] = React52.useState(false);
2311
2614
  const canCopy = copyable && typeof navigator !== "undefined" && !!navigator.clipboard;
2312
2615
  const onCopy = () => {
2313
2616
  navigator.clipboard.writeText(code).then(
@@ -2353,26 +2656,40 @@ var CodeBlock = React18.forwardRef(
2353
2656
  }
2354
2657
  );
2355
2658
  CodeBlock.displayName = "CodeBlock";
2356
- var PopoverContext = React18.createContext(null);
2659
+ function Separator({ orientation = "horizontal", decorative = true, className }) {
2660
+ const semanticProps = decorative ? { role: "none", "aria-hidden": true } : { role: "separator", "aria-orientation": orientation };
2661
+ return /* @__PURE__ */ jsx(
2662
+ "div",
2663
+ {
2664
+ className: cn(
2665
+ "shrink-0 bg-border",
2666
+ orientation === "horizontal" ? "h-px w-full" : "w-px h-full",
2667
+ className
2668
+ ),
2669
+ ...semanticProps
2670
+ }
2671
+ );
2672
+ }
2673
+ var PopoverContext = React52.createContext(null);
2357
2674
  function usePopoverContext(part) {
2358
- const ctx = React18.useContext(PopoverContext);
2675
+ const ctx = React52.useContext(PopoverContext);
2359
2676
  if (!ctx) throw new Error(`${part} must be used inside <Popover>`);
2360
2677
  return ctx;
2361
2678
  }
2362
2679
  function Popover({ children, className, open: openProp, onOpenChange }) {
2363
- const [openState, setOpenState] = React18.useState(false);
2680
+ const [openState, setOpenState] = React52.useState(false);
2364
2681
  const controlled = openProp !== void 0;
2365
2682
  const open = controlled ? openProp : openState;
2366
- const triggerRef = React18.useRef(null);
2367
- const ref = React18.useRef(null);
2368
- const setOpen = React18.useCallback(
2683
+ const triggerRef = React52.useRef(null);
2684
+ const ref = React52.useRef(null);
2685
+ const setOpen = React52.useCallback(
2369
2686
  (v) => {
2370
2687
  if (!controlled) setOpenState(v);
2371
2688
  onOpenChange?.(v);
2372
2689
  },
2373
2690
  [controlled, onOpenChange]
2374
2691
  );
2375
- React18.useEffect(() => {
2692
+ React52.useEffect(() => {
2376
2693
  if (!open) return;
2377
2694
  const onDoc = (e) => {
2378
2695
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -2392,7 +2709,7 @@ function Popover({ children, className, open: openProp, onOpenChange }) {
2392
2709
  }, [open, setOpen]);
2393
2710
  return /* @__PURE__ */ jsx(PopoverContext.Provider, { value: { open, setOpen, triggerRef }, children: /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block", className), children }) });
2394
2711
  }
2395
- var PopoverTrigger = React18.forwardRef(({ onClick, asChild, ...props }, ref) => {
2712
+ var PopoverTrigger = React52.forwardRef(({ onClick, asChild, ...props }, ref) => {
2396
2713
  const ctx = usePopoverContext("PopoverTrigger");
2397
2714
  const setRefs = (node) => {
2398
2715
  ctx.triggerRef.current = node;
@@ -2400,8 +2717,8 @@ var PopoverTrigger = React18.forwardRef(({ onClick, asChild, ...props }, ref) =>
2400
2717
  else if (ref) ref.current = node;
2401
2718
  };
2402
2719
  if (asChild) {
2403
- const child = React18.Children.only(props.children);
2404
- return React18.cloneElement(child, {
2720
+ const child = React52.Children.only(props.children);
2721
+ return React52.cloneElement(child, {
2405
2722
  ref: setRefs,
2406
2723
  ...props,
2407
2724
  ...child.props,
@@ -2452,8 +2769,8 @@ function PopoverContent({
2452
2769
  ...props
2453
2770
  }) {
2454
2771
  const ctx = usePopoverContext("PopoverContent");
2455
- const ref = React18.useRef(null);
2456
- React18.useEffect(() => {
2772
+ const ref = React52.useRef(null);
2773
+ React52.useEffect(() => {
2457
2774
  if (ctx.open) ref.current?.focus();
2458
2775
  }, [ctx.open]);
2459
2776
  if (!ctx.open) return null;
@@ -2474,7 +2791,3275 @@ function PopoverContent({
2474
2791
  }
2475
2792
  );
2476
2793
  }
2794
+ function toArray(v) {
2795
+ if (v == null) return [];
2796
+ return Array.isArray(v) ? v : [v];
2797
+ }
2798
+ var Combobox = React52.forwardRef(
2799
+ function Combobox2(props, ref) {
2800
+ const {
2801
+ options,
2802
+ placeholder = "Select\u2026",
2803
+ searchPlaceholder = "Search\u2026",
2804
+ emptyMessage = "No results",
2805
+ disabled = false,
2806
+ error = false,
2807
+ required = false,
2808
+ className,
2809
+ id,
2810
+ name,
2811
+ "aria-describedby": describedBy
2812
+ } = props;
2813
+ const multiple = props.multiple ?? false;
2814
+ const reactId = React52.useId();
2815
+ const baseId = id ?? reactId;
2816
+ const listId = `${baseId}-listbox`;
2817
+ const optionId = (i) => `${baseId}-opt-${i}`;
2818
+ const controlled = props.value !== void 0;
2819
+ const [internal, setInternal] = React52.useState(() => toArray(props.defaultValue));
2820
+ const selected = controlled ? toArray(props.value) : internal;
2821
+ const [open, setOpen] = React52.useState(false);
2822
+ const [search, setSearch] = React52.useState("");
2823
+ const [activeIndex, setActiveIndex] = React52.useState(0);
2824
+ const containerRef = React52.useRef(null);
2825
+ const triggerRef = React52.useRef(null);
2826
+ const inputRef = React52.useRef(null);
2827
+ const listRef = React52.useRef(null);
2828
+ const setTriggerRef = (node) => {
2829
+ triggerRef.current = node;
2830
+ if (typeof ref === "function") ref(node);
2831
+ else if (ref) ref.current = node;
2832
+ };
2833
+ const filtered = React52.useMemo(() => {
2834
+ const q = search.trim().toLowerCase();
2835
+ return q ? options.filter((o) => o.label.toLowerCase().includes(q)) : options;
2836
+ }, [search, options]);
2837
+ const firstEnabled = React52.useCallback(() => {
2838
+ const i = filtered.findIndex((o) => !o.disabled);
2839
+ return i === -1 ? 0 : i;
2840
+ }, [filtered]);
2841
+ const labelFor = (v) => options.find((o) => o.value === v)?.label ?? v;
2842
+ const emit = (next) => {
2843
+ if (!controlled) setInternal(next);
2844
+ if (props.multiple) {
2845
+ props.onChange?.(next);
2846
+ } else {
2847
+ props.onChange?.(next[0] ?? "");
2848
+ }
2849
+ };
2850
+ const closePopup = React52.useCallback(() => {
2851
+ setOpen(false);
2852
+ setSearch("");
2853
+ }, []);
2854
+ const openPopup = () => {
2855
+ if (!disabled) setOpen(true);
2856
+ };
2857
+ const handleSelect = (opt) => {
2858
+ if (opt.disabled) return;
2859
+ if (multiple) {
2860
+ const next = selected.includes(opt.value) ? selected.filter((v) => v !== opt.value) : [...selected, opt.value];
2861
+ emit(next);
2862
+ inputRef.current?.focus();
2863
+ } else {
2864
+ emit([opt.value]);
2865
+ closePopup();
2866
+ triggerRef.current?.focus();
2867
+ }
2868
+ };
2869
+ const removeValue = (value) => {
2870
+ emit(selected.filter((v) => v !== value));
2871
+ };
2872
+ React52.useEffect(() => {
2873
+ if (open) setActiveIndex(firstEnabled());
2874
+ }, [open, firstEnabled]);
2875
+ React52.useEffect(() => {
2876
+ if (open) inputRef.current?.focus();
2877
+ }, [open]);
2878
+ React52.useEffect(() => {
2879
+ if (!open) return;
2880
+ const onDown = (e) => {
2881
+ if (containerRef.current && !containerRef.current.contains(e.target)) closePopup();
2882
+ };
2883
+ document.addEventListener("mousedown", onDown);
2884
+ return () => document.removeEventListener("mousedown", onDown);
2885
+ }, [open, closePopup]);
2886
+ React52.useEffect(() => {
2887
+ const el = listRef.current?.querySelector('[data-active="true"]');
2888
+ el?.scrollIntoView({ block: "nearest" });
2889
+ }, [activeIndex]);
2890
+ const moveActive = (dir) => {
2891
+ if (filtered.length === 0) return;
2892
+ setActiveIndex((prev) => {
2893
+ let i = prev;
2894
+ for (let step = 0; step < filtered.length; step++) {
2895
+ i = (i + dir + filtered.length) % filtered.length;
2896
+ if (!filtered[i]?.disabled) return i;
2897
+ }
2898
+ return prev;
2899
+ });
2900
+ };
2901
+ const onInputKeyDown = (e) => {
2902
+ switch (e.key) {
2903
+ case "ArrowDown":
2904
+ e.preventDefault();
2905
+ moveActive(1);
2906
+ break;
2907
+ case "ArrowUp":
2908
+ e.preventDefault();
2909
+ moveActive(-1);
2910
+ break;
2911
+ case "Home":
2912
+ e.preventDefault();
2913
+ setActiveIndex(firstEnabled());
2914
+ break;
2915
+ case "End":
2916
+ e.preventDefault();
2917
+ for (let i = filtered.length - 1; i >= 0; i--) {
2918
+ if (!filtered[i].disabled) {
2919
+ setActiveIndex(i);
2920
+ break;
2921
+ }
2922
+ }
2923
+ break;
2924
+ case "Enter":
2925
+ e.preventDefault();
2926
+ if (filtered[activeIndex]) handleSelect(filtered[activeIndex]);
2927
+ break;
2928
+ case "Escape":
2929
+ e.preventDefault();
2930
+ closePopup();
2931
+ triggerRef.current?.focus();
2932
+ break;
2933
+ case "Backspace":
2934
+ if (multiple && search === "" && selected.length > 0) {
2935
+ removeValue(selected[selected.length - 1]);
2936
+ }
2937
+ break;
2938
+ }
2939
+ };
2940
+ const onTriggerKeyDown = (e) => {
2941
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
2942
+ e.preventDefault();
2943
+ openPopup();
2944
+ }
2945
+ };
2946
+ const showPlaceholder = multiple ? selected.length === 0 : !selected[0];
2947
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
2948
+ /* @__PURE__ */ jsxs(
2949
+ "div",
2950
+ {
2951
+ className: cn(
2952
+ "relative flex w-full items-center gap-1.5 flex-wrap border rounded-kj-md bg-surface",
2953
+ "pl-[0.85rem] pr-9 py-[calc(0.45rem*var(--kj-density,1))] min-h-[calc(2.5rem*var(--kj-density,1))]",
2954
+ "transition-[border-color,box-shadow] duration-150",
2955
+ "focus-within:outline-none focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/30",
2956
+ disabled && "bg-muted cursor-not-allowed",
2957
+ error ? "border-danger" : "border-input"
2958
+ ),
2959
+ children: [
2960
+ multiple && selected.map((value) => /* @__PURE__ */ jsxs(
2961
+ "span",
2962
+ {
2963
+ className: "inline-flex items-center gap-1 rounded-kj-sm bg-primary/10 text-primary text-xs font-medium pl-2 pr-1 py-0.5",
2964
+ children: [
2965
+ labelFor(value),
2966
+ /* @__PURE__ */ jsx(
2967
+ "button",
2968
+ {
2969
+ type: "button",
2970
+ "aria-label": `Remove ${labelFor(value)}`,
2971
+ disabled,
2972
+ onClick: (e) => {
2973
+ e.stopPropagation();
2974
+ removeValue(value);
2975
+ },
2976
+ className: "rounded-full p-0.5 leading-none hover:bg-primary/20",
2977
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-3 w-3", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" }) })
2978
+ }
2979
+ )
2980
+ ]
2981
+ },
2982
+ value
2983
+ )),
2984
+ /* @__PURE__ */ jsx(
2985
+ "button",
2986
+ {
2987
+ ref: setTriggerRef,
2988
+ type: "button",
2989
+ id,
2990
+ disabled,
2991
+ "aria-haspopup": "listbox",
2992
+ "aria-expanded": open,
2993
+ "aria-controls": open && filtered.length > 0 ? listId : void 0,
2994
+ "aria-invalid": error || void 0,
2995
+ "aria-required": required || void 0,
2996
+ "aria-describedby": describedBy,
2997
+ onClick: () => open ? closePopup() : openPopup(),
2998
+ onKeyDown: onTriggerKeyDown,
2999
+ className: cn(
3000
+ "flex-1 min-w-[60px] text-left bg-transparent border-none outline-none cursor-pointer text-sm disabled:cursor-not-allowed",
3001
+ showPlaceholder && "text-muted-foreground"
3002
+ ),
3003
+ children: multiple ? selected.length === 0 ? placeholder : "" : (selected[0] ? labelFor(selected[0]) : "") || placeholder
3004
+ }
3005
+ ),
3006
+ name && multiple && selected.map((v) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value: v }, v)),
3007
+ name && !multiple && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected[0] ?? "" }),
3008
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) }) })
3009
+ ]
3010
+ }
3011
+ ),
3012
+ open && /* @__PURE__ */ jsxs("div", { className: "absolute z-40 top-[calc(100%+6px)] left-0 w-full min-w-[12rem] bg-surface border border-border rounded-kj-md shadow-kj-lg overflow-hidden animate-[kjpop_.12s_ease]", children: [
3013
+ /* @__PURE__ */ jsx("div", { className: "p-2 border-b border-border", children: /* @__PURE__ */ jsx(
3014
+ "input",
3015
+ {
3016
+ ref: inputRef,
3017
+ type: "text",
3018
+ role: "combobox",
3019
+ "aria-autocomplete": "list",
3020
+ "aria-expanded": open,
3021
+ "aria-controls": filtered.length > 0 ? listId : void 0,
3022
+ "aria-activedescendant": filtered[activeIndex] ? optionId(activeIndex) : void 0,
3023
+ "aria-label": searchPlaceholder,
3024
+ placeholder: searchPlaceholder,
3025
+ value: search,
3026
+ onChange: (e) => setSearch(e.target.value),
3027
+ onKeyDown: onInputKeyDown,
3028
+ className: "w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-none px-2 py-1"
3029
+ }
3030
+ ) }),
3031
+ filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ jsx(
3032
+ "div",
3033
+ {
3034
+ ref: listRef,
3035
+ id: listId,
3036
+ role: "listbox",
3037
+ "aria-multiselectable": multiple || void 0,
3038
+ "aria-label": placeholder,
3039
+ className: "max-h-60 overflow-y-auto p-1",
3040
+ children: filtered.map((opt, i) => {
3041
+ const isSelected = selected.includes(opt.value);
3042
+ const isActive = i === activeIndex;
3043
+ return /* @__PURE__ */ jsxs(
3044
+ "div",
3045
+ {
3046
+ id: optionId(i),
3047
+ role: "option",
3048
+ "aria-selected": isSelected,
3049
+ "aria-disabled": opt.disabled || void 0,
3050
+ "data-active": isActive || void 0,
3051
+ onMouseEnter: () => !opt.disabled && setActiveIndex(i),
3052
+ onClick: () => handleSelect(opt),
3053
+ className: cn(
3054
+ "flex items-center justify-between gap-2 px-3 py-2 rounded-kj-sm text-sm cursor-pointer select-none",
3055
+ opt.disabled && "text-muted-foreground opacity-50 cursor-not-allowed",
3056
+ !opt.disabled && isActive && "bg-primary/10 text-primary",
3057
+ !opt.disabled && !isActive && "text-foreground hover:bg-muted"
3058
+ ),
3059
+ children: [
3060
+ /* @__PURE__ */ jsx("span", { children: opt.label }),
3061
+ isSelected && /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4 text-primary shrink-0", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m5 12 5 5L20 7" }) })
3062
+ ]
3063
+ },
3064
+ opt.value
3065
+ );
3066
+ })
3067
+ }
3068
+ )
3069
+ ] })
3070
+ ] });
3071
+ }
3072
+ );
3073
+ Combobox.displayName = "Combobox";
3074
+ var ComboboxField = React52.forwardRef(
3075
+ function ComboboxField2({ label, hint, error, required, className, ...props }, ref) {
3076
+ return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(Combobox, { ref, error: !!error, ...props }) });
3077
+ }
3078
+ );
3079
+ ComboboxField.displayName = "ComboboxField";
3080
+
3081
+ // src/components/calendar-internals.ts
3082
+ function startOfDay(d) {
3083
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
3084
+ }
3085
+ function isSameDay(a, b) {
3086
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
3087
+ }
3088
+ function addDays(d, n) {
3089
+ const r = new Date(d);
3090
+ r.setDate(r.getDate() + n);
3091
+ return r;
3092
+ }
3093
+ function addMonths(d, n) {
3094
+ const r = new Date(d);
3095
+ r.setMonth(r.getMonth() + n);
3096
+ return r;
3097
+ }
3098
+ function clampToMonth(year, month, day) {
3099
+ const daysInTarget = new Date(year, month + 1, 0).getDate();
3100
+ return new Date(year, month, Math.min(day, daysInTarget));
3101
+ }
3102
+ function addMonthsClamped(d, n) {
3103
+ return clampToMonth(d.getFullYear(), d.getMonth() + n, d.getDate());
3104
+ }
3105
+ function addYearsClamped(d, n) {
3106
+ return clampToMonth(d.getFullYear() + n, d.getMonth(), d.getDate());
3107
+ }
3108
+ function startOfMonth(d) {
3109
+ return new Date(d.getFullYear(), d.getMonth(), 1);
3110
+ }
3111
+ function startOfWeek(d) {
3112
+ const r = startOfDay(d);
3113
+ r.setDate(r.getDate() - r.getDay());
3114
+ return r;
3115
+ }
3116
+ function buildGridDays(viewMonth) {
3117
+ const gridStart = startOfWeek(startOfMonth(viewMonth));
3118
+ return Array.from({ length: 42 }, (_, i) => addDays(gridStart, i));
3119
+ }
3120
+ function dateKey(d) {
3121
+ return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
3122
+ }
3123
+ function toISODateString(d) {
3124
+ const y = d.getFullYear();
3125
+ const m = String(d.getMonth() + 1).padStart(2, "0");
3126
+ const day = String(d.getDate()).padStart(2, "0");
3127
+ return `${y}-${m}-${day}`;
3128
+ }
3129
+ var monthLabelFormat = new Intl.DateTimeFormat(void 0, { month: "long", year: "numeric" });
3130
+ var fullDateFormat = new Intl.DateTimeFormat(void 0, { dateStyle: "full" });
3131
+ var weekdayShortFormat = new Intl.DateTimeFormat(void 0, { weekday: "short" });
3132
+ var weekdayLongFormat = new Intl.DateTimeFormat(void 0, { weekday: "long" });
3133
+ var weekdayLabels = Array.from({ length: 7 }, (_, i) => {
3134
+ const d = addDays(startOfWeek(/* @__PURE__ */ new Date()), i);
3135
+ return { key: i, short: weekdayShortFormat.format(d), long: weekdayLongFormat.format(d) };
3136
+ });
3137
+ var CalendarGrid = React52.forwardRef(function CalendarGrid2({ viewMonth, isDayDisabled, cellState, onSelectDay, onFocusDay, initialFocusDate, className }, ref) {
3138
+ const gridDays = React52.useMemo(() => buildGridDays(viewMonth), [viewMonth]);
3139
+ const inViewMonth = (d) => d.getMonth() === viewMonth.getMonth() && d.getFullYear() === viewMonth.getFullYear();
3140
+ const [focusedDate, setFocusedDate] = React52.useState(() => startOfDay(initialFocusDate));
3141
+ const pendingFocusRef = React52.useRef(false);
3142
+ const gridRef = React52.useRef(null);
3143
+ const rovingTarget = inViewMonth(focusedDate) && !isDayDisabled(focusedDate) ? focusedDate : gridDays.find((d) => inViewMonth(d) && !isDayDisabled(d)) ?? gridDays.find((d) => inViewMonth(d)) ?? gridDays[0];
3144
+ const moveFocusTo = (next, viaKeyboard) => {
3145
+ pendingFocusRef.current = viaKeyboard;
3146
+ setFocusedDate(next);
3147
+ onFocusDay?.(next);
3148
+ };
3149
+ React52.useEffect(() => {
3150
+ if (!pendingFocusRef.current) return;
3151
+ pendingFocusRef.current = false;
3152
+ const el = gridRef.current?.querySelector(`[data-date="${dateKey(rovingTarget)}"]`);
3153
+ el?.focus();
3154
+ }, [rovingTarget]);
3155
+ const step = (from, delta) => {
3156
+ let next = addDays(from, delta);
3157
+ let guard = 0;
3158
+ while (isDayDisabled(next) && guard < 1e3) {
3159
+ next = addDays(next, delta);
3160
+ guard++;
3161
+ }
3162
+ return isDayDisabled(next) ? from : next;
3163
+ };
3164
+ const selectDay = (d) => {
3165
+ if (isDayDisabled(d)) return;
3166
+ moveFocusTo(d, false);
3167
+ onSelectDay(d);
3168
+ };
3169
+ const onGridKeyDown = (e) => {
3170
+ switch (e.key) {
3171
+ case "ArrowRight":
3172
+ e.preventDefault();
3173
+ moveFocusTo(step(rovingTarget, 1), true);
3174
+ break;
3175
+ case "ArrowLeft":
3176
+ e.preventDefault();
3177
+ moveFocusTo(step(rovingTarget, -1), true);
3178
+ break;
3179
+ case "ArrowDown":
3180
+ e.preventDefault();
3181
+ moveFocusTo(step(rovingTarget, 7), true);
3182
+ break;
3183
+ case "ArrowUp":
3184
+ e.preventDefault();
3185
+ moveFocusTo(step(rovingTarget, -7), true);
3186
+ break;
3187
+ case "Home": {
3188
+ e.preventDefault();
3189
+ const target = startOfWeek(rovingTarget);
3190
+ moveFocusTo(isDayDisabled(target) ? step(target, 1) : target, true);
3191
+ break;
3192
+ }
3193
+ case "End": {
3194
+ e.preventDefault();
3195
+ const target = addDays(startOfWeek(rovingTarget), 6);
3196
+ moveFocusTo(isDayDisabled(target) ? step(target, -1) : target, true);
3197
+ break;
3198
+ }
3199
+ case "PageUp": {
3200
+ e.preventDefault();
3201
+ const target = e.shiftKey ? addYearsClamped(rovingTarget, -1) : addMonthsClamped(rovingTarget, -1);
3202
+ moveFocusTo(isDayDisabled(target) ? step(target, -1) : target, true);
3203
+ break;
3204
+ }
3205
+ case "PageDown": {
3206
+ e.preventDefault();
3207
+ const target = e.shiftKey ? addYearsClamped(rovingTarget, 1) : addMonthsClamped(rovingTarget, 1);
3208
+ moveFocusTo(isDayDisabled(target) ? step(target, 1) : target, true);
3209
+ break;
3210
+ }
3211
+ case "Enter":
3212
+ case " ":
3213
+ e.preventDefault();
3214
+ selectDay(rovingTarget);
3215
+ break;
3216
+ }
3217
+ };
3218
+ return /* @__PURE__ */ jsx("div", { ref, className: cn(className), children: /* @__PURE__ */ jsxs("div", { ref: gridRef, role: "grid", "aria-label": monthLabelFormat.format(viewMonth), onKeyDown: onGridKeyDown, children: [
3219
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7", children: weekdayLabels.map((w) => /* @__PURE__ */ jsx(
3220
+ "div",
3221
+ {
3222
+ role: "columnheader",
3223
+ "aria-label": w.long,
3224
+ className: "h-8 flex items-center justify-center text-xs font-medium text-muted-foreground",
3225
+ children: w.short
3226
+ },
3227
+ w.key
3228
+ )) }),
3229
+ Array.from({ length: 6 }, (_, week) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7", children: gridDays.slice(week * 7, week * 7 + 7).map((day) => {
3230
+ if (!inViewMonth(day)) {
3231
+ return /* @__PURE__ */ jsx("div", { role: "gridcell", "aria-hidden": "true", className: "h-9" }, dateKey(day));
3232
+ }
3233
+ const disabled = isDayDisabled(day);
3234
+ const state = cellState(day);
3235
+ const isRoving = isSameDay(day, rovingTarget);
3236
+ const isEndpoint = !!(state.selected || state.rangeStart || state.rangeEnd);
3237
+ return /* @__PURE__ */ jsx(
3238
+ "button",
3239
+ {
3240
+ type: "button",
3241
+ role: "gridcell",
3242
+ "data-date": dateKey(day),
3243
+ tabIndex: isRoving ? 0 : -1,
3244
+ "aria-selected": isEndpoint,
3245
+ "aria-disabled": disabled || void 0,
3246
+ "aria-label": fullDateFormat.format(day),
3247
+ onMouseEnter: () => onFocusDay?.(day),
3248
+ onClick: () => selectDay(day),
3249
+ className: cn(
3250
+ "h-9 w-9 mx-auto flex items-center justify-center text-sm",
3251
+ state.inRange && !isEndpoint ? "rounded-none" : "rounded-full",
3252
+ "focus:outline-none focus:ring-[3px] focus:ring-ring/30",
3253
+ disabled && "text-muted-foreground opacity-40 cursor-not-allowed",
3254
+ !disabled && isEndpoint && "bg-primary text-primary-foreground",
3255
+ !disabled && state.inRange && !isEndpoint && "bg-primary/15 text-foreground",
3256
+ !disabled && !isEndpoint && !state.inRange && state.today && "ring-1 ring-primary text-primary font-medium",
3257
+ !disabled && !isEndpoint && !state.inRange && !state.today && "text-foreground hover:bg-muted"
3258
+ ),
3259
+ children: day.getDate()
3260
+ },
3261
+ dateKey(day)
3262
+ );
3263
+ }) }, week))
3264
+ ] }) });
3265
+ });
3266
+ CalendarGrid.displayName = "CalendarGrid";
3267
+ var Calendar = React52.forwardRef(
3268
+ function Calendar2({ value, defaultValue, onChange, month, defaultMonth, onMonthChange, min, max, disabledDates, className }, ref) {
3269
+ const controlled = value !== void 0;
3270
+ const [internalValue, setInternalValue] = React52.useState(defaultValue);
3271
+ const selected = controlled ? value : internalValue;
3272
+ const monthControlled = month !== void 0;
3273
+ const [internalMonth, setInternalMonth] = React52.useState(
3274
+ () => startOfMonth(month ?? defaultMonth ?? selected ?? /* @__PURE__ */ new Date())
3275
+ );
3276
+ const viewMonth = monthControlled ? startOfMonth(month) : internalMonth;
3277
+ const setViewMonth = (next) => {
3278
+ const normalized = startOfMonth(next);
3279
+ if (!monthControlled) setInternalMonth(normalized);
3280
+ onMonthChange?.(normalized);
3281
+ };
3282
+ const isDayDisabled = React52.useCallback(
3283
+ (d) => {
3284
+ if (min && d < startOfDay(min)) return true;
3285
+ if (max && d > startOfDay(max)) return true;
3286
+ return disabledDates?.(d) ?? false;
3287
+ },
3288
+ [min, max, disabledDates]
3289
+ );
3290
+ const commitValue = (d) => {
3291
+ if (!controlled) setInternalValue(d);
3292
+ onChange?.(d);
3293
+ };
3294
+ const today = /* @__PURE__ */ new Date();
3295
+ const cellState = (d) => ({
3296
+ selected: selected ? isSameDay(d, selected) : false,
3297
+ today: isSameDay(d, today)
3298
+ });
3299
+ const inViewMonth = (d) => d.getMonth() === viewMonth.getMonth() && d.getFullYear() === viewMonth.getFullYear();
3300
+ const handleFocusDay = (d) => {
3301
+ if (!inViewMonth(d)) setViewMonth(d);
3302
+ };
3303
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-[280px] select-none", className), children: [
3304
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
3305
+ /* @__PURE__ */ jsx(
3306
+ "button",
3307
+ {
3308
+ type: "button",
3309
+ "aria-label": "Previous month",
3310
+ onClick: () => setViewMonth(addMonths(viewMonth, -1)),
3311
+ className: "p-1.5 rounded-kj-sm text-muted-foreground hover:bg-muted hover:text-foreground",
3312
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) })
3313
+ }
3314
+ ),
3315
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: monthLabelFormat.format(viewMonth) }),
3316
+ /* @__PURE__ */ jsx(
3317
+ "button",
3318
+ {
3319
+ type: "button",
3320
+ "aria-label": "Next month",
3321
+ onClick: () => setViewMonth(addMonths(viewMonth, 1)),
3322
+ className: "p-1.5 rounded-kj-sm text-muted-foreground hover:bg-muted hover:text-foreground",
3323
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
3324
+ }
3325
+ )
3326
+ ] }),
3327
+ /* @__PURE__ */ jsx(
3328
+ CalendarGrid,
3329
+ {
3330
+ viewMonth,
3331
+ isDayDisabled,
3332
+ cellState,
3333
+ onSelectDay: commitValue,
3334
+ onFocusDay: handleFocusDay,
3335
+ initialFocusDate: selected ?? /* @__PURE__ */ new Date()
3336
+ }
3337
+ )
3338
+ ] });
3339
+ }
3340
+ );
3341
+ Calendar.displayName = "Calendar";
3342
+ var displayFormat = new Intl.DateTimeFormat(void 0, { dateStyle: "medium" });
3343
+ var DatePicker = React52.forwardRef(
3344
+ function DatePicker2({
3345
+ value,
3346
+ defaultValue,
3347
+ onChange,
3348
+ min,
3349
+ max,
3350
+ disabledDates,
3351
+ placeholder = "Pick a date\u2026",
3352
+ disabled = false,
3353
+ error = false,
3354
+ required = false,
3355
+ className,
3356
+ id,
3357
+ name,
3358
+ "aria-describedby": describedBy
3359
+ }, ref) {
3360
+ const controlled = value !== void 0;
3361
+ const [internalValue, setInternalValue] = React52.useState(defaultValue);
3362
+ const selected = controlled ? value : internalValue;
3363
+ const [open, setOpen] = React52.useState(false);
3364
+ const containerRef = React52.useRef(null);
3365
+ const triggerRef = React52.useRef(null);
3366
+ const setTriggerRef = (node) => {
3367
+ triggerRef.current = node;
3368
+ if (typeof ref === "function") ref(node);
3369
+ else if (ref) ref.current = node;
3370
+ };
3371
+ const commitValue = (d) => {
3372
+ if (!controlled) setInternalValue(d);
3373
+ onChange?.(d);
3374
+ };
3375
+ const closePopup = React52.useCallback(() => setOpen(false), []);
3376
+ const openPopup = () => {
3377
+ if (!disabled) setOpen(true);
3378
+ };
3379
+ const handleCalendarChange = (d) => {
3380
+ commitValue(d);
3381
+ closePopup();
3382
+ triggerRef.current?.focus();
3383
+ };
3384
+ React52.useEffect(() => {
3385
+ if (!open) return;
3386
+ const onDown = (e) => {
3387
+ if (containerRef.current && !containerRef.current.contains(e.target)) closePopup();
3388
+ };
3389
+ document.addEventListener("mousedown", onDown);
3390
+ return () => document.removeEventListener("mousedown", onDown);
3391
+ }, [open, closePopup]);
3392
+ const onTriggerKeyDown = (e) => {
3393
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
3394
+ e.preventDefault();
3395
+ openPopup();
3396
+ } else if (e.key === "Escape" && open) {
3397
+ e.preventDefault();
3398
+ closePopup();
3399
+ }
3400
+ };
3401
+ const onPanelKeyDown = (e) => {
3402
+ if (e.key === "Escape") {
3403
+ e.preventDefault();
3404
+ closePopup();
3405
+ triggerRef.current?.focus();
3406
+ }
3407
+ };
3408
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative inline-block w-full", className), children: [
3409
+ /* @__PURE__ */ jsxs(
3410
+ "button",
3411
+ {
3412
+ ref: setTriggerRef,
3413
+ type: "button",
3414
+ id,
3415
+ disabled,
3416
+ "aria-haspopup": "grid",
3417
+ "aria-expanded": open,
3418
+ "aria-invalid": error || void 0,
3419
+ "aria-required": required || void 0,
3420
+ "aria-describedby": describedBy,
3421
+ onClick: () => open ? closePopup() : openPopup(),
3422
+ onKeyDown: onTriggerKeyDown,
3423
+ className: cn(
3424
+ "w-full flex items-center justify-between gap-2 border rounded-kj-md bg-surface text-left text-sm",
3425
+ "pl-[0.85rem] pr-3 py-[calc(0.6rem*var(--kj-density,1))]",
3426
+ "transition-[border-color,box-shadow] duration-150",
3427
+ "focus:outline-none focus:border-ring focus:ring-[3px] focus:ring-ring/30",
3428
+ disabled && "bg-muted text-muted-foreground cursor-not-allowed",
3429
+ error ? "border-danger" : "border-input",
3430
+ !selected && "text-muted-foreground"
3431
+ ),
3432
+ children: [
3433
+ /* @__PURE__ */ jsx("span", { children: selected ? displayFormat.format(selected) : placeholder }),
3434
+ /* @__PURE__ */ jsxs(
3435
+ "svg",
3436
+ {
3437
+ viewBox: "0 0 24 24",
3438
+ className: "h-4 w-4 text-muted-foreground shrink-0",
3439
+ fill: "none",
3440
+ stroke: "currentColor",
3441
+ strokeWidth: "2",
3442
+ strokeLinecap: "round",
3443
+ strokeLinejoin: "round",
3444
+ children: [
3445
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
3446
+ /* @__PURE__ */ jsx("path", { d: "M3 10h18M8 2v4M16 2v4" })
3447
+ ]
3448
+ }
3449
+ )
3450
+ ]
3451
+ }
3452
+ ),
3453
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected ? toISODateString(selected) : "" }),
3454
+ open && /* @__PURE__ */ jsx(
3455
+ "div",
3456
+ {
3457
+ onKeyDown: onPanelKeyDown,
3458
+ className: "absolute z-40 top-[calc(100%+6px)] left-0 bg-surface border border-border rounded-kj-md shadow-kj-lg p-3 animate-[kjpop_.12s_ease]",
3459
+ children: /* @__PURE__ */ jsx(Calendar, { value: selected, onChange: handleCalendarChange, min, max, disabledDates })
3460
+ }
3461
+ )
3462
+ ] });
3463
+ }
3464
+ );
3465
+ DatePicker.displayName = "DatePicker";
3466
+ var DatePickerField = React52.forwardRef(
3467
+ function DatePickerField2({ label, hint, error, required, className, ...props }, ref) {
3468
+ return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(DatePicker, { ref, error: !!error, ...props }) });
3469
+ }
3470
+ );
3471
+ DatePickerField.displayName = "DatePickerField";
3472
+ function isBetweenExclusive(d, lo, hi) {
3473
+ const t = startOfDay(d).getTime();
3474
+ return t > startOfDay(lo).getTime() && t < startOfDay(hi).getTime();
3475
+ }
3476
+ var RangeCalendar = React52.forwardRef(
3477
+ function RangeCalendar2({ value, defaultValue, onChange, month, defaultMonth, onMonthChange, min, max, disabledDates, className }, ref) {
3478
+ const controlled = value !== void 0;
3479
+ const [internalValue, setInternalValue] = React52.useState(defaultValue ?? {});
3480
+ const range2 = controlled ? value : internalValue;
3481
+ const monthControlled = month !== void 0;
3482
+ const [internalMonth, setInternalMonth] = React52.useState(
3483
+ () => startOfMonth(month ?? defaultMonth ?? range2.start ?? /* @__PURE__ */ new Date())
3484
+ );
3485
+ const leftMonth = monthControlled ? startOfMonth(month) : internalMonth;
3486
+ const rightMonth = addMonths(leftMonth, 1);
3487
+ const setViewMonth = (next) => {
3488
+ const normalized = startOfMonth(next);
3489
+ if (!monthControlled) setInternalMonth(normalized);
3490
+ onMonthChange?.(normalized);
3491
+ };
3492
+ const isDayDisabled = React52.useCallback(
3493
+ (d) => {
3494
+ if (min && d < startOfDay(min)) return true;
3495
+ if (max && d > startOfDay(max)) return true;
3496
+ return disabledDates?.(d) ?? false;
3497
+ },
3498
+ [min, max, disabledDates]
3499
+ );
3500
+ const [pendingStart, setPendingStart] = React52.useState(void 0);
3501
+ const [hoverDate, setHoverDate] = React52.useState(void 0);
3502
+ const commitRange = (next) => {
3503
+ if (!controlled) setInternalValue(next);
3504
+ onChange?.(next);
3505
+ };
3506
+ const handleSelectDay = (d) => {
3507
+ const day = startOfDay(d);
3508
+ if (pendingStart === void 0) {
3509
+ setPendingStart(day);
3510
+ setHoverDate(void 0);
3511
+ return;
3512
+ }
3513
+ if (day < startOfDay(pendingStart)) {
3514
+ setPendingStart(day);
3515
+ setHoverDate(void 0);
3516
+ return;
3517
+ }
3518
+ commitRange({ start: pendingStart, end: day });
3519
+ setPendingStart(void 0);
3520
+ setHoverDate(void 0);
3521
+ };
3522
+ const today = /* @__PURE__ */ new Date();
3523
+ const previewEnd = pendingStart !== void 0 ? hoverDate : void 0;
3524
+ const cellState = (d) => {
3525
+ const isToday = isSameDay(d, today);
3526
+ if (pendingStart !== void 0) {
3527
+ if (previewEnd === void 0) {
3528
+ return { rangeStart: isSameDay(d, pendingStart), today: isToday };
3529
+ }
3530
+ if (startOfDay(previewEnd) < startOfDay(pendingStart)) {
3531
+ return { rangeStart: isSameDay(d, previewEnd), today: isToday };
3532
+ }
3533
+ return {
3534
+ rangeStart: isSameDay(d, pendingStart),
3535
+ rangeEnd: isSameDay(d, previewEnd),
3536
+ inRange: isBetweenExclusive(d, pendingStart, previewEnd),
3537
+ today: isToday
3538
+ };
3539
+ }
3540
+ if (range2.start && range2.end) {
3541
+ return {
3542
+ rangeStart: isSameDay(d, range2.start),
3543
+ rangeEnd: isSameDay(d, range2.end),
3544
+ inRange: isBetweenExclusive(d, range2.start, range2.end),
3545
+ today: isToday
3546
+ };
3547
+ }
3548
+ return { rangeStart: range2.start ? isSameDay(d, range2.start) : false, today: isToday };
3549
+ };
3550
+ const inLockedRange = (d) => d.getMonth() === leftMonth.getMonth() && d.getFullYear() === leftMonth.getFullYear() || d.getMonth() === rightMonth.getMonth() && d.getFullYear() === rightMonth.getFullYear();
3551
+ const handleFocusDay = (d) => {
3552
+ if (pendingStart !== void 0) setHoverDate(d);
3553
+ if (!inLockedRange(d)) setViewMonth(d);
3554
+ };
3555
+ const navButton = (direction) => /* @__PURE__ */ jsx(
3556
+ "button",
3557
+ {
3558
+ type: "button",
3559
+ "aria-label": direction === "prev" ? "Previous month" : "Next month",
3560
+ onClick: () => setViewMonth(addMonths(leftMonth, direction === "prev" ? -1 : 1)),
3561
+ className: "p-1.5 rounded-kj-sm text-muted-foreground hover:bg-muted hover:text-foreground",
3562
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: direction === "prev" ? /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) : /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
3563
+ }
3564
+ );
3565
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex gap-6 select-none", className), children: [
3566
+ /* @__PURE__ */ jsxs("div", { className: "w-[280px]", children: [
3567
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
3568
+ navButton("prev"),
3569
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: monthLabelFormat.format(leftMonth) }),
3570
+ /* @__PURE__ */ jsx("span", { className: "w-[26px]", "aria-hidden": "true" })
3571
+ ] }),
3572
+ /* @__PURE__ */ jsx(
3573
+ CalendarGrid,
3574
+ {
3575
+ viewMonth: leftMonth,
3576
+ isDayDisabled,
3577
+ cellState,
3578
+ onSelectDay: handleSelectDay,
3579
+ onFocusDay: handleFocusDay,
3580
+ initialFocusDate: pendingStart ?? range2.start ?? /* @__PURE__ */ new Date()
3581
+ }
3582
+ )
3583
+ ] }),
3584
+ /* @__PURE__ */ jsxs("div", { className: "w-[280px]", children: [
3585
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
3586
+ /* @__PURE__ */ jsx("span", { className: "w-[26px]", "aria-hidden": "true" }),
3587
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: monthLabelFormat.format(rightMonth) }),
3588
+ navButton("next")
3589
+ ] }),
3590
+ /* @__PURE__ */ jsx(
3591
+ CalendarGrid,
3592
+ {
3593
+ viewMonth: rightMonth,
3594
+ isDayDisabled,
3595
+ cellState,
3596
+ onSelectDay: handleSelectDay,
3597
+ onFocusDay: handleFocusDay,
3598
+ initialFocusDate: pendingStart ?? range2.start ?? /* @__PURE__ */ new Date()
3599
+ }
3600
+ )
3601
+ ] })
3602
+ ] });
3603
+ }
3604
+ );
3605
+ RangeCalendar.displayName = "RangeCalendar";
3606
+ var displayFormat2 = new Intl.DateTimeFormat(void 0, { dateStyle: "medium" });
3607
+ function formatRangeLabel(range2) {
3608
+ if (!range2.start || !range2.end) return void 0;
3609
+ return `${displayFormat2.format(range2.start)} \u2013 ${displayFormat2.format(range2.end)}`;
3610
+ }
3611
+ var DateRangePicker = React52.forwardRef(
3612
+ function DateRangePicker2({
3613
+ value,
3614
+ defaultValue,
3615
+ onChange,
3616
+ min,
3617
+ max,
3618
+ disabledDates,
3619
+ placeholder = "Pick a date range\u2026",
3620
+ disabled = false,
3621
+ error = false,
3622
+ required = false,
3623
+ className,
3624
+ id,
3625
+ name,
3626
+ "aria-describedby": describedBy
3627
+ }, ref) {
3628
+ const controlled = value !== void 0;
3629
+ const [internalValue, setInternalValue] = React52.useState(defaultValue ?? {});
3630
+ const selected = controlled ? value : internalValue;
3631
+ const [open, setOpen] = React52.useState(false);
3632
+ const containerRef = React52.useRef(null);
3633
+ const triggerRef = React52.useRef(null);
3634
+ const setTriggerRef = (node) => {
3635
+ triggerRef.current = node;
3636
+ if (typeof ref === "function") ref(node);
3637
+ else if (ref) ref.current = node;
3638
+ };
3639
+ const commitValue = (r) => {
3640
+ if (!controlled) setInternalValue(r);
3641
+ onChange?.(r);
3642
+ };
3643
+ const closePopup = React52.useCallback(() => setOpen(false), []);
3644
+ const openPopup = () => {
3645
+ if (!disabled) setOpen(true);
3646
+ };
3647
+ const handleRangeChange = (r) => {
3648
+ commitValue(r);
3649
+ closePopup();
3650
+ triggerRef.current?.focus();
3651
+ };
3652
+ React52.useEffect(() => {
3653
+ if (!open) return;
3654
+ const onDown = (e) => {
3655
+ if (containerRef.current && !containerRef.current.contains(e.target)) closePopup();
3656
+ };
3657
+ document.addEventListener("mousedown", onDown);
3658
+ return () => document.removeEventListener("mousedown", onDown);
3659
+ }, [open, closePopup]);
3660
+ const onTriggerKeyDown = (e) => {
3661
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
3662
+ e.preventDefault();
3663
+ openPopup();
3664
+ } else if (e.key === "Escape" && open) {
3665
+ e.preventDefault();
3666
+ closePopup();
3667
+ triggerRef.current?.focus();
3668
+ }
3669
+ };
3670
+ const onPanelKeyDown = (e) => {
3671
+ if (e.key === "Escape") {
3672
+ e.preventDefault();
3673
+ closePopup();
3674
+ triggerRef.current?.focus();
3675
+ }
3676
+ };
3677
+ const label = formatRangeLabel(selected);
3678
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative inline-block w-full", className), children: [
3679
+ /* @__PURE__ */ jsxs(
3680
+ "button",
3681
+ {
3682
+ ref: setTriggerRef,
3683
+ type: "button",
3684
+ id,
3685
+ disabled,
3686
+ "aria-haspopup": "dialog",
3687
+ "aria-expanded": open,
3688
+ "aria-invalid": error || void 0,
3689
+ "aria-required": required || void 0,
3690
+ "aria-describedby": describedBy,
3691
+ onClick: () => open ? closePopup() : openPopup(),
3692
+ onKeyDown: onTriggerKeyDown,
3693
+ className: cn(
3694
+ "w-full flex items-center justify-between gap-2 border rounded-kj-md bg-surface text-left text-sm",
3695
+ "pl-[0.85rem] pr-3 py-[calc(0.6rem*var(--kj-density,1))]",
3696
+ "transition-[border-color,box-shadow] duration-150",
3697
+ "focus:outline-none focus:border-ring focus:ring-[3px] focus:ring-ring/30",
3698
+ disabled && "bg-muted text-muted-foreground cursor-not-allowed",
3699
+ error ? "border-danger" : "border-input",
3700
+ !label && "text-muted-foreground"
3701
+ ),
3702
+ children: [
3703
+ /* @__PURE__ */ jsx("span", { children: label ?? placeholder }),
3704
+ /* @__PURE__ */ jsxs(
3705
+ "svg",
3706
+ {
3707
+ viewBox: "0 0 24 24",
3708
+ className: "h-4 w-4 text-muted-foreground shrink-0",
3709
+ fill: "none",
3710
+ stroke: "currentColor",
3711
+ strokeWidth: "2",
3712
+ strokeLinecap: "round",
3713
+ strokeLinejoin: "round",
3714
+ children: [
3715
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
3716
+ /* @__PURE__ */ jsx("path", { d: "M3 10h18M8 2v4M16 2v4" })
3717
+ ]
3718
+ }
3719
+ )
3720
+ ]
3721
+ }
3722
+ ),
3723
+ name && /* @__PURE__ */ jsxs(Fragment, { children: [
3724
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: `${name}Start`, value: selected.start ? toISODateString(selected.start) : "" }),
3725
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: `${name}End`, value: selected.end ? toISODateString(selected.end) : "" })
3726
+ ] }),
3727
+ open && /* @__PURE__ */ jsx(
3728
+ "div",
3729
+ {
3730
+ onKeyDown: onPanelKeyDown,
3731
+ className: "absolute z-40 top-[calc(100%+6px)] left-0 bg-surface border border-border rounded-kj-md shadow-kj-lg p-3 animate-[kjpop_.12s_ease]",
3732
+ children: /* @__PURE__ */ jsx(RangeCalendar, { value: selected, onChange: handleRangeChange, min, max, disabledDates })
3733
+ }
3734
+ )
3735
+ ] });
3736
+ }
3737
+ );
3738
+ DateRangePicker.displayName = "DateRangePicker";
3739
+ var DateRangePickerField = React52.forwardRef(
3740
+ function DateRangePickerField2({ label, hint, error, required, className, ...props }, ref) {
3741
+ return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(DateRangePicker, { ref, error: !!error, ...props }) });
3742
+ }
3743
+ );
3744
+ DateRangePickerField.displayName = "DateRangePickerField";
3745
+ var Dropzone = React52.forwardRef(
3746
+ function Dropzone2({
3747
+ onFiles,
3748
+ accept,
3749
+ multiple = true,
3750
+ disabled = false,
3751
+ className,
3752
+ children,
3753
+ id,
3754
+ "aria-describedby": describedBy,
3755
+ "aria-invalid": ariaInvalid,
3756
+ "aria-required": ariaRequired
3757
+ }, ref) {
3758
+ const inputRef = React52.useRef(null);
3759
+ const [dragActive, setDragActive] = React52.useState(false);
3760
+ const dragCounter = React52.useRef(0);
3761
+ const invalid = ariaInvalid === true || ariaInvalid === "true";
3762
+ const openPicker = () => {
3763
+ if (!disabled) inputRef.current?.click();
3764
+ };
3765
+ const handleInputChange = (e) => {
3766
+ const files = e.target.files ? Array.from(e.target.files) : [];
3767
+ if (files.length > 0) onFiles(files);
3768
+ e.target.value = "";
3769
+ };
3770
+ const handleDragEnter = (e) => {
3771
+ e.preventDefault();
3772
+ if (disabled) return;
3773
+ dragCounter.current += 1;
3774
+ setDragActive(true);
3775
+ };
3776
+ const handleDragOver = (e) => {
3777
+ e.preventDefault();
3778
+ };
3779
+ const handleDragLeave = (e) => {
3780
+ e.preventDefault();
3781
+ if (disabled) return;
3782
+ dragCounter.current -= 1;
3783
+ if (dragCounter.current <= 0) {
3784
+ dragCounter.current = 0;
3785
+ setDragActive(false);
3786
+ }
3787
+ };
3788
+ const handleDrop = (e) => {
3789
+ e.preventDefault();
3790
+ dragCounter.current = 0;
3791
+ setDragActive(false);
3792
+ if (disabled) return;
3793
+ const files = e.dataTransfer?.files ? Array.from(e.dataTransfer.files) : [];
3794
+ if (files.length > 0) onFiles(files);
3795
+ };
3796
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
3797
+ /* @__PURE__ */ jsx(
3798
+ "button",
3799
+ {
3800
+ ref,
3801
+ type: "button",
3802
+ id,
3803
+ disabled,
3804
+ "aria-describedby": describedBy,
3805
+ "aria-invalid": ariaInvalid,
3806
+ "aria-required": ariaRequired,
3807
+ "data-drag-active": dragActive || void 0,
3808
+ onClick: openPicker,
3809
+ onDragEnter: handleDragEnter,
3810
+ onDragOver: handleDragOver,
3811
+ onDragLeave: handleDragLeave,
3812
+ onDrop: handleDrop,
3813
+ className: cn(
3814
+ "w-full flex flex-col items-center justify-center gap-2 rounded-kj-md border-2 border-dashed px-6 py-8 text-center text-sm transition-colors",
3815
+ "focus:outline-none focus:ring-[3px] focus:ring-ring/30",
3816
+ disabled ? "border-input bg-muted text-muted-foreground cursor-not-allowed" : dragActive ? "border-primary bg-primary/5 text-foreground cursor-pointer" : invalid ? "border-danger bg-surface text-muted-foreground hover:text-foreground cursor-pointer" : "border-input bg-surface text-muted-foreground hover:border-primary hover:text-foreground cursor-pointer"
3817
+ ),
3818
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
3819
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className: "h-7 w-7", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
3820
+ /* @__PURE__ */ jsx("path", { d: "M12 16V4M7 9l5-5 5 5" }),
3821
+ /* @__PURE__ */ jsx("path", { d: "M20 16v3a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-3" })
3822
+ ] }),
3823
+ /* @__PURE__ */ jsxs("span", { children: [
3824
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Drag and drop files here" }),
3825
+ ", or click to browse"
3826
+ ] })
3827
+ ] })
3828
+ }
3829
+ ),
3830
+ /* @__PURE__ */ jsx(
3831
+ "input",
3832
+ {
3833
+ ref: inputRef,
3834
+ type: "file",
3835
+ accept,
3836
+ multiple,
3837
+ disabled,
3838
+ onChange: handleInputChange,
3839
+ className: "sr-only",
3840
+ tabIndex: -1,
3841
+ "aria-hidden": "true"
3842
+ }
3843
+ )
3844
+ ] });
3845
+ }
3846
+ );
3847
+ Dropzone.displayName = "Dropzone";
3848
+
3849
+ // src/components/file-upload-internals.ts
3850
+ var KB = 1024;
3851
+ function formatBytes(bytes) {
3852
+ if (bytes < KB) return `${bytes} B`;
3853
+ const units = ["KB", "MB", "GB", "TB"];
3854
+ let value = bytes / KB;
3855
+ let unitIndex = 0;
3856
+ while (value >= KB && unitIndex < units.length - 1) {
3857
+ value /= KB;
3858
+ unitIndex++;
3859
+ }
3860
+ return `${value.toFixed(1)} ${units[unitIndex]}`;
3861
+ }
3862
+ function matchesAccept(file, accept) {
3863
+ if (!accept || accept.trim() === "") return true;
3864
+ const tokens = accept.split(",").map((t) => t.trim()).filter(Boolean);
3865
+ if (tokens.length === 0) return true;
3866
+ const fileType = file.type.toLowerCase();
3867
+ const fileName = file.name.toLowerCase();
3868
+ return tokens.some((token) => {
3869
+ const t = token.toLowerCase();
3870
+ if (t.startsWith(".")) return fileName.endsWith(t);
3871
+ if (t.endsWith("/*")) return fileType.startsWith(t.slice(0, -1));
3872
+ return fileType === t;
3873
+ });
3874
+ }
3875
+ var idCounter = 0;
3876
+ function generateId() {
3877
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
3878
+ return crypto.randomUUID();
3879
+ }
3880
+ idCounter += 1;
3881
+ return `file-${Date.now().toString(36)}-${idCounter}-${Math.random().toString(36).slice(2, 8)}`;
3882
+ }
3883
+ function validateFiles(files, { accept, maxSize, maxFiles, existingCount = 0, multiple = true }) {
3884
+ const accepted = [];
3885
+ const rejected = [];
3886
+ const effectiveExisting = multiple ? existingCount : 0;
3887
+ for (const file of files) {
3888
+ if (!matchesAccept(file, accept)) {
3889
+ rejected.push({ file, reason: "type" });
3890
+ continue;
3891
+ }
3892
+ if (maxSize != null && file.size > maxSize) {
3893
+ rejected.push({ file, reason: "size" });
3894
+ continue;
3895
+ }
3896
+ if (!multiple) {
3897
+ if (accepted.length === 0) accepted.push(file);
3898
+ else rejected.push({ file, reason: "count" });
3899
+ continue;
3900
+ }
3901
+ const capacity = maxFiles != null ? maxFiles - effectiveExisting - accepted.length : Infinity;
3902
+ if (capacity <= 0) {
3903
+ rejected.push({ file, reason: "count" });
3904
+ continue;
3905
+ }
3906
+ accepted.push(file);
3907
+ }
3908
+ return { accepted, rejected };
3909
+ }
3910
+ function FileIcon({ className }) {
3911
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className, fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
3912
+ /* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
3913
+ /* @__PURE__ */ jsx("path", { d: "M14 2v6h6" })
3914
+ ] });
3915
+ }
3916
+ var FileUpload = React52.forwardRef(
3917
+ function FileUpload2({
3918
+ value,
3919
+ defaultValue,
3920
+ onChange,
3921
+ onReject,
3922
+ accept,
3923
+ maxSize,
3924
+ maxFiles,
3925
+ multiple = true,
3926
+ disabled = false,
3927
+ error = false,
3928
+ required = false,
3929
+ className,
3930
+ id,
3931
+ "aria-describedby": describedBy
3932
+ }, ref) {
3933
+ const controlled = value !== void 0;
3934
+ const [internalItems, setInternalItems] = React52.useState(defaultValue ?? []);
3935
+ const items = controlled ? value : internalItems;
3936
+ const commit = (next) => {
3937
+ if (!controlled) setInternalItems(next);
3938
+ onChange?.(next);
3939
+ };
3940
+ const handleFiles = (files) => {
3941
+ const { accepted, rejected } = validateFiles(files, {
3942
+ accept,
3943
+ maxSize,
3944
+ maxFiles,
3945
+ existingCount: items.length,
3946
+ multiple
3947
+ });
3948
+ if (rejected.length > 0) onReject?.(rejected);
3949
+ if (accepted.length === 0) return;
3950
+ const newItems = accepted.map((file) => ({
3951
+ id: generateId(),
3952
+ file,
3953
+ status: "pending"
3954
+ }));
3955
+ commit(multiple ? [...items, ...newItems] : newItems.slice(0, 1));
3956
+ };
3957
+ const removeItem = (itemId) => {
3958
+ commit(items.filter((it) => it.id !== itemId));
3959
+ };
3960
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full flex flex-col gap-3", className), children: [
3961
+ /* @__PURE__ */ jsx(
3962
+ Dropzone,
3963
+ {
3964
+ ref,
3965
+ onFiles: handleFiles,
3966
+ accept,
3967
+ multiple,
3968
+ disabled,
3969
+ id,
3970
+ "aria-describedby": describedBy,
3971
+ "aria-invalid": error || void 0,
3972
+ "aria-required": required || void 0
3973
+ }
3974
+ ),
3975
+ items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", children: items.map((item) => /* @__PURE__ */ jsxs(
3976
+ "li",
3977
+ {
3978
+ className: "flex items-center gap-3 rounded-kj-md border border-border bg-surface px-3 py-2",
3979
+ children: [
3980
+ /* @__PURE__ */ jsx(FileIcon, { className: "h-5 w-5 shrink-0 text-muted-foreground" }),
3981
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3982
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3983
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-foreground", children: item.file.name }),
3984
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatBytes(item.file.size) })
3985
+ ] }),
3986
+ item.status === "uploading" && item.progress != null && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex items-center gap-2", children: [
3987
+ /* @__PURE__ */ jsx(Progress, { value: item.progress, className: "h-1.5" }),
3988
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
3989
+ Math.round(item.progress),
3990
+ "%"
3991
+ ] })
3992
+ ] }),
3993
+ item.status === "success" && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-success", children: "Uploaded" }),
3994
+ item.status === "error" && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-danger", children: item.error ?? "Upload failed" })
3995
+ ] }),
3996
+ !disabled && /* @__PURE__ */ jsx(
3997
+ "button",
3998
+ {
3999
+ type: "button",
4000
+ "aria-label": `Remove ${item.file.name}`,
4001
+ onClick: () => removeItem(item.id),
4002
+ className: "shrink-0 rounded-kj-sm p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
4003
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
4004
+ }
4005
+ )
4006
+ ]
4007
+ },
4008
+ item.id
4009
+ )) })
4010
+ ] });
4011
+ }
4012
+ );
4013
+ FileUpload.displayName = "FileUpload";
4014
+ var FileUploadField = React52.forwardRef(
4015
+ function FileUploadField2({ label, hint, error, required, className, ...props }, ref) {
4016
+ return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(FileUpload, { ref, error: !!error, ...props }) });
4017
+ }
4018
+ );
4019
+ FileUploadField.displayName = "FileUploadField";
4020
+ var TimelineContext = React52.createContext(null);
4021
+ var TimelineItemContext = React52.createContext(null);
4022
+ var Timeline = React52.forwardRef(
4023
+ ({ align = "left", className, children, ...props }, ref) => {
4024
+ const validChildren = React52.Children.toArray(children).filter(React52.isValidElement);
4025
+ const count = validChildren.length;
4026
+ const childrenWithIndex = validChildren.map((child, index) => {
4027
+ return React52.cloneElement(child, {
4028
+ index
4029
+ });
4030
+ });
4031
+ return /* @__PURE__ */ jsx(TimelineContext.Provider, { value: { align, count }, children: /* @__PURE__ */ jsx(
4032
+ "div",
4033
+ {
4034
+ ref,
4035
+ role: "list",
4036
+ className: cn("flex flex-col gap-6 w-full", className),
4037
+ ...props,
4038
+ children: childrenWithIndex
4039
+ }
4040
+ ) });
4041
+ }
4042
+ );
4043
+ Timeline.displayName = "Timeline";
4044
+ var TimelineItem = React52.forwardRef(
4045
+ ({ className, index = 0, children, ...props }, ref) => {
4046
+ const ctx = React52.useContext(TimelineContext);
4047
+ if (!ctx) throw new Error("TimelineItem must be used within a Timeline");
4048
+ const { align, count } = ctx;
4049
+ const isEven = index % 2 === 0;
4050
+ const isLast = index === count - 1;
4051
+ const gridClass = cn(
4052
+ "grid w-full gap-4 items-start group/timeline-item",
4053
+ align === "left" && "grid-cols-[auto_1fr]",
4054
+ align === "right" && "grid-cols-[1fr_auto] text-right",
4055
+ align === "alternate" && "md:grid-cols-[1fr_auto_1fr] grid-cols-[auto_1fr]"
4056
+ );
4057
+ return /* @__PURE__ */ jsx(TimelineItemContext.Provider, { value: { isEven, align, isLast }, children: /* @__PURE__ */ jsx(
4058
+ "div",
4059
+ {
4060
+ ref,
4061
+ role: "listitem",
4062
+ className: cn(gridClass, className),
4063
+ ...props,
4064
+ children
4065
+ }
4066
+ ) });
4067
+ }
4068
+ );
4069
+ TimelineItem.displayName = "TimelineItem";
4070
+ var TimelineSeparator = React52.forwardRef(
4071
+ ({ className, align: propAlign, ...props }, ref) => {
4072
+ const ctx = React52.useContext(TimelineItemContext);
4073
+ const align = propAlign ?? ctx?.align ?? "left";
4074
+ const separatorClass = cn(
4075
+ "flex flex-col items-center justify-self-center h-full min-h-[40px]",
4076
+ align === "alternate" && "md:col-start-2 col-start-1",
4077
+ align === "left" && "col-start-1",
4078
+ align === "right" && "col-start-2",
4079
+ className
4080
+ );
4081
+ return /* @__PURE__ */ jsx("div", { ref, className: separatorClass, ...props });
4082
+ }
4083
+ );
4084
+ TimelineSeparator.displayName = "TimelineSeparator";
4085
+ var TimelineConnector = React52.forwardRef(
4086
+ ({ className, dashed, ...props }, ref) => {
4087
+ const ctx = React52.useContext(TimelineItemContext);
4088
+ const isLast = ctx?.isLast ?? false;
4089
+ const connectorClass = cn(
4090
+ "grow",
4091
+ isLast && "hidden",
4092
+ dashed ? "w-0 border-l border-dashed border-border" : "w-0.5 bg-border",
4093
+ className
4094
+ );
4095
+ return /* @__PURE__ */ jsx("div", { ref, className: connectorClass, ...props });
4096
+ }
4097
+ );
4098
+ TimelineConnector.displayName = "TimelineConnector";
4099
+ var TimelineDot = React52.forwardRef(
4100
+ ({ className, variant = "default", size = "md", ...props }, ref) => {
4101
+ const sizeClasses2 = {
4102
+ sm: "w-2.5 h-2.5 rounded-full my-1.5",
4103
+ md: "w-4 h-4 rounded-full my-1 border-2 bg-background border-border",
4104
+ lg: "w-8 h-8 rounded-full border flex items-center justify-center bg-background text-[0.8rem]"
4105
+ };
4106
+ const variantClasses = {
4107
+ default: "border-border text-muted-foreground",
4108
+ primary: "border-primary bg-primary/5 text-primary",
4109
+ secondary: "border-secondary bg-secondary/5 text-secondary",
4110
+ success: "border-success bg-success-surface text-success",
4111
+ warning: "border-warning bg-warning-surface text-warning",
4112
+ danger: "border-danger bg-danger-surface text-danger"
4113
+ };
4114
+ return /* @__PURE__ */ jsx(
4115
+ "div",
4116
+ {
4117
+ ref,
4118
+ className: cn(
4119
+ "shrink-0 select-none",
4120
+ sizeClasses2[size],
4121
+ variantClasses[variant],
4122
+ className
4123
+ ),
4124
+ ...props
4125
+ }
4126
+ );
4127
+ }
4128
+ );
4129
+ TimelineDot.displayName = "TimelineDot";
4130
+ var TimelineContent = React52.forwardRef(
4131
+ ({ className, isEven: propIsEven, align: propAlign, ...props }, ref) => {
4132
+ const ctx = React52.useContext(TimelineItemContext);
4133
+ const isEven = propIsEven ?? ctx?.isEven ?? false;
4134
+ const align = propAlign ?? ctx?.align ?? "left";
4135
+ const contentClass = cn(
4136
+ "pb-8 flex flex-col gap-1 w-full",
4137
+ align === "left" && "col-start-2 text-left",
4138
+ align === "right" && "col-start-1 text-right",
4139
+ align === "alternate" && (isEven ? "md:col-start-3 col-start-2 text-left" : "md:col-start-1 col-start-2 md:text-right text-left"),
4140
+ className
4141
+ );
4142
+ return /* @__PURE__ */ jsx("div", { ref, className: contentClass, ...props });
4143
+ }
4144
+ );
4145
+ TimelineContent.displayName = "TimelineContent";
4146
+ var TimelineTitle = React52.forwardRef(
4147
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
4148
+ "h4",
4149
+ {
4150
+ ref,
4151
+ className: cn("m-0 text-sm font-semibold tracking-[-0.01em] text-foreground", className),
4152
+ ...props,
4153
+ children
4154
+ }
4155
+ )
4156
+ );
4157
+ TimelineTitle.displayName = "TimelineTitle";
4158
+ var TimelineTime = React52.forwardRef(
4159
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4160
+ "time",
4161
+ {
4162
+ ref,
4163
+ className: cn("text-[0.75rem] text-muted-foreground font-normal", className),
4164
+ ...props
4165
+ }
4166
+ )
4167
+ );
4168
+ TimelineTime.displayName = "TimelineTime";
4169
+ function useStepper({ initialStep = 0, stepsCount }) {
4170
+ const safeStepsCount = Math.max(1, stepsCount);
4171
+ const [activeStep, setActiveStep] = React52.useState(initialStep);
4172
+ const [completedSteps, setCompletedSteps] = React52.useState([]);
4173
+ const setStep = React52.useCallback((step) => {
4174
+ if (step >= 0 && step < safeStepsCount) {
4175
+ setActiveStep(step);
4176
+ }
4177
+ }, [safeStepsCount]);
4178
+ const nextStep = React52.useCallback(() => {
4179
+ setActiveStep((prev) => Math.min(prev + 1, safeStepsCount - 1));
4180
+ }, [safeStepsCount]);
4181
+ const prevStep = React52.useCallback(() => {
4182
+ setActiveStep((prev) => Math.max(prev - 1, 0));
4183
+ }, []);
4184
+ const completeStep = React52.useCallback((step) => {
4185
+ setCompletedSteps((prev) => {
4186
+ if (step < 0 || step >= safeStepsCount) return prev;
4187
+ if (prev.includes(step)) return prev;
4188
+ return [...prev, step].sort((a, b) => a - b);
4189
+ });
4190
+ }, [safeStepsCount]);
4191
+ const uncompleteStep = React52.useCallback((step) => {
4192
+ setCompletedSteps((prev) => prev.filter((s) => s !== step));
4193
+ }, []);
4194
+ const reset = React52.useCallback(() => {
4195
+ setActiveStep(initialStep);
4196
+ setCompletedSteps([]);
4197
+ }, [initialStep]);
4198
+ return {
4199
+ activeStep,
4200
+ completedSteps,
4201
+ setStep,
4202
+ nextStep,
4203
+ prevStep,
4204
+ completeStep,
4205
+ uncompleteStep,
4206
+ reset,
4207
+ isFirstStep: activeStep === 0,
4208
+ isLastStep: activeStep === safeStepsCount - 1
4209
+ };
4210
+ }
4211
+ var StepperContext = React52.createContext(null);
4212
+ var StepperItemContext = React52.createContext(null);
4213
+ var Stepper = React52.forwardRef(
4214
+ ({ value, defaultValue, onValueChange, orientation = "horizontal", linear = true, children, className, ...props }, ref) => {
4215
+ const [localValue, setLocalValue] = React52.useState(defaultValue ?? 0);
4216
+ const isControlled = value !== void 0;
4217
+ const activeStep = isControlled ? value : localValue;
4218
+ const [steps, setSteps] = React52.useState([]);
4219
+ const [completedSteps, setCompletedSteps] = React52.useState([]);
4220
+ const registerStep = React52.useCallback((val) => {
4221
+ setSteps((prev) => prev.includes(val) ? prev : [...prev, val].sort((a, b) => a - b));
4222
+ }, []);
4223
+ const unregisterStep = React52.useCallback((val) => {
4224
+ setSteps((prev) => prev.filter((v) => v !== val));
4225
+ }, []);
4226
+ const setStep = React52.useCallback(
4227
+ (step) => {
4228
+ if (!isControlled) {
4229
+ setLocalValue(step);
4230
+ }
4231
+ onValueChange?.(step);
4232
+ if (linear) {
4233
+ setCompletedSteps((prev) => {
4234
+ const priorSteps = steps.filter((s) => s < step);
4235
+ const nextCompleted = Array.from(/* @__PURE__ */ new Set([...prev, ...priorSteps])).sort((a, b) => a - b);
4236
+ return nextCompleted;
4237
+ });
4238
+ }
4239
+ },
4240
+ [isControlled, onValueChange, linear, steps]
4241
+ );
4242
+ const contextValue = React52.useMemo(
4243
+ () => ({
4244
+ activeStep,
4245
+ orientation,
4246
+ linear,
4247
+ setStep,
4248
+ completedSteps,
4249
+ registerStep,
4250
+ unregisterStep,
4251
+ steps
4252
+ }),
4253
+ [activeStep, orientation, linear, setStep, completedSteps, registerStep, unregisterStep, steps]
4254
+ );
4255
+ return /* @__PURE__ */ jsx(StepperContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
4256
+ "div",
4257
+ {
4258
+ ref,
4259
+ className: cn(
4260
+ "w-full",
4261
+ orientation === "vertical" ? "flex flex-col gap-6" : "space-y-6",
4262
+ className
4263
+ ),
4264
+ ...props,
4265
+ children
4266
+ }
4267
+ ) });
4268
+ }
4269
+ );
4270
+ Stepper.displayName = "Stepper";
4271
+ var StepperContent = React52.forwardRef(
4272
+ ({ value, className, children, ...props }, ref) => {
4273
+ const ctx = React52.useContext(StepperContext);
4274
+ if (!ctx) throw new Error("StepperContent must be used within a Stepper");
4275
+ const isActive = ctx.activeStep === value;
4276
+ if (!isActive) return null;
4277
+ return /* @__PURE__ */ jsx(
4278
+ "div",
4279
+ {
4280
+ ref,
4281
+ role: "tabpanel",
4282
+ tabIndex: 0,
4283
+ className: cn("focus-visible:outline-none w-full", className),
4284
+ ...props,
4285
+ children
4286
+ }
4287
+ );
4288
+ }
4289
+ );
4290
+ StepperContent.displayName = "StepperContent";
4291
+ var StepperList = React52.forwardRef(
4292
+ ({ className, children, ...props }, ref) => {
4293
+ const ctx = React52.useContext(StepperContext);
4294
+ if (!ctx) throw new Error("StepperList must be used within a Stepper");
4295
+ return /* @__PURE__ */ jsx(
4296
+ "div",
4297
+ {
4298
+ ref,
4299
+ role: "tablist",
4300
+ "aria-orientation": ctx.orientation,
4301
+ className: cn(
4302
+ ctx.orientation === "horizontal" ? "flex items-center justify-between w-full relative gap-4" : "flex flex-col items-start gap-6 relative w-full",
4303
+ className
4304
+ ),
4305
+ ...props,
4306
+ children
4307
+ }
4308
+ );
4309
+ }
4310
+ );
4311
+ StepperList.displayName = "StepperList";
4312
+ var StepperItem = React52.forwardRef(
4313
+ ({ value, disabled = false, className, children, ...props }, ref) => {
4314
+ const ctx = React52.useContext(StepperContext);
4315
+ if (!ctx) throw new Error("StepperItem must be used within a Stepper");
4316
+ const { registerStep, unregisterStep } = ctx;
4317
+ React52.useEffect(() => {
4318
+ registerStep(value);
4319
+ return () => unregisterStep(value);
4320
+ }, [value, registerStep, unregisterStep]);
4321
+ const isLast = ctx.steps[ctx.steps.length - 1] === value;
4322
+ return /* @__PURE__ */ jsx(StepperItemContext.Provider, { value: { value, disabled, isLast }, children: /* @__PURE__ */ jsx(
4323
+ "div",
4324
+ {
4325
+ ref,
4326
+ className: cn(
4327
+ "group relative flex items-center gap-3 z-10",
4328
+ ctx.orientation === "vertical" ? "flex items-start w-full" : "",
4329
+ className
4330
+ ),
4331
+ ...props,
4332
+ children
4333
+ }
4334
+ ) });
4335
+ }
4336
+ );
4337
+ StepperItem.displayName = "StepperItem";
4338
+ var StepperSeparator = React52.forwardRef(
4339
+ ({ className, ...props }, ref) => {
4340
+ const ctx = React52.useContext(StepperContext);
4341
+ if (!ctx) throw new Error("StepperSeparator must be used within a Stepper");
4342
+ return /* @__PURE__ */ jsx(
4343
+ "div",
4344
+ {
4345
+ ref,
4346
+ className: cn(
4347
+ ctx.orientation === "horizontal" ? "flex-1 h-[2px] bg-border transition-all duration-300" : "w-[2px] bg-border absolute left-[17px] top-8 bottom-0 -ml-px z-0",
4348
+ className
4349
+ ),
4350
+ ...props
4351
+ }
4352
+ );
4353
+ }
4354
+ );
4355
+ StepperSeparator.displayName = "StepperSeparator";
4356
+ var StepperTrigger = React52.forwardRef(
4357
+ ({ className, children, onClick, onKeyDown, ...props }, ref) => {
4358
+ const ctx = React52.useContext(StepperContext);
4359
+ const itemCtx = React52.useContext(StepperItemContext);
4360
+ if (!ctx || !itemCtx) {
4361
+ throw new Error("StepperTrigger must be used within a StepperItem inside a Stepper");
4362
+ }
4363
+ const isCompleted = ctx.completedSteps.includes(itemCtx.value) || itemCtx.value < ctx.activeStep;
4364
+ const isActive = ctx.activeStep === itemCtx.value;
4365
+ const isClickable = !ctx.linear || isCompleted || isActive;
4366
+ const disabled = itemCtx.disabled || !isClickable;
4367
+ const handleClick = (e) => {
4368
+ if (disabled) {
4369
+ e.preventDefault();
4370
+ return;
4371
+ }
4372
+ ctx.setStep(itemCtx.value);
4373
+ onClick?.(e);
4374
+ };
4375
+ const handleKeyDown = (e) => {
4376
+ onKeyDown?.(e);
4377
+ const triggers = Array.from(
4378
+ e.currentTarget.closest('[role="tablist"]')?.querySelectorAll('[role="tab"]') || []
4379
+ );
4380
+ const currentIndex = triggers.indexOf(e.currentTarget);
4381
+ if (currentIndex === -1) return;
4382
+ let targetIndex = -1;
4383
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
4384
+ targetIndex = Math.min(currentIndex + 1, triggers.length - 1);
4385
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
4386
+ targetIndex = Math.max(currentIndex - 1, 0);
4387
+ }
4388
+ if (targetIndex !== -1 && triggers[targetIndex]) {
4389
+ e.preventDefault();
4390
+ triggers[targetIndex].focus();
4391
+ }
4392
+ };
4393
+ return /* @__PURE__ */ jsx(
4394
+ "button",
4395
+ {
4396
+ ref,
4397
+ type: "button",
4398
+ role: "tab",
4399
+ "aria-selected": isActive,
4400
+ "aria-disabled": disabled,
4401
+ tabIndex: isActive ? 0 : -1,
4402
+ onClick: handleClick,
4403
+ onKeyDown: handleKeyDown,
4404
+ className: cn(
4405
+ "flex items-center gap-3 bg-transparent border-0 p-0 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-md disabled:cursor-not-allowed disabled:opacity-50",
4406
+ ctx.orientation === "vertical" ? "w-full" : "",
4407
+ className
4408
+ ),
4409
+ ...props,
4410
+ children
4411
+ }
4412
+ );
4413
+ }
4414
+ );
4415
+ StepperTrigger.displayName = "StepperTrigger";
4416
+ var StepperIndicator = React52.forwardRef(
4417
+ ({ className, children, ...props }, ref) => {
4418
+ const ctx = React52.useContext(StepperContext);
4419
+ const itemCtx = React52.useContext(StepperItemContext);
4420
+ if (!ctx || !itemCtx) {
4421
+ throw new Error("StepperIndicator must be used within a StepperItem inside a Stepper");
4422
+ }
4423
+ const isCompleted = ctx.completedSteps.includes(itemCtx.value) || itemCtx.value < ctx.activeStep;
4424
+ const isActive = ctx.activeStep === itemCtx.value;
4425
+ return /* @__PURE__ */ jsx(
4426
+ "span",
4427
+ {
4428
+ ref,
4429
+ className: cn(
4430
+ "flex h-9 w-9 shrink-0 items-center justify-center rounded-full border text-sm font-semibold transition-all duration-200",
4431
+ isCompleted ? "bg-primary border-primary text-primary-foreground" : isActive ? "border-primary text-primary bg-primary/10 ring-2 ring-primary/20" : "border-border text-muted-foreground bg-muted/40",
4432
+ className
4433
+ ),
4434
+ ...props,
4435
+ children: children ?? (isCompleted ? "\u2713" : itemCtx.value + 1)
4436
+ }
4437
+ );
4438
+ }
4439
+ );
4440
+ StepperIndicator.displayName = "StepperIndicator";
4441
+ var StepperTitle = React52.forwardRef(
4442
+ ({ className, ...props }, ref) => {
4443
+ const ctx = React52.useContext(StepperContext);
4444
+ const itemCtx = React52.useContext(StepperItemContext);
4445
+ if (!ctx || !itemCtx) {
4446
+ throw new Error("StepperTitle must be used within a StepperItem inside a Stepper");
4447
+ }
4448
+ const isActive = ctx.activeStep === itemCtx.value;
4449
+ return /* @__PURE__ */ jsx(
4450
+ "span",
4451
+ {
4452
+ ref,
4453
+ className: cn(
4454
+ "text-sm font-medium transition-colors duration-200",
4455
+ isActive ? "text-foreground font-semibold" : "text-muted-foreground",
4456
+ className
4457
+ ),
4458
+ ...props
4459
+ }
4460
+ );
4461
+ }
4462
+ );
4463
+ StepperTitle.displayName = "StepperTitle";
4464
+ var StepperDescription = React52.forwardRef(
4465
+ ({ className, ...props }, ref) => {
4466
+ const itemCtx = React52.useContext(StepperItemContext);
4467
+ if (!itemCtx) throw new Error("StepperDescription must be used within a StepperItem");
4468
+ return /* @__PURE__ */ jsx(
4469
+ "span",
4470
+ {
4471
+ ref,
4472
+ className: cn("text-xs text-muted-foreground text-left block", className),
4473
+ ...props
4474
+ }
4475
+ );
4476
+ }
4477
+ );
4478
+ StepperDescription.displayName = "StepperDescription";
4479
+ var AppShellBanner = React52.forwardRef(
4480
+ ({ className, variant = "primary", closable, onClose, children, ...props }, ref) => {
4481
+ const variantClass = {
4482
+ primary: "bg-primary text-primary-foreground",
4483
+ accent: "bg-secondary text-secondary-foreground",
4484
+ muted: "bg-surface border-b border-border text-muted-foreground"
4485
+ }[variant];
4486
+ return /* @__PURE__ */ jsxs(
4487
+ "div",
4488
+ {
4489
+ ref,
4490
+ className: cn("px-4 py-2 text-sm font-medium text-center flex items-center justify-center relative z-30", variantClass, className),
4491
+ ...props,
4492
+ children: [
4493
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children }),
4494
+ closable && /* @__PURE__ */ jsx(
4495
+ "button",
4496
+ {
4497
+ type: "button",
4498
+ onClick: onClose,
4499
+ className: "p-1 rounded opacity-80 hover:opacity-100 transition-opacity ml-2 cursor-pointer",
4500
+ "aria-label": "Close banner",
4501
+ children: /* @__PURE__ */ jsx("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
4502
+ }
4503
+ )
4504
+ ]
4505
+ }
4506
+ );
4507
+ }
4508
+ );
4509
+ AppShellBanner.displayName = "AppShellBanner";
4510
+ var AppShellHeader = React52.forwardRef(
4511
+ ({
4512
+ className,
4513
+ variant = "glass",
4514
+ position = "sticky",
4515
+ bordered = true,
4516
+ children,
4517
+ mobileNav,
4518
+ ...props
4519
+ }, ref) => {
4520
+ const [mobileOpen, setMobileOpen] = React52.useState(false);
4521
+ React52.useEffect(() => {
4522
+ const handleKeyDown = (e) => {
4523
+ if (e.key === "Escape" && mobileOpen) {
4524
+ setMobileOpen(false);
4525
+ }
4526
+ };
4527
+ window.addEventListener("keydown", handleKeyDown);
4528
+ return () => window.removeEventListener("keydown", handleKeyDown);
4529
+ }, [mobileOpen]);
4530
+ const positionClass = {
4531
+ sticky: "sticky top-0 z-40",
4532
+ fixed: "fixed top-0 left-0 right-0 z-40",
4533
+ static: "relative z-40"
4534
+ }[position];
4535
+ const variantClass = {
4536
+ glass: "bg-surface/80 backdrop-blur-md text-foreground",
4537
+ solid: "bg-surface text-foreground",
4538
+ transparent: "bg-transparent text-foreground"
4539
+ }[variant];
4540
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4541
+ /* @__PURE__ */ jsx(
4542
+ "header",
4543
+ {
4544
+ ref,
4545
+ className: cn(
4546
+ "w-full transition-colors",
4547
+ positionClass,
4548
+ variantClass,
4549
+ bordered && "border-b border-border/60",
4550
+ className
4551
+ ),
4552
+ ...props,
4553
+ children: /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between gap-4", children: [
4554
+ mobileNav && /* @__PURE__ */ jsx(
4555
+ "button",
4556
+ {
4557
+ type: "button",
4558
+ onClick: () => setMobileOpen(true),
4559
+ className: "md:hidden p-2 -ml-2 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-pointer",
4560
+ "aria-label": "Open navigation",
4561
+ children: /* @__PURE__ */ jsxs("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
4562
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }),
4563
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
4564
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
4565
+ ] })
4566
+ }
4567
+ ),
4568
+ children
4569
+ ] })
4570
+ }
4571
+ ),
4572
+ mobileNav && mobileOpen && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 md:hidden flex", children: [
4573
+ /* @__PURE__ */ jsx(
4574
+ "div",
4575
+ {
4576
+ "aria-hidden": "true",
4577
+ onClick: () => setMobileOpen(false),
4578
+ className: "fixed inset-0 bg-overlay backdrop-blur-[2px] transition-opacity duration-300"
4579
+ }
4580
+ ),
4581
+ /* @__PURE__ */ jsxs(
4582
+ "aside",
4583
+ {
4584
+ role: "dialog",
4585
+ "aria-modal": "true",
4586
+ "aria-label": "Mobile Navigation",
4587
+ className: "relative z-10 w-72 h-full bg-surface border-r border-border flex flex-col shadow-kj-lg",
4588
+ children: [
4589
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-border", children: [
4590
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "Navigation" }),
4591
+ /* @__PURE__ */ jsx(
4592
+ "button",
4593
+ {
4594
+ type: "button",
4595
+ onClick: () => setMobileOpen(false),
4596
+ className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-pointer",
4597
+ "aria-label": "Close navigation",
4598
+ children: /* @__PURE__ */ jsx("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
4599
+ }
4600
+ )
4601
+ ] }),
4602
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4", children: mobileNav })
4603
+ ]
4604
+ }
4605
+ )
4606
+ ] })
4607
+ ] });
4608
+ }
4609
+ );
4610
+ AppShellHeader.displayName = "AppShellHeader";
4611
+ var AppShellMain = React52.forwardRef(
4612
+ ({ className, width = "default", padded = true, children, ...props }, ref) => {
4613
+ const widthClass = {
4614
+ default: "max-w-7xl mx-auto w-full",
4615
+ narrow: "max-w-5xl mx-auto w-full",
4616
+ wide: "max-w-screen-2xl mx-auto w-full",
4617
+ full: "max-w-none w-full"
4618
+ }[width];
4619
+ return /* @__PURE__ */ jsx(
4620
+ "main",
4621
+ {
4622
+ ref,
4623
+ className: cn("flex-1", padded && "px-4 sm:px-6 lg:px-8 py-8 md:py-12", widthClass, className),
4624
+ ...props,
4625
+ children
4626
+ }
4627
+ );
4628
+ }
4629
+ );
4630
+ AppShellMain.displayName = "AppShellMain";
4631
+ var AppShellFooter = React52.forwardRef(
4632
+ ({ className, bordered = true, children, ...props }, ref) => {
4633
+ return /* @__PURE__ */ jsx(
4634
+ "footer",
4635
+ {
4636
+ ref,
4637
+ className: cn(
4638
+ "w-full bg-surface text-foreground py-12 mt-auto",
4639
+ bordered && "border-t border-border",
4640
+ className
4641
+ ),
4642
+ ...props,
4643
+ children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children })
4644
+ }
4645
+ );
4646
+ }
4647
+ );
4648
+ AppShellFooter.displayName = "AppShellFooter";
4649
+ var AppShell = React52.forwardRef(
4650
+ ({
4651
+ className,
4652
+ banner,
4653
+ header,
4654
+ children,
4655
+ footer,
4656
+ contentWidth = "default",
4657
+ headerPosition = "sticky",
4658
+ headerVariant = "glass",
4659
+ paddedContent = true,
4660
+ mobileNav,
4661
+ ...props
4662
+ }, ref) => {
4663
+ return /* @__PURE__ */ jsxs(
4664
+ "div",
4665
+ {
4666
+ ref,
4667
+ className: cn("min-h-screen bg-canvas text-foreground flex flex-col antialiased", className),
4668
+ ...props,
4669
+ children: [
4670
+ banner && /* @__PURE__ */ jsx(AppShellBanner, { children: banner }),
4671
+ header && /* @__PURE__ */ jsx(AppShellHeader, { variant: headerVariant, position: headerPosition, mobileNav, children: header }),
4672
+ /* @__PURE__ */ jsx(AppShellMain, { width: contentWidth, padded: paddedContent, children }),
4673
+ footer && /* @__PURE__ */ jsx(AppShellFooter, { children: footer })
4674
+ ]
4675
+ }
4676
+ );
4677
+ }
4678
+ );
4679
+ AppShell.displayName = "AppShell";
4680
+ AppShell.Banner = AppShellBanner;
4681
+ AppShell.Header = AppShellHeader;
4682
+ AppShell.Main = AppShellMain;
4683
+ AppShell.Footer = AppShellFooter;
4684
+ var sizeClasses = {
4685
+ sm: { wrapper: "kj-rating-sm gap-1", star: "w-4 h-4", text: "text-xs" },
4686
+ md: { wrapper: "kj-rating-md gap-1.5", star: "w-5 h-5", text: "text-sm" },
4687
+ lg: { wrapper: "kj-rating-lg gap-2", star: "w-6 h-6", text: "text-base" },
4688
+ xl: { wrapper: "kj-rating-xl gap-2.5", star: "w-8 h-8", text: "text-lg" }
4689
+ };
4690
+ var iconPaths = {
4691
+ star: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
4692
+ heart: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z",
4693
+ flame: "M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z",
4694
+ shield: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",
4695
+ thumb: "M7 10v12M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"
4696
+ };
4697
+ function roundToPrecision(value, precision) {
4698
+ const inv = 1 / precision;
4699
+ return Math.round(value * inv) / inv;
4700
+ }
4701
+ function isCssColor(str) {
4702
+ return str.startsWith("#") || str.startsWith("rgb") || str.startsWith("hsl");
4703
+ }
4704
+ var Rating = React52.forwardRef(
4705
+ ({
4706
+ value: controlledValue,
4707
+ defaultValue = 0,
4708
+ max = 5,
4709
+ precision = 1,
4710
+ size = "md",
4711
+ icon = "star",
4712
+ color = "#f59e0b",
4713
+ emptyColor = "#e5e7eb",
4714
+ allowClear = true,
4715
+ disabled = false,
4716
+ readOnly = false,
4717
+ hoverPreview = true,
4718
+ showValue = false,
4719
+ showCount = false,
4720
+ count,
4721
+ name,
4722
+ onChange,
4723
+ onHoverChange,
4724
+ className,
4725
+ onKeyDown,
4726
+ onMouseLeave,
4727
+ "aria-label": ariaLabel,
4728
+ ...props
4729
+ }, ref) => {
4730
+ const isControlled = controlledValue !== void 0;
4731
+ const [internalValue, setInternalValue] = React52.useState(
4732
+ controlledValue ?? defaultValue
4733
+ );
4734
+ const [hoverValue, setHoverValue] = React52.useState(null);
4735
+ const instanceId = React52.useId();
4736
+ React52.useEffect(() => {
4737
+ if (isControlled) {
4738
+ setInternalValue(controlledValue);
4739
+ }
4740
+ }, [isControlled, controlledValue]);
4741
+ const currentValue = isControlled ? controlledValue : internalValue;
4742
+ const displayValue = hoverPreview && hoverValue !== null ? hoverValue : currentValue;
4743
+ const handleValueChange = (newValue) => {
4744
+ if (disabled || readOnly) return;
4745
+ const clamped = Math.max(0, Math.min(max, roundToPrecision(newValue, precision)));
4746
+ if (!isControlled) {
4747
+ setInternalValue(clamped);
4748
+ }
4749
+ onChange?.(clamped);
4750
+ };
4751
+ const getEventValue = (e, starIndex) => {
4752
+ const target = e.currentTarget;
4753
+ const width = target.offsetWidth;
4754
+ const nativeEv = e.nativeEvent;
4755
+ const offsetX = nativeEv?.offsetX ?? nativeEv?.nativeEvent?.offsetX ?? e.offsetX;
4756
+ if (width > 0 && offsetX !== void 0 && offsetX !== null) {
4757
+ const ratio = Math.max(1e-3, Math.min(1, offsetX / width));
4758
+ const stepInStar = Math.ceil(ratio / precision) * precision;
4759
+ return Math.min(max, Math.max(precision, starIndex - 1 + stepInStar));
4760
+ }
4761
+ return starIndex;
4762
+ };
4763
+ const handleStarClick = (e, starIndex) => {
4764
+ if (disabled || readOnly) return;
4765
+ const targetValue = getEventValue(e, starIndex);
4766
+ let nextValue = targetValue;
4767
+ if (allowClear && targetValue === currentValue) {
4768
+ nextValue = 0;
4769
+ }
4770
+ handleValueChange(nextValue);
4771
+ };
4772
+ const handleStarMouseMove = (e, starIndex) => {
4773
+ if (disabled || readOnly) return;
4774
+ if (hoverPreview) {
4775
+ const hoveredVal = getEventValue(e, starIndex);
4776
+ setHoverValue(hoveredVal);
4777
+ onHoverChange?.(hoveredVal);
4778
+ }
4779
+ };
4780
+ const handleKeyDown = (e) => {
4781
+ if (disabled || readOnly) {
4782
+ onKeyDown?.(e);
4783
+ return;
4784
+ }
4785
+ let nextVal = null;
4786
+ switch (e.key) {
4787
+ case "ArrowRight":
4788
+ case "ArrowUp":
4789
+ nextVal = Math.min(max, currentValue + precision);
4790
+ break;
4791
+ case "ArrowLeft":
4792
+ case "ArrowDown":
4793
+ nextVal = Math.max(0, currentValue - precision);
4794
+ break;
4795
+ case "Home":
4796
+ nextVal = 0;
4797
+ break;
4798
+ case "End":
4799
+ nextVal = max;
4800
+ break;
4801
+ }
4802
+ if (nextVal !== null) {
4803
+ e.preventDefault();
4804
+ handleValueChange(nextVal);
4805
+ }
4806
+ onKeyDown?.(e);
4807
+ };
4808
+ const handleContainerMouseLeave = (e) => {
4809
+ if (hoverPreview) {
4810
+ setHoverValue(null);
4811
+ onHoverChange?.(null);
4812
+ }
4813
+ onMouseLeave?.(e);
4814
+ };
4815
+ const sizing = sizeClasses[size] || sizeClasses.md;
4816
+ return /* @__PURE__ */ jsxs(
4817
+ "div",
4818
+ {
4819
+ ref,
4820
+ role: readOnly ? "img" : "radiogroup",
4821
+ tabIndex: disabled || readOnly ? -1 : 0,
4822
+ "aria-valuenow": displayValue,
4823
+ "aria-valuemin": 0,
4824
+ "aria-valuemax": max,
4825
+ "aria-disabled": disabled || void 0,
4826
+ "aria-readonly": readOnly || void 0,
4827
+ "aria-label": ariaLabel || (readOnly ? `Rated ${displayValue} out of ${max}` : "Rating"),
4828
+ onKeyDown: handleKeyDown,
4829
+ onMouseLeave: handleContainerMouseLeave,
4830
+ className: cn(
4831
+ "inline-flex items-center select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-md",
4832
+ disabled && "opacity-50 cursor-not-allowed",
4833
+ readOnly && "cursor-default",
4834
+ !disabled && !readOnly && "cursor-pointer",
4835
+ sizing.wrapper,
4836
+ className
4837
+ ),
4838
+ ...props,
4839
+ children: [
4840
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: currentValue }),
4841
+ /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-0.5", children: Array.from({ length: max }, (_, index) => {
4842
+ const starIndex = index + 1;
4843
+ const fillFraction = Math.max(
4844
+ 0,
4845
+ Math.min(1, displayValue - index)
4846
+ );
4847
+ const gradientId = `rating-grad-${instanceId}-${index}-${Math.round(
4848
+ fillFraction * 100
4849
+ )}`;
4850
+ const CustomIconComponent = typeof icon === "function" || typeof icon === "object" ? icon : null;
4851
+ const builtInIconType = typeof icon === "string" ? icon : "star";
4852
+ const pathD = iconPaths[builtInIconType] || iconPaths.star;
4853
+ return /* @__PURE__ */ jsx(
4854
+ "button",
4855
+ {
4856
+ type: "button",
4857
+ tabIndex: -1,
4858
+ "data-testid": "rating-star",
4859
+ role: readOnly ? void 0 : "radio",
4860
+ "aria-checked": readOnly ? void 0 : displayValue >= starIndex,
4861
+ "aria-label": `${starIndex} of ${max} stars`,
4862
+ disabled,
4863
+ onClick: (e) => handleStarClick(e, starIndex),
4864
+ onMouseMove: (e) => handleStarMouseMove(e, starIndex),
4865
+ onMouseEnter: (e) => handleStarMouseMove(e, starIndex),
4866
+ className: cn(
4867
+ "p-0.5 focus:outline-none transition-transform active:scale-95 disabled:scale-100",
4868
+ sizing.star
4869
+ ),
4870
+ children: CustomIconComponent ? /* @__PURE__ */ jsx(
4871
+ CustomIconComponent,
4872
+ {
4873
+ className: cn(
4874
+ "w-full h-full",
4875
+ fillFraction >= 0.5 ? !isCssColor(color) && color : !isCssColor(emptyColor) && emptyColor
4876
+ )
4877
+ }
4878
+ ) : /* @__PURE__ */ jsxs(
4879
+ "svg",
4880
+ {
4881
+ viewBox: "0 0 24 24",
4882
+ className: "w-full h-full",
4883
+ xmlns: "http://www.w3.org/2000/svg",
4884
+ children: [
4885
+ fillFraction > 0 && fillFraction < 1 && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
4886
+ "linearGradient",
4887
+ {
4888
+ id: gradientId,
4889
+ x1: "0%",
4890
+ y1: "0%",
4891
+ x2: "100%",
4892
+ y2: "0%",
4893
+ children: [
4894
+ isCssColor(color) ? /* @__PURE__ */ jsx(
4895
+ "stop",
4896
+ {
4897
+ offset: `${fillFraction * 100}%`,
4898
+ stopColor: color
4899
+ }
4900
+ ) : /* @__PURE__ */ jsx(
4901
+ "stop",
4902
+ {
4903
+ offset: `${fillFraction * 100}%`,
4904
+ stopColor: "currentColor",
4905
+ className: color
4906
+ }
4907
+ ),
4908
+ isCssColor(emptyColor) ? /* @__PURE__ */ jsx(
4909
+ "stop",
4910
+ {
4911
+ offset: `${fillFraction * 100}%`,
4912
+ stopColor: emptyColor
4913
+ }
4914
+ ) : /* @__PURE__ */ jsx(
4915
+ "stop",
4916
+ {
4917
+ offset: `${fillFraction * 100}%`,
4918
+ stopColor: "currentColor",
4919
+ className: emptyColor
4920
+ }
4921
+ )
4922
+ ]
4923
+ }
4924
+ ) }),
4925
+ /* @__PURE__ */ jsx(
4926
+ "path",
4927
+ {
4928
+ d: pathD,
4929
+ fill: fillFraction === 1 ? isCssColor(color) ? color : void 0 : fillFraction === 0 ? isCssColor(emptyColor) ? emptyColor : void 0 : `url(#${gradientId})`,
4930
+ className: fillFraction === 1 ? !isCssColor(color) ? color : void 0 : fillFraction === 0 ? !isCssColor(emptyColor) ? emptyColor : void 0 : void 0,
4931
+ stroke: "none"
4932
+ }
4933
+ )
4934
+ ]
4935
+ }
4936
+ )
4937
+ },
4938
+ index
4939
+ );
4940
+ }) }),
4941
+ showValue && /* @__PURE__ */ jsx("span", { className: cn("font-medium text-foreground ml-1.5", sizing.text), children: displayValue }),
4942
+ showCount && count !== void 0 && /* @__PURE__ */ jsxs("span", { className: cn("text-muted-foreground ml-1", sizing.text), children: [
4943
+ "(",
4944
+ count,
4945
+ ")"
4946
+ ] })
4947
+ ]
4948
+ }
4949
+ );
4950
+ }
4951
+ );
4952
+ Rating.displayName = "Rating";
4953
+ var RatingField = React52.forwardRef(
4954
+ ({
4955
+ id: customId,
4956
+ label,
4957
+ helperText,
4958
+ errorMessage,
4959
+ required,
4960
+ className,
4961
+ containerClassName,
4962
+ "aria-describedby": ariaDescribedBy,
4963
+ ...ratingProps
4964
+ }, ref) => {
4965
+ const generatedId = React52.useId();
4966
+ const id = customId || generatedId;
4967
+ const helperId = helperText ? `${id}-helper` : void 0;
4968
+ const errorId = errorMessage ? `${id}-error` : void 0;
4969
+ const describedBy = [ariaDescribedBy, helperId, errorId].filter(Boolean).join(" ") || void 0;
4970
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", containerClassName), children: [
4971
+ label && /* @__PURE__ */ jsx(Label, { htmlFor: id, required, children: label }),
4972
+ /* @__PURE__ */ jsx(
4973
+ Rating,
4974
+ {
4975
+ ref,
4976
+ id,
4977
+ className,
4978
+ "aria-describedby": describedBy,
4979
+ ...ratingProps
4980
+ }
4981
+ ),
4982
+ helperText && /* @__PURE__ */ jsx(Hint, { id: helperId, children: helperText }),
4983
+ errorMessage && /* @__PURE__ */ jsx(Hint, { id: errorId, error: true, children: errorMessage })
4984
+ ] });
4985
+ }
4986
+ );
4987
+ RatingField.displayName = "RatingField";
4988
+ var RatingSummary = React52.forwardRef(
4989
+ ({
4990
+ average,
4991
+ totalCount,
4992
+ distribution,
4993
+ max = 5,
4994
+ size = "md",
4995
+ icon = "star",
4996
+ className,
4997
+ ...props
4998
+ }, ref) => {
4999
+ const formattedAverage = Number.isInteger(average) ? average.toString() : average.toFixed(1);
5000
+ const formattedTotalCount = totalCount.toLocaleString();
5001
+ return /* @__PURE__ */ jsxs(
5002
+ "div",
5003
+ {
5004
+ ref,
5005
+ className: cn(
5006
+ "flex flex-col gap-6 sm:flex-row sm:items-center sm:gap-8 p-4 rounded-xl border bg-card text-card-foreground shadow-sm",
5007
+ className
5008
+ ),
5009
+ ...props,
5010
+ children: [
5011
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg bg-muted/40 text-center min-w-[140px]", children: [
5012
+ /* @__PURE__ */ jsx("div", { className: "text-4xl font-bold tracking-tight text-foreground", children: formattedAverage }),
5013
+ /* @__PURE__ */ jsx("div", { className: "my-1.5", children: /* @__PURE__ */ jsx(
5014
+ Rating,
5015
+ {
5016
+ value: average,
5017
+ max,
5018
+ size,
5019
+ icon,
5020
+ readOnly: true,
5021
+ allowClear: false,
5022
+ hoverPreview: false
5023
+ }
5024
+ ) }),
5025
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
5026
+ formattedTotalCount,
5027
+ " total ratings"
5028
+ ] })
5029
+ ] }),
5030
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col gap-2.5", children: distribution.map((item) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
5031
+ /* @__PURE__ */ jsxs("span", { className: "w-12 text-right font-medium text-foreground whitespace-nowrap", children: [
5032
+ item.stars,
5033
+ " star"
5034
+ ] }),
5035
+ /* @__PURE__ */ jsx(
5036
+ Progress,
5037
+ {
5038
+ value: item.percentage,
5039
+ "aria-label": `${item.stars} star ratings: ${item.percentage}%`,
5040
+ className: "h-2.5 flex-1"
5041
+ }
5042
+ ),
5043
+ /* @__PURE__ */ jsxs("span", { className: "w-12 text-right text-xs text-muted-foreground font-mono", children: [
5044
+ item.percentage,
5045
+ "%"
5046
+ ] })
5047
+ ] }, item.stars)) })
5048
+ ]
5049
+ }
5050
+ );
5051
+ }
5052
+ );
5053
+ RatingSummary.displayName = "RatingSummary";
5054
+ var PricingCard = React52.forwardRef(
5055
+ ({
5056
+ className,
5057
+ name,
5058
+ price,
5059
+ period,
5060
+ description,
5061
+ features = [],
5062
+ variant,
5063
+ badge,
5064
+ popular = false,
5065
+ ctaText = "Get Started",
5066
+ onCtaClick,
5067
+ ctaHref,
5068
+ cta,
5069
+ disabled = false,
5070
+ ...props
5071
+ }, ref) => {
5072
+ const resolvedVariant = variant || (popular ? "featured" : "default");
5073
+ const displayBadge = badge || (popular ? "Most Popular" : void 0);
5074
+ const isFeatured = resolvedVariant === "featured";
5075
+ return /* @__PURE__ */ jsxs(
5076
+ Card,
5077
+ {
5078
+ ref,
5079
+ className: cn(
5080
+ "relative flex flex-col p-6 transition-all duration-300",
5081
+ isFeatured ? "border-amber-500/80 shadow-kj-lg ring-1 ring-amber-500/30" : resolvedVariant === "outline" ? "border-border/80 bg-transparent shadow-none" : "border-border shadow-kj-sm",
5082
+ className
5083
+ ),
5084
+ ...props,
5085
+ children: [
5086
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
5087
+ /* @__PURE__ */ jsxs("div", { children: [
5088
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-bold tracking-tight text-foreground", children: name }),
5089
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: description })
5090
+ ] }),
5091
+ displayBadge && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-amber-500 px-3 py-0.5 text-[0.7rem] font-bold uppercase tracking-wider text-black shadow-sm shrink-0 mt-0.5", children: displayBadge })
5092
+ ] }),
5093
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-baseline gap-1", children: [
5094
+ /* @__PURE__ */ jsx("span", { className: "text-3xl font-extrabold tracking-tight text-foreground", children: price }),
5095
+ period && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: period })
5096
+ ] }),
5097
+ features.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mb-6 flex flex-1 flex-col gap-2.5 p-0 text-xs list-none", children: features.map((feature, idx) => {
5098
+ const text = typeof feature === "string" ? feature : feature.text;
5099
+ const included = typeof feature === "string" ? true : feature.included ?? true;
5100
+ return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2.5", children: [
5101
+ /* @__PURE__ */ jsx(
5102
+ "span",
5103
+ {
5104
+ className: cn(
5105
+ "flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[0.65rem] font-bold",
5106
+ included ? "bg-amber-500/15 text-amber-600 dark:text-amber-400" : "bg-muted text-muted-foreground/60"
5107
+ ),
5108
+ children: included ? "\u2713" : "\u2715"
5109
+ }
5110
+ ),
5111
+ /* @__PURE__ */ jsx("span", { className: cn(included ? "text-foreground" : "text-muted-foreground line-through opacity-70"), children: text })
5112
+ ] }, idx);
5113
+ }) }),
5114
+ /* @__PURE__ */ jsx("div", { className: "mt-auto pt-2", children: cta ? cta : ctaHref ? (
5115
+ /* eslint-disable-next-line jsx-a11y/anchor-is-valid */
5116
+ /* @__PURE__ */ jsx(
5117
+ "a",
5118
+ {
5119
+ href: disabled ? void 0 : ctaHref,
5120
+ "aria-disabled": disabled || void 0,
5121
+ tabIndex: disabled ? -1 : void 0,
5122
+ className: cn(
5123
+ buttonVariants({ variant: isFeatured ? "primary" : "outline" }),
5124
+ "w-full justify-center",
5125
+ disabled && "pointer-events-none opacity-50"
5126
+ ),
5127
+ children: ctaText
5128
+ }
5129
+ )
5130
+ ) : /* @__PURE__ */ jsx(
5131
+ Button,
5132
+ {
5133
+ variant: isFeatured ? "primary" : "outline",
5134
+ className: "w-full justify-center",
5135
+ onClick: onCtaClick,
5136
+ disabled,
5137
+ children: ctaText
5138
+ }
5139
+ ) })
5140
+ ]
5141
+ }
5142
+ );
5143
+ }
5144
+ );
5145
+ PricingCard.displayName = "PricingCard";
5146
+ var BlogCard = React52.forwardRef(
5147
+ ({
5148
+ className,
5149
+ title,
5150
+ description,
5151
+ coverUrl,
5152
+ coverAlt,
5153
+ category,
5154
+ readTime,
5155
+ publishedAt,
5156
+ author,
5157
+ orientation = "vertical",
5158
+ href,
5159
+ ...props
5160
+ }, ref) => {
5161
+ const isHorizontal = orientation === "horizontal";
5162
+ const isInteractive = Boolean(href || props.onClick);
5163
+ return /* @__PURE__ */ jsxs(
5164
+ Card,
5165
+ {
5166
+ ref,
5167
+ as: href ? "a" : "div",
5168
+ href,
5169
+ interactive: isInteractive,
5170
+ className: cn(
5171
+ "group flex overflow-hidden transition-all duration-300",
5172
+ isHorizontal ? "flex-col sm:flex-row" : "flex-col",
5173
+ className
5174
+ ),
5175
+ ...props,
5176
+ children: [
5177
+ coverUrl && /* @__PURE__ */ jsx(
5178
+ "div",
5179
+ {
5180
+ className: cn(
5181
+ "relative overflow-hidden bg-muted shrink-0",
5182
+ isHorizontal ? "w-full sm:w-2/5 min-h-[160px]" : "w-full aspect-[16/9]"
5183
+ ),
5184
+ children: /* @__PURE__ */ jsx(
5185
+ "img",
5186
+ {
5187
+ src: coverUrl,
5188
+ alt: coverAlt || title,
5189
+ className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
5190
+ }
5191
+ )
5192
+ }
5193
+ ),
5194
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col justify-between p-5", children: [
5195
+ /* @__PURE__ */ jsxs("div", { children: [
5196
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2 text-[0.75rem] text-muted-foreground", children: [
5197
+ category && /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: category }),
5198
+ (readTime || publishedAt) && /* @__PURE__ */ jsxs("span", { children: [
5199
+ publishedAt,
5200
+ " ",
5201
+ publishedAt && readTime && "\u2022",
5202
+ " ",
5203
+ readTime
5204
+ ] })
5205
+ ] }),
5206
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-bold tracking-tight text-foreground transition-colors group-hover:text-primary", children: title }),
5207
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: description })
5208
+ ] }),
5209
+ author && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2.5 border-t border-border/60 pt-3", children: [
5210
+ /* @__PURE__ */ jsx(Avatar, { src: author.avatarUrl, alt: author.name, size: "sm", children: author.name.charAt(0) }),
5211
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col text-xs", children: [
5212
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: author.name }),
5213
+ author.role && /* @__PURE__ */ jsx("span", { className: "text-[0.7rem] text-muted-foreground", children: author.role })
5214
+ ] })
5215
+ ] })
5216
+ ] })
5217
+ ]
5218
+ }
5219
+ );
5220
+ }
5221
+ );
5222
+ BlogCard.displayName = "BlogCard";
5223
+ var ProjectCard = React52.forwardRef(
5224
+ ({
5225
+ className,
5226
+ title,
5227
+ description,
5228
+ status,
5229
+ techStack = [],
5230
+ metrics = [],
5231
+ updatedAt,
5232
+ actions,
5233
+ href,
5234
+ ...props
5235
+ }, ref) => {
5236
+ const isInteractive = Boolean(href || props.onClick);
5237
+ return /* @__PURE__ */ jsxs(
5238
+ Card,
5239
+ {
5240
+ ref,
5241
+ as: href ? "a" : "div",
5242
+ href,
5243
+ interactive: isInteractive,
5244
+ className: cn(
5245
+ "group flex flex-col justify-between p-5 transition-all duration-300",
5246
+ className
5247
+ ),
5248
+ ...props,
5249
+ children: [
5250
+ /* @__PURE__ */ jsxs("div", { children: [
5251
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
5252
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-bold tracking-tight text-foreground transition-colors group-hover:text-primary", children: title }),
5253
+ status && /* @__PURE__ */ jsx(Badge, { variant: status.variant || "neutral", className: "shrink-0 text-[0.65rem]", children: status.label })
5254
+ ] }),
5255
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: description }),
5256
+ techStack.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-3.5 flex flex-wrap gap-1.5", children: techStack.map((tech) => /* @__PURE__ */ jsx(Badge, { variant: "neutral", className: "text-[0.65rem] py-0 px-2 font-mono", children: tech }, tech)) })
5257
+ ] }),
5258
+ (metrics.length > 0 || updatedAt || actions) && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center justify-between border-t border-border/60 pt-3 text-[0.75rem] text-muted-foreground", children: [
5259
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
5260
+ metrics.map((metric, idx) => /* @__PURE__ */ jsxs(
5261
+ "span",
5262
+ {
5263
+ className: "flex items-center gap-1 font-mono font-medium",
5264
+ children: [
5265
+ metric.icon && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: metric.icon }),
5266
+ /* @__PURE__ */ jsx("span", { children: metric.value }),
5267
+ metric.label && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70", children: metric.label })
5268
+ ]
5269
+ },
5270
+ metric.label ? `${metric.label}-${idx}` : idx
5271
+ )),
5272
+ updatedAt && /* @__PURE__ */ jsx("span", { children: updatedAt })
5273
+ ] }),
5274
+ actions && /* @__PURE__ */ jsx(
5275
+ "div",
5276
+ {
5277
+ onClick: (e) => e.stopPropagation(),
5278
+ className: "flex items-center gap-1 shrink-0",
5279
+ children: actions
5280
+ }
5281
+ )
5282
+ ] })
5283
+ ]
5284
+ }
5285
+ );
5286
+ }
5287
+ );
5288
+ ProjectCard.displayName = "ProjectCard";
5289
+ function formatRelativeTime(date) {
5290
+ const d = typeof date === "string" ? new Date(date) : date;
5291
+ if (isNaN(d.getTime())) return "";
5292
+ const diffMs = Date.now() - d.getTime();
5293
+ const diffMin = Math.floor(diffMs / 6e4);
5294
+ const diffH = Math.floor(diffMs / 36e5);
5295
+ const diffD = Math.floor(diffMs / 864e5);
5296
+ if (diffMin < 1) return "Just now";
5297
+ if (diffMin < 60) return `${diffMin} min ago`;
5298
+ if (diffH < 24) return diffH === 1 ? "1 hour ago" : `${diffH} hours ago`;
5299
+ if (diffD === 1) return "Yesterday";
5300
+ if (diffD < 7) return `${diffD} days ago`;
5301
+ return d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
5302
+ }
5303
+ function useInboxState(initial) {
5304
+ const [items, setItems] = React52.useState(initial);
5305
+ const unreadCount = items.filter((i) => !i.read).length;
5306
+ const markRead = React52.useCallback((id) => {
5307
+ setItems(
5308
+ (prev) => prev.map((item) => item.id === id ? { ...item, read: true } : item)
5309
+ );
5310
+ }, []);
5311
+ const markAllRead = React52.useCallback(() => {
5312
+ setItems((prev) => prev.map((item) => ({ ...item, read: true })));
5313
+ }, []);
5314
+ const dismiss = React52.useCallback((id) => {
5315
+ setItems((prev) => prev.filter((item) => item.id !== id));
5316
+ }, []);
5317
+ return { items, unreadCount, markRead, markAllRead, dismiss };
5318
+ }
5319
+ var InboxContext = React52.createContext(null);
5320
+ function useInboxContext(part) {
5321
+ const ctx = React52.useContext(InboxContext);
5322
+ if (!ctx) throw new Error(`${part} must be used inside <InboxPopover>`);
5323
+ return ctx;
5324
+ }
5325
+ function InboxPopover({
5326
+ children,
5327
+ className,
5328
+ open: openProp,
5329
+ onOpenChange
5330
+ }) {
5331
+ const [openState, setOpenState] = React52.useState(false);
5332
+ const controlled = openProp !== void 0;
5333
+ const open = controlled ? openProp : openState;
5334
+ const triggerRef = React52.useRef(null);
5335
+ const ref = React52.useRef(null);
5336
+ const setOpen = React52.useCallback(
5337
+ (v) => {
5338
+ if (!controlled) setOpenState(v);
5339
+ onOpenChange?.(v);
5340
+ },
5341
+ [controlled, onOpenChange]
5342
+ );
5343
+ React52.useEffect(() => {
5344
+ if (!open) return;
5345
+ const onDoc = (e) => {
5346
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
5347
+ };
5348
+ const onEsc = (e) => {
5349
+ if (e.key === "Escape") {
5350
+ setOpen(false);
5351
+ triggerRef.current?.focus();
5352
+ }
5353
+ };
5354
+ document.addEventListener("mousedown", onDoc);
5355
+ document.addEventListener("keydown", onEsc);
5356
+ return () => {
5357
+ document.removeEventListener("mousedown", onDoc);
5358
+ document.removeEventListener("keydown", onEsc);
5359
+ };
5360
+ }, [open, setOpen]);
5361
+ const ctxValue = React52.useMemo(() => ({ open, setOpen, triggerRef }), [open, setOpen]);
5362
+ return /* @__PURE__ */ jsx(InboxContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block", className), children }) });
5363
+ }
5364
+ function InboxTrigger({
5365
+ unreadCount,
5366
+ label = "Notifications",
5367
+ className,
5368
+ onClick,
5369
+ ...props
5370
+ }) {
5371
+ const ctx = useInboxContext("InboxTrigger");
5372
+ const setRef = (node) => {
5373
+ ctx.triggerRef.current = node;
5374
+ };
5375
+ const displayCount = unreadCount == null || unreadCount <= 0 ? null : unreadCount > 99 ? "99+" : String(unreadCount);
5376
+ const defaultLabel = displayCount ? `${label} (${displayCount} unread)` : label;
5377
+ return /* @__PURE__ */ jsxs(
5378
+ "button",
5379
+ {
5380
+ ref: setRef,
5381
+ type: "button",
5382
+ "aria-label": defaultLabel,
5383
+ "aria-haspopup": "dialog",
5384
+ "aria-expanded": ctx.open,
5385
+ className: cn(
5386
+ "relative inline-flex items-center justify-center w-9 h-9 rounded-full",
5387
+ "bg-surface border border-border text-muted-foreground",
5388
+ "hover:bg-muted hover:text-foreground transition-colors",
5389
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
5390
+ className
5391
+ ),
5392
+ onClick: (e) => {
5393
+ ctx.setOpen(!ctx.open);
5394
+ onClick?.(e);
5395
+ },
5396
+ ...props,
5397
+ children: [
5398
+ /* @__PURE__ */ jsxs(
5399
+ "svg",
5400
+ {
5401
+ width: 16,
5402
+ height: 16,
5403
+ viewBox: "0 0 24 24",
5404
+ fill: "none",
5405
+ stroke: "currentColor",
5406
+ strokeWidth: 2,
5407
+ strokeLinecap: "round",
5408
+ strokeLinejoin: "round",
5409
+ "aria-hidden": "true",
5410
+ children: [
5411
+ /* @__PURE__ */ jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
5412
+ /* @__PURE__ */ jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
5413
+ ]
5414
+ }
5415
+ ),
5416
+ displayCount != null && /* @__PURE__ */ jsx(
5417
+ "span",
5418
+ {
5419
+ "aria-hidden": "true",
5420
+ className: cn(
5421
+ "absolute -top-0.5 -right-0.5 min-w-[16px] h-4 px-1",
5422
+ "rounded-full bg-primary text-primary-foreground",
5423
+ "text-[9px] font-bold leading-4 flex items-center justify-center"
5424
+ ),
5425
+ children: displayCount
5426
+ }
5427
+ )
5428
+ ]
5429
+ }
5430
+ );
5431
+ }
5432
+ function NotificationItem({ item, onDismiss, className }) {
5433
+ const leadingSlot = item.avatarSrc ? /* @__PURE__ */ jsx(
5434
+ "img",
5435
+ {
5436
+ src: item.avatarSrc,
5437
+ alt: item.avatarFallback ?? "",
5438
+ className: "w-7 h-7 rounded-full object-cover flex-shrink-0"
5439
+ }
5440
+ ) : item.avatarFallback ? /* @__PURE__ */ jsx("span", { className: "w-7 h-7 rounded-full bg-primary/20 text-primary text-[10px] font-bold flex items-center justify-center flex-shrink-0 select-none", children: item.avatarFallback }) : item.icon ? /* @__PURE__ */ jsx("span", { className: "w-7 h-7 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 text-primary", children: item.icon }) : null;
5441
+ const textBlock = /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
5442
+ /* @__PURE__ */ jsx("p", { className: cn("text-sm leading-snug", item.read ? "text-muted-foreground" : "text-foreground"), children: item.title }),
5443
+ item.body && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 truncate", children: item.body }),
5444
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: formatRelativeTime(item.timestamp) })
5445
+ ] });
5446
+ const sharedClass = cn(
5447
+ "group relative flex items-start gap-2.5 px-3.5 py-2.5 w-full text-left",
5448
+ "border-b border-border last:border-0",
5449
+ !item.read && "bg-primary/[0.04]",
5450
+ "hover:bg-muted/50 transition-colors",
5451
+ className
5452
+ );
5453
+ const mainContent = /* @__PURE__ */ jsxs(Fragment, { children: [
5454
+ leadingSlot,
5455
+ textBlock
5456
+ ] });
5457
+ const mainAction = item.href ? /* @__PURE__ */ jsx(
5458
+ "a",
5459
+ {
5460
+ href: item.href,
5461
+ onClick: () => item.onClick?.(item.id),
5462
+ className: "flex-1 flex items-start gap-2.5 min-w-0 text-left outline-none",
5463
+ children: mainContent
5464
+ }
5465
+ ) : /* @__PURE__ */ jsx(
5466
+ "button",
5467
+ {
5468
+ type: "button",
5469
+ onClick: () => item.onClick?.(item.id),
5470
+ className: "flex-1 flex items-start gap-2.5 min-w-0 text-left outline-none",
5471
+ children: mainContent
5472
+ }
5473
+ );
5474
+ return /* @__PURE__ */ jsxs("div", { className: sharedClass, children: [
5475
+ mainAction,
5476
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 flex-shrink-0 mt-0.5", children: [
5477
+ /* @__PURE__ */ jsx(
5478
+ "button",
5479
+ {
5480
+ type: "button",
5481
+ "aria-label": "Dismiss",
5482
+ onClick: (e) => {
5483
+ e.preventDefault();
5484
+ e.stopPropagation();
5485
+ onDismiss?.(item.id);
5486
+ },
5487
+ className: cn(
5488
+ "text-muted-foreground hover:text-foreground transition-colors",
5489
+ "opacity-0 group-hover:opacity-100 focus:opacity-100",
5490
+ "text-base leading-none p-0.5 rounded"
5491
+ ),
5492
+ children: "\xD7"
5493
+ }
5494
+ ),
5495
+ /* @__PURE__ */ jsx(
5496
+ "span",
5497
+ {
5498
+ "aria-label": item.read ? void 0 : "Unread",
5499
+ "aria-hidden": item.read ? true : void 0,
5500
+ className: cn(
5501
+ "w-[7px] h-[7px] rounded-full flex-shrink-0 mt-1",
5502
+ item.read ? "border border-border" : "bg-primary"
5503
+ )
5504
+ }
5505
+ )
5506
+ ] })
5507
+ ] });
5508
+ }
5509
+ function InboxContent({
5510
+ items,
5511
+ onMarkAllRead,
5512
+ onDismiss,
5513
+ viewAllHref,
5514
+ viewAllLabel = "View all notifications",
5515
+ emptyState,
5516
+ width = 360,
5517
+ maxHeight = 320,
5518
+ className
5519
+ }) {
5520
+ const ctx = useInboxContext("InboxContent");
5521
+ const ref = React52.useRef(null);
5522
+ React52.useEffect(() => {
5523
+ if (ctx.open) ref.current?.focus();
5524
+ }, [ctx.open]);
5525
+ if (!ctx.open) return null;
5526
+ const allRead = items.length === 0 || items.every((i) => i.read);
5527
+ const defaultEmptyState = /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-10 px-5 text-center", children: [
5528
+ /* @__PURE__ */ jsx("span", { className: "w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3 text-muted-foreground", children: /* @__PURE__ */ jsxs("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
5529
+ /* @__PURE__ */ jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
5530
+ /* @__PURE__ */ jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
5531
+ ] }) }),
5532
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "All caught up" }),
5533
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: "No new notifications" })
5534
+ ] });
5535
+ return /* @__PURE__ */ jsxs(
5536
+ "div",
5537
+ {
5538
+ ref,
5539
+ role: "dialog",
5540
+ "aria-label": "Notifications",
5541
+ tabIndex: -1,
5542
+ style: { width },
5543
+ className: cn(
5544
+ "absolute right-0 top-[calc(100%+6px)] z-40",
5545
+ "bg-surface border border-border rounded-kj-md shadow-kj-lg outline-none",
5546
+ "animate-[kjpop_.12s_ease]",
5547
+ className
5548
+ ),
5549
+ children: [
5550
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3.5 py-2.5 border-b border-border", children: [
5551
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Notifications" }),
5552
+ /* @__PURE__ */ jsx(
5553
+ "button",
5554
+ {
5555
+ type: "button",
5556
+ disabled: allRead,
5557
+ "aria-disabled": allRead,
5558
+ onClick: allRead ? void 0 : onMarkAllRead,
5559
+ className: cn(
5560
+ "text-xs font-medium transition-colors",
5561
+ allRead ? "text-muted-foreground cursor-not-allowed" : "text-primary hover:text-primary/80"
5562
+ ),
5563
+ children: "Mark all read"
5564
+ }
5565
+ )
5566
+ ] }),
5567
+ items.length === 0 ? emptyState ?? defaultEmptyState : /* @__PURE__ */ jsx(
5568
+ "div",
5569
+ {
5570
+ className: "overflow-y-auto",
5571
+ style: { maxHeight },
5572
+ children: items.map((item) => /* @__PURE__ */ jsx(
5573
+ NotificationItem,
5574
+ {
5575
+ item,
5576
+ onDismiss
5577
+ },
5578
+ item.id
5579
+ ))
5580
+ }
5581
+ ),
5582
+ items.length > 0 && viewAllHref && /* @__PURE__ */ jsx("div", { className: "border-t border-border px-3.5 py-2.5 text-center", children: /* @__PURE__ */ jsxs(
5583
+ "a",
5584
+ {
5585
+ href: viewAllHref,
5586
+ className: "text-xs font-medium text-primary hover:text-primary/80 transition-colors",
5587
+ children: [
5588
+ viewAllLabel,
5589
+ " \u2192"
5590
+ ]
5591
+ }
5592
+ ) })
5593
+ ]
5594
+ }
5595
+ );
5596
+ }
5597
+ var DEFAULT_COLOR_SWATCHES = [
5598
+ "#EF4444",
5599
+ // Red
5600
+ "#F97316",
5601
+ // Orange
5602
+ "#F59E0B",
5603
+ // Amber
5604
+ "#10B981",
5605
+ // Emerald
5606
+ "#14B8A6",
5607
+ // Teal
5608
+ "#06B6D4",
5609
+ // Sky
5610
+ "#3B82F6",
5611
+ // Blue
5612
+ "#6366F1",
5613
+ // Indigo
5614
+ "#8B5CF6",
5615
+ // Purple
5616
+ "#EC4899",
5617
+ // Pink
5618
+ "#64748B",
5619
+ // Slate
5620
+ "#1E293B"
5621
+ // Charcoal
5622
+ ];
5623
+ function isValidHex(hex) {
5624
+ const trimmed = hex.trim();
5625
+ const clean = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
5626
+ return /^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(clean);
5627
+ }
5628
+ function normalizeHex(hex) {
5629
+ let clean = hex.trim();
5630
+ if (!clean.startsWith("#")) clean = `#${clean}`;
5631
+ if (/^#[0-9A-Fa-f]{3}$/.test(clean)) {
5632
+ clean = `#${clean[1]}${clean[1]}${clean[2]}${clean[2]}${clean[3]}${clean[3]}`;
5633
+ }
5634
+ return clean.toUpperCase();
5635
+ }
5636
+ function hexToHsl(hex) {
5637
+ if (!isValidHex(hex)) return { h: 0, s: 0, l: 0 };
5638
+ const normalized = normalizeHex(hex);
5639
+ const r = parseInt(normalized.slice(1, 3), 16) / 255;
5640
+ const g = parseInt(normalized.slice(3, 5), 16) / 255;
5641
+ const b = parseInt(normalized.slice(5, 7), 16) / 255;
5642
+ const max = Math.max(r, g, b);
5643
+ const min = Math.min(r, g, b);
5644
+ let h = 0;
5645
+ let s = 0;
5646
+ const l = (max + min) / 2;
5647
+ if (max !== min) {
5648
+ const d = max - min;
5649
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
5650
+ switch (max) {
5651
+ case r:
5652
+ h = (g - b) / d + (g < b ? 6 : 0);
5653
+ break;
5654
+ case g:
5655
+ h = (b - r) / d + 2;
5656
+ break;
5657
+ case b:
5658
+ h = (r - g) / d + 4;
5659
+ break;
5660
+ }
5661
+ h /= 6;
5662
+ }
5663
+ return {
5664
+ h: Math.round(h * 360),
5665
+ s: Math.round(s * 100),
5666
+ l: Math.round(l * 100)
5667
+ };
5668
+ }
5669
+ function hslToHex(h, s, l) {
5670
+ const normalizedH = (h % 360 + 360) % 360;
5671
+ const sPct = s / 100;
5672
+ const lPct = l / 100;
5673
+ const c = (1 - Math.abs(2 * lPct - 1)) * sPct;
5674
+ const x = c * (1 - Math.abs(normalizedH / 60 % 2 - 1));
5675
+ const m = lPct - c / 2;
5676
+ let r = 0;
5677
+ let g = 0;
5678
+ let b = 0;
5679
+ if (0 <= normalizedH && normalizedH < 60) {
5680
+ r = c;
5681
+ g = x;
5682
+ b = 0;
5683
+ } else if (60 <= normalizedH && normalizedH < 120) {
5684
+ r = x;
5685
+ g = c;
5686
+ b = 0;
5687
+ } else if (120 <= normalizedH && normalizedH < 180) {
5688
+ r = 0;
5689
+ g = c;
5690
+ b = x;
5691
+ } else if (180 <= normalizedH && normalizedH < 240) {
5692
+ r = 0;
5693
+ g = x;
5694
+ b = c;
5695
+ } else if (240 <= normalizedH && normalizedH < 300) {
5696
+ r = x;
5697
+ g = 0;
5698
+ b = c;
5699
+ } else if (300 <= normalizedH && normalizedH < 360) {
5700
+ r = c;
5701
+ g = 0;
5702
+ b = x;
5703
+ }
5704
+ const toHex = (n) => {
5705
+ const hex = Math.round((n + m) * 255).toString(16);
5706
+ return hex.length === 1 ? `0${hex}` : hex;
5707
+ };
5708
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
5709
+ }
5710
+ var ColorPickerSwatch = React52.forwardRef(({ color, selected = false, size = "md", className, style, ...props }, ref) => {
5711
+ const sizeClasses2 = {
5712
+ sm: "w-5 h-5",
5713
+ md: "w-7 h-7",
5714
+ lg: "w-9 h-9"
5715
+ };
5716
+ return /* @__PURE__ */ jsx(
5717
+ "button",
5718
+ {
5719
+ ref,
5720
+ type: "button",
5721
+ "aria-pressed": selected,
5722
+ style: { backgroundColor: color, ...style },
5723
+ className: cn(
5724
+ "relative shrink-0 rounded-full border border-border/50 shadow-sm transition-transform hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary flex items-center justify-center text-white text-xs font-bold",
5725
+ sizeClasses2[size],
5726
+ selected && "ring-2 ring-primary ring-offset-2 ring-offset-surface",
5727
+ className
5728
+ ),
5729
+ ...props,
5730
+ children: selected && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2713" })
5731
+ }
5732
+ );
5733
+ });
5734
+ ColorPickerSwatch.displayName = "ColorPickerSwatch";
5735
+ var ColorPicker = React52.forwardRef(
5736
+ ({
5737
+ value,
5738
+ defaultValue = "#3B82F6",
5739
+ onChange,
5740
+ swatches = DEFAULT_COLOR_SWATCHES,
5741
+ disabled = false,
5742
+ showHexInput = true,
5743
+ className,
5744
+ ...props
5745
+ }, ref) => {
5746
+ const isControlled = value !== void 0;
5747
+ const [localColor, setLocalColor] = React52.useState(
5748
+ normalizeHex(defaultValue)
5749
+ );
5750
+ const color = isControlled ? normalizeHex(value) : localColor;
5751
+ const [hexInputValue, setHexInputValue] = React52.useState(color);
5752
+ React52.useEffect(() => {
5753
+ setHexInputValue(color);
5754
+ }, [color]);
5755
+ const handleColorChange = React52.useCallback(
5756
+ (newHex) => {
5757
+ const normalized = normalizeHex(newHex);
5758
+ if (!isControlled) {
5759
+ setLocalColor(normalized);
5760
+ }
5761
+ onChange?.(normalized);
5762
+ },
5763
+ [isControlled, onChange]
5764
+ );
5765
+ const hsl = React52.useMemo(() => hexToHsl(color), [color]);
5766
+ const handleHueChange = (e) => {
5767
+ const h = Number(e.target.value);
5768
+ const s = hsl.s === 0 ? 100 : hsl.s;
5769
+ const l = hsl.s === 0 ? 50 : hsl.l;
5770
+ const newHex = hslToHex(h, s, l);
5771
+ handleColorChange(newHex);
5772
+ };
5773
+ const handleHexSubmit = () => {
5774
+ if (isValidHex(hexInputValue)) {
5775
+ handleColorChange(hexInputValue);
5776
+ } else {
5777
+ setHexInputValue(color);
5778
+ }
5779
+ };
5780
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("inline-block", className), ...props, children: /* @__PURE__ */ jsxs(Popover, { children: [
5781
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
5782
+ "button",
5783
+ {
5784
+ type: "button",
5785
+ disabled,
5786
+ "aria-label": `Select color, current ${color}`,
5787
+ className: cn(
5788
+ "inline-flex items-center gap-2.5 px-3 py-1.5 rounded-kj-md border border-border bg-surface text-sm font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
5789
+ ),
5790
+ children: [
5791
+ /* @__PURE__ */ jsx(
5792
+ "span",
5793
+ {
5794
+ style: { backgroundColor: color },
5795
+ className: "w-5 h-5 rounded-full border border-border/50 shadow-sm shrink-0"
5796
+ }
5797
+ ),
5798
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs uppercase", children: color }),
5799
+ /* @__PURE__ */ jsx(
5800
+ "svg",
5801
+ {
5802
+ width: 12,
5803
+ height: 12,
5804
+ viewBox: "0 0 24 24",
5805
+ fill: "none",
5806
+ stroke: "currentColor",
5807
+ strokeWidth: 2,
5808
+ className: "text-muted-foreground ml-auto",
5809
+ "aria-hidden": "true",
5810
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
5811
+ }
5812
+ )
5813
+ ]
5814
+ }
5815
+ ) }),
5816
+ /* @__PURE__ */ jsxs(PopoverContent, { side: "bottom", align: "start", className: "w-64 p-3.5 space-y-3.5", children: [
5817
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-2 rounded-kj-md bg-muted/40 border border-border", children: [
5818
+ /* @__PURE__ */ jsx(
5819
+ "span",
5820
+ {
5821
+ style: { backgroundColor: color },
5822
+ className: "w-8 h-8 rounded-full border border-border/50 shadow-sm shrink-0"
5823
+ }
5824
+ ),
5825
+ /* @__PURE__ */ jsxs("div", { children: [
5826
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Selected Color" }),
5827
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-bold text-foreground uppercase", children: color })
5828
+ ] })
5829
+ ] }),
5830
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
5831
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground", children: "Preset Swatches" }),
5832
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-6 gap-2", children: swatches.map((swatch) => {
5833
+ const normalizedSwatch = normalizeHex(swatch);
5834
+ return /* @__PURE__ */ jsx(
5835
+ ColorPickerSwatch,
5836
+ {
5837
+ color: swatch,
5838
+ selected: normalizedSwatch === color,
5839
+ "aria-label": `Select color ${swatch}`,
5840
+ onClick: () => handleColorChange(swatch)
5841
+ },
5842
+ swatch
5843
+ );
5844
+ }) })
5845
+ ] }),
5846
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
5847
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground", children: "Hue" }),
5848
+ /* @__PURE__ */ jsx(
5849
+ "input",
5850
+ {
5851
+ type: "range",
5852
+ min: 0,
5853
+ max: 360,
5854
+ value: hsl.h,
5855
+ onChange: handleHueChange,
5856
+ "aria-label": "Hue slider",
5857
+ className: "w-full h-3 rounded-full appearance-none cursor-pointer",
5858
+ style: {
5859
+ background: "linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)"
5860
+ }
5861
+ }
5862
+ )
5863
+ ] }),
5864
+ showHexInput && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
5865
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground", children: "Hex Code" }),
5866
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
5867
+ "input",
5868
+ {
5869
+ type: "text",
5870
+ "aria-label": "Hex Code",
5871
+ value: hexInputValue,
5872
+ onChange: (e) => setHexInputValue(e.target.value),
5873
+ onBlur: handleHexSubmit,
5874
+ onKeyDown: (e) => {
5875
+ if (e.key === "Enter") handleHexSubmit();
5876
+ },
5877
+ placeholder: "#000000",
5878
+ className: "flex-1 px-2.5 py-1 text-xs font-mono rounded-kj-md border border-border bg-surface text-foreground uppercase focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
5879
+ }
5880
+ ) })
5881
+ ] })
5882
+ ] })
5883
+ ] }) });
5884
+ }
5885
+ );
5886
+ ColorPicker.displayName = "ColorPicker";
5887
+ var ColorPickerField = React52.forwardRef(({ label, hint, error, required, ...props }, ref) => {
5888
+ return /* @__PURE__ */ jsxs(Field, { children: [
5889
+ label && /* @__PURE__ */ jsx(Label, { required, children: label }),
5890
+ /* @__PURE__ */ jsx(ColorPicker, { ref, ...props }),
5891
+ hint && /* @__PURE__ */ jsx(Hint, { children: hint }),
5892
+ error && /* @__PURE__ */ jsx(Hint, { error: true, children: error })
5893
+ ] });
5894
+ });
5895
+ ColorPickerField.displayName = "ColorPickerField";
5896
+ var COLUMN_CLASSES = {
5897
+ 2: "grid-cols-2",
5898
+ 3: "grid-cols-3",
5899
+ 4: "grid-cols-4",
5900
+ 5: "grid-cols-5"
5901
+ };
5902
+ var ASPECT_CLASSES = {
5903
+ square: "aspect-square",
5904
+ video: "aspect-video",
5905
+ "4/3": "aspect-[4/3]",
5906
+ auto: "aspect-auto"
5907
+ };
5908
+ function ImageGallery({
5909
+ images = [],
5910
+ columns = 3,
5911
+ aspectRatio = "square",
5912
+ maxVisible,
5913
+ className,
5914
+ thumbnailClassName,
5915
+ lightboxClassName,
5916
+ onImageClick
5917
+ }) {
5918
+ const [selectedIndex, setSelectedIndex] = React52.useState(null);
5919
+ const columnClass = COLUMN_CLASSES[columns] || "grid-cols-3";
5920
+ const aspectClass = ASPECT_CLASSES[aspectRatio] || (aspectRatio.startsWith("aspect-") ? aspectRatio : `aspect-[${aspectRatio}]`);
5921
+ const hasMaxVisible = typeof maxVisible === "number" && maxVisible > 0 && images.length > maxVisible;
5922
+ const visibleImages = hasMaxVisible ? images.slice(0, maxVisible) : images;
5923
+ const remainingCount = hasMaxVisible ? images.length - maxVisible + 1 : 0;
5924
+ const handleThumbnailClick = (index) => {
5925
+ setSelectedIndex(index);
5926
+ if (onImageClick) {
5927
+ onImageClick(index);
5928
+ }
5929
+ };
5930
+ const handleClose = React52.useCallback(() => {
5931
+ setSelectedIndex(null);
5932
+ }, []);
5933
+ const handleNext = React52.useCallback(() => {
5934
+ setSelectedIndex((prev) => prev === null ? null : (prev + 1) % images.length);
5935
+ }, [images.length]);
5936
+ const handlePrev = React52.useCallback(() => {
5937
+ setSelectedIndex((prev) => prev === null ? null : (prev - 1 + images.length) % images.length);
5938
+ }, [images.length]);
5939
+ React52.useEffect(() => {
5940
+ if (selectedIndex === null) return;
5941
+ const originalOverflow = document.body.style.overflow;
5942
+ const handleKeyDown = (e) => {
5943
+ if (e.key === "Escape") {
5944
+ handleClose();
5945
+ } else if (e.key === "ArrowRight") {
5946
+ handleNext();
5947
+ } else if (e.key === "ArrowLeft") {
5948
+ handlePrev();
5949
+ }
5950
+ };
5951
+ document.addEventListener("keydown", handleKeyDown);
5952
+ document.body.style.overflow = "hidden";
5953
+ return () => {
5954
+ document.removeEventListener("keydown", handleKeyDown);
5955
+ document.body.style.overflow = originalOverflow;
5956
+ };
5957
+ }, [selectedIndex, handleClose, handleNext, handlePrev]);
5958
+ const activeImage = selectedIndex !== null ? images[selectedIndex] : null;
5959
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
5960
+ /* @__PURE__ */ jsx("div", { className: cn("grid gap-3 w-full", columnClass, className), children: visibleImages.map((img, idx) => {
5961
+ const isLastOverlay = hasMaxVisible && idx === maxVisible - 1;
5962
+ const thumbSrc = img.thumbnailSrc || img.src;
5963
+ const defaultAlt = img.alt || `Thumbnail ${idx + 1}`;
5964
+ const buttonAriaLabel = isLastOverlay ? `View all ${images.length} images` : defaultAlt;
5965
+ return /* @__PURE__ */ jsxs(
5966
+ "button",
5967
+ {
5968
+ type: "button",
5969
+ "aria-label": buttonAriaLabel,
5970
+ onClick: () => handleThumbnailClick(idx),
5971
+ className: cn(
5972
+ "relative group overflow-hidden rounded-kj-lg bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 cursor-pointer transition-all duration-200",
5973
+ aspectClass,
5974
+ thumbnailClassName
5975
+ ),
5976
+ children: [
5977
+ /* @__PURE__ */ jsx(
5978
+ "img",
5979
+ {
5980
+ src: thumbSrc,
5981
+ alt: img.alt || `Thumbnail ${idx + 1}`,
5982
+ className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
5983
+ }
5984
+ ),
5985
+ isLastOverlay && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 bg-black/60 backdrop-blur-[2px] flex items-center justify-center text-white font-bold text-xl group-hover:bg-black/70 transition-colors", children: [
5986
+ "+",
5987
+ remainingCount
5988
+ ] })
5989
+ ]
5990
+ },
5991
+ idx
5992
+ );
5993
+ }) }),
5994
+ selectedIndex !== null && activeImage && /* @__PURE__ */ jsxs(
5995
+ "div",
5996
+ {
5997
+ role: "dialog",
5998
+ "aria-modal": "true",
5999
+ "aria-label": "Image gallery lightbox",
6000
+ onClick: (e) => e.target === e.currentTarget && handleClose(),
6001
+ className: cn(
6002
+ "fixed inset-0 z-[100] bg-black/85 backdrop-blur-md flex flex-col justify-between p-4 sm:p-6 transition-opacity duration-200 animate-in fade-in-0",
6003
+ lightboxClassName
6004
+ ),
6005
+ children: [
6006
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between w-full text-white z-10", children: [
6007
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-white/80 select-none", children: [
6008
+ selectedIndex + 1,
6009
+ " of ",
6010
+ images.length
6011
+ ] }),
6012
+ /* @__PURE__ */ jsx(
6013
+ "button",
6014
+ {
6015
+ type: "button",
6016
+ onClick: handleClose,
6017
+ "aria-label": "Close",
6018
+ className: "p-2 rounded-full text-white/80 hover:text-white hover:bg-white/10 transition-colors cursor-pointer",
6019
+ children: /* @__PURE__ */ jsx("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
6020
+ }
6021
+ )
6022
+ ] }),
6023
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 flex items-center justify-between gap-4 my-auto min-h-0 w-full", children: [
6024
+ images.length > 1 && /* @__PURE__ */ jsx(
6025
+ "button",
6026
+ {
6027
+ type: "button",
6028
+ onClick: handlePrev,
6029
+ "aria-label": "Previous image",
6030
+ className: "p-3 rounded-full text-white/80 hover:text-white hover:bg-white/10 transition-colors cursor-pointer z-10 shrink-0",
6031
+ children: /* @__PURE__ */ jsx("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) })
6032
+ }
6033
+ ),
6034
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col items-center justify-center h-full min-h-0 px-2 select-none", children: /* @__PURE__ */ jsx(
6035
+ "img",
6036
+ {
6037
+ src: activeImage.src,
6038
+ alt: activeImage.alt || `Image ${selectedIndex + 1}`,
6039
+ className: "max-h-[70vh] max-w-full object-contain rounded-kj-md shadow-2xl transition-transform"
6040
+ }
6041
+ ) }),
6042
+ images.length > 1 && /* @__PURE__ */ jsx(
6043
+ "button",
6044
+ {
6045
+ type: "button",
6046
+ onClick: handleNext,
6047
+ "aria-label": "Next image",
6048
+ className: "p-3 rounded-full text-white/80 hover:text-white hover:bg-white/10 transition-colors cursor-pointer z-10 shrink-0",
6049
+ children: /* @__PURE__ */ jsx("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
6050
+ }
6051
+ )
6052
+ ] }),
6053
+ (activeImage.title || activeImage.caption) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-center text-white pb-2 z-10", children: [
6054
+ activeImage.title && /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-white", children: activeImage.title }),
6055
+ activeImage.caption && /* @__PURE__ */ jsx("p", { className: "text-xs sm:text-sm text-white/70 max-w-lg mt-0.5", children: activeImage.caption })
6056
+ ] })
6057
+ ]
6058
+ }
6059
+ )
6060
+ ] });
6061
+ }
2477
6062
 
2478
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, Avatar, AvatarGroup, Badge, BottomNavigation, BottomSheet, BottomSheetContent, BottomSheetDescription, BottomSheetFooter, BottomSheetHeader, BottomSheetTitle, Breadcrumb, BreadcrumbItem, BreadcrumbSeparator, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxField, CodeBlock, CommandPalette, ConfirmDialog, DashboardShell, DataTable, DetailPageLayout, Drawer, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptyState, ErrorState, Fab, Field, FormField, Hint, Input, Kbd, Label, MetricCard, Modal, ModalActions, ModalDescription, ModalTitle, PageHeader, Pagination, Popover, PopoverContent, PopoverTrigger, Progress, Radio, Segmented, Select, SelectField, SettingsLayout, SidebarNav, Skeleton, Slider, Spinner, Stat, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, TableToolbar, TableWrap, Tabs, TabsContent, TabsList, TabsTrigger, TextField, Textarea, ToastProvider, Tooltip, badgeVariants, buttonVariants, cn, sliderThumbCSS, useToast };
6063
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AppShell, AppShellBanner, AppShellFooter, AppShellHeader, AppShellMain, Avatar, AvatarGroup, Badge, BlogCard, BottomNavigation, BottomSheet, BottomSheetContent, BottomSheetDescription, BottomSheetFooter, BottomSheetHeader, BottomSheetTitle, Breadcrumb, BreadcrumbItem, BreadcrumbSeparator, Button, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxField, CodeBlock, ColorPicker, ColorPickerField, ColorPickerSwatch, Combobox, ComboboxField, CommandPalette, ConfirmDialog, DEFAULT_COLOR_SWATCHES, DashboardShell, DataTable, DatePicker, DatePickerField, DateRangePicker, DateRangePickerField, DetailPageLayout, Drawer, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, Dropzone, EmptyState, ErrorState, Fab, Field, FileUpload, FileUploadField, FormField, Hint, ImageGallery, InboxContent, InboxPopover, InboxTrigger, Input, Kbd, Label, MetricCard, Modal, ModalActions, ModalDescription, ModalTitle, NotificationItem, PageHeader, Pagination, Popover, PopoverContent, PopoverTrigger, PricingCard, Progress, ProgressRing, ProgressRingField, ProjectCard, Radio, RangeCalendar, Rating, RatingField, RatingSummary, SectionHeader, Segmented, Select, SelectField, Separator, SettingsLayout, SidebarNav, Skeleton, Slider, Spinner, Stat, Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, StepperList, StepperSeparator, StepperTitle, StepperTrigger, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, TableToolbar, TableWrap, Tabs, TabsContent, TabsList, TabsTrigger, TextField, Textarea, Timeline, TimelineConnector, TimelineContent, TimelineDot, TimelineItem, TimelineSeparator, TimelineTime, TimelineTitle, ToastProvider, ToggleGroup, Tooltip, badgeVariants, buttonVariants, cn, formatRelativeTime, hexToHsl, hslToHex, isValidHex, normalizeHex, sliderThumbCSS, useInboxState, useStepper, useToast };
2479
6064
  //# sourceMappingURL=index.js.map
2480
6065
  //# sourceMappingURL=index.js.map