@godxjp/ui 21.0.0 → 23.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +4 -4
  2. package/dist/app/app-provider.js +2 -1
  3. package/dist/components/data-display/badge.d.ts +28 -1
  4. package/dist/components/data-display/badge.js +2 -0
  5. package/dist/components/data-display/data-table.d.ts +18 -2
  6. package/dist/components/data-display/data-table.js +4 -0
  7. package/dist/components/data-display/index.d.ts +1 -1
  8. package/dist/components/data-display/popover.d.ts +19 -1
  9. package/dist/components/data-display/popover.js +9 -1
  10. package/dist/components/data-display/progress.d.ts +37 -0
  11. package/dist/components/data-display/progress.js +57 -5
  12. package/dist/components/data-display/scroll-area.d.ts +24 -4
  13. package/dist/components/data-display/scroll-area.js +34 -72
  14. package/dist/components/data-entry/cascader.js +8 -6
  15. package/dist/components/data-entry/checkbox.js +2 -0
  16. package/dist/components/data-entry/choice-hit-target.d.ts +24 -0
  17. package/dist/components/data-entry/choice-hit-target.js +12 -0
  18. package/dist/components/data-entry/control-surface.d.ts +8 -0
  19. package/dist/components/data-entry/control-surface.js +10 -1
  20. package/dist/components/data-entry/date-picker.d.ts +10 -3
  21. package/dist/components/data-entry/date-picker.js +503 -238
  22. package/dist/components/data-entry/form-field.d.ts +1 -1
  23. package/dist/components/data-entry/form-field.js +7 -3
  24. package/dist/components/data-entry/form.js +10 -1
  25. package/dist/components/data-entry/index.d.ts +0 -6
  26. package/dist/components/data-entry/index.js +0 -6
  27. package/dist/components/data-entry/label.d.ts +14 -1
  28. package/dist/components/data-entry/label.js +6 -1
  29. package/dist/components/data-entry/radio.d.ts +27 -6
  30. package/dist/components/data-entry/radio.js +89 -43
  31. package/dist/components/data-entry/search-select.js +226 -89
  32. package/dist/components/data-entry/select.d.ts +76 -14
  33. package/dist/components/data-entry/select.js +497 -163
  34. package/dist/components/data-entry/slider.d.ts +3 -11
  35. package/dist/components/data-entry/slider.js +582 -93
  36. package/dist/components/data-entry/switch.d.ts +6 -3
  37. package/dist/components/data-entry/switch.js +36 -11
  38. package/dist/components/layout/mobile-shell.d.ts +1 -1
  39. package/dist/components/layout/mobile-shell.js +2 -0
  40. package/dist/components/navigation/dropdown-menu.d.ts +11 -3
  41. package/dist/components/navigation/dropdown-menu.js +190 -15
  42. package/dist/components/navigation/index.d.ts +0 -3
  43. package/dist/components/navigation/index.js +0 -78
  44. package/dist/components/navigation/steps.d.ts +1 -1
  45. package/dist/components/navigation/steps.js +3 -1
  46. package/dist/components/navigation/tabs-scroll.d.ts +27 -0
  47. package/dist/components/navigation/tabs-scroll.js +52 -1
  48. package/dist/components/navigation/tabs.d.ts +2 -2
  49. package/dist/components/navigation/tabs.js +88 -23
  50. package/dist/components/ui/index.d.ts +0 -4
  51. package/dist/components/ui/index.js +0 -4
  52. package/dist/components/ui/input-otp.d.ts +1 -1
  53. package/dist/components/ui/segmented.d.ts +15 -1
  54. package/dist/components/ui/segmented.js +15 -6
  55. package/dist/components/ui/tag-input.js +3 -5
  56. package/dist/i18n/messages/en.json +3 -2
  57. package/dist/i18n/messages/ja.json +3 -2
  58. package/dist/i18n/messages/vi.json +3 -2
  59. package/dist/lib/datetime/picker-format.d.ts +18 -0
  60. package/dist/lib/datetime/picker-format.js +35 -1
  61. package/dist/lib/field-a11y.d.ts +12 -0
  62. package/dist/lib/field-a11y.js +14 -1
  63. package/dist/lib/select-options.d.ts +21 -0
  64. package/dist/lib/select-options.js +46 -0
  65. package/dist/props/components/data-display.prop.d.ts +16 -6
  66. package/dist/props/components/data-entry.prop.d.ts +503 -129
  67. package/dist/props/components/index.d.ts +2 -2
  68. package/dist/props/components/layout.prop.d.ts +12 -1
  69. package/dist/props/components/navigation.prop.d.ts +93 -1
  70. package/dist/props/registry.d.ts +79 -16
  71. package/dist/props/registry.js +85 -15
  72. package/dist/props/vocabulary/data.prop.d.ts +12 -0
  73. package/dist/props/vocabulary/index.d.ts +2 -2
  74. package/dist/props/vocabulary/layout.prop.d.ts +11 -0
  75. package/dist/styles/badge-layout.css +4 -0
  76. package/dist/styles/card-layout.css +6 -6
  77. package/dist/styles/control.css +220 -29
  78. package/dist/styles/data-display-layout.css +94 -23
  79. package/dist/styles/focus-ring.css +6 -4
  80. package/dist/styles/fonts.css +88 -4
  81. package/dist/styles/navigation-layout.css +36 -236
  82. package/dist/styles/shell-layout.css +149 -139
  83. package/dist/styles/table-layout.css +41 -3
  84. package/dist/tokens/components/control.css +4 -0
  85. package/dist/tokens/components/data-display.css +9 -5
  86. package/dist/tokens/components/navigation.css +9 -5
  87. package/dist/tokens/components/scroll-area.css +7 -0
  88. package/dist/tokens/components/segmented.css +0 -2
  89. package/dist/tokens/components/shell.css +2 -0
  90. package/dist/tokens/components/table.css +3 -0
  91. package/dist/tokens/foundation.css +7 -0
  92. package/docs/COMPONENTS.md +14 -14
  93. package/docs/CONSUMER-RULES.md +24 -1
  94. package/docs/CUSTOMER-THEMING.md +7 -1
  95. package/docs/FRAME-COVERAGE-REPORT.md +5 -11
  96. package/docs/TOKENS.md +65 -0
  97. package/docs/data-display/badge.tsx +37 -0
  98. package/docs/data-display/data-table/index.tsx +45 -1
  99. package/docs/data-display/popover.tsx +43 -5
  100. package/docs/data-display/progress.tsx +28 -0
  101. package/docs/data-display/scroll-area.tsx +51 -45
  102. package/docs/data-entry/calendar.tsx +2 -2
  103. package/docs/data-entry/date-picker.tsx +144 -5
  104. package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
  105. package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
  106. package/docs/data-entry/form.tsx +4 -4
  107. package/docs/data-entry/input-otp.tsx +5 -2
  108. package/docs/data-entry/number-input.tsx +31 -0
  109. package/docs/data-entry/segmented.tsx +143 -2
  110. package/docs/data-entry/select-matrix.tsx +1 -2
  111. package/docs/data-entry/select.tsx +108 -3
  112. package/docs/data-entry/slider.tsx +125 -47
  113. package/docs/feedback/dialog.tsx +1 -2
  114. package/docs/feedback/sheet.tsx +1 -2
  115. package/docs/layout/app-shell-states.tsx +143 -0
  116. package/docs/layout/mobile-shell.tsx +5 -0
  117. package/docs/navigation/dropdown-menu.tsx +75 -4
  118. package/docs/navigation/tabs.tsx +149 -0
  119. package/docs/roadmap/antd-parity.md +35 -34
  120. package/docs/roadmap/parity-audit-data-entry.md +196 -172
  121. package/docs/roadmap/parity-audit-layout-navigation-general.md +304 -310
  122. package/docs/roadmap/parity-backlog.md +8 -10
  123. package/docs/showcase/acme-portal.tsx +7 -0
  124. package/package.json +28 -64
  125. package/scripts/ui-audit.mjs +18 -5
  126. package/scripts/visual-audit-rules.mjs +0 -7
  127. package/scripts/visual-audit.mjs +6 -27
  128. package/dist/components/data-entry/date-range-picker.d.ts +0 -11
  129. package/dist/components/data-entry/date-range-picker.js +0 -349
  130. package/dist/components/data-entry/month-picker.d.ts +0 -10
  131. package/dist/components/data-entry/month-picker.js +0 -241
  132. package/dist/components/data-entry/month-range-picker.d.ts +0 -10
  133. package/dist/components/data-entry/month-range-picker.js +0 -301
  134. package/dist/components/navigation/context-menu.d.ts +0 -21
  135. package/dist/components/navigation/context-menu.js +0 -149
  136. package/dist/components/navigation/menubar.d.ts +0 -21
  137. package/dist/components/navigation/menubar.js +0 -136
  138. package/dist/components/navigation/navigation-menu.d.ts +0 -10
  139. package/dist/components/navigation/navigation-menu.js +0 -91
  140. package/dist/components/ui/context-menu.d.ts +0 -1
  141. package/dist/components/ui/context-menu.js +0 -2
  142. package/dist/components/ui/date-range-picker.d.ts +0 -1
  143. package/dist/components/ui/date-range-picker.js +0 -2
  144. package/dist/components/ui/menubar.d.ts +0 -1
  145. package/dist/components/ui/menubar.js +0 -2
  146. package/dist/components/ui/navigation-menu.d.ts +0 -1
  147. package/dist/components/ui/navigation-menu.js +0 -2
  148. package/docs/FRAME-A11Y-CI.md +0 -349
  149. package/docs/data-entry/date-range-picker.tsx +0 -175
  150. package/docs/data-entry/month-picker.tsx +0 -101
  151. package/docs/data-entry/month-range-picker.tsx +0 -115
  152. package/docs/navigation/context-menu.tsx +0 -128
  153. package/docs/navigation/menubar.tsx +0 -141
  154. package/docs/navigation/navigation-menu.tsx +0 -158
@@ -1,4 +1,4 @@
1
1
  import * as React from "react";
2
2
  import type { FormFieldProp } from "../../props/components/data-entry.prop.js";
3
3
  export type { FormFieldProp, FormFieldProp as FormFieldProps, } from "../../props/components/data-entry.prop.js";
4
- export declare function FormField({ id, name, field, label, required, helper, error: errorProp, validateStatus, hasFeedback, feedback, labelAddon, layout: layoutProp, labelWidth: labelWidthProp, controlWidth: controlWidthProp, colSpan, className, children, staticText, }: FormFieldProp): React.JSX.Element;
4
+ export declare function FormField({ id, name, field, label, required, helper, helperPlacement, error: errorProp, validateStatus, hasFeedback, feedback, labelAddon, layout: layoutProp, labelWidth: labelWidthProp, controlWidth: controlWidthProp, colSpan, className, children, staticText, }: FormFieldProp): React.JSX.Element;
@@ -19,6 +19,7 @@ function FormField({
19
19
  label,
20
20
  required,
21
21
  helper,
22
+ helperPlacement = "after",
22
23
  error: errorProp,
23
24
  validateStatus,
24
25
  hasFeedback,
@@ -46,6 +47,7 @@ function FormField({
46
47
  const resolvedId = id ?? autoId;
47
48
  const labelId = `${resolvedId}-label`;
48
49
  const helperId = helper ? `${resolvedId}-helper` : void 0;
50
+ const helperNode = helper ? /* @__PURE__ */ jsx("p", { id: helperId, className: "text-muted-foreground text-xs", children: helper }) : null;
49
51
  const validationStatus = error ? "error" : validateStatus;
50
52
  const feedbackId = hasFeedback && validationStatus ? `${resolvedId}-feedback` : void 0;
51
53
  const FeedbackIcon = validationStatus === "validating" ? LoaderCircle : validationStatus === "error" ? CircleAlert : validationStatus === "warning" ? TriangleAlert : CheckCircle;
@@ -67,6 +69,7 @@ function FormField({
67
69
  [fieldKey, emitFieldNames]
68
70
  );
69
71
  const childProps = React.isValidElement(children) ? children.props : void 0;
72
+ const controlId = childProps?.id ?? resolvedId;
70
73
  const mergeIds = mergeAriaIds;
71
74
  const childWithA11y = isStatic ? (
72
75
  // read-only FormField row and a Descriptions value are indistinguishable when mixed. That
@@ -76,7 +79,7 @@ function FormField({
76
79
  // The label is associated via aria-labelledby (not <label for>): composite
77
80
  // controls (Radio.Group, checkbox lists, range pairs) have no labelable root,
78
81
  // and a dangling `for` triggers Chrome's "Incorrect use of <label>" issue.
79
- id: childProps?.id ?? resolvedId,
82
+ id: controlId,
80
83
  ...form?.disabled && childProps?.disabled !== false ? { disabled: true } : {},
81
84
  ...validationStatus === "validating" ? { "aria-busy": true } : {},
82
85
  // Read the child's own value first in BOTH cases: cloneElement
@@ -131,7 +134,7 @@ function FormField({
131
134
  "span",
132
135
  {
133
136
  onClick: () => {
134
- const el = document.getElementById(resolvedId);
137
+ const el = document.getElementById(controlId);
135
138
  if (!(el instanceof HTMLElement)) return;
136
139
  const focusable = el.matches(FOCUSABLE_SELECTOR) ? el : el.querySelector(FOCUSABLE_SELECTOR);
137
140
  (focusable ?? el).focus();
@@ -151,6 +154,7 @@ function FormField({
151
154
  labelAddon
152
155
  ] }),
153
156
  /* @__PURE__ */ jsxs("div", { "data-slot": "form-field-control", className: "ui-form-field-control", children: [
157
+ helperPlacement === "before" ? helperNode : null,
154
158
  isStatic ? childWithA11y : /* @__PURE__ */ jsx(FieldNameContext.Provider, { value: fieldNameContext, children: /* @__PURE__ */ jsx(FieldIdentityContext.Provider, { value: fieldIdentityContext, children: /* @__PURE__ */ jsx(
155
159
  "fieldset",
156
160
  {
@@ -170,7 +174,7 @@ function FormField({
170
174
  ),
171
175
  t(`dataEntry.form.${validationStatus}`)
172
176
  ] }) : null,
173
- helper ? /* @__PURE__ */ jsx("p", { id: helperId, className: "text-muted-foreground text-xs", children: helper }) : null,
177
+ helperPlacement === "after" ? helperNode : null,
174
178
  error ? /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-destructive text-xs", children: error }) : null
175
179
  ] })
176
180
  ]
@@ -9,6 +9,15 @@ const FormLayoutContext = React.createContext(null);
9
9
  function useFormLayout() {
10
10
  return React.useContext(FormLayoutContext);
11
11
  }
12
+ function asChildContent(children, columns) {
13
+ if (columns == null) return children;
14
+ const child = React.Children.only(children);
15
+ return React.cloneElement(
16
+ child,
17
+ void 0,
18
+ /* @__PURE__ */ jsx(ResponsiveGrid, { columns, children: child.props.children })
19
+ );
20
+ }
12
21
  const Form = React.forwardRef(function Form2({
13
22
  layout = "vertical",
14
23
  disabled,
@@ -41,7 +50,7 @@ const Form = React.forwardRef(function Form2({
41
50
  "data-layout": layout,
42
51
  className: cn("ui-form", density && `ui-density-${density}`, className),
43
52
  ...props,
44
- children
53
+ children: asChildContent(children, columns)
45
54
  }
46
55
  )
47
56
  ) });
@@ -32,14 +32,8 @@ export { Slider } from "./slider.js";
32
32
  export type { SliderProps } from "./slider.js";
33
33
  export { Calendar } from "./calendar.js";
34
34
  export type { CalendarProps } from "./calendar.js";
35
- export { MonthPicker } from "./month-picker.js";
36
- export type { MonthPickerProps } from "./month-picker.js";
37
- export { MonthRangePicker } from "./month-range-picker.js";
38
- export type { MonthRangePickerProps } from "./month-range-picker.js";
39
35
  export { DatePicker } from "./date-picker.js";
40
36
  export type { DatePickerProps } from "./date-picker.js";
41
- export { DateRangePicker } from "./date-range-picker.js";
42
- export type { DateRangePickerProps } from "./date-range-picker.js";
43
37
  export { TimePicker } from "./time-picker.js";
44
38
  export type { TimePickerProps } from "./time-picker.js";
45
39
  export { ColorPicker } from "./color-picker.js";
@@ -29,10 +29,7 @@ import { Toggle } from "./toggle.js";
29
29
  import { ToggleGroup, ToggleGroupItem } from "./toggle-group.js";
30
30
  import { Slider } from "./slider.js";
31
31
  import { Calendar } from "./calendar.js";
32
- import { MonthPicker } from "./month-picker.js";
33
- import { MonthRangePicker } from "./month-range-picker.js";
34
32
  import { DatePicker } from "./date-picker.js";
35
- import { DateRangePicker } from "./date-range-picker.js";
36
33
  import { TimePicker } from "./time-picker.js";
37
34
  import { ColorPicker } from "./color-picker.js";
38
35
  import {
@@ -79,7 +76,6 @@ export {
79
76
  CommandList,
80
77
  CommandPalette,
81
78
  DatePicker,
82
- DateRangePicker,
83
79
  Field,
84
80
  Form,
85
81
  FormErrors,
@@ -91,8 +87,6 @@ export {
91
87
  InputOTPSeparator,
92
88
  InputOTPSlot,
93
89
  Label,
94
- MonthPicker,
95
- MonthRangePicker,
96
90
  NumberInput,
97
91
  PasswordInput,
98
92
  PasswordStrength,
@@ -1,5 +1,18 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
- export declare const Label: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "ref"> & VariantProps<(props?: import("class-variance-authority/types").ClassProp | undefined) => string> & {
3
+ export declare const Label: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "ref">, "slot"> & VariantProps<(props?: import("class-variance-authority/types").ClassProp | undefined) => string> & {
4
4
  asChild?: boolean;
5
+ /**
6
+ * react-aria's slot channel, DEFAULTED OFF.
7
+ *
8
+ * A `Label` rendered inside a react-aria collection (a `RadioGroup`, say) is otherwise
9
+ * claimed by that collection as the GROUP's label: it comes out as a `<span>`, the `htmlFor`
10
+ * this library passed is dropped on the floor, and the control it was meant to name ends up
11
+ * with no accessible name at all — a silent failure, measured on `Radio.Group` the moment
12
+ * that group moved to react-aria. `<Label htmlFor="x">` means one thing in this library and
13
+ * a surrounding primitive does not get to redefine it, so the default is react-aria's own
14
+ * opt-out: "an explicit `null` value indicates that the local props completely override all
15
+ * props received from a parent". Pass a slot name to opt back in.
16
+ */
17
+ slot?: string | null;
5
18
  } & React.RefAttributes<HTMLLabelElement>>;
@@ -7,8 +7,13 @@ import { Slot } from "../../lib/slot.js";
7
7
  const labelVariants = cva(
8
8
  "font-medium leading-[var(--control-label-line-height)] peer-disabled:cursor-not-allowed peer-disabled:opacity-[var(--control-label-disabled-alpha)]"
9
9
  );
10
- const Label = React.forwardRef(({ className, onMouseDown, asChild = false, ...props }, ref) => {
10
+ const Label = React.forwardRef(({ className, onMouseDown, asChild = false, slot = null, ...props }, ref) => {
11
11
  const shared = {
12
+ // `string | null`, cast because react-aria's own `LabelProps` extends `LabelHTMLAttributes`
13
+ // and so re-types `slot` as `string | undefined` — narrower than the `SlotProps` contract the
14
+ // rest of the library publishes, and narrower than `useSlottedContext`, which branches on
15
+ // `slot === null` explicitly. React drops a `null` attribute, so nothing reaches the DOM.
16
+ slot,
12
17
  // `...props` đứng TRƯỚC, không phải sau: giữ đúng thứ tự thuộc tính mà
13
18
  // @radix-ui/react-label phát ra (`for` → `data-slot` → `class`), nên phép so
14
19
  // `outerHTML` trong __tests__/label-checkbox-rac.test.tsx khớp từng ký tự.
@@ -1,14 +1,35 @@
1
1
  import * as React from "react";
2
- import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
3
2
  import type { RadioGroupProp } from "../../props/components/data-entry.prop.js";
4
3
  export type { RadioGroupProp, RadioGroupProp as RadioGroupProps, } from "../../props/components/data-entry.prop.js";
5
- declare const RadioGroupRoot: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
6
- declare const RadioItem: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
4
+ declare const RadioGroupRoot: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "defaultValue" | "dir" | "onChange"> & {
5
+ value?: string;
6
+ defaultValue?: string;
7
+ onValueChange?: (value: string) => void;
8
+ disabled?: boolean;
9
+ required?: boolean;
10
+ name?: string;
11
+ orientation?: "horizontal" | "vertical";
12
+ } & React.RefAttributes<HTMLDivElement>>;
13
+ declare const RadioItem: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & {
14
+ value: string;
15
+ } & React.RefAttributes<HTMLLabelElement>>;
7
16
  declare function RadioGroupOptions({ value, defaultValue, onValueChange, options, orientation, optionType, buttonStyle, disabled, name, id, className, children, ...ariaProps }: RadioGroupProp): React.JSX.Element;
8
17
  /** Single radio — use inside `Radio.Group` / `RadioGroupRoot`, or via `options` API. */
9
- export declare const Radio: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>> & {
10
- Root: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
+ export declare const Radio: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & {
19
+ value: string;
20
+ } & React.RefAttributes<HTMLLabelElement>> & {
21
+ Root: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "defaultValue" | "dir" | "onChange"> & {
22
+ value?: string;
23
+ defaultValue?: string;
24
+ onValueChange?: (value: string) => void;
25
+ disabled?: boolean;
26
+ required?: boolean;
27
+ name?: string;
28
+ orientation?: "horizontal" | "vertical";
29
+ } & React.RefAttributes<HTMLDivElement>>;
11
30
  Group: typeof RadioGroupOptions;
12
- Item: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
31
+ Item: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & {
32
+ value: string;
33
+ } & React.RefAttributes<HTMLLabelElement>>;
13
34
  };
14
35
  export { RadioGroupRoot, RadioItem, RadioGroupOptions as RadioGroup };
@@ -1,64 +1,109 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
4
+ import { Radio as AriaRadio, RadioGroup as AriaRadioGroup } from "react-aria-components";
5
5
  import { Circle } from "lucide-react";
6
6
  import { cn } from "../../lib/utils.js";
7
- import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
7
+ import { pickFieldA11y, useFieldIdentity, useMirroredInputAttributes } from "../../lib/field-a11y.js";
8
8
  import { Field } from "./field.js";
9
9
  import { choiceGroupClassName } from "./choice-option.js";
10
- const RadioGroupRoot = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
11
- RadioGroupPrimitive.Root,
12
- {
13
- ref,
14
- "data-slot": "radio-group",
15
- className: cn("ui-choice-group", className),
16
- ...props
10
+ import { withOwnHitTarget } from "./choice-hit-target.js";
11
+ function checkedState(isSelected) {
12
+ return isSelected ? "checked" : "unchecked";
13
+ }
14
+ function useFieldKeyedInput(fieldKey) {
15
+ const inputRef = React.useRef(null);
16
+ useMirroredInputAttributes(inputRef, { "data-field": fieldKey });
17
+ return inputRef;
18
+ }
19
+ const RadioGroupRoot = React.forwardRef(
20
+ ({ className, value, defaultValue, onValueChange, disabled, required, orientation, ...props }, ref) => {
21
+ const invalid = props["aria-invalid"];
22
+ const { "aria-label": ariaLabel, ...rest } = props;
23
+ return /* @__PURE__ */ jsx(
24
+ AriaRadioGroup,
25
+ {
26
+ ref,
27
+ "data-slot": "radio-group",
28
+ className: cn("ui-choice-group", className),
29
+ value,
30
+ defaultValue,
31
+ onChange: onValueChange,
32
+ isDisabled: disabled,
33
+ isRequired: required,
34
+ isInvalid: invalid !== void 0 && invalid !== false && invalid !== "false",
35
+ orientation,
36
+ "aria-label": props["aria-labelledby"] ? void 0 : ariaLabel,
37
+ ...rest
38
+ }
39
+ );
17
40
  }
18
- ));
19
- RadioGroupRoot.displayName = RadioGroupPrimitive.Root.displayName;
20
- const RadioItem = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21
- RadioGroupPrimitive.Item,
22
- {
23
- ref,
24
- "data-slot": "radio-group-item",
25
- className: cn(
26
- // `.ui-radio:disabled, .ui-radio[data-disabled]` in styles/control.css already declares both
27
- // and reads --disabled-opacity. The utility was layered after components, so it silently
28
- // outranked that token — a service theme's --disabled-opacity never reached a radio.
29
- // Byte-identical: --disabled-opacity defaults to 0.5.
30
- "ui-radio aria-invalid:border-destructive shrink-0 shadow-xs transition-shadow outline-none",
31
- className
32
- ),
33
- ...props,
34
- children: /* @__PURE__ */ jsx(
35
- RadioGroupPrimitive.Indicator,
41
+ );
42
+ RadioGroupRoot.displayName = "RadioGroup";
43
+ const RadioItem = React.forwardRef(
44
+ ({ className, disabled, value, ...props }, ref) => {
45
+ const { "data-field": fieldKey } = props;
46
+ const inputRef = useFieldKeyedInput(fieldKey);
47
+ return /* @__PURE__ */ jsx(
48
+ AriaRadio,
36
49
  {
37
- "data-slot": "radio-group-indicator",
38
- className: "ui-choice-indicator",
39
- children: /* @__PURE__ */ jsx(Circle, { className: "ui-radio-icon", "aria-hidden": "true" })
50
+ ref,
51
+ inputRef,
52
+ value,
53
+ isDisabled: disabled,
54
+ "data-slot": "radio-group-item",
55
+ className: cn(
56
+ // `.ui-radio:disabled, .ui-radio[data-disabled]` in styles/control.css already declares both
57
+ // and reads --disabled-opacity. The utility was layered after components, so it silently
58
+ // outranked that token — a service theme's --disabled-opacity never reached a radio.
59
+ // Byte-identical: --disabled-opacity defaults to 0.5.
60
+ //
61
+ // `inline-flex items-center justify-center` is load-bearing, for the reason Checkbox
62
+ // records: react-aria's root is a `<label>`, which is `display:inline`, so without it the
63
+ // 16px dot collapses to nothing. `data-[invalid]` replaces `aria-invalid:` because
64
+ // react-aria puts `aria-invalid` on the `<input>` and `data-invalid` on this box.
65
+ "ui-radio data-[invalid]:border-destructive inline-flex shrink-0 items-center justify-center shadow-xs transition-shadow outline-none",
66
+ className
67
+ ),
68
+ ...props,
69
+ render: (domProps, state) => /* @__PURE__ */ jsx("label", { ...domProps, "data-state": checkedState(state.isSelected), children: withOwnHitTarget(domProps.children) }),
70
+ children: /* @__PURE__ */ jsx("span", { "data-slot": "radio-group-indicator", className: "ui-choice-indicator", children: /* @__PURE__ */ jsx(Circle, { className: "ui-radio-icon", "aria-hidden": "true" }) })
40
71
  }
41
- )
72
+ );
42
73
  }
43
- ));
44
- RadioItem.displayName = RadioGroupPrimitive.Item.displayName;
74
+ );
75
+ RadioItem.displayName = "Radio";
76
+ function RadioBarButton({
77
+ id,
78
+ option,
79
+ resolvedField
80
+ }) {
81
+ const inputRef = useFieldKeyedInput(resolvedField);
82
+ return /* @__PURE__ */ jsx(
83
+ AriaRadio,
84
+ {
85
+ id,
86
+ inputRef,
87
+ value: option.value,
88
+ isDisabled: option.disabled,
89
+ "data-slot": "radio-button",
90
+ className: "ui-radio-button ui-focus-ring",
91
+ render: (domProps, state) => /* @__PURE__ */ jsx("label", { ...domProps, "data-state": checkedState(state.isSelected), children: withOwnHitTarget(domProps.children) }),
92
+ children: /* @__PURE__ */ jsx("span", { className: "ui-radio-button-label", children: option.label })
93
+ }
94
+ );
95
+ }
45
96
  function RadioButtonBar({
46
97
  options,
47
- value,
48
98
  optionDomId,
49
99
  resolvedField
50
100
  }) {
51
101
  return /* @__PURE__ */ jsx(Fragment, { children: options.map((opt, index) => /* @__PURE__ */ jsx(
52
- RadioGroupPrimitive.Item,
102
+ RadioBarButton,
53
103
  {
54
104
  id: optionDomId(opt.value, index),
55
- value: opt.value,
56
- disabled: opt.disabled,
57
- "data-slot": "radio-button",
58
- "data-field": resolvedField,
59
- "data-state": value === opt.value ? "checked" : "unchecked",
60
- className: "ui-radio-button ui-focus-ring",
61
- children: /* @__PURE__ */ jsx("span", { className: "ui-radio-button-label", children: opt.label })
105
+ option: opt,
106
+ resolvedField
62
107
  },
63
108
  opt.value
64
109
  )) });
@@ -94,6 +139,7 @@ function RadioGroupOptions({
94
139
  disabled,
95
140
  name: resolvedName,
96
141
  id,
142
+ orientation,
97
143
  ...groupA11y,
98
144
  "data-field": resolvedField,
99
145
  "data-orientation": orientation,
@@ -108,7 +154,6 @@ function RadioGroupOptions({
108
154
  RadioButtonBar,
109
155
  {
110
156
  options,
111
- value,
112
157
  optionDomId,
113
158
  resolvedField
114
159
  }
@@ -147,6 +192,7 @@ function RadioGroupOptions({
147
192
  disabled,
148
193
  name: resolvedName,
149
194
  id,
195
+ orientation,
150
196
  ...groupA11y,
151
197
  "data-field": resolvedField,
152
198
  "data-orientation": orientation,