@seekalfred/primitives 0.4.0 → 0.5.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.
Files changed (99) hide show
  1. package/dist/accordion/accordion.d.ts +5 -5
  2. package/dist/accordion/accordion.js +13 -8
  3. package/dist/accordion/accordion.js.map +1 -1
  4. package/dist/alert/alert.d.ts +2 -2
  5. package/dist/alert/alert.js +4 -2
  6. package/dist/alert/alert.js.map +1 -1
  7. package/dist/alert-dialog/alert-dialog.d.ts +1 -1
  8. package/dist/alert-dialog/alert-dialog.js +4 -2
  9. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  10. package/dist/avatar/avatar.d.ts +1 -1
  11. package/dist/avatar/avatar.js +6 -4
  12. package/dist/avatar/avatar.js.map +1 -1
  13. package/dist/badge/badge.d.ts +2 -2
  14. package/dist/badge/badge.js +4 -2
  15. package/dist/badge/badge.js.map +1 -1
  16. package/dist/breadcrumb/breadcrumb.d.ts +3 -3
  17. package/dist/breadcrumb/breadcrumb.js +7 -4
  18. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  19. package/dist/button/button.d.ts +3 -3
  20. package/dist/button/button.js +4 -2
  21. package/dist/button/button.js.map +1 -1
  22. package/dist/calendar/calendar.d.ts +2 -2
  23. package/dist/calendar/calendar.js +7 -5
  24. package/dist/calendar/calendar.js.map +1 -1
  25. package/dist/card/card.d.ts +6 -6
  26. package/dist/card/card.js +19 -12
  27. package/dist/card/card.js.map +1 -1
  28. package/dist/checkbox/checkbox.d.ts +2 -2
  29. package/dist/checkbox/checkbox.js +4 -2
  30. package/dist/checkbox/checkbox.js.map +1 -1
  31. package/dist/dialog/dialog.d.ts +3 -3
  32. package/dist/dialog/dialog.js +7 -4
  33. package/dist/dialog/dialog.js.map +1 -1
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.js +2 -2
  36. package/dist/input/input.d.ts +2 -2
  37. package/dist/input/input.js +4 -2
  38. package/dist/input/input.js.map +1 -1
  39. package/dist/label/label.d.ts +1 -1
  40. package/dist/label/label.js +4 -2
  41. package/dist/label/label.js.map +1 -1
  42. package/dist/lib/cn.d.ts +55 -5
  43. package/dist/lib/cn.js +64 -6
  44. package/dist/lib/cn.js.map +1 -1
  45. package/dist/lib/merge-refs.js +22 -0
  46. package/dist/lib/merge-refs.js.map +1 -0
  47. package/dist/menu/menu.d.ts +5 -5
  48. package/dist/menu/menu.js +10 -6
  49. package/dist/menu/menu.js.map +1 -1
  50. package/dist/popover/popover.d.ts +2 -2
  51. package/dist/popover/popover.js +4 -2
  52. package/dist/popover/popover.js.map +1 -1
  53. package/dist/progress/progress.d.ts +2 -2
  54. package/dist/progress/progress.js +7 -5
  55. package/dist/progress/progress.js.map +1 -1
  56. package/dist/radio-group/radio-group.d.ts +3 -3
  57. package/dist/radio-group/radio-group.js +9 -6
  58. package/dist/radio-group/radio-group.js.map +1 -1
  59. package/dist/scroll-area/scroll-area.d.ts +7 -6
  60. package/dist/scroll-area/scroll-area.js +6 -4
  61. package/dist/scroll-area/scroll-area.js.map +1 -1
  62. package/dist/select/select.d.ts +3 -3
  63. package/dist/select/select.js +9 -6
  64. package/dist/select/select.js.map +1 -1
  65. package/dist/separator/separator.d.ts +1 -1
  66. package/dist/separator/separator.js +4 -2
  67. package/dist/separator/separator.js.map +1 -1
  68. package/dist/sheet/sheet.d.ts +1 -1
  69. package/dist/sheet/sheet.js +7 -4
  70. package/dist/sheet/sheet.js.map +1 -1
  71. package/dist/skeleton/skeleton.d.ts +1 -1
  72. package/dist/skeleton/skeleton.js +4 -2
  73. package/dist/skeleton/skeleton.js.map +1 -1
  74. package/dist/slider/slider.d.ts +2 -2
  75. package/dist/slider/slider.js +6 -4
  76. package/dist/slider/slider.js.map +1 -1
  77. package/dist/spinner/spinner.d.ts +1 -1
  78. package/dist/spinner/spinner.js +4 -2
  79. package/dist/spinner/spinner.js.map +1 -1
  80. package/dist/styles.css +1 -1
  81. package/dist/switch/switch.d.ts +2 -2
  82. package/dist/switch/switch.js +4 -2
  83. package/dist/switch/switch.js.map +1 -1
  84. package/dist/table/table.d.ts +4 -4
  85. package/dist/table/table.js +18 -17
  86. package/dist/table/table.js.map +1 -1
  87. package/dist/tabs/tabs.d.ts +4 -4
  88. package/dist/tabs/tabs.js +13 -8
  89. package/dist/tabs/tabs.js.map +1 -1
  90. package/dist/textarea/textarea.d.ts +1 -1
  91. package/dist/textarea/textarea.js +4 -2
  92. package/dist/textarea/textarea.js.map +1 -1
  93. package/dist/toast/toast.d.ts +2 -2
  94. package/dist/toast/toast.js +4 -2
  95. package/dist/toast/toast.js.map +1 -1
  96. package/dist/tooltip/tooltip.d.ts +2 -2
  97. package/dist/tooltip/tooltip.js +4 -2
  98. package/dist/tooltip/tooltip.js.map +1 -1
  99. package/package.json +3 -3
@@ -1,5 +1,5 @@
1
- import { MenuItemProps, MenuProps, MenuTrigger, PopoverProps } from "react-aria-components";
2
1
  import { ReactNode } from "react";
2
+ import { MenuItemProps, MenuProps, MenuTrigger, PopoverProps } from "react-aria-components";
3
3
  //#region src/menu/menu.d.ts
4
4
  /**
5
5
  * Menu, ported from the legacy shadcn/Radix `dropdown-menu` onto React
@@ -68,7 +68,7 @@ interface MenuProps$1 extends Omit<MenuProps<object>, "className" | "style" | "c
68
68
  /** Gap between the trigger and the menu. @default 4 */
69
69
  offset?: number;
70
70
  }
71
- declare function Menu$1({ children, className, placement, offset, ...props }: MenuProps$1): import("react").JSX.Element;
71
+ declare const Menu$1: import("react").ForwardRefExoticComponent<MenuProps$1 & import("react").RefAttributes<HTMLDivElement>>;
72
72
  /**
73
73
  * One menu row.
74
74
  *
@@ -80,7 +80,7 @@ interface MenuItemProps$1 extends Omit<MenuItemProps, "className" | "style"> {
80
80
  /** Extra classes. Merged last, so they win conflicts with ours. */
81
81
  className?: string;
82
82
  }
83
- declare function MenuItem$1({ className, ...props }: MenuItemProps$1): import("react").JSX.Element;
83
+ declare const MenuItem$1: import("react").ForwardRefExoticComponent<MenuItemProps$1 & import("react").RefAttributes<HTMLDivElement>>;
84
84
  /**
85
85
  * A divider between groups of items.
86
86
  *
@@ -90,9 +90,9 @@ declare function MenuItem$1({ className, ...props }: MenuItemProps$1): import("r
90
90
  * standalone, because `MenuInner` supplies `elementType: 'div'` -- an
91
91
  * `<hr>` is not valid inside a `role="menu"` container.
92
92
  */
93
- export declare function MenuSeparator({ className }: {
93
+ export declare const MenuSeparator: import("react").ForwardRefExoticComponent<{
94
94
  className?: string;
95
- }): import("react").JSX.Element;
95
+ } & import("react").RefAttributes<HTMLElement>>;
96
96
  //#endregion
97
97
  export { Menu$1 as Menu, MenuItem$1 as MenuItem, MenuItemProps$1 as MenuItemProps, MenuProps$1 as MenuProps, MenuTrigger };
98
98
  //# sourceMappingURL=menu.d.ts.map
package/dist/menu/menu.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { cva } from "class-variance-authority";
4
+ import { forwardRef } from "react";
4
5
  import { Menu, MenuItem, MenuTrigger, Popover, Separator } from "react-aria-components";
5
6
  import { jsx } from "react/jsx-runtime";
6
7
  //#region src/menu/menu.tsx
@@ -57,27 +58,29 @@ const menuItemVariants = cva([
57
58
  "[&_svg:not([class*='size-'])]:size-(--icon-sm)"
58
59
  ]);
59
60
  const menuSeparatorVariants = cva(["-mx-1 my-1 h-px bg-border border-none"]);
60
- function Menu$1({ children, className, placement = "bottom start", offset = 4, ...props }) {
61
+ const Menu$1 = forwardRef(function Menu$2({ children, className, placement = "bottom start", offset = 4, ...props }, ref) {
61
62
  return /* @__PURE__ */ jsx(Popover, {
62
63
  placement,
63
64
  offset,
64
65
  "data-slot": "menu-popover",
65
66
  className: cn(menuPopoverVariants(), className),
66
67
  children: /* @__PURE__ */ jsx(Menu, {
68
+ ref,
67
69
  ...props,
68
70
  "data-slot": "menu",
69
71
  className: menuVariants(),
70
72
  children
71
73
  })
72
74
  });
73
- }
74
- function MenuItem$1({ className, ...props }) {
75
+ });
76
+ const MenuItem$1 = forwardRef(function MenuItem$2({ className, ...props }, ref) {
75
77
  return /* @__PURE__ */ jsx(MenuItem, {
78
+ ref,
76
79
  ...props,
77
80
  "data-slot": "menu-item",
78
81
  className: cn(menuItemVariants(), className)
79
82
  });
80
- }
83
+ });
81
84
  /**
82
85
  * A divider between groups of items.
83
86
  *
@@ -87,12 +90,13 @@ function MenuItem$1({ className, ...props }) {
87
90
  * standalone, because `MenuInner` supplies `elementType: 'div'` -- an
88
91
  * `<hr>` is not valid inside a `role="menu"` container.
89
92
  */
90
- function MenuSeparator({ className }) {
93
+ const MenuSeparator = forwardRef(function MenuSeparator({ className }, ref) {
91
94
  return /* @__PURE__ */ jsx(Separator, {
95
+ ref,
92
96
  "data-slot": "menu-separator",
93
97
  className: cn(menuSeparatorVariants(), className)
94
98
  });
95
- }
99
+ });
96
100
  //#endregion
97
101
  export { Menu$1 as Menu, MenuItem$1 as MenuItem, MenuSeparator, MenuTrigger, menuItemVariants, menuPopoverVariants, menuSeparatorVariants, menuVariants };
98
102
 
@@ -1 +1 @@
1
- {"version":3,"file":"menu.js","names":["Menu","AriaPopover","AriaMenu","MenuItem","AriaMenuItem","AriaSeparator"],"sources":["../../src/menu/menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n Popover as AriaPopover,\n Separator as AriaSeparator,\n type MenuItemProps as AriaMenuItemProps,\n type MenuProps as AriaMenuProps,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Menu, ported from the legacy shadcn/Radix `dropdown-menu` onto React\n * Aria's Menu collection.\n *\n * FIFTEEN EXPORTS BECOME FOUR, and the census is unambiguous about it. Ten\n * files import the legacy component but only EIGHT render anything --\n * `header.tsx` and `PollQuizCreator.tsx` import parts and use none. Across\n * those eight: `DropdownMenuItem` 42, `DropdownMenu` 13,\n * `DropdownMenuTrigger` 13, `DropdownMenuContent` 13,\n * `DropdownMenuSeparator` 2.\n *\n * Everything else is ZERO: `Group`, `CheckboxItem`, `RadioGroup`,\n * `RadioItem`, `Label`, `Shortcut`, `Sub`, `SubTrigger`, `SubContent`,\n * `Portal`. Nine exports, no call sites. React Aria has `MenuSection`,\n * `Header` and selection modes ready if any of them turns out to be\n * wanted -- they are one component each, added the day something needs one.\n *\n * `variant=\"destructive\"` is also never passed, which resolves a real\n * problem rather than dodging one: `destructive-strong` on a `muted` hover\n * fill measures 4.29:1 against a 4.5 floor. The legacy has the same defect\n * unmeasured and worse (its `destructive/10` composite gives 4.01:1). With\n * no call site there is nothing to ship and nothing to weaken the contract\n * for.\n *\n * The surface is `card`, not a `popover` role: in the legacy theme\n * `--popover` is byte-identical to `--card` in both themes. Item hover is\n * `muted`, not `accent`: `--accent` is byte-identical to `--muted`. Two\n * more of the migration plan's proposed roles that already exist.\n *\n * Every string in the cva blocks below is a STATIC literal.\n */\nexport const menuPopoverVariants = cva([\n // min-w-32 is 8rem, the legacy min-w-[8rem] -- but as a spacing-scale\n // step rather than a bracket value that check:hardcoding rejects.\n \"min-w-32 overflow-y-auto\",\n \"rounded-md border-(length:--border-width) border-border p-1\",\n \"bg-card text-card-foreground shadow-floating\",\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n]);\n\nexport const menuVariants = cva([\n \"flex flex-col outline-none\",\n]);\n\nexport const menuItemVariants = cva([\n \"relative flex cursor-pointer select-none items-center gap-2\",\n \"rounded-2xs px-2 py-1.5\",\n \"box-border text-sm leading-(--leading-normal) text-card-foreground\",\n \"outline-none\",\n\n // data-focused, NOT :hover. React Aria drives one highlight from both\n // pointer and keyboard, so the row under the mouse and the row the arrow\n // keys reached are the same row -- the legacy used `focus:` for the\n // keyboard and nothing for hover, so a mouse user saw no highlight at all\n // until the item took focus.\n \"data-[focused]:bg-muted\",\n\n // The focus ring goes on the ITEM, and it is an outline rather than a\n // background alone: a highlight made only of colour disappears under\n // forced-colors, and axe has no rule that would notice.\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n]);\n\nexport const menuSeparatorVariants = cva([\"-mx-1 my-1 h-px bg-border border-none\"]);\n\n/**\n * A dropdown menu.\n *\n * @example\n * <MenuTrigger>\n * <Button>Actions</Button>\n * <Menu onAction={(key) => run(key)}>\n * <MenuItem id=\"edit\">Edit</MenuItem>\n * <MenuSeparator />\n * <MenuItem id=\"delete\">Delete</MenuItem>\n * </Menu>\n * </MenuTrigger>\n */\nexport interface MenuProps\n extends Omit<AriaMenuProps<object>, \"className\" | \"style\" | \"children\" | \"items\"> {\n /** `MenuItem` and `MenuSeparator` children. */\n children?: ReactNode;\n /** Extra classes for the popover. Merged last, so they win conflicts. */\n className?: string;\n /**\n * Where the menu opens, as React Aria's single `placement` string.\n *\n * The legacy split this across `side`, `align`, `sideOffset` and\n * `alignOffset`. Its census reads `align=\"end\"` 10 and `align=\"center\"`\n * 2, always with the default bottom side -- so `\"bottom end\"` covers ten\n * of the twelve.\n *\n * @default \"bottom start\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /** Gap between the trigger and the menu. @default 4 */\n offset?: number;\n}\n\nexport function Menu({\n children,\n className,\n placement = \"bottom start\",\n offset = 4,\n ...props\n}: MenuProps) {\n return (\n <AriaPopover\n placement={placement}\n offset={offset}\n data-slot=\"menu-popover\"\n className={cn(menuPopoverVariants(), className)}\n >\n <AriaMenu {...props} data-slot=\"menu\" className={menuVariants()}>\n {children}\n </AriaMenu>\n </AriaPopover>\n );\n}\n\n/**\n * One menu row.\n *\n * @example\n * <MenuItem id=\"edit\">Edit</MenuItem>\n * <MenuItem href=\"/settings\">Settings</MenuItem>\n */\nexport interface MenuItemProps extends Omit<AriaMenuItemProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport function MenuItem({ className, ...props }: MenuItemProps) {\n return (\n <AriaMenuItem {...props} data-slot=\"menu-item\" className={cn(menuItemVariants(), className)} />\n );\n}\n\n/**\n * A divider between groups of items.\n *\n * A real collection child: React Aria's `Separator` is a `SeparatorNode`,\n * so unlike an arbitrary element it is legal inside a Menu. Inside one it\n * renders `<div role=\"separator\">` rather than the `<hr>` it uses\n * standalone, because `MenuInner` supplies `elementType: 'div'` -- an\n * `<hr>` is not valid inside a `role=\"menu\"` container.\n */\nexport function MenuSeparator({ className }: { className?: string }) {\n return (\n <AriaSeparator\n data-slot=\"menu-separator\"\n className={cn(menuSeparatorVariants(), className)}\n />\n );\n}\n\n/**\n * Pairs a trigger with a menu and owns the open state.\n *\n * Re-exported from React Aria unchanged. It takes the trigger element\n * directly as its first child, which is what `asChild` was doing on all 13\n * legacy call sites.\n */\nexport { MenuTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAa,sBAAsB,IAAI;CAGrC;CACA;CACA;CACA;AACF,CAAC;AAED,MAAa,eAAe,IAAI,CAC9B,4BACF,CAAC;AAED,MAAa,mBAAmB,IAAI;CAClC;CACA;CACA;CACA;CAOA;CAKA;CACA;CACA;CACA;CAEA;CACA;CACA;AACF,CAAC;AAED,MAAa,wBAAwB,IAAI,CAAC,uCAAuC,CAAC;AAoClF,SAAgBA,OAAK,EACnB,UACA,WACA,YAAY,gBACZ,SAAS,GACT,GAAG,SACS;CACZ,OACE,oBAACC,SAAD;EACa;EACH;EACR,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;EAE9C,UAAA,oBAACC,MAAD;GAAU,GAAI;GAAO,aAAU;GAAO,WAAW,aAAa;GAC3D;EACO,CAAA;CACC,CAAA;AAEjB;AAcA,SAAgBC,WAAS,EAAE,WAAW,GAAG,SAAwB;CAC/D,OACE,oBAACC,UAAD;EAAc,GAAI;EAAO,aAAU;EAAY,WAAW,GAAG,iBAAiB,GAAG,SAAS;CAAI,CAAA;AAElG;;;;;;;;;;AAWA,SAAgB,cAAc,EAAE,aAAqC;CACnE,OACE,oBAACC,WAAD;EACE,aAAU;EACV,WAAW,GAAG,sBAAsB,GAAG,SAAS;CACjD,CAAA;AAEL"}
1
+ {"version":3,"file":"menu.js","names":["Menu","AriaPopover","AriaMenu","MenuItem","AriaMenuItem","AriaSeparator"],"sources":["../../src/menu/menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n Popover as AriaPopover,\n Separator as AriaSeparator,\n type MenuItemProps as AriaMenuItemProps,\n type MenuProps as AriaMenuProps,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Menu, ported from the legacy shadcn/Radix `dropdown-menu` onto React\n * Aria's Menu collection.\n *\n * FIFTEEN EXPORTS BECOME FOUR, and the census is unambiguous about it. Ten\n * files import the legacy component but only EIGHT render anything --\n * `header.tsx` and `PollQuizCreator.tsx` import parts and use none. Across\n * those eight: `DropdownMenuItem` 42, `DropdownMenu` 13,\n * `DropdownMenuTrigger` 13, `DropdownMenuContent` 13,\n * `DropdownMenuSeparator` 2.\n *\n * Everything else is ZERO: `Group`, `CheckboxItem`, `RadioGroup`,\n * `RadioItem`, `Label`, `Shortcut`, `Sub`, `SubTrigger`, `SubContent`,\n * `Portal`. Nine exports, no call sites. React Aria has `MenuSection`,\n * `Header` and selection modes ready if any of them turns out to be\n * wanted -- they are one component each, added the day something needs one.\n *\n * `variant=\"destructive\"` is also never passed, which resolves a real\n * problem rather than dodging one: `destructive-strong` on a `muted` hover\n * fill measures 4.29:1 against a 4.5 floor. The legacy has the same defect\n * unmeasured and worse (its `destructive/10` composite gives 4.01:1). With\n * no call site there is nothing to ship and nothing to weaken the contract\n * for.\n *\n * The surface is `card`, not a `popover` role: in the legacy theme\n * `--popover` is byte-identical to `--card` in both themes. Item hover is\n * `muted`, not `accent`: `--accent` is byte-identical to `--muted`. Two\n * more of the migration plan's proposed roles that already exist.\n *\n * Every string in the cva blocks below is a STATIC literal.\n */\nexport const menuPopoverVariants = cva([\n // min-w-32 is 8rem, the legacy min-w-[8rem] -- but as a spacing-scale\n // step rather than a bracket value that check:hardcoding rejects.\n \"min-w-32 overflow-y-auto\",\n \"rounded-md border-(length:--border-width) border-border p-1\",\n \"bg-card text-card-foreground shadow-floating\",\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n]);\n\nexport const menuVariants = cva([\n \"flex flex-col outline-none\",\n]);\n\nexport const menuItemVariants = cva([\n \"relative flex cursor-pointer select-none items-center gap-2\",\n \"rounded-2xs px-2 py-1.5\",\n \"box-border text-sm leading-(--leading-normal) text-card-foreground\",\n \"outline-none\",\n\n // data-focused, NOT :hover. React Aria drives one highlight from both\n // pointer and keyboard, so the row under the mouse and the row the arrow\n // keys reached are the same row -- the legacy used `focus:` for the\n // keyboard and nothing for hover, so a mouse user saw no highlight at all\n // until the item took focus.\n \"data-[focused]:bg-muted\",\n\n // The focus ring goes on the ITEM, and it is an outline rather than a\n // background alone: a highlight made only of colour disappears under\n // forced-colors, and axe has no rule that would notice.\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n]);\n\nexport const menuSeparatorVariants = cva([\"-mx-1 my-1 h-px bg-border border-none\"]);\n\n/**\n * A dropdown menu.\n *\n * @example\n * <MenuTrigger>\n * <Button>Actions</Button>\n * <Menu onAction={(key) => run(key)}>\n * <MenuItem id=\"edit\">Edit</MenuItem>\n * <MenuSeparator />\n * <MenuItem id=\"delete\">Delete</MenuItem>\n * </Menu>\n * </MenuTrigger>\n */\nexport interface MenuProps\n extends Omit<AriaMenuProps<object>, \"className\" | \"style\" | \"children\" | \"items\"> {\n /** `MenuItem` and `MenuSeparator` children. */\n children?: ReactNode;\n /** Extra classes for the popover. Merged last, so they win conflicts. */\n className?: string;\n /**\n * Where the menu opens, as React Aria's single `placement` string.\n *\n * The legacy split this across `side`, `align`, `sideOffset` and\n * `alignOffset`. Its census reads `align=\"end\"` 10 and `align=\"center\"`\n * 2, always with the default bottom side -- so `\"bottom end\"` covers ten\n * of the twelve.\n *\n * @default \"bottom start\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /** Gap between the trigger and the menu. @default 4 */\n offset?: number;\n}\n\nexport const Menu = forwardRef<HTMLDivElement, MenuProps>(function Menu(\n { children, className, placement = \"bottom start\", offset = 4, ...props },\n ref,\n) {\n return (\n <AriaPopover\n placement={placement}\n offset={offset}\n data-slot=\"menu-popover\"\n className={cn(menuPopoverVariants(), className)}\n >\n <AriaMenu\n // The ref goes on the MENU, not the POPOVER above. The popover here\n // is purely a positioner -- same role as an overlay wrapper -- and\n // the props spread and children already land on AriaMenu.\n ref={ref}\n {...props}\n data-slot=\"menu\"\n className={menuVariants()}\n >\n {children}\n </AriaMenu>\n </AriaPopover>\n );\n});\n\n/**\n * One menu row.\n *\n * @example\n * <MenuItem id=\"edit\">Edit</MenuItem>\n * <MenuItem href=\"/settings\">Settings</MenuItem>\n */\nexport interface MenuItemProps extends Omit<AriaMenuItemProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport const MenuItem = forwardRef<HTMLDivElement, MenuItemProps>(function MenuItem(\n { className, ...props },\n ref,\n) {\n return (\n <AriaMenuItem\n ref={ref}\n {...props}\n data-slot=\"menu-item\"\n className={cn(menuItemVariants(), className)}\n />\n );\n});\n\n/**\n * A divider between groups of items.\n *\n * A real collection child: React Aria's `Separator` is a `SeparatorNode`,\n * so unlike an arbitrary element it is legal inside a Menu. Inside one it\n * renders `<div role=\"separator\">` rather than the `<hr>` it uses\n * standalone, because `MenuInner` supplies `elementType: 'div'` -- an\n * `<hr>` is not valid inside a `role=\"menu\"` container.\n */\nexport const MenuSeparator = forwardRef<HTMLElement, { className?: string }>(\n function MenuSeparator({ className }, ref) {\n return (\n <AriaSeparator\n ref={ref}\n data-slot=\"menu-separator\"\n className={cn(menuSeparatorVariants(), className)}\n />\n );\n },\n);\n\n/**\n * Pairs a trigger with a menu and owns the open state.\n *\n * Re-exported from React Aria unchanged. It takes the trigger element\n * directly as its first child, which is what `asChild` was doing on all 13\n * legacy call sites.\n */\nexport { MenuTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAa,sBAAsB,IAAI;CAGrC;CACA;CACA;CACA;AACF,CAAC;AAED,MAAa,eAAe,IAAI,CAC9B,4BACF,CAAC;AAED,MAAa,mBAAmB,IAAI;CAClC;CACA;CACA;CACA;CAOA;CAKA;CACA;CACA;CACA;CAEA;CACA;CACA;AACF,CAAC;AAED,MAAa,wBAAwB,IAAI,CAAC,uCAAuC,CAAC;AAoClF,MAAaA,SAAO,WAAsC,SAASA,OACjE,EAAE,UAAU,WAAW,YAAY,gBAAgB,SAAS,GAAG,GAAG,SAClE,KACA;CACA,OACE,oBAACC,SAAD;EACa;EACH;EACR,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;EAE9C,UAAA,oBAACC,MAAD;GAIO;GACL,GAAI;GACJ,aAAU;GACV,WAAW,aAAa;GAEvB;EACO,CAAA;CACC,CAAA;AAEjB,CAAC;AAcD,MAAaC,aAAW,WAA0C,SAASA,WACzE,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAACC,UAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,iBAAiB,GAAG,SAAS;CAC5C,CAAA;AAEL,CAAC;;;;;;;;;;AAWD,MAAa,gBAAgB,WAC3B,SAAS,cAAc,EAAE,aAAa,KAAK;CACzC,OACE,oBAACC,WAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,sBAAsB,GAAG,SAAS;CACjD,CAAA;AAEL,CACF"}
@@ -1,5 +1,5 @@
1
- import { DialogTrigger as PopoverTrigger, PopoverProps } from "react-aria-components";
2
1
  import { ReactNode } from "react";
2
+ import { DialogTrigger as PopoverTrigger, PopoverProps } from "react-aria-components";
3
3
  //#region src/popover/popover.d.ts
4
4
  /**
5
5
  * Popover, ported from the legacy shadcn/Radix component.
@@ -44,7 +44,7 @@ interface PopoverProps$1 extends Omit<PopoverProps, "className" | "style" | "chi
44
44
  */
45
45
  placement?: PopoverProps["placement"];
46
46
  }
47
- declare function Popover$1({ children, className, placement, offset, ...props }: PopoverProps$1): import("react").JSX.Element;
47
+ declare const Popover$1: import("react").ForwardRefExoticComponent<PopoverProps$1 & import("react").RefAttributes<HTMLElement>>;
48
48
  //#endregion
49
49
  export { Popover$1 as Popover, PopoverProps$1 as PopoverProps, PopoverTrigger };
50
50
  //# sourceMappingURL=popover.d.ts.map
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { cva } from "class-variance-authority";
4
+ import { forwardRef } from "react";
4
5
  import { DialogTrigger as PopoverTrigger, Popover } from "react-aria-components";
5
6
  import { jsx } from "react/jsx-runtime";
6
7
  //#region src/popover/popover.tsx
@@ -30,8 +31,9 @@ const popoverVariants = cva([
30
31
  "box-border font-sans text-sm leading-(--leading-normal)",
31
32
  "outline-none"
32
33
  ]);
33
- function Popover$1({ children, className, placement = "bottom", offset = 4, ...props }) {
34
+ const Popover$1 = forwardRef(function Popover$2({ children, className, placement = "bottom", offset = 4, ...props }, ref) {
34
35
  return /* @__PURE__ */ jsx(Popover, {
36
+ ref,
35
37
  ...props,
36
38
  placement,
37
39
  offset,
@@ -39,7 +41,7 @@ function Popover$1({ children, className, placement = "bottom", offset = 4, ...p
39
41
  className: cn(popoverVariants(), className),
40
42
  children
41
43
  });
42
- }
44
+ });
43
45
  //#endregion
44
46
  export { Popover$1 as Popover, PopoverTrigger, popoverVariants };
45
47
 
@@ -1 +1 @@
1
- {"version":3,"file":"popover.js","names":["Popover","AriaPopover"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Popover as AriaPopover,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-md border-(length:--border-width) border-border p-4\",\n \"bg-card text-card-foreground shadow-floating\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load\n // none: an unstyled document renders text in a serif, and content-box\n // would add the 16px padding on top of the width.\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // React Aria gives the popover tabindex=-1 and focuses it on open, so it\n // can take a focus ring it does not want.\n \"outline-none\",\n]);\n\n/**\n * Content anchored to a trigger.\n *\n * @example\n * <PopoverTrigger>\n * <Button>Pick a date</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverTrigger>\n */\nexport interface PopoverProps\n extends Omit<AriaPopoverProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Where it opens, as React Aria's single `placement` string. The legacy\n * split this across `side` and `align`; its one non-default call site is\n * `align=\"start\"`, which is `\"bottom start\"` here.\n *\n * @default \"bottom\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n}\n\nexport function Popover({\n children,\n className,\n placement = \"bottom\",\n offset = 4,\n ...props\n}: PopoverProps) {\n return (\n <AriaPopover\n {...props}\n placement={placement}\n offset={offset}\n data-slot=\"popover\"\n className={cn(popoverVariants(), className)}\n >\n {/*\n No inner Dialog. Wrapping the content in one is the common React\n Aria idiom and it was in the first draft here -- but deleting it\n broke no assertion, so it was doing nothing: a Popover under a\n DialogTrigger already gets role=\"dialog\", a focus scope, and\n aria-labelledby pointing at the trigger. The wrapper was one more\n element and one more className to explain.\n */}\n {children}\n </AriaPopover>\n );\n}\n\n/**\n * Pairs a trigger with a popover and owns the open state.\n *\n * React Aria's `DialogTrigger`, renamed for the migration: it takes the\n * trigger element directly as its first child, which is what `asChild` was\n * doing on both legacy call sites.\n */\nexport { DialogTrigger as PopoverTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,kBAAkB,IAAI;CAEjC;CACA;CAIA;CAGA;AACF,CAAC;AA0BD,SAAgBA,UAAQ,EACtB,UACA,WACA,YAAY,UACZ,SAAS,GACT,GAAG,SACY;CACf,OACE,oBAACC,SAAD;EACE,GAAI;EACO;EACH;EACR,aAAU;EACV,WAAW,GAAG,gBAAgB,GAAG,SAAS;EAUzC;CACU,CAAA;AAEjB"}
1
+ {"version":3,"file":"popover.js","names":["Popover","AriaPopover"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Popover as AriaPopover,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-md border-(length:--border-width) border-border p-4\",\n \"bg-card text-card-foreground shadow-floating\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load\n // none: an unstyled document renders text in a serif, and content-box\n // would add the 16px padding on top of the width.\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // React Aria gives the popover tabindex=-1 and focuses it on open, so it\n // can take a focus ring it does not want.\n \"outline-none\",\n]);\n\n/**\n * Content anchored to a trigger.\n *\n * @example\n * <PopoverTrigger>\n * <Button>Pick a date</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverTrigger>\n */\nexport interface PopoverProps\n extends Omit<AriaPopoverProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Where it opens, as React Aria's single `placement` string. The legacy\n * split this across `side` and `align`; its one non-default call site is\n * `align=\"start\"`, which is `\"bottom start\"` here.\n *\n * @default \"bottom\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n}\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(function Popover(\n { children, className, placement = \"bottom\", offset = 4, ...props },\n ref,\n) {\n return (\n <AriaPopover\n ref={ref}\n {...props}\n placement={placement}\n offset={offset}\n data-slot=\"popover\"\n className={cn(popoverVariants(), className)}\n >\n {/*\n No inner Dialog. Wrapping the content in one is the common React\n Aria idiom and it was in the first draft here -- but deleting it\n broke no assertion, so it was doing nothing: a Popover under a\n DialogTrigger already gets role=\"dialog\", a focus scope, and\n aria-labelledby pointing at the trigger. The wrapper was one more\n element and one more className to explain.\n */}\n {children}\n </AriaPopover>\n );\n});\n\n/**\n * Pairs a trigger with a popover and owns the open state.\n *\n * React Aria's `DialogTrigger`, renamed for the migration: it takes the\n * trigger element directly as its first child, which is what `asChild` was\n * doing on both legacy call sites.\n */\nexport { DialogTrigger as PopoverTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,kBAAkB,IAAI;CAEjC;CACA;CAIA;CAGA;AACF,CAAC;AA0BD,MAAaA,YAAU,WAAsC,SAASA,UACpE,EAAE,UAAU,WAAW,YAAY,UAAU,SAAS,GAAG,GAAG,SAC5D,KACA;CACA,OACE,oBAACC,SAAD;EACO;EACL,GAAI;EACO;EACH;EACR,aAAU;EACV,WAAW,GAAG,gBAAgB,GAAG,SAAS;EAUzC;CACU,CAAA;AAEjB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { VariantProps } from "class-variance-authority";
2
- import { ProgressBarProps } from "react-aria-components";
3
2
  import { ReactNode } from "react";
3
+ import { ProgressBarProps } from "react-aria-components";
4
4
  //#region src/progress/progress.d.ts
5
5
  /**
6
6
  * Progress, ported from the legacy shadcn/Radix component.
@@ -77,6 +77,6 @@ export interface ProgressProps extends Omit<ProgressBarProps, "className" | "sty
77
77
  /** Extra classes. Merged last, so they win conflicts with ours. */
78
78
  className?: string;
79
79
  }
80
- export declare function Progress({ label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props }: ProgressProps): import("react").JSX.Element;
80
+ export declare const Progress: import("react").ForwardRefExoticComponent<ProgressProps & import("react").RefAttributes<HTMLDivElement>>;
81
81
  //#endregion
82
82
  //# sourceMappingURL=progress.d.ts.map
@@ -1,6 +1,7 @@
1
1
  "use client";
2
- import { cn } from "../lib/cn.js";
2
+ import { cn, cnAll } from "../lib/cn.js";
3
3
  import { cva } from "class-variance-authority";
4
+ import { forwardRef } from "react";
4
5
  import { Label, ProgressBar } from "react-aria-components";
5
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  //#region src/progress/progress.tsx
@@ -46,17 +47,18 @@ const progressFillVariants = cva(["h-full rounded-pill", "motion-safe:transition
46
47
  } },
47
48
  defaultVariants: { tone: "neutral" }
48
49
  });
49
- function Progress({ label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props }) {
50
+ const Progress = forwardRef(function Progress({ label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props }, ref) {
50
51
  return /* @__PURE__ */ jsx(ProgressBar, {
52
+ ref,
51
53
  ...props,
52
54
  "data-slot": "progress",
53
55
  className: cn(progressVariants(), className),
54
56
  children: ({ percentage, isIndeterminate, valueText }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
55
57
  "data-slot": "progress-header",
56
- className: cn(showValue && "flex items-baseline justify-between gap-2"),
58
+ className: cnAll(showValue && "flex items-baseline justify-between gap-2"),
57
59
  children: [/* @__PURE__ */ jsx(Label, {
58
60
  "data-slot": "progress-label",
59
- className: cn("text-sm leading-(--leading-normal) text-foreground", labelHidden && "sr-only"),
61
+ className: cnAll("text-sm leading-(--leading-normal) text-foreground", labelHidden && "sr-only"),
60
62
  children: label
61
63
  }), showValue ? /* @__PURE__ */ jsx("span", {
62
64
  "data-slot": "progress-value",
@@ -75,7 +77,7 @@ function Progress({ label, labelHidden, showValue, tone, trackClassName, fillCla
75
77
  })
76
78
  })] })
77
79
  });
78
- }
80
+ });
79
81
  //#endregion
80
82
  export { Progress, progressFillVariants, progressTrackVariants, progressVariants };
81
83
 
@@ -1 +1 @@
1
- {"version":3,"file":"progress.js","names":["AriaProgressBar","AriaLabel"],"sources":["../../src/progress/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Label as AriaLabel,\n ProgressBar as AriaProgressBar,\n type ProgressBarProps as AriaProgressBarProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Progress, ported from the legacy shadcn/Radix component.\n *\n * Census: seven files import it and only TWO render anything --\n * `ResponseOption.tsx` and `LiveQA.tsx`. Both pass `value` and\n * `className` and nothing else.\n *\n * `label` IS REQUIRED, and that is the point. Neither live call site\n * passes `aria-label`, `aria-labelledby` or an `id` a label targets, and\n * `role=\"progressbar\"` does not take its name from content -- so both\n * rendered progress bars are announced as an unnamed progressbar. React\n * Aria supplies the plumbing for three naming routes but no name of its\n * own: `<ProgressBar value={60}/>` is just as nameless there. Making the\n * prop required is what turns it into something you cannot forget.\n *\n * The migration plan said this component \"adds the missing\n * aria-valuenow\". That is wrong -- Radix already supplies `aria-valuenow`,\n * `aria-valuemin`, `aria-valuemax` and `aria-valuetext`. The missing thing\n * was always the NAME.\n *\n * THE COLOURS ARE NOT THE LEGACY'S. Its track is `bg-primary/20`, an alpha\n * fill that composites against unknown page content and therefore has no\n * fixed contrast pair at all -- over white it is roughly 1.05:1, an\n * invisible boundary. Its indicator is `bg-primary`, 2.44:1 on the page.\n * Here the track is `muted` with a `border` boundary and the fill is\n * `primary-strong`, which puts the fill against the track at 4.61:1 and\n * the track against the page at 5.25:1.\n *\n * Both are in the build-failing contrast contract.\n */\nexport const progressVariants = cva([\"flex w-full flex-col gap-1.5 box-border font-sans\"]);\n\nexport const progressTrackVariants = cva([\n \"relative h-2 w-full overflow-hidden rounded-pill box-border\",\n // muted, NOT `border` as the Switch uses. A switch has no fill; a\n // progress bar does, and primary-strong on a `border` track is 1.01:1 --\n // the two halves are the same grey and the value disappears.\n \"bg-muted border-(length:--border-width) border-border\",\n]);\n\nexport const progressFillVariants = cva(\n [\n \"h-full rounded-pill\",\n \"motion-safe:transition-[width] motion-safe:duration-(--duration-base) motion-safe:ease-standard\",\n ],\n {\n variants: {\n /*\n * What the value MEANS, which for a progress bar is usually a score\n * rather than a completion. The product's bar carried exactly this axis\n * (`default`/`success`/`warning`/`error`/`info`) and reached for it to\n * colour performance metrics.\n *\n * `-strong` throughout: the fill sits on the `muted` track and has to\n * clear the 3:1 non-text floor of WCAG 1.4.11 against it, which is the\n * same reason the track is muted rather than `border` -- primary-strong\n * on a `border` track measures 1.01:1 and the value disappears.\n */\n tone: {\n neutral: \"bg-primary-strong\",\n success: \"bg-success-strong\",\n warning: \"bg-warning-strong\",\n danger: \"bg-destructive-strong\",\n info: \"bg-info-strong\",\n },\n },\n defaultVariants: { tone: \"neutral\" },\n },\n);\n\n/**\n * A determinate or indeterminate progress bar.\n *\n * @example\n * <Progress label=\"Upload\" value={62} />\n * <Progress label=\"Loading results\" />\n */\nexport interface ProgressProps\n extends Omit<AriaProgressBarProps, \"className\" | \"style\" | \"children\">,\n VariantProps<typeof progressFillVariants> {\n /**\n * What is progressing, and the bar's accessible name.\n *\n * **Required.** Both legacy call sites render an unnamed progressbar.\n * Pass `labelHidden` when the surrounding UI already says it.\n */\n label: ReactNode;\n /**\n * Render the label for screen readers only.\n *\n * Both legacy call sites sit beside visible text that was never\n * associated with the bar -- this is how that text becomes the name\n * without printing it twice.\n */\n labelHidden?: boolean;\n /**\n * Print the value beside the label.\n *\n * The text itself comes from React Aria, which already formats it from\n * `value`/`minValue`/`maxValue` and honours `formatOptions` and\n * `valueLabel` -- so a percentage, a currency or \"3 of 7\" are all reachable\n * without a second prop here. It was never RENDERED before: the bar\n * announced its value to assistive tech and showed a sighted reader nothing.\n */\n showValue?: boolean;\n /** Extra classes for the track. */\n trackClassName?: string;\n /** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */\n fillClassName?: string;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport function Progress({\n label,\n labelHidden,\n showValue,\n tone,\n trackClassName,\n fillClassName,\n className,\n ...props\n}: ProgressProps) {\n return (\n <AriaProgressBar\n {...props}\n data-slot=\"progress\"\n className={cn(progressVariants(), className)}\n >\n {({ percentage, isIndeterminate, valueText }) => (\n <>\n {/*\n The row exists only when the value is printed. Wrapping the label\n in a flex row unconditionally would change the label's box for\n every existing consumer to no purpose.\n */}\n <div\n data-slot=\"progress-header\"\n className={cn(showValue && \"flex items-baseline justify-between gap-2\")}\n >\n <AriaLabel\n data-slot=\"progress-label\"\n className={cn(\n \"text-sm leading-(--leading-normal) text-foreground\",\n labelHidden && \"sr-only\",\n )}\n >\n {label}\n </AriaLabel>\n {showValue ? (\n <span\n data-slot=\"progress-value\"\n // aria-hidden: React Aria already announces the value on the\n // progressbar itself. Without this a screen reader reads it\n // twice, once as the widget's value and once as loose text.\n aria-hidden=\"true\"\n className=\"text-sm leading-(--leading-normal) text-muted-foreground tabular-nums\"\n >\n {valueText}\n </span>\n ) : null}\n </div>\n <div\n data-slot=\"progress-track\"\n className={cn(progressTrackVariants(), trackClassName)}\n >\n {/*\n width, not the legacy translateX(-(100 - value)%). That\n overflowed for value > 100 -- a positive translate slid the\n indicator off to the right, while the announced value stayed\n whatever was passed. React Aria clamps the value, so the two\n can no longer disagree.\n\n An indeterminate bar renders no fill rather than the\n legacy's silent 0%: `value={undefined}` became `(value || 0)`\n there, so a sighted user saw \"0% done\" while a screen reader\n said \"indeterminate\".\n */}\n <div\n data-slot=\"progress-fill\"\n data-indeterminate={isIndeterminate || undefined}\n className={cn(progressFillVariants({ tone }), fillClassName)}\n style={{ width: isIndeterminate ? \"0%\" : `${percentage ?? 0}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,mBAAmB,IAAI,CAAC,mDAAmD,CAAC;AAEzF,MAAa,wBAAwB,IAAI,CACvC,+DAIA,uDACF,CAAC;AAED,MAAa,uBAAuB,IAClC,CACE,uBACA,iGACF,GACA;CACE,UAAU,EAYR,MAAM;EACJ,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;CACR,EACF;CACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CACF;AA6CA,SAAgB,SAAS,EACvB,OACA,aACA,WACA,MACA,gBACA,eACA,WACA,GAAG,SACa;CAChB,OACE,oBAACA,aAAD;EACE,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,iBAAiB,GAAG,SAAS;EAEzC,WAAA,EAAE,YAAY,iBAAiB,gBAC/B,qBAAA,UAAA,EAAA,UAAA,CAME,qBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,aAAa,2CAA2C;GAFxE,UAAA,CAIE,oBAACC,OAAD;IACE,aAAU;IACV,WAAW,GACT,sDACA,eAAe,SACjB;IAEC,UAAA;GACQ,CAAA,GACV,YACC,oBAAC,QAAD;IACE,aAAU;IAIV,eAAY;IACZ,WAAU;IAET,UAAA;GACG,CAAA,IACJ,IACD;EACL,CAAA,GAAA,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,sBAAsB,GAAG,cAAc;GAcrD,UAAA,oBAAC,OAAD;IACE,aAAU;IACV,sBAAoB,mBAAmB,KAAA;IACvC,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,aAAa;IAC3D,OAAO,EAAE,OAAO,kBAAkB,OAAO,GAAG,cAAc,EAAE,GAAG;GAChE,CAAA;EACE,CAAA,CACL,EAAA,CAAA;CAEW,CAAA;AAErB"}
1
+ {"version":3,"file":"progress.js","names":["AriaProgressBar","AriaLabel"],"sources":["../../src/progress/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Label as AriaLabel,\n ProgressBar as AriaProgressBar,\n type ProgressBarProps as AriaProgressBarProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\n\n/**\n * Progress, ported from the legacy shadcn/Radix component.\n *\n * Census: seven files import it and only TWO render anything --\n * `ResponseOption.tsx` and `LiveQA.tsx`. Both pass `value` and\n * `className` and nothing else.\n *\n * `label` IS REQUIRED, and that is the point. Neither live call site\n * passes `aria-label`, `aria-labelledby` or an `id` a label targets, and\n * `role=\"progressbar\"` does not take its name from content -- so both\n * rendered progress bars are announced as an unnamed progressbar. React\n * Aria supplies the plumbing for three naming routes but no name of its\n * own: `<ProgressBar value={60}/>` is just as nameless there. Making the\n * prop required is what turns it into something you cannot forget.\n *\n * The migration plan said this component \"adds the missing\n * aria-valuenow\". That is wrong -- Radix already supplies `aria-valuenow`,\n * `aria-valuemin`, `aria-valuemax` and `aria-valuetext`. The missing thing\n * was always the NAME.\n *\n * THE COLOURS ARE NOT THE LEGACY'S. Its track is `bg-primary/20`, an alpha\n * fill that composites against unknown page content and therefore has no\n * fixed contrast pair at all -- over white it is roughly 1.05:1, an\n * invisible boundary. Its indicator is `bg-primary`, 2.44:1 on the page.\n * Here the track is `muted` with a `border` boundary and the fill is\n * `primary-strong`, which puts the fill against the track at 4.61:1 and\n * the track against the page at 5.25:1.\n *\n * Both are in the build-failing contrast contract.\n */\nexport const progressVariants = cva([\"flex w-full flex-col gap-1.5 box-border font-sans\"]);\n\nexport const progressTrackVariants = cva([\n \"relative h-2 w-full overflow-hidden rounded-pill box-border\",\n // muted, NOT `border` as the Switch uses. A switch has no fill; a\n // progress bar does, and primary-strong on a `border` track is 1.01:1 --\n // the two halves are the same grey and the value disappears.\n \"bg-muted border-(length:--border-width) border-border\",\n]);\n\nexport const progressFillVariants = cva(\n [\n \"h-full rounded-pill\",\n \"motion-safe:transition-[width] motion-safe:duration-(--duration-base) motion-safe:ease-standard\",\n ],\n {\n variants: {\n /*\n * What the value MEANS, which for a progress bar is usually a score\n * rather than a completion. The product's bar carried exactly this axis\n * (`default`/`success`/`warning`/`error`/`info`) and reached for it to\n * colour performance metrics.\n *\n * `-strong` throughout: the fill sits on the `muted` track and has to\n * clear the 3:1 non-text floor of WCAG 1.4.11 against it, which is the\n * same reason the track is muted rather than `border` -- primary-strong\n * on a `border` track measures 1.01:1 and the value disappears.\n */\n tone: {\n neutral: \"bg-primary-strong\",\n success: \"bg-success-strong\",\n warning: \"bg-warning-strong\",\n danger: \"bg-destructive-strong\",\n info: \"bg-info-strong\",\n },\n },\n defaultVariants: { tone: \"neutral\" },\n },\n);\n\n/**\n * A determinate or indeterminate progress bar.\n *\n * @example\n * <Progress label=\"Upload\" value={62} />\n * <Progress label=\"Loading results\" />\n */\nexport interface ProgressProps\n extends Omit<AriaProgressBarProps, \"className\" | \"style\" | \"children\">,\n VariantProps<typeof progressFillVariants> {\n /**\n * What is progressing, and the bar's accessible name.\n *\n * **Required.** Both legacy call sites render an unnamed progressbar.\n * Pass `labelHidden` when the surrounding UI already says it.\n */\n label: ReactNode;\n /**\n * Render the label for screen readers only.\n *\n * Both legacy call sites sit beside visible text that was never\n * associated with the bar -- this is how that text becomes the name\n * without printing it twice.\n */\n labelHidden?: boolean;\n /**\n * Print the value beside the label.\n *\n * The text itself comes from React Aria, which already formats it from\n * `value`/`minValue`/`maxValue` and honours `formatOptions` and\n * `valueLabel` -- so a percentage, a currency or \"3 of 7\" are all reachable\n * without a second prop here. It was never RENDERED before: the bar\n * announced its value to assistive tech and showed a sighted reader nothing.\n */\n showValue?: boolean;\n /** Extra classes for the track. */\n trackClassName?: string;\n /** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */\n fillClassName?: string;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n { label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props },\n ref,\n) {\n return (\n <AriaProgressBar\n ref={ref}\n {...props}\n data-slot=\"progress\"\n className={cn(progressVariants(), className)}\n >\n {({ percentage, isIndeterminate, valueText }) => (\n <>\n {/*\n The row exists only when the value is printed. Wrapping the label\n in a flex row unconditionally would change the label's box for\n every existing consumer to no purpose.\n */}\n <div\n data-slot=\"progress-header\"\n className={cnAll(showValue && \"flex items-baseline justify-between gap-2\")}\n >\n <AriaLabel\n data-slot=\"progress-label\"\n className={cnAll(\n \"text-sm leading-(--leading-normal) text-foreground\",\n labelHidden && \"sr-only\",\n )}\n >\n {label}\n </AriaLabel>\n {showValue ? (\n <span\n data-slot=\"progress-value\"\n // aria-hidden: React Aria already announces the value on the\n // progressbar itself. Without this a screen reader reads it\n // twice, once as the widget's value and once as loose text.\n aria-hidden=\"true\"\n className=\"text-sm leading-(--leading-normal) text-muted-foreground tabular-nums\"\n >\n {valueText}\n </span>\n ) : null}\n </div>\n <div\n data-slot=\"progress-track\"\n className={cn(progressTrackVariants(), trackClassName)}\n >\n {/*\n width, not the legacy translateX(-(100 - value)%). That\n overflowed for value > 100 -- a positive translate slid the\n indicator off to the right, while the announced value stayed\n whatever was passed. React Aria clamps the value, so the two\n can no longer disagree.\n\n An indeterminate bar renders no fill rather than the\n legacy's silent 0%: `value={undefined}` became `(value || 0)`\n there, so a sighted user saw \"0% done\" while a screen reader\n said \"indeterminate\".\n */}\n <div\n data-slot=\"progress-fill\"\n data-indeterminate={isIndeterminate || undefined}\n className={cn(progressFillVariants({ tone }), fillClassName)}\n style={{ width: isIndeterminate ? \"0%\" : `${percentage ?? 0}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,mBAAmB,IAAI,CAAC,mDAAmD,CAAC;AAEzF,MAAa,wBAAwB,IAAI,CACvC,+DAIA,uDACF,CAAC;AAED,MAAa,uBAAuB,IAClC,CACE,uBACA,iGACF,GACA;CACE,UAAU,EAYR,MAAM;EACJ,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;CACR,EACF;CACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CACF;AA6CA,MAAa,WAAW,WAA0C,SAAS,SACzE,EAAE,OAAO,aAAa,WAAW,MAAM,gBAAgB,eAAe,WAAW,GAAG,SACpF,KACA;CACA,OACE,oBAACA,aAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,iBAAiB,GAAG,SAAS;EAEzC,WAAA,EAAE,YAAY,iBAAiB,gBAC/B,qBAAA,UAAA,EAAA,UAAA,CAME,qBAAC,OAAD;GACE,aAAU;GACV,WAAW,MAAM,aAAa,2CAA2C;GAF3E,UAAA,CAIE,oBAACC,OAAD;IACE,aAAU;IACV,WAAW,MACT,sDACA,eAAe,SACjB;IAEC,UAAA;GACQ,CAAA,GACV,YACC,oBAAC,QAAD;IACE,aAAU;IAIV,eAAY;IACZ,WAAU;IAET,UAAA;GACG,CAAA,IACJ,IACD;EACL,CAAA,GAAA,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,sBAAsB,GAAG,cAAc;GAcrD,UAAA,oBAAC,OAAD;IACE,aAAU;IACV,sBAAoB,mBAAmB,KAAA;IACvC,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,aAAa;IAC3D,OAAO,EAAE,OAAO,kBAAkB,OAAO,GAAG,cAAc,EAAE,GAAG;GAChE,CAAA;EACE,CAAA,CACL,EAAA,CAAA;CAEW,CAAA;AAErB,CAAC"}
@@ -1,5 +1,5 @@
1
- import { RadioGroupProps, RadioProps } from "react-aria-components";
2
1
  import { ReactNode } from "react";
2
+ import { RadioGroupProps, RadioProps } from "react-aria-components";
3
3
  //#region src/radio-group/radio-group.d.ts
4
4
  /**
5
5
  * RadioGroup, ported from the legacy shadcn/Radix component onto React Aria.
@@ -34,7 +34,7 @@ interface RadioGroupProps$1 extends Omit<RadioGroupProps, "className" | "style">
34
34
  /** Extra classes. Merged last, so they win conflicts with ours. */
35
35
  className?: string;
36
36
  }
37
- declare function RadioGroup$1({ className, ...props }: RadioGroupProps$1): import("react").JSX.Element;
37
+ declare const RadioGroup$1: import("react").ForwardRefExoticComponent<RadioGroupProps$1 & import("react").RefAttributes<HTMLDivElement>>;
38
38
  /**
39
39
  * One choice.
40
40
  *
@@ -47,7 +47,7 @@ interface RadioProps$1 extends Omit<RadioProps, "className" | "style" | "childre
47
47
  /** Extra classes for the label. Merged last, so they win conflicts. */
48
48
  className?: string;
49
49
  }
50
- declare function Radio$1({ className, children, ...props }: RadioProps$1): import("react").JSX.Element;
50
+ declare const Radio$1: import("react").ForwardRefExoticComponent<RadioProps$1 & import("react").RefAttributes<HTMLInputElement>>;
51
51
  //#endregion
52
52
  export { Radio$1 as Radio, RadioGroup$1 as RadioGroup, RadioGroupProps$1 as RadioGroupProps, RadioProps$1 as RadioProps };
53
53
  //# sourceMappingURL=radio-group.d.ts.map
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { cn } from "../lib/cn.js";
2
+ import { cn, cnAll } from "../lib/cn.js";
3
3
  import { selectionLabelVariants, selectionRingVariants } from "../lib/selection-control.js";
4
4
  import { cva } from "class-variance-authority";
5
+ import { forwardRef } from "react";
5
6
  import { Radio, RadioGroup } from "react-aria-components";
6
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
8
  //#region src/radio-group/radio-group.tsx
@@ -33,18 +34,20 @@ const radioVariants = cva([
33
34
  "group-data-[selected]:text-primary-foreground",
34
35
  selectionRingVariants()
35
36
  ]);
36
- function RadioGroup$1({ className, ...props }) {
37
+ const RadioGroup$1 = forwardRef(function RadioGroup$2({ className, ...props }, ref) {
37
38
  return /* @__PURE__ */ jsx(RadioGroup, {
39
+ ref,
38
40
  ...props,
39
41
  "data-slot": "radio-group",
40
42
  className: cn(radioGroupVariants(), className)
41
43
  });
42
- }
43
- function Radio$1({ className, children, ...props }) {
44
+ });
45
+ const Radio$1 = forwardRef(function Radio$2({ className, children, ...props }, ref) {
44
46
  return /* @__PURE__ */ jsx(Radio, {
45
47
  ...props,
48
+ inputRef: ref,
46
49
  "data-slot": "radio",
47
- className: cn(selectionLabelVariants(), "justify-start", className),
50
+ className: cn(cnAll(selectionLabelVariants(), "justify-start"), className),
48
51
  children: ({ isSelected }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
49
52
  "aria-hidden": "true",
50
53
  "data-slot": "radio-dot",
@@ -58,7 +61,7 @@ function Radio$1({ className, children, ...props }) {
58
61
  children
59
62
  })] })
60
63
  });
61
- }
64
+ });
62
65
  //#endregion
63
66
  export { Radio$1 as Radio, RadioGroup$1 as RadioGroup, radioGroupVariants, radioVariants };
64
67
 
@@ -1 +1 @@
1
- {"version":3,"file":"radio-group.js","names":["RadioGroup","AriaRadioGroup","Radio","AriaRadio"],"sources":["../../src/radio-group/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Radio as AriaRadio,\n RadioGroup as AriaRadioGroup,\n type RadioGroupProps as AriaRadioGroupProps,\n type RadioProps as AriaRadioProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { selectionLabelVariants, selectionRingVariants } from \"../lib/selection-control.js\";\n\n/**\n * RadioGroup, ported from the legacy shadcn/Radix component onto React Aria.\n *\n * Census: 1 file, 3 uses -- `RadioGroup` 1, `RadioGroupItem` 2. The\n * smallest real surface in the migration, so the port is deliberately plain.\n *\n * THE LABEL MOVES INSIDE THE ITEM. Radix's `RadioGroupItem` is a bare\n * button and the caller pairs it with a separate `<Label htmlFor>`; React\n * Aria's `Radio` takes its label as `children` and wires the association\n * itself. That is the one rename a call site has to make, and it removes\n * the id bookkeeping.\n *\n * `border-primary` becomes `border-border` for the same reason it did on\n * Checkbox: brand orange is 2.44:1 on white and cannot legally be the only\n * thing describing a control's edge (WCAG 1.4.11). Selected reuses\n * Checkbox's exact pair -- `bg-primary` with a `primary-strong` boundary --\n * so a radio and a checkbox in the same form agree.\n */\nexport const radioGroupVariants = cva([\n \"flex flex-col gap-2\",\n \"box-border font-sans\",\n]);\n\nexport const radioVariants = cva([\n \"inline-flex size-4 shrink-0 items-center justify-center\",\n \"rounded-full border-(length:--border-width) box-border\",\n \"bg-background border-border\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"group-data-[selected]:bg-primary group-data-[selected]:border-primary-strong\",\n \"group-data-[selected]:text-primary-foreground\",\n selectionRingVariants(),\n]);\n\n/**\n * A set of mutually exclusive choices.\n *\n * @example\n * <RadioGroup value={plan} onChange={setPlan} aria-label=\"Plan\">\n * <Radio value=\"free\">Free</Radio>\n * <Radio value=\"pro\">Pro</Radio>\n * </RadioGroup>\n */\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport function RadioGroup({ className, ...props }: RadioGroupProps) {\n return (\n <AriaRadioGroup\n {...props}\n data-slot=\"radio-group\"\n className={cn(radioGroupVariants(), className)}\n />\n );\n}\n\n/**\n * One choice.\n *\n * @example\n * <Radio value=\"pro\">Pro</Radio>\n */\nexport interface RadioProps extends Omit<AriaRadioProps, \"className\" | \"style\" | \"children\"> {\n /** The label, and this option's accessible name. */\n children?: ReactNode;\n /** Extra classes for the label. Merged last, so they win conflicts. */\n className?: string;\n}\n\nexport function Radio({ className, children, ...props }: RadioProps) {\n return (\n <AriaRadio\n {...props}\n data-slot=\"radio\"\n // justify-start, overriding the shared label's centring: a radio sits\n // in a vertical list where every label must start on the same line,\n // not centre itself in a 24px box.\n className={cn(selectionLabelVariants(), \"justify-start\", className)}\n >\n {({ isSelected }) => (\n <>\n <span aria-hidden=\"true\" data-slot=\"radio-dot\" className={radioVariants()}>\n {isSelected ? (\n // Rendered in JS rather than toggled with CSS, so the\n // precedence is explicit and a test can read it from the DOM.\n <span data-slot=\"radio-indicator\" className=\"size-1.5 rounded-full bg-current\" />\n ) : null}\n </span>\n {children == null ? null : <span data-slot=\"radio-label\">{children}</span>}\n </>\n )}\n </AriaRadio>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,qBAAqB,IAAI,CACpC,uBACA,sBACF,CAAC;AAED,MAAa,gBAAgB,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA,sBAAsB;AACxB,CAAC;AAgBD,SAAgBA,aAAW,EAAE,WAAW,GAAG,SAA0B;CACnE,OACE,oBAACC,YAAD;EACE,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL;AAeA,SAAgBC,QAAM,EAAE,WAAW,UAAU,GAAG,SAAqB;CACnE,OACE,oBAACC,OAAD;EACE,GAAI;EACJ,aAAU;EAIV,WAAW,GAAG,uBAAuB,GAAG,iBAAiB,SAAS;EAEhE,WAAA,EAAE,iBACF,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAY,WAAW,cAAc;GACrE,UAAA,aAGC,oBAAC,QAAD;IAAM,aAAU;IAAkB,WAAU;GAAoC,CAAA,IAC9E;EACA,CAAA,GACL,YAAY,OAAO,OAAO,oBAAC,QAAD;GAAM,aAAU;GAAe;EAAe,CAAA,CACzE,EAAA,CAAA;CAEK,CAAA;AAEf"}
1
+ {"version":3,"file":"radio-group.js","names":["RadioGroup","AriaRadioGroup","Radio","AriaRadio"],"sources":["../../src/radio-group/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Radio as AriaRadio,\n RadioGroup as AriaRadioGroup,\n type RadioGroupProps as AriaRadioGroupProps,\n type RadioProps as AriaRadioProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { selectionLabelVariants, selectionRingVariants } from \"../lib/selection-control.js\";\n\n/**\n * RadioGroup, ported from the legacy shadcn/Radix component onto React Aria.\n *\n * Census: 1 file, 3 uses -- `RadioGroup` 1, `RadioGroupItem` 2. The\n * smallest real surface in the migration, so the port is deliberately plain.\n *\n * THE LABEL MOVES INSIDE THE ITEM. Radix's `RadioGroupItem` is a bare\n * button and the caller pairs it with a separate `<Label htmlFor>`; React\n * Aria's `Radio` takes its label as `children` and wires the association\n * itself. That is the one rename a call site has to make, and it removes\n * the id bookkeeping.\n *\n * `border-primary` becomes `border-border` for the same reason it did on\n * Checkbox: brand orange is 2.44:1 on white and cannot legally be the only\n * thing describing a control's edge (WCAG 1.4.11). Selected reuses\n * Checkbox's exact pair -- `bg-primary` with a `primary-strong` boundary --\n * so a radio and a checkbox in the same form agree.\n */\nexport const radioGroupVariants = cva([\n \"flex flex-col gap-2\",\n \"box-border font-sans\",\n]);\n\nexport const radioVariants = cva([\n \"inline-flex size-4 shrink-0 items-center justify-center\",\n \"rounded-full border-(length:--border-width) box-border\",\n \"bg-background border-border\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"group-data-[selected]:bg-primary group-data-[selected]:border-primary-strong\",\n \"group-data-[selected]:text-primary-foreground\",\n selectionRingVariants(),\n]);\n\n/**\n * A set of mutually exclusive choices.\n *\n * @example\n * <RadioGroup value={plan} onChange={setPlan} aria-label=\"Plan\">\n * <Radio value=\"free\">Free</Radio>\n * <Radio value=\"pro\">Pro</Radio>\n * </RadioGroup>\n */\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(function RadioGroup(\n { className, ...props },\n ref,\n) {\n return (\n <AriaRadioGroup\n ref={ref}\n {...props}\n data-slot=\"radio-group\"\n className={cn(radioGroupVariants(), className)}\n />\n );\n});\n\n/**\n * One choice.\n *\n * @example\n * <Radio value=\"pro\">Pro</Radio>\n */\nexport interface RadioProps extends Omit<AriaRadioProps, \"className\" | \"style\" | \"children\"> {\n /** The label, and this option's accessible name. */\n children?: ReactNode;\n /** Extra classes for the label. Merged last, so they win conflicts. */\n className?: string;\n}\n\nexport const Radio = forwardRef<HTMLInputElement, RadioProps>(function Radio(\n { className, children, ...props },\n ref,\n) {\n return (\n <AriaRadio\n {...props}\n // inputRef, not ref: AriaRadio's own ref targets the label it renders\n // around a visually-hidden native input, and a label cannot be\n // focused -- ref.current.focus() would silently no-op. inputRef is\n // React Aria's own escape hatch to the real control.\n inputRef={ref}\n data-slot=\"radio\"\n // justify-start, overriding the shared label's centring: a radio sits\n // in a vertical list where every label must start on the same line,\n // not centre itself in a 24px box.\n className={cn(cnAll(selectionLabelVariants(), \"justify-start\"), className)}\n >\n {({ isSelected }) => (\n <>\n <span aria-hidden=\"true\" data-slot=\"radio-dot\" className={radioVariants()}>\n {isSelected ? (\n // Rendered in JS rather than toggled with CSS, so the\n // precedence is explicit and a test can read it from the DOM.\n <span data-slot=\"radio-indicator\" className=\"size-1.5 rounded-full bg-current\" />\n ) : null}\n </span>\n {children == null ? null : <span data-slot=\"radio-label\">{children}</span>}\n </>\n )}\n </AriaRadio>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,qBAAqB,IAAI,CACpC,uBACA,sBACF,CAAC;AAED,MAAa,gBAAgB,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA,sBAAsB;AACxB,CAAC;AAgBD,MAAaA,eAAa,WAA4C,SAASA,aAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAACC,YAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAeD,MAAaC,UAAQ,WAAyC,SAASA,QACrE,EAAE,WAAW,UAAU,GAAG,SAC1B,KACA;CACA,OACE,oBAACC,OAAD;EACE,GAAI;EAKJ,UAAU;EACV,aAAU;EAIV,WAAW,GAAG,MAAM,uBAAuB,GAAG,eAAe,GAAG,SAAS;EAEvE,WAAA,EAAE,iBACF,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAY,WAAW,cAAc;GACrE,UAAA,aAGC,oBAAC,QAAD;IAAM,aAAU;IAAkB,WAAU;GAAoC,CAAA,IAC9E;EACA,CAAA,GACL,YAAY,OAAO,OAAO,oBAAC,QAAD;GAAM,aAAU;GAAe;EAAe,CAAA,CACzE,EAAA,CAAA;CAEK,CAAA;AAEf,CAAC"}
@@ -57,11 +57,12 @@ export interface ScrollAreaProps extends ComponentPropsWithoutRef<"div"> {
57
57
  * Change this when the contents change, and the region scrolls back to the
58
58
  * top.
59
59
  *
60
- * It lives here rather than on the caller because the caller cannot reach
61
- * this node: no component in this package exposes a `ref`, and it must not
62
- * -- React 18 is a supported peer, where a `ref` passed as a plain prop to a
63
- * function component is silently dropped. The scroller owns its own
64
- * scrollTop, so the reset belongs with it.
60
+ * This stays a declarative prop even though a `ref` now exists: reaching
61
+ * for it would mean the caller writes `ref.current.scrollTop = 0` in their
62
+ * own effect on every content change, which is worse ergonomics than a
63
+ * prop that says "reset when this value changes." That is a design choice
64
+ * independent of whether a ref is available, not a consequence of one
65
+ * being withheld.
65
66
  *
66
67
  * Sheet forwards its own `scrollResetKey` straight through: going a level
67
68
  * deeper in a panel and landing halfway down the new view is disorienting,
@@ -69,6 +70,6 @@ export interface ScrollAreaProps extends ComponentPropsWithoutRef<"div"> {
69
70
  */
70
71
  scrollResetKey?: string | number;
71
72
  }
72
- export declare function ScrollArea({ className, children, label, scrollResetKey, ...props }: ScrollAreaProps): import("react").JSX.Element;
73
+ export declare const ScrollArea: import("react").ForwardRefExoticComponent<ScrollAreaProps & import("react").RefAttributes<HTMLDivElement>>;
73
74
  //#endregion
74
75
  //# sourceMappingURL=scroll-area.d.ts.map
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
+ import { mergeRefs } from "../lib/merge-refs.js";
3
4
  import { cva } from "class-variance-authority";
5
+ import { forwardRef, useEffect, useMemo, useRef } from "react";
4
6
  import { jsx } from "react/jsx-runtime";
5
- import { useEffect, useRef } from "react";
6
7
  //#region src/scroll-area/scroll-area.tsx
7
8
  /**
8
9
  * ScrollArea. React Aria has no equivalent, so this is a plain scrolling
@@ -41,14 +42,15 @@ const scrollAreaVariants = cva([
41
42
  "focus-visible:outline-offset-(--focus-ring-offset)",
42
43
  "focus-visible:outline-(--color-ring)"
43
44
  ]);
44
- function ScrollArea({ className, children, label, scrollResetKey, ...props }) {
45
+ const ScrollArea = forwardRef(function ScrollArea({ className, children, label, scrollResetKey, ...props }, ref) {
45
46
  const region = useRef(null);
46
47
  useEffect(() => {
47
48
  if (region.current) region.current.scrollTop = 0;
48
49
  }, [scrollResetKey]);
50
+ const rootRef = useMemo(() => mergeRefs(ref, region), [ref, region]);
49
51
  return /* @__PURE__ */ jsx("div", {
50
52
  ...props,
51
- ref: region,
53
+ ref: rootRef,
52
54
  tabIndex: 0,
53
55
  role: "region",
54
56
  "aria-label": label,
@@ -56,7 +58,7 @@ function ScrollArea({ className, children, label, scrollResetKey, ...props }) {
56
58
  className: cn(scrollAreaVariants(), className),
57
59
  children
58
60
  });
59
- }
61
+ });
60
62
  //#endregion
61
63
  export { ScrollArea, scrollAreaVariants };
62
64
 
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-area.js","names":[],"sources":["../../src/scroll-area/scroll-area.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { useEffect, useRef, type ComponentPropsWithoutRef, type ReactNode } from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * ScrollArea. React Aria has no equivalent, so this is a plain scrolling\n * div -- and that is the honest answer rather than a shortcut.\n *\n * Census: fourteen paths match, thirteen are counted, ELEVEN render\n * anything, and across them 17 uses. Every single one passes `className`\n * and nothing else, and 13 of the 17 are a height or flex constraint plus\n * padding. `ScrollBar` is exported by the legacy and imported by nobody.\n * `orientation=\"horizontal\"` has zero call sites.\n *\n * So there is no styled-scrollbar requirement to port. The legacy pays for\n * one anyway: Radix injects a `<style>` tag that hides the native\n * scrollbar, then re-draws it in JavaScript with a ResizeObserver, a\n * portal-free overlay and a `display: table` content wrapper. That is a\n * lot of machinery to reproduce a scrollbar, and it is why the legacy\n * `ScrollArea` cannot be scrolled by keyboard at all.\n *\n * Tailwind 4.3.3 ships `scrollbar-width` and `scrollbar-color` utilities\n * (verified in the installed dist, not assumed), which gets the same look\n * in three classes and no JavaScript.\n *\n * WHAT CANNOT BE STYLED, stated rather than glossed: `scrollbar-color`\n * takes a thumb and a track colour and nothing else -- no width in pixels,\n * no radius, no hover state, no margins. `::-webkit-scrollbar` can do\n * those but is non-standard and Firefox has never supported it. This\n * component uses the standard properties only.\n */\nexport const scrollAreaVariants = cva([\n \"overflow-y-auto overscroll-contain\",\n \"box-border\",\n // Standard scrollbar properties. thumb = --color-border (5.25:1 on the\n // page), track transparent so it does not draw a channel.\n \"scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent\",\n // A scrollable region must be reachable by keyboard, or its content is\n // unreachable without a pointer -- WCAG 2.1.1, and axe's\n // scrollable-region-focusable rule. The legacy has no tabIndex at all,\n // so all 17 of its scroll areas are keyboard traps in the other\n // direction: you can see the content but never scroll to it.\n \"outline-none\",\n \"focus-visible:outline-solid\",\n \"focus-visible:outline-(length:--focus-ring-width)\",\n \"focus-visible:outline-offset-(--focus-ring-offset)\",\n \"focus-visible:outline-(--color-ring)\",\n]);\n\n/**\n * A scrolling region.\n *\n * @example\n * <ScrollArea label=\"Chat messages\" className=\"h-64\">...</ScrollArea>\n */\nexport interface ScrollAreaProps extends ComponentPropsWithoutRef<\"div\"> {\n children?: ReactNode;\n /**\n * Names the scrolling region.\n *\n * **Required**, and it took a failing test to get here. A focusable\n * element with no role fails axe `focus-order-semantics`, so the\n * keyboard fix below cannot ship without a role -- and a role with no\n * accessible name is a violation of its own. Naming it is the only\n * combination that is actually correct.\n *\n * `role=\"region\"` specifically. `group` was the first attempt -- it is\n * lighter, since region is a landmark and seventeen landmarks in one app\n * is noise -- but axe does not accept `group` as an appropriate role for\n * a focusable element, and it is right: a named region is the role the\n * ARIA practices name for a scrollable content area, and being able to\n * jump to it is the point.\n */\n label: string;\n /**\n * Change this when the contents change, and the region scrolls back to the\n * top.\n *\n * It lives here rather than on the caller because the caller cannot reach\n * this node: no component in this package exposes a `ref`, and it must not\n * -- React 18 is a supported peer, where a `ref` passed as a plain prop to a\n * function component is silently dropped. The scroller owns its own\n * scrollTop, so the reset belongs with it.\n *\n * Sheet forwards its own `scrollResetKey` straight through: going a level\n * deeper in a panel and landing halfway down the new view is disorienting,\n * and it is exactly the thing every call site forgets.\n */\n scrollResetKey?: string | number;\n}\n\nexport function ScrollArea({\n className,\n children,\n label,\n scrollResetKey,\n ...props\n}: ScrollAreaProps) {\n const region = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // Unconditional rather than guarded on scrollResetKey being set: with no\n // key this runs once on mount, where scrollTop is already 0.\n if (region.current) region.current.scrollTop = 0;\n }, [scrollResetKey]);\n\n return (\n <div\n {...props}\n ref={region}\n // tabIndex 0 unconditionally: whether the content overflows is a\n // runtime fact that changes as the content does, and a region that\n // becomes scrollable after a fetch would otherwise never gain focus.\n tabIndex={0}\n role=\"region\"\n aria-label={label}\n data-slot=\"scroll-area\"\n className={cn(scrollAreaVariants(), className)}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,qBAAqB,IAAI;CACpC;CACA;CAGA;CAMA;CACA;CACA;CACA;CACA;AACF,CAAC;AA4CD,SAAgB,WAAW,EACzB,WACA,UACA,OACA,gBACA,GAAG,SACe;CAClB,MAAM,SAAS,OAAuB,IAAI;CAE1C,gBAAgB;EAGd,IAAI,OAAO,SAAS,OAAO,QAAQ,YAAY;CACjD,GAAG,CAAC,cAAc,CAAC;CAEnB,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,KAAK;EAIL,UAAU;EACV,MAAK;EACL,cAAY;EACZ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;EAE5C;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"scroll-area.js","names":[],"sources":["../../src/scroll-area/scroll-area.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n forwardRef,\n useEffect,\n useMemo,\n useRef,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\n\n/**\n * ScrollArea. React Aria has no equivalent, so this is a plain scrolling\n * div -- and that is the honest answer rather than a shortcut.\n *\n * Census: fourteen paths match, thirteen are counted, ELEVEN render\n * anything, and across them 17 uses. Every single one passes `className`\n * and nothing else, and 13 of the 17 are a height or flex constraint plus\n * padding. `ScrollBar` is exported by the legacy and imported by nobody.\n * `orientation=\"horizontal\"` has zero call sites.\n *\n * So there is no styled-scrollbar requirement to port. The legacy pays for\n * one anyway: Radix injects a `<style>` tag that hides the native\n * scrollbar, then re-draws it in JavaScript with a ResizeObserver, a\n * portal-free overlay and a `display: table` content wrapper. That is a\n * lot of machinery to reproduce a scrollbar, and it is why the legacy\n * `ScrollArea` cannot be scrolled by keyboard at all.\n *\n * Tailwind 4.3.3 ships `scrollbar-width` and `scrollbar-color` utilities\n * (verified in the installed dist, not assumed), which gets the same look\n * in three classes and no JavaScript.\n *\n * WHAT CANNOT BE STYLED, stated rather than glossed: `scrollbar-color`\n * takes a thumb and a track colour and nothing else -- no width in pixels,\n * no radius, no hover state, no margins. `::-webkit-scrollbar` can do\n * those but is non-standard and Firefox has never supported it. This\n * component uses the standard properties only.\n */\nexport const scrollAreaVariants = cva([\n \"overflow-y-auto overscroll-contain\",\n \"box-border\",\n // Standard scrollbar properties. thumb = --color-border (5.25:1 on the\n // page), track transparent so it does not draw a channel.\n \"scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent\",\n // A scrollable region must be reachable by keyboard, or its content is\n // unreachable without a pointer -- WCAG 2.1.1, and axe's\n // scrollable-region-focusable rule. The legacy has no tabIndex at all,\n // so all 17 of its scroll areas are keyboard traps in the other\n // direction: you can see the content but never scroll to it.\n \"outline-none\",\n \"focus-visible:outline-solid\",\n \"focus-visible:outline-(length:--focus-ring-width)\",\n \"focus-visible:outline-offset-(--focus-ring-offset)\",\n \"focus-visible:outline-(--color-ring)\",\n]);\n\n/**\n * A scrolling region.\n *\n * @example\n * <ScrollArea label=\"Chat messages\" className=\"h-64\">...</ScrollArea>\n */\nexport interface ScrollAreaProps extends ComponentPropsWithoutRef<\"div\"> {\n children?: ReactNode;\n /**\n * Names the scrolling region.\n *\n * **Required**, and it took a failing test to get here. A focusable\n * element with no role fails axe `focus-order-semantics`, so the\n * keyboard fix below cannot ship without a role -- and a role with no\n * accessible name is a violation of its own. Naming it is the only\n * combination that is actually correct.\n *\n * `role=\"region\"` specifically. `group` was the first attempt -- it is\n * lighter, since region is a landmark and seventeen landmarks in one app\n * is noise -- but axe does not accept `group` as an appropriate role for\n * a focusable element, and it is right: a named region is the role the\n * ARIA practices name for a scrollable content area, and being able to\n * jump to it is the point.\n */\n label: string;\n /**\n * Change this when the contents change, and the region scrolls back to the\n * top.\n *\n * This stays a declarative prop even though a `ref` now exists: reaching\n * for it would mean the caller writes `ref.current.scrollTop = 0` in their\n * own effect on every content change, which is worse ergonomics than a\n * prop that says \"reset when this value changes.\" That is a design choice\n * independent of whether a ref is available, not a consequence of one\n * being withheld.\n *\n * Sheet forwards its own `scrollResetKey` straight through: going a level\n * deeper in a panel and landing halfway down the new view is disorienting,\n * and it is exactly the thing every call site forgets.\n */\n scrollResetKey?: string | number;\n}\n\nexport const ScrollArea = forwardRef<HTMLDivElement, ScrollAreaProps>(function ScrollArea(\n { className, children, label, scrollResetKey, ...props },\n ref,\n) {\n const region = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // Unconditional rather than guarded on scrollResetKey being set: with no\n // key this runs once on mount, where scrollTop is already 0.\n if (region.current) region.current.scrollTop = 0;\n }, [scrollResetKey]);\n\n // Memoized, not called inline on the div below: mergeRefs returns a new\n // callback every call, and an inline call is a new callback every render --\n // React would detach and reattach the DOM ref on every re-render instead of\n // only on mount, which matters here more than most, since ScrollArea's own\n // purpose is wrapping content that re-renders often.\n const rootRef = useMemo(() => mergeRefs(ref, region), [ref, region]);\n\n return (\n <div\n {...props}\n ref={rootRef}\n // tabIndex 0 unconditionally: whether the content overflows is a\n // runtime fact that changes as the content does, and a region that\n // becomes scrollable after a fetch would otherwise never gain focus.\n tabIndex={0}\n role=\"region\"\n aria-label={label}\n data-slot=\"scroll-area\"\n className={cn(scrollAreaVariants(), className)}\n >\n {children}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,qBAAqB,IAAI;CACpC;CACA;CAGA;CAMA;CACA;CACA;CACA;CACA;AACF,CAAC;AA6CD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,UAAU,OAAO,gBAAgB,GAAG,SACjD,KACA;CACA,MAAM,SAAS,OAAuB,IAAI;CAE1C,gBAAgB;EAGd,IAAI,OAAO,SAAS,OAAO,QAAQ,YAAY;CACjD,GAAG,CAAC,cAAc,CAAC;CAOnB,MAAM,UAAU,cAAc,UAAU,KAAK,MAAM,GAAG,CAAC,KAAK,MAAM,CAAC;CAEnE,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,KAAK;EAIL,UAAU;EACV,MAAK;EACL,cAAY;EACZ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;EAE5C;CACE,CAAA;AAET,CAAC"}
@@ -1,5 +1,5 @@
1
- import { ListBoxItemProps, SelectProps } from "react-aria-components";
2
1
  import { ReactNode } from "react";
2
+ import { ListBoxItemProps, SelectProps } from "react-aria-components";
3
3
  //#region src/select/select.d.ts
4
4
  /**
5
5
  * Select, ported from the legacy shadcn/Radix component.
@@ -80,7 +80,7 @@ interface SelectProps$1 extends Omit<SelectProps<object>, "className" | "style"
80
80
  /** Extra classes for the wrapper. Merged last. */
81
81
  className?: string;
82
82
  }
83
- declare function Select$1({ children, label, labelHidden, placeholder, errorMessage, size, className, ...props }: SelectProps$1): import("react").JSX.Element;
83
+ declare const Select$1: import("react").ForwardRefExoticComponent<SelectProps$1 & import("react").RefAttributes<HTMLButtonElement>>;
84
84
  /**
85
85
  * One option. Its `id` is the value.
86
86
  *
@@ -99,7 +99,7 @@ export interface SelectItemProps extends Omit<ListBoxItemProps, "className" | "s
99
99
  /** Extra classes. Merged last, so they win conflicts with ours. */
100
100
  className?: string;
101
101
  }
102
- export declare function SelectItem({ className, children, textValue, ...props }: SelectItemProps): import("react").JSX.Element;
102
+ export declare const SelectItem: import("react").ForwardRefExoticComponent<SelectItemProps & import("react").RefAttributes<HTMLDivElement>>;
103
103
  //#endregion
104
104
  export { Select$1 as Select, SelectProps$1 as SelectProps };
105
105
  //# sourceMappingURL=select.d.ts.map
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { cn } from "../lib/cn.js";
2
+ import { cn, cnAll } from "../lib/cn.js";
3
3
  import { fieldSurfaceVariants } from "../lib/field-surface.js";
4
4
  import { cva } from "class-variance-authority";
5
+ import { forwardRef } from "react";
5
6
  import { Button, FieldError, Label, ListBox, ListBoxItem, Popover, Select, SelectValue } from "react-aria-components";
6
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
8
  //#region src/select/select.tsx
@@ -73,7 +74,7 @@ const selectItemVariants = cva([
73
74
  "data-[focus-visible]:outline-(--color-ring)",
74
75
  "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)"
75
76
  ]);
76
- function Select$1({ children, label, labelHidden, placeholder, errorMessage, size = "md", className, ...props }) {
77
+ const Select$1 = forwardRef(function Select$2({ children, label, labelHidden, placeholder, errorMessage, size = "md", className, ...props }, ref) {
77
78
  return /* @__PURE__ */ jsxs(Select, {
78
79
  ...props,
79
80
  "data-slot": "select",
@@ -81,10 +82,11 @@ function Select$1({ children, label, labelHidden, placeholder, errorMessage, siz
81
82
  children: [
82
83
  /* @__PURE__ */ jsx(Label, {
83
84
  "data-slot": "select-label",
84
- className: cn("text-sm leading-(--leading-normal) text-foreground", labelHidden && "sr-only"),
85
+ className: cnAll("text-sm leading-(--leading-normal) text-foreground", labelHidden && "sr-only"),
85
86
  children: label
86
87
  }),
87
88
  /* @__PURE__ */ jsxs(Button, {
89
+ ref,
88
90
  "data-slot": "select-trigger",
89
91
  className: selectTriggerVariants({ size }),
90
92
  children: [/* @__PURE__ */ jsx(SelectValue, {
@@ -122,9 +124,10 @@ function Select$1({ children, label, labelHidden, placeholder, errorMessage, siz
122
124
  })
123
125
  ]
124
126
  });
125
- }
126
- function SelectItem({ className, children, textValue, ...props }) {
127
+ });
128
+ const SelectItem = forwardRef(function SelectItem({ className, children, textValue, ...props }, ref) {
127
129
  return /* @__PURE__ */ jsx(ListBoxItem, {
130
+ ref,
128
131
  ...props,
129
132
  textValue: textValue ?? (typeof children === "string" ? children : void 0),
130
133
  "data-slot": "select-item",
@@ -142,7 +145,7 @@ function SelectItem({ className, children, textValue, ...props }) {
142
145
  children: /* @__PURE__ */ jsx("path", { d: "m3.5 8.5 3 3 6-6" })
143
146
  }) : null, children] })
144
147
  });
145
- }
148
+ });
146
149
  //#endregion
147
150
  export { Select$1 as Select, SelectItem, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants };
148
151