@kjaniec-dev/ui 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var clsx = require('clsx');
4
4
  var tailwindMerge = require('tailwind-merge');
5
- var React16 = require('react');
5
+ var React17 = require('react');
6
6
  var classVarianceAuthority = require('class-variance-authority');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
 
@@ -24,8 +24,9 @@ function _interopNamespace(e) {
24
24
  return Object.freeze(n);
25
25
  }
26
26
 
27
- var React16__namespace = /*#__PURE__*/_interopNamespace(React16);
27
+ var React17__namespace = /*#__PURE__*/_interopNamespace(React17);
28
28
 
29
+ // src/lib/cn.ts
29
30
  function cn(...inputs) {
30
31
  return tailwindMerge.twMerge(clsx.clsx(inputs));
31
32
  }
@@ -59,7 +60,7 @@ var buttonVariants = classVarianceAuthority.cva(
59
60
  defaultVariants: { variant: "primary", size: "md" }
60
61
  }
61
62
  );
62
- var Button = React16__namespace.forwardRef(
63
+ var Button = React17__namespace.forwardRef(
63
64
  ({ className, variant, size, loading, leadingIcon, trailingIcon, children, disabled, ...props }, ref) => {
64
65
  return /* @__PURE__ */ jsxRuntime.jsxs(
65
66
  "button",
@@ -106,7 +107,7 @@ var badgeVariants = classVarianceAuthority.cva(
106
107
  defaultVariants: { variant: "neutral" }
107
108
  }
108
109
  );
109
- var Badge = React16__namespace.forwardRef(
110
+ var Badge = React17__namespace.forwardRef(
110
111
  ({ className, variant, dot, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs("span", { ref, className: cn(badgeVariants({ variant }), className), ...props, children: [
111
112
  dot && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-current", "aria-hidden": true }),
112
113
  children
@@ -133,7 +134,7 @@ var iconColor = {
133
134
  warning: "text-warning",
134
135
  danger: "text-danger"
135
136
  };
136
- var Alert = React16__namespace.forwardRef(
137
+ var Alert = React17__namespace.forwardRef(
137
138
  ({ className, variant = "info", icon, title, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, role: "alert", className: cn(alertVariants({ variant }), className), ...props, children: [
138
139
  icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("shrink-0 mt-0.5 [&_svg]:h-[1.15rem] [&_svg]:w-[1.15rem]", iconColor[variant ?? "info"]), children: icon }),
139
140
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-foreground", children: [
@@ -143,7 +144,7 @@ var Alert = React16__namespace.forwardRef(
143
144
  ] })
144
145
  );
145
146
  Alert.displayName = "Alert";
146
- var Spinner = React16__namespace.forwardRef(
147
+ var Spinner = React17__namespace.forwardRef(
147
148
  ({ className, size = 24, style, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
148
149
  "span",
149
150
  {
@@ -160,7 +161,7 @@ var Spinner = React16__namespace.forwardRef(
160
161
  )
161
162
  );
162
163
  Spinner.displayName = "Spinner";
163
- var Progress = React16__namespace.forwardRef(
164
+ var Progress = React17__namespace.forwardRef(
164
165
  ({ className, value = 0, tone = "primary", barClassName, ...props }, ref) => {
165
166
  const pct = Math.max(0, Math.min(100, value));
166
167
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -168,6 +169,7 @@ var Progress = React16__namespace.forwardRef(
168
169
  {
169
170
  ref,
170
171
  role: "progressbar",
172
+ "aria-label": props["aria-label"] ?? "Progress",
171
173
  "aria-valuenow": pct,
172
174
  "aria-valuemin": 0,
173
175
  "aria-valuemax": 100,
@@ -190,7 +192,7 @@ var Progress = React16__namespace.forwardRef(
190
192
  );
191
193
  Progress.displayName = "Progress";
192
194
  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";
193
- var Input = React16__namespace.forwardRef(
195
+ var Input = React17__namespace.forwardRef(
194
196
  ({ className, error, leadingIcon, style, ...props }, ref) => {
195
197
  const field = /* @__PURE__ */ jsxRuntime.jsx(
196
198
  "input",
@@ -215,7 +217,7 @@ var Input = React16__namespace.forwardRef(
215
217
  }
216
218
  );
217
219
  Input.displayName = "Input";
218
- var Textarea = React16__namespace.forwardRef(
220
+ var Textarea = React17__namespace.forwardRef(
219
221
  ({ className, error, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
220
222
  "textarea",
221
223
  {
@@ -232,14 +234,14 @@ var Textarea = React16__namespace.forwardRef(
232
234
  )
233
235
  );
234
236
  Textarea.displayName = "Textarea";
235
- var Label = React16__namespace.forwardRef(
237
+ var Label = React17__namespace.forwardRef(
236
238
  ({ className, required, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs("label", { ref, className: cn("text-[0.8rem] font-semibold text-foreground", className), ...props, children: [
237
239
  children,
238
240
  required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-danger", children: " *" })
239
241
  ] })
240
242
  );
241
243
  Label.displayName = "Label";
242
- var Hint = React16__namespace.forwardRef(
244
+ var Hint = React17__namespace.forwardRef(
243
245
  ({ className, error, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
244
246
  "span",
245
247
  {
@@ -250,12 +252,12 @@ var Hint = React16__namespace.forwardRef(
250
252
  )
251
253
  );
252
254
  Hint.displayName = "Hint";
253
- var Field = React16__namespace.forwardRef(
255
+ var Field = React17__namespace.forwardRef(
254
256
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("flex flex-col gap-1.5", className), ...props })
255
257
  );
256
258
  Field.displayName = "Field";
257
259
  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";
258
- var Select = React16__namespace.forwardRef(
260
+ var Select = React17__namespace.forwardRef(
259
261
  ({ className, error, style, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
260
262
  "select",
261
263
  {
@@ -283,9 +285,9 @@ var Select = React16__namespace.forwardRef(
283
285
  )
284
286
  );
285
287
  Select.displayName = "Select";
286
- var Checkbox = React16__namespace.forwardRef(
288
+ var Checkbox = React17__namespace.forwardRef(
287
289
  ({ className, label, id, ...props }, ref) => {
288
- const autoId = React16__namespace.useId();
290
+ const autoId = React17__namespace.useId();
289
291
  const inputId = id ?? autoId;
290
292
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
291
293
  /* @__PURE__ */ jsxRuntime.jsx("input", { ref, id: inputId, type: "checkbox", className: "peer sr-only", ...props }),
@@ -295,9 +297,9 @@ var Checkbox = React16__namespace.forwardRef(
295
297
  }
296
298
  );
297
299
  Checkbox.displayName = "Checkbox";
298
- var Radio = React16__namespace.forwardRef(
300
+ var Radio = React17__namespace.forwardRef(
299
301
  ({ className, label, id, ...props }, ref) => {
300
- const autoId = React16__namespace.useId();
302
+ const autoId = React17__namespace.useId();
301
303
  const inputId = id ?? autoId;
302
304
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
303
305
  /* @__PURE__ */ jsxRuntime.jsx("input", { ref, id: inputId, type: "radio", className: "peer sr-only", ...props }),
@@ -307,9 +309,9 @@ var Radio = React16__namespace.forwardRef(
307
309
  }
308
310
  );
309
311
  Radio.displayName = "Radio";
310
- var Switch = React16__namespace.forwardRef(
312
+ var Switch = React17__namespace.forwardRef(
311
313
  ({ className, label, id, ...props }, ref) => {
312
- const autoId = React16__namespace.useId();
314
+ const autoId = React17__namespace.useId();
313
315
  const inputId = id ?? autoId;
314
316
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
315
317
  /* @__PURE__ */ jsxRuntime.jsx("input", { ref, id: inputId, type: "checkbox", role: "switch", className: "peer sr-only", ...props }),
@@ -319,7 +321,7 @@ var Switch = React16__namespace.forwardRef(
319
321
  }
320
322
  );
321
323
  Switch.displayName = "Switch";
322
- var Slider = React16__namespace.forwardRef(
324
+ var Slider = React17__namespace.forwardRef(
323
325
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
324
326
  "input",
325
327
  {
@@ -372,14 +374,15 @@ function Segmented({
372
374
  }
373
375
  );
374
376
  }
375
- var Card = React16__namespace.forwardRef(
376
- ({ as: Tag = "div", className, elevated, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
377
+ var Card = React17__namespace.forwardRef(
378
+ ({ as: Tag = "div", className, elevated, interactive, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
377
379
  Tag,
378
380
  {
379
381
  ref,
380
382
  className: cn(
381
383
  "bg-card text-card-foreground rounded-kj-xl overflow-hidden border",
382
384
  elevated ? "border-transparent shadow-kj-lg" : "border-border shadow-kj-sm",
385
+ interactive && "cursor-pointer transition-all duration-300 hover:-translate-y-1 hover:shadow-kj-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
383
386
  className
384
387
  ),
385
388
  ...props
@@ -387,23 +390,23 @@ var Card = React16__namespace.forwardRef(
387
390
  )
388
391
  );
389
392
  Card.displayName = "Card";
390
- var CardHeader = React16__namespace.forwardRef(
393
+ var CardHeader = React17__namespace.forwardRef(
391
394
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("p-[1.35rem] flex flex-col gap-1", className), ...props })
392
395
  );
393
396
  CardHeader.displayName = "CardHeader";
394
- var CardTitle = React16__namespace.forwardRef(
397
+ var CardTitle = React17__namespace.forwardRef(
395
398
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("h3", { ref, className: cn("m-0 text-base font-bold tracking-[-0.01em]", className), ...props })
396
399
  );
397
400
  CardTitle.displayName = "CardTitle";
398
- var CardDescription = React16__namespace.forwardRef(
401
+ var CardDescription = React17__namespace.forwardRef(
399
402
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("p", { ref, className: cn("m-0 text-[0.85rem] text-muted-foreground", className), ...props })
400
403
  );
401
404
  CardDescription.displayName = "CardDescription";
402
- var CardContent = React16__namespace.forwardRef(
405
+ var CardContent = React17__namespace.forwardRef(
403
406
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("px-[1.35rem] pb-[1.35rem]", className), ...props })
404
407
  );
405
408
  CardContent.displayName = "CardContent";
406
- var CardFooter = React16__namespace.forwardRef(
409
+ var CardFooter = React17__namespace.forwardRef(
407
410
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
408
411
  "div",
409
412
  {
@@ -414,7 +417,7 @@ var CardFooter = React16__namespace.forwardRef(
414
417
  )
415
418
  );
416
419
  CardFooter.displayName = "CardFooter";
417
- var Stat = React16__namespace.forwardRef(
420
+ var Stat = React17__namespace.forwardRef(
418
421
  ({ className, label, value, delta, trend, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(Card, { ref, className: cn("p-[1.35rem] flex flex-col gap-2", className), ...props, children: [
419
422
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.78rem] font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: label }),
420
423
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[2rem] font-bold tracking-[-0.03em] tabular-nums leading-none", children: value }),
@@ -454,7 +457,7 @@ var avatarVariants = classVarianceAuthority.cva(
454
457
  defaultVariants: { size: "md", tone: "secondary" }
455
458
  }
456
459
  );
457
- var Avatar = React16__namespace.forwardRef(
460
+ var Avatar = React17__namespace.forwardRef(
458
461
  ({ className, size, tone, src, alt, status, children, ...props }, ref) => {
459
462
  const node = /* @__PURE__ */ jsxRuntime.jsx("span", { ref, className: cn(avatarVariants({ size, tone }), "overflow-hidden", className), ...props, children: src ? /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "h-full w-full object-cover" }) : children });
460
463
  if (!status) return node;
@@ -465,20 +468,20 @@ var Avatar = React16__namespace.forwardRef(
465
468
  }
466
469
  );
467
470
  Avatar.displayName = "Avatar";
468
- var AvatarGroup = React16__namespace.forwardRef(
471
+ var AvatarGroup = React17__namespace.forwardRef(
469
472
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("flex [&>*:not(:first-child)]:-ml-2.5", className), ...props })
470
473
  );
471
474
  AvatarGroup.displayName = "AvatarGroup";
472
- var TabsContext = React16__namespace.createContext(null);
475
+ var TabsContext = React17__namespace.createContext(null);
473
476
  function useTabs() {
474
- const ctx = React16__namespace.useContext(TabsContext);
477
+ const ctx = React17__namespace.useContext(TabsContext);
475
478
  if (!ctx) throw new Error("Tabs.* must be used within <Tabs>");
476
479
  return ctx;
477
480
  }
478
481
  function Tabs({ value, defaultValue, onValueChange, className, children, ...props }) {
479
- const [internal, setInternal] = React16__namespace.useState(defaultValue ?? "");
482
+ const [internal, setInternal] = React17__namespace.useState(defaultValue ?? "");
480
483
  const current = value ?? internal;
481
- const setValue = React16__namespace.useCallback(
484
+ const setValue = React17__namespace.useCallback(
482
485
  (v) => {
483
486
  if (value === void 0) setInternal(v);
484
487
  onValueChange?.(v);
@@ -487,11 +490,51 @@ function Tabs({ value, defaultValue, onValueChange, className, children, ...prop
487
490
  );
488
491
  return /* @__PURE__ */ jsxRuntime.jsx(TabsContext.Provider, { value: { value: current, setValue }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className, ...props, children }) });
489
492
  }
490
- var TabsList = React16__namespace.forwardRef(
491
- ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, role: "tablist", className: cn("flex gap-1 border-b border-border", className), ...props })
493
+ var TabsList = React17__namespace.forwardRef(
494
+ ({ className, onKeyDown, ...props }, ref) => {
495
+ const handleKeyDown = (e) => {
496
+ const container = e.currentTarget;
497
+ const tabs = Array.from(container.querySelectorAll('[role="tab"]'));
498
+ if (tabs.length === 0) return;
499
+ const activeIndex = tabs.findIndex((tab) => tab.getAttribute("aria-selected") === "true");
500
+ let newIndex = activeIndex;
501
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
502
+ e.preventDefault();
503
+ newIndex = (activeIndex + 1) % tabs.length;
504
+ tabs[newIndex].focus();
505
+ tabs[newIndex].click();
506
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
507
+ e.preventDefault();
508
+ newIndex = (activeIndex - 1 + tabs.length) % tabs.length;
509
+ tabs[newIndex].focus();
510
+ tabs[newIndex].click();
511
+ } else if (e.key === "Home") {
512
+ e.preventDefault();
513
+ newIndex = 0;
514
+ tabs[newIndex].focus();
515
+ tabs[newIndex].click();
516
+ } else if (e.key === "End") {
517
+ e.preventDefault();
518
+ newIndex = tabs.length - 1;
519
+ tabs[newIndex].focus();
520
+ tabs[newIndex].click();
521
+ }
522
+ onKeyDown?.(e);
523
+ };
524
+ return /* @__PURE__ */ jsxRuntime.jsx(
525
+ "div",
526
+ {
527
+ ref,
528
+ role: "tablist",
529
+ onKeyDown: handleKeyDown,
530
+ className: cn("flex gap-1 border-b border-border", className),
531
+ ...props
532
+ }
533
+ );
534
+ }
492
535
  );
493
536
  TabsList.displayName = "TabsList";
494
- var TabsTrigger = React16__namespace.forwardRef(
537
+ var TabsTrigger = React17__namespace.forwardRef(
495
538
  ({ className, value, children, ...props }, ref) => {
496
539
  const { value: current, setValue } = useTabs();
497
540
  const active = current === value;
@@ -501,10 +544,14 @@ var TabsTrigger = React16__namespace.forwardRef(
501
544
  ref,
502
545
  type: "button",
503
546
  role: "tab",
547
+ id: `tabs-trigger-${value}`,
548
+ "aria-controls": `tabs-panel-${value}`,
504
549
  "aria-selected": active,
550
+ tabIndex: active ? 0 : -1,
505
551
  onClick: () => setValue(value),
506
552
  className: cn(
507
- "relative px-[0.9rem] py-2.5 text-sm font-semibold cursor-pointer transition-colors duration-150",
553
+ "relative px-[0.9rem] py-2.5 text-sm font-semibold cursor-pointer transition-colors duration-150 outline-none",
554
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:rounded-sm",
508
555
  "after:content-[''] after:absolute after:left-[0.9rem] after:right-[0.9rem] after:-bottom-px after:h-0.5 after:rounded-sm after:transition-colors",
509
556
  active ? "text-foreground after:bg-primary" : "text-muted-foreground hover:text-foreground after:bg-transparent",
510
557
  className
@@ -516,18 +563,30 @@ var TabsTrigger = React16__namespace.forwardRef(
516
563
  }
517
564
  );
518
565
  TabsTrigger.displayName = "TabsTrigger";
519
- var TabsContent = React16__namespace.forwardRef(
566
+ var TabsContent = React17__namespace.forwardRef(
520
567
  ({ className, value, ...props }, ref) => {
521
568
  const { value: current } = useTabs();
522
- if (current !== value) return null;
523
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, role: "tabpanel", className: cn("pt-5 text-[0.9rem] text-muted-foreground", className), ...props });
569
+ const active = current === value;
570
+ if (!active) return null;
571
+ return /* @__PURE__ */ jsxRuntime.jsx(
572
+ "div",
573
+ {
574
+ ref,
575
+ role: "tabpanel",
576
+ id: `tabs-panel-${value}`,
577
+ "aria-labelledby": `tabs-trigger-${value}`,
578
+ tabIndex: 0,
579
+ className: cn("pt-5 text-[0.9rem] text-muted-foreground outline-none", className),
580
+ ...props
581
+ }
582
+ );
524
583
  }
525
584
  );
526
585
  TabsContent.displayName = "TabsContent";
527
- var AccordionContext = React16__namespace.createContext(null);
586
+ var AccordionContext = React17__namespace.createContext(null);
528
587
  function Accordion({ type = "single", defaultValue = [], className, children, ...props }) {
529
- const [open, setOpen] = React16__namespace.useState(defaultValue);
530
- const toggle = React16__namespace.useCallback(
588
+ const [open, setOpen] = React17__namespace.useState(defaultValue);
589
+ const toggle = React17__namespace.useCallback(
531
590
  (v) => {
532
591
  setOpen((prev) => {
533
592
  const isOpen = prev.includes(v);
@@ -539,15 +598,15 @@ function Accordion({ type = "single", defaultValue = [], className, children, ..
539
598
  );
540
599
  return /* @__PURE__ */ jsxRuntime.jsx(AccordionContext.Provider, { value: { open, toggle }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("border border-border rounded-kj-lg overflow-hidden", className), ...props, children }) });
541
600
  }
542
- var ItemContext = React16__namespace.createContext("");
543
- var AccordionItem = React16__namespace.forwardRef(
601
+ var ItemContext = React17__namespace.createContext("");
602
+ var AccordionItem = React17__namespace.forwardRef(
544
603
  ({ className, value, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(ItemContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("border-t border-border first:border-t-0", className), ...props }) })
545
604
  );
546
605
  AccordionItem.displayName = "AccordionItem";
547
- var AccordionTrigger = React16__namespace.forwardRef(
606
+ var AccordionTrigger = React17__namespace.forwardRef(
548
607
  ({ className, children, ...props }, ref) => {
549
- const ctx = React16__namespace.useContext(AccordionContext);
550
- const value = React16__namespace.useContext(ItemContext);
608
+ const ctx = React17__namespace.useContext(AccordionContext);
609
+ const value = React17__namespace.useContext(ItemContext);
551
610
  const isOpen = ctx.open.includes(value);
552
611
  return /* @__PURE__ */ jsxRuntime.jsxs(
553
612
  "button",
@@ -584,10 +643,10 @@ var AccordionTrigger = React16__namespace.forwardRef(
584
643
  }
585
644
  );
586
645
  AccordionTrigger.displayName = "AccordionTrigger";
587
- var AccordionContent = React16__namespace.forwardRef(
646
+ var AccordionContent = React17__namespace.forwardRef(
588
647
  ({ className, children, ...props }, ref) => {
589
- const ctx = React16__namespace.useContext(AccordionContext);
590
- const value = React16__namespace.useContext(ItemContext);
648
+ const ctx = React17__namespace.useContext(AccordionContext);
649
+ const value = React17__namespace.useContext(ItemContext);
591
650
  const isOpen = ctx.open.includes(value);
592
651
  return /* @__PURE__ */ jsxRuntime.jsx(
593
652
  "div",
@@ -601,11 +660,11 @@ var AccordionContent = React16__namespace.forwardRef(
601
660
  }
602
661
  );
603
662
  AccordionContent.displayName = "AccordionContent";
604
- var MenuContext = React16__namespace.createContext(null);
663
+ var MenuContext = React17__namespace.createContext(null);
605
664
  function DropdownMenu({ children, className }) {
606
- const [open, setOpen] = React16__namespace.useState(false);
607
- const ref = React16__namespace.useRef(null);
608
- React16__namespace.useEffect(() => {
665
+ const [open, setOpen] = React17__namespace.useState(false);
666
+ const ref = React17__namespace.useRef(null);
667
+ React17__namespace.useEffect(() => {
609
668
  if (!open) return;
610
669
  const onDoc = (e) => {
611
670
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -620,8 +679,23 @@ function DropdownMenu({ children, className }) {
620
679
  }, [open]);
621
680
  return /* @__PURE__ */ jsxRuntime.jsx(MenuContext.Provider, { value: { open, setOpen }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("relative inline-block", className), children }) });
622
681
  }
623
- var DropdownMenuTrigger = React16__namespace.forwardRef(({ onClick, ...props }, ref) => {
624
- const ctx = React16__namespace.useContext(MenuContext);
682
+ var DropdownMenuTrigger = React17__namespace.forwardRef(({ onClick, asChild, ...props }, ref) => {
683
+ const ctx = React17__namespace.useContext(MenuContext);
684
+ if (asChild) {
685
+ const child = React17__namespace.Children.only(props.children);
686
+ return React17__namespace.cloneElement(child, {
687
+ ref,
688
+ ...props,
689
+ ...child.props,
690
+ onClick: (e) => {
691
+ ctx.setOpen(!ctx.open);
692
+ onClick?.(e);
693
+ child.props.onClick?.(e);
694
+ },
695
+ "aria-haspopup": "menu",
696
+ "aria-expanded": ctx.open
697
+ });
698
+ }
625
699
  return /* @__PURE__ */ jsxRuntime.jsx(
626
700
  "button",
627
701
  {
@@ -639,14 +713,46 @@ var DropdownMenuTrigger = React16__namespace.forwardRef(({ onClick, ...props },
639
713
  });
640
714
  DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
641
715
  function DropdownMenuContent({ className, align = "start", children, ...props }) {
642
- const ctx = React16__namespace.useContext(MenuContext);
716
+ const ctx = React17__namespace.useContext(MenuContext);
717
+ const ref = React17__namespace.useRef(null);
718
+ React17__namespace.useEffect(() => {
719
+ if (!ctx.open) return;
720
+ const container = ref.current;
721
+ if (container) {
722
+ const items = Array.from(container.querySelectorAll('[role="menuitem"]'));
723
+ if (items.length > 0) {
724
+ setTimeout(() => items[0].focus(), 50);
725
+ }
726
+ }
727
+ }, [ctx.open]);
728
+ const handleKeyDown = (e) => {
729
+ const container = ref.current;
730
+ if (!container) return;
731
+ const items = Array.from(container.querySelectorAll('[role="menuitem"]'));
732
+ if (items.length === 0) return;
733
+ const currentIndex = items.indexOf(document.activeElement);
734
+ if (e.key === "ArrowDown") {
735
+ e.preventDefault();
736
+ const nextIndex = (currentIndex + 1) % items.length;
737
+ items[nextIndex].focus();
738
+ } else if (e.key === "ArrowUp") {
739
+ e.preventDefault();
740
+ const prevIndex = (currentIndex - 1 + items.length) % items.length;
741
+ items[prevIndex].focus();
742
+ } else if (e.key === "Tab" || e.key === "Escape") {
743
+ ctx.setOpen(false);
744
+ }
745
+ };
643
746
  if (!ctx.open) return null;
644
747
  return /* @__PURE__ */ jsxRuntime.jsx(
645
748
  "div",
646
749
  {
750
+ ref,
647
751
  role: "menu",
752
+ tabIndex: -1,
753
+ onKeyDown: handleKeyDown,
648
754
  className: cn(
649
- "absolute top-[calc(100%+6px)] z-40 min-w-[200px] p-1.5 rounded-kj-md bg-surface border border-border shadow-kj-lg",
755
+ "absolute top-[calc(100%+6px)] z-40 min-w-[200px] p-1.5 rounded-kj-md bg-surface border border-border shadow-kj-lg outline-none",
650
756
  "animate-[kjpop_.12s_ease]",
651
757
  align === "end" ? "right-0" : "left-0",
652
758
  className
@@ -656,9 +762,9 @@ function DropdownMenuContent({ className, align = "start", children, ...props })
656
762
  }
657
763
  );
658
764
  }
659
- var DropdownMenuItem = React16__namespace.forwardRef(
765
+ var DropdownMenuItem = React17__namespace.forwardRef(
660
766
  ({ className, danger, icon, children, onClick, ...props }, ref) => {
661
- const ctx = React16__namespace.useContext(MenuContext);
767
+ const ctx = React17__namespace.useContext(MenuContext);
662
768
  return /* @__PURE__ */ jsxRuntime.jsxs(
663
769
  "button",
664
770
  {
@@ -688,11 +794,11 @@ DropdownMenuItem.displayName = "DropdownMenuItem";
688
794
  function DropdownMenuSeparator({ className }) {
689
795
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-px bg-border my-1.5", className), role: "separator" });
690
796
  }
691
- var Breadcrumb = React16__namespace.forwardRef(
797
+ var Breadcrumb = React17__namespace.forwardRef(
692
798
  ({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("nav", { ref, "aria-label": "Breadcrumb", className: cn("flex items-center gap-1.5 text-[0.85rem] text-muted-foreground", className), ...props, children })
693
799
  );
694
800
  Breadcrumb.displayName = "Breadcrumb";
695
- var BreadcrumbItem = React16__namespace.forwardRef(
801
+ var BreadcrumbItem = React17__namespace.forwardRef(
696
802
  ({ className, current, children, ...props }, ref) => {
697
803
  if (current) {
698
804
  return /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-current": "page", className: cn("text-foreground font-semibold", className), children });
@@ -739,27 +845,27 @@ function Pagination({ page, pageCount, onPageChange, className }) {
739
845
  /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Next", disabled: page >= pageCount, onClick: () => onPageChange(page + 1), children: /* @__PURE__ */ jsxRuntime.jsx(Chevron, { dir: "right" }) })
740
846
  ] });
741
847
  }
742
- var TableWrap = React16__namespace.forwardRef(
848
+ var TableWrap = React17__namespace.forwardRef(
743
849
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("border border-border rounded-kj-lg overflow-hidden", className), ...props })
744
850
  );
745
851
  TableWrap.displayName = "TableWrap";
746
- var Table = React16__namespace.forwardRef(
852
+ var Table = React17__namespace.forwardRef(
747
853
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("table", { ref, className: cn("w-full border-collapse text-[0.85rem]", className), ...props })
748
854
  );
749
855
  Table.displayName = "Table";
750
- var TableHeader = React16__namespace.forwardRef(
856
+ var TableHeader = React17__namespace.forwardRef(
751
857
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("thead", { ref, className, ...props })
752
858
  );
753
859
  TableHeader.displayName = "TableHeader";
754
- var TableBody = React16__namespace.forwardRef(
860
+ var TableBody = React17__namespace.forwardRef(
755
861
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { ref, className, ...props })
756
862
  );
757
863
  TableBody.displayName = "TableBody";
758
- var TableRow = React16__namespace.forwardRef(
864
+ var TableRow = React17__namespace.forwardRef(
759
865
  ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("tr", { ref, className: cn("transition-colors hover:bg-muted/50", className), ...props })
760
866
  );
761
867
  TableRow.displayName = "TableRow";
762
- var TableHead = React16__namespace.forwardRef(
868
+ var TableHead = React17__namespace.forwardRef(
763
869
  ({ className, numeric, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
764
870
  "th",
765
871
  {
@@ -774,7 +880,7 @@ var TableHead = React16__namespace.forwardRef(
774
880
  )
775
881
  );
776
882
  TableHead.displayName = "TableHead";
777
- var TableCell = React16__namespace.forwardRef(
883
+ var TableCell = React17__namespace.forwardRef(
778
884
  ({ className, numeric, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
779
885
  "td",
780
886
  {
@@ -807,14 +913,68 @@ function Tooltip({ content, children, className }) {
807
913
  )
808
914
  ] });
809
915
  }
810
- function Modal({ open, onClose, children, width = 440, className }) {
811
- React16__namespace.useEffect(() => {
812
- if (!open) return;
813
- const onEsc = (e) => e.key === "Escape" && onClose();
814
- document.addEventListener("keydown", onEsc);
916
+ var ModalContext = React17__namespace.createContext(null);
917
+ function Modal({ open, onClose, children, width = 440, className, showClose = true }) {
918
+ const titleId = React17__namespace.useId();
919
+ const descId = React17__namespace.useId();
920
+ const containerRef = React17__namespace.useRef(null);
921
+ const lastActiveElement = React17__namespace.useRef(null);
922
+ React17__namespace.useEffect(() => {
923
+ if (!open) {
924
+ if (lastActiveElement.current) {
925
+ lastActiveElement.current.focus();
926
+ lastActiveElement.current = null;
927
+ }
928
+ return;
929
+ }
930
+ lastActiveElement.current = document.activeElement;
931
+ const container = containerRef.current;
932
+ if (container) {
933
+ const focusables = container.querySelectorAll(
934
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
935
+ );
936
+ if (focusables.length > 0) {
937
+ setTimeout(() => focusables[0].focus(), 50);
938
+ } else {
939
+ container.focus();
940
+ }
941
+ }
942
+ const handleKeyDown = (e) => {
943
+ if (e.key === "Escape") {
944
+ onClose();
945
+ return;
946
+ }
947
+ if (e.key === "Tab") {
948
+ const container2 = containerRef.current;
949
+ if (!container2) return;
950
+ const focusables = Array.from(
951
+ container2.querySelectorAll(
952
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
953
+ )
954
+ );
955
+ if (focusables.length === 0) {
956
+ e.preventDefault();
957
+ return;
958
+ }
959
+ const first = focusables[0];
960
+ const last = focusables[focusables.length - 1];
961
+ if (e.shiftKey) {
962
+ if (document.activeElement === first || document.activeElement === container2) {
963
+ last.focus();
964
+ e.preventDefault();
965
+ }
966
+ } else {
967
+ if (document.activeElement === last) {
968
+ first.focus();
969
+ e.preventDefault();
970
+ }
971
+ }
972
+ }
973
+ };
974
+ document.addEventListener("keydown", handleKeyDown);
815
975
  document.body.style.overflow = "hidden";
816
976
  return () => {
817
- document.removeEventListener("keydown", onEsc);
977
+ document.removeEventListener("keydown", handleKeyDown);
818
978
  document.body.style.overflow = "";
819
979
  };
820
980
  }, [open, onClose]);
@@ -827,35 +987,53 @@ function Modal({ open, onClose, children, width = 440, className }) {
827
987
  "bg-[color-mix(in_oklch,#09090b_55%,transparent)]",
828
988
  open ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"
829
989
  ),
830
- children: /* @__PURE__ */ jsxRuntime.jsx(
990
+ children: /* @__PURE__ */ jsxRuntime.jsx(ModalContext.Provider, { value: { titleId, descId }, children: /* @__PURE__ */ jsxRuntime.jsxs(
831
991
  "div",
832
992
  {
993
+ ref: containerRef,
833
994
  role: "dialog",
834
995
  "aria-modal": "true",
996
+ "aria-labelledby": titleId,
997
+ "aria-describedby": descId,
998
+ tabIndex: -1,
835
999
  style: { maxWidth: width },
836
1000
  className: cn(
837
- "w-full p-7 rounded-kj-2xl bg-surface border border-border shadow-kj-lg transition-transform duration-200",
1001
+ "relative w-full p-7 rounded-kj-2xl bg-surface border border-border shadow-kj-lg transition-transform duration-200 outline-none",
838
1002
  open ? "scale-100 translate-y-0" : "scale-95 translate-y-2",
839
1003
  className
840
1004
  ),
841
- children
1005
+ children: [
1006
+ showClose && /* @__PURE__ */ jsxRuntime.jsx(
1007
+ "button",
1008
+ {
1009
+ type: "button",
1010
+ onClick: onClose,
1011
+ "aria-label": "Close",
1012
+ className: "absolute top-4 right-4 p-1.5 rounded-kj-sm text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-pointer",
1013
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
1014
+ }
1015
+ ),
1016
+ children
1017
+ ]
842
1018
  }
843
- )
1019
+ ) })
844
1020
  }
845
1021
  );
846
1022
  }
847
- function ModalTitle({ className, ...props }) {
848
- return /* @__PURE__ */ jsxRuntime.jsx("h3", { className: cn("m-0 mb-2 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props });
1023
+ function ModalTitle({ className, id, ...props }) {
1024
+ const ctx = React17__namespace.useContext(ModalContext);
1025
+ return /* @__PURE__ */ jsxRuntime.jsx("h3", { id: id ?? ctx?.titleId, className: cn("m-0 mb-2 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props });
849
1026
  }
850
- function ModalDescription({ className, ...props }) {
851
- return /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("m-0 text-[0.9rem] text-muted-foreground", className), ...props });
1027
+ function ModalDescription({ className, id, ...props }) {
1028
+ const ctx = React17__namespace.useContext(ModalContext);
1029
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { id: id ?? ctx?.descId, className: cn("m-0 text-[0.9rem] text-muted-foreground", className), ...props });
852
1030
  }
853
1031
  function ModalActions({ className, ...props }) {
854
1032
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex gap-2.5 justify-end mt-6", className), ...props });
855
1033
  }
856
- var ToastContext = React16__namespace.createContext(null);
1034
+ var ToastContext = React17__namespace.createContext(null);
857
1035
  function useToast() {
858
- const ctx = React16__namespace.useContext(ToastContext);
1036
+ const ctx = React17__namespace.useContext(ToastContext);
859
1037
  if (!ctx) throw new Error("useToast must be used within <ToastProvider>");
860
1038
  return ctx;
861
1039
  }
@@ -876,13 +1054,13 @@ var toneBorder = {
876
1054
  danger: "border-l-danger"
877
1055
  };
878
1056
  function ToastProvider({ children }) {
879
- const [items, setItems] = React16__namespace.useState([]);
880
- const idRef = React16__namespace.useRef(0);
881
- const remove = React16__namespace.useCallback((id) => {
1057
+ const [items, setItems] = React17__namespace.useState([]);
1058
+ const idRef = React17__namespace.useRef(0);
1059
+ const remove = React17__namespace.useCallback((id) => {
882
1060
  setItems((prev) => prev.map((t) => t.id === id ? { ...t, leaving: true } : t));
883
1061
  setTimeout(() => setItems((prev) => prev.filter((t) => t.id !== id)), 250);
884
1062
  }, []);
885
- const toast = React16__namespace.useCallback(
1063
+ const toast = React17__namespace.useCallback(
886
1064
  ({ message, tone = "default", duration = 3200 }) => {
887
1065
  const id = ++idRef.current;
888
1066
  setItems((prev) => [...prev, { id, message, tone, duration }]);
@@ -912,6 +1090,17 @@ function ToastProvider({ children }) {
912
1090
  )) })
913
1091
  ] });
914
1092
  }
1093
+ var PageHeader = React17__namespace.forwardRef(
1094
+ ({ eyebrow, title, description, actions }, ref) => /* @__PURE__ */ jsxRuntime.jsx("header", { ref, className: "py-12 md:py-16", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6 md:flex-row md:items-end md:justify-between", children: [
1095
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 max-w-2xl", children: [
1096
+ eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-mono text-xs font-bold uppercase tracking-[0.2em] text-primary", children: eyebrow }),
1097
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "font-sans text-4xl font-bold tracking-tight text-foreground sm:text-5xl md:text-6xl", children: title }),
1098
+ description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base leading-relaxed text-muted-foreground sm:text-lg", children: description })
1099
+ ] }),
1100
+ actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3", children: actions })
1101
+ ] }) })
1102
+ );
1103
+ PageHeader.displayName = "PageHeader";
915
1104
 
916
1105
  exports.Accordion = Accordion;
917
1106
  exports.AccordionContent = AccordionContent;
@@ -945,6 +1134,7 @@ exports.Modal = Modal;
945
1134
  exports.ModalActions = ModalActions;
946
1135
  exports.ModalDescription = ModalDescription;
947
1136
  exports.ModalTitle = ModalTitle;
1137
+ exports.PageHeader = PageHeader;
948
1138
  exports.Pagination = Pagination;
949
1139
  exports.Progress = Progress;
950
1140
  exports.Radio = Radio;