@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.
- package/README.md +4 -4
- package/dist/app/app-provider.js +2 -1
- package/dist/components/data-display/badge.d.ts +28 -1
- package/dist/components/data-display/badge.js +2 -0
- package/dist/components/data-display/data-table.d.ts +18 -2
- package/dist/components/data-display/data-table.js +4 -0
- package/dist/components/data-display/index.d.ts +1 -1
- package/dist/components/data-display/popover.d.ts +19 -1
- package/dist/components/data-display/popover.js +9 -1
- package/dist/components/data-display/progress.d.ts +37 -0
- package/dist/components/data-display/progress.js +57 -5
- package/dist/components/data-display/scroll-area.d.ts +24 -4
- package/dist/components/data-display/scroll-area.js +34 -72
- package/dist/components/data-entry/cascader.js +8 -6
- package/dist/components/data-entry/checkbox.js +2 -0
- package/dist/components/data-entry/choice-hit-target.d.ts +24 -0
- package/dist/components/data-entry/choice-hit-target.js +12 -0
- package/dist/components/data-entry/control-surface.d.ts +8 -0
- package/dist/components/data-entry/control-surface.js +10 -1
- package/dist/components/data-entry/date-picker.d.ts +10 -3
- package/dist/components/data-entry/date-picker.js +503 -238
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +7 -3
- package/dist/components/data-entry/form.js +10 -1
- package/dist/components/data-entry/index.d.ts +0 -6
- package/dist/components/data-entry/index.js +0 -6
- package/dist/components/data-entry/label.d.ts +14 -1
- package/dist/components/data-entry/label.js +6 -1
- package/dist/components/data-entry/radio.d.ts +27 -6
- package/dist/components/data-entry/radio.js +89 -43
- package/dist/components/data-entry/search-select.js +226 -89
- package/dist/components/data-entry/select.d.ts +76 -14
- package/dist/components/data-entry/select.js +497 -163
- package/dist/components/data-entry/slider.d.ts +3 -11
- package/dist/components/data-entry/slider.js +582 -93
- package/dist/components/data-entry/switch.d.ts +6 -3
- package/dist/components/data-entry/switch.js +36 -11
- package/dist/components/layout/mobile-shell.d.ts +1 -1
- package/dist/components/layout/mobile-shell.js +2 -0
- package/dist/components/navigation/dropdown-menu.d.ts +11 -3
- package/dist/components/navigation/dropdown-menu.js +190 -15
- package/dist/components/navigation/index.d.ts +0 -3
- package/dist/components/navigation/index.js +0 -78
- package/dist/components/navigation/steps.d.ts +1 -1
- package/dist/components/navigation/steps.js +3 -1
- package/dist/components/navigation/tabs-scroll.d.ts +27 -0
- package/dist/components/navigation/tabs-scroll.js +52 -1
- package/dist/components/navigation/tabs.d.ts +2 -2
- package/dist/components/navigation/tabs.js +88 -23
- package/dist/components/ui/index.d.ts +0 -4
- package/dist/components/ui/index.js +0 -4
- package/dist/components/ui/input-otp.d.ts +1 -1
- package/dist/components/ui/segmented.d.ts +15 -1
- package/dist/components/ui/segmented.js +15 -6
- package/dist/components/ui/tag-input.js +3 -5
- package/dist/i18n/messages/en.json +3 -2
- package/dist/i18n/messages/ja.json +3 -2
- package/dist/i18n/messages/vi.json +3 -2
- package/dist/lib/datetime/picker-format.d.ts +18 -0
- package/dist/lib/datetime/picker-format.js +35 -1
- package/dist/lib/field-a11y.d.ts +12 -0
- package/dist/lib/field-a11y.js +14 -1
- package/dist/lib/select-options.d.ts +21 -0
- package/dist/lib/select-options.js +46 -0
- package/dist/props/components/data-display.prop.d.ts +16 -6
- package/dist/props/components/data-entry.prop.d.ts +503 -129
- package/dist/props/components/index.d.ts +2 -2
- package/dist/props/components/layout.prop.d.ts +12 -1
- package/dist/props/components/navigation.prop.d.ts +93 -1
- package/dist/props/registry.d.ts +79 -16
- package/dist/props/registry.js +85 -15
- package/dist/props/vocabulary/data.prop.d.ts +12 -0
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/layout.prop.d.ts +11 -0
- package/dist/styles/badge-layout.css +4 -0
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/control.css +220 -29
- package/dist/styles/data-display-layout.css +94 -23
- package/dist/styles/focus-ring.css +6 -4
- package/dist/styles/fonts.css +88 -4
- package/dist/styles/navigation-layout.css +36 -236
- package/dist/styles/shell-layout.css +149 -139
- package/dist/styles/table-layout.css +41 -3
- package/dist/tokens/components/control.css +4 -0
- package/dist/tokens/components/data-display.css +9 -5
- package/dist/tokens/components/navigation.css +9 -5
- package/dist/tokens/components/scroll-area.css +7 -0
- package/dist/tokens/components/segmented.css +0 -2
- package/dist/tokens/components/shell.css +2 -0
- package/dist/tokens/components/table.css +3 -0
- package/dist/tokens/foundation.css +7 -0
- package/docs/COMPONENTS.md +14 -14
- package/docs/CONSUMER-RULES.md +24 -1
- package/docs/CUSTOMER-THEMING.md +7 -1
- package/docs/FRAME-COVERAGE-REPORT.md +5 -11
- package/docs/TOKENS.md +65 -0
- package/docs/data-display/badge.tsx +37 -0
- package/docs/data-display/data-table/index.tsx +45 -1
- package/docs/data-display/popover.tsx +43 -5
- package/docs/data-display/progress.tsx +28 -0
- package/docs/data-display/scroll-area.tsx +51 -45
- package/docs/data-entry/calendar.tsx +2 -2
- package/docs/data-entry/date-picker.tsx +144 -5
- package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
- package/docs/data-entry/form.tsx +4 -4
- package/docs/data-entry/input-otp.tsx +5 -2
- package/docs/data-entry/number-input.tsx +31 -0
- package/docs/data-entry/segmented.tsx +143 -2
- package/docs/data-entry/select-matrix.tsx +1 -2
- package/docs/data-entry/select.tsx +108 -3
- package/docs/data-entry/slider.tsx +125 -47
- package/docs/feedback/dialog.tsx +1 -2
- package/docs/feedback/sheet.tsx +1 -2
- package/docs/layout/app-shell-states.tsx +143 -0
- package/docs/layout/mobile-shell.tsx +5 -0
- package/docs/navigation/dropdown-menu.tsx +75 -4
- package/docs/navigation/tabs.tsx +149 -0
- package/docs/roadmap/antd-parity.md +35 -34
- package/docs/roadmap/parity-audit-data-entry.md +196 -172
- package/docs/roadmap/parity-audit-layout-navigation-general.md +304 -310
- package/docs/roadmap/parity-backlog.md +8 -10
- package/docs/showcase/acme-portal.tsx +7 -0
- package/package.json +28 -64
- package/scripts/ui-audit.mjs +18 -5
- package/scripts/visual-audit-rules.mjs +0 -7
- package/scripts/visual-audit.mjs +6 -27
- package/dist/components/data-entry/date-range-picker.d.ts +0 -11
- package/dist/components/data-entry/date-range-picker.js +0 -349
- package/dist/components/data-entry/month-picker.d.ts +0 -10
- package/dist/components/data-entry/month-picker.js +0 -241
- package/dist/components/data-entry/month-range-picker.d.ts +0 -10
- package/dist/components/data-entry/month-range-picker.js +0 -301
- package/dist/components/navigation/context-menu.d.ts +0 -21
- package/dist/components/navigation/context-menu.js +0 -149
- package/dist/components/navigation/menubar.d.ts +0 -21
- package/dist/components/navigation/menubar.js +0 -136
- package/dist/components/navigation/navigation-menu.d.ts +0 -10
- package/dist/components/navigation/navigation-menu.js +0 -91
- package/dist/components/ui/context-menu.d.ts +0 -1
- package/dist/components/ui/context-menu.js +0 -2
- package/dist/components/ui/date-range-picker.d.ts +0 -1
- package/dist/components/ui/date-range-picker.js +0 -2
- package/dist/components/ui/menubar.d.ts +0 -1
- package/dist/components/ui/menubar.js +0 -2
- package/dist/components/ui/navigation-menu.d.ts +0 -1
- package/dist/components/ui/navigation-menu.js +0 -2
- package/docs/FRAME-A11Y-CI.md +0 -349
- package/docs/data-entry/date-range-picker.tsx +0 -175
- package/docs/data-entry/month-picker.tsx +0 -101
- package/docs/data-entry/month-range-picker.tsx +0 -115
- package/docs/navigation/context-menu.tsx +0 -128
- package/docs/navigation/menubar.tsx +0 -141
- 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:
|
|
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(
|
|
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
|
-
|
|
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<
|
|
6
|
-
|
|
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<
|
|
10
|
-
|
|
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<
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 =
|
|
20
|
-
const RadioItem = React.forwardRef(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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 =
|
|
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
|
-
|
|
102
|
+
RadioBarButton,
|
|
53
103
|
{
|
|
54
104
|
id: optionDomId(opt.value, index),
|
|
55
|
-
|
|
56
|
-
|
|
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,
|