@kjaniec-dev/ui 0.8.0 → 0.9.0

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