@godxjp/ui 21.0.0 → 22.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 (103) hide show
  1. package/dist/app/app-provider.js +2 -1
  2. package/dist/components/data-display/badge.d.ts +28 -1
  3. package/dist/components/data-display/badge.js +2 -0
  4. package/dist/components/data-display/data-table.d.ts +18 -2
  5. package/dist/components/data-display/data-table.js +4 -0
  6. package/dist/components/data-display/index.d.ts +1 -1
  7. package/dist/components/data-display/popover.d.ts +19 -1
  8. package/dist/components/data-display/popover.js +9 -1
  9. package/dist/components/data-display/progress.d.ts +37 -0
  10. package/dist/components/data-display/progress.js +57 -5
  11. package/dist/components/data-entry/date-picker.d.ts +10 -3
  12. package/dist/components/data-entry/date-picker.js +503 -238
  13. package/dist/components/data-entry/form-field.d.ts +1 -1
  14. package/dist/components/data-entry/form-field.js +4 -1
  15. package/dist/components/data-entry/form.js +10 -1
  16. package/dist/components/data-entry/index.d.ts +0 -6
  17. package/dist/components/data-entry/index.js +0 -6
  18. package/dist/components/data-entry/label.d.ts +14 -1
  19. package/dist/components/data-entry/label.js +6 -1
  20. package/dist/components/data-entry/radio.d.ts +27 -6
  21. package/dist/components/data-entry/radio.js +88 -43
  22. package/dist/components/data-entry/search-select.js +8 -1
  23. package/dist/components/data-entry/select.js +2 -0
  24. package/dist/components/data-entry/switch.d.ts +6 -3
  25. package/dist/components/data-entry/switch.js +34 -11
  26. package/dist/components/layout/mobile-shell.d.ts +1 -1
  27. package/dist/components/layout/mobile-shell.js +2 -0
  28. package/dist/components/navigation/menubar.d.ts +10 -3
  29. package/dist/components/navigation/steps.d.ts +1 -1
  30. package/dist/components/navigation/steps.js +3 -1
  31. package/dist/components/navigation/tabs-scroll.d.ts +27 -0
  32. package/dist/components/navigation/tabs-scroll.js +52 -1
  33. package/dist/components/navigation/tabs.d.ts +2 -2
  34. package/dist/components/navigation/tabs.js +88 -23
  35. package/dist/components/ui/index.d.ts +0 -1
  36. package/dist/components/ui/index.js +0 -1
  37. package/dist/components/ui/segmented.d.ts +15 -1
  38. package/dist/components/ui/segmented.js +13 -6
  39. package/dist/i18n/messages/en.json +3 -2
  40. package/dist/i18n/messages/ja.json +3 -2
  41. package/dist/i18n/messages/vi.json +3 -2
  42. package/dist/lib/datetime/picker-format.d.ts +18 -0
  43. package/dist/lib/datetime/picker-format.js +35 -1
  44. package/dist/lib/field-a11y.d.ts +12 -0
  45. package/dist/lib/field-a11y.js +14 -1
  46. package/dist/props/components/data-display.prop.d.ts +7 -0
  47. package/dist/props/components/data-entry.prop.d.ts +180 -99
  48. package/dist/props/components/index.d.ts +1 -1
  49. package/dist/props/components/layout.prop.d.ts +12 -1
  50. package/dist/props/components/navigation.prop.d.ts +76 -1
  51. package/dist/props/registry.d.ts +16 -16
  52. package/dist/props/registry.js +16 -15
  53. package/dist/props/vocabulary/data.prop.d.ts +12 -0
  54. package/dist/props/vocabulary/index.d.ts +2 -2
  55. package/dist/props/vocabulary/layout.prop.d.ts +11 -0
  56. package/dist/styles/badge-layout.css +4 -0
  57. package/dist/styles/card-layout.css +6 -6
  58. package/dist/styles/control.css +15 -6
  59. package/dist/styles/data-display-layout.css +76 -13
  60. package/dist/styles/focus-ring.css +4 -2
  61. package/dist/styles/navigation-layout.css +25 -0
  62. package/dist/styles/shell-layout.css +5 -0
  63. package/dist/styles/table-layout.css +34 -0
  64. package/dist/tokens/components/data-display.css +9 -0
  65. package/dist/tokens/components/navigation.css +6 -0
  66. package/dist/tokens/components/segmented.css +0 -2
  67. package/dist/tokens/components/shell.css +2 -0
  68. package/dist/tokens/components/table.css +3 -0
  69. package/dist/tokens/foundation.css +7 -0
  70. package/docs/COMPONENTS.md +14 -14
  71. package/docs/CONSUMER-RULES.md +24 -1
  72. package/docs/FRAME-COVERAGE-REPORT.md +3 -6
  73. package/docs/TOKENS.md +65 -0
  74. package/docs/data-display/badge.tsx +37 -0
  75. package/docs/data-display/data-table/index.tsx +18 -0
  76. package/docs/data-display/popover.tsx +41 -1
  77. package/docs/data-display/progress.tsx +28 -0
  78. package/docs/data-entry/calendar.tsx +2 -2
  79. package/docs/data-entry/date-picker.tsx +143 -3
  80. package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
  81. package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
  82. package/docs/data-entry/form.tsx +4 -4
  83. package/docs/data-entry/number-input.tsx +31 -0
  84. package/docs/data-entry/segmented.tsx +103 -1
  85. package/docs/layout/mobile-shell.tsx +5 -0
  86. package/docs/navigation/tabs.tsx +149 -0
  87. package/docs/roadmap/antd-parity.md +35 -34
  88. package/docs/roadmap/parity-audit-data-entry.md +178 -165
  89. package/docs/roadmap/parity-backlog.md +6 -7
  90. package/docs/showcase/acme-portal.tsx +7 -0
  91. package/package.json +11 -23
  92. package/scripts/ui-audit.mjs +17 -4
  93. package/dist/components/data-entry/date-range-picker.d.ts +0 -11
  94. package/dist/components/data-entry/date-range-picker.js +0 -349
  95. package/dist/components/data-entry/month-picker.d.ts +0 -10
  96. package/dist/components/data-entry/month-picker.js +0 -241
  97. package/dist/components/data-entry/month-range-picker.d.ts +0 -10
  98. package/dist/components/data-entry/month-range-picker.js +0 -301
  99. package/dist/components/ui/date-range-picker.d.ts +0 -1
  100. package/dist/components/ui/date-range-picker.js +0 -2
  101. package/docs/data-entry/date-range-picker.tsx +0 -175
  102. package/docs/data-entry/month-picker.tsx +0 -101
  103. package/docs/data-entry/month-range-picker.tsx +0 -115
@@ -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;
@@ -151,6 +153,7 @@ function FormField({
151
153
  labelAddon
152
154
  ] }),
153
155
  /* @__PURE__ */ jsxs("div", { "data-slot": "form-field-control", className: "ui-form-field-control", children: [
156
+ helperPlacement === "before" ? helperNode : null,
154
157
  isStatic ? childWithA11y : /* @__PURE__ */ jsx(FieldNameContext.Provider, { value: fieldNameContext, children: /* @__PURE__ */ jsx(FieldIdentityContext.Provider, { value: fieldIdentityContext, children: /* @__PURE__ */ jsx(
155
158
  "fieldset",
156
159
  {
@@ -170,7 +173,7 @@ function FormField({
170
173
  ),
171
174
  t(`dataEntry.form.${validationStatus}`)
172
175
  ] }) : null,
173
- helper ? /* @__PURE__ */ jsx("p", { id: helperId, className: "text-muted-foreground text-xs", children: helper }) : null,
176
+ helperPlacement === "after" ? helperNode : null,
174
177
  error ? /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-destructive text-xs", children: error }) : null
175
178
  ] })
176
179
  ]
@@ -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,108 @@
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
+ function checkedState(isSelected) {
11
+ return isSelected ? "checked" : "unchecked";
12
+ }
13
+ function useFieldKeyedInput(fieldKey) {
14
+ const inputRef = React.useRef(null);
15
+ useMirroredInputAttributes(inputRef, { "data-field": fieldKey });
16
+ return inputRef;
17
+ }
18
+ const RadioGroupRoot = React.forwardRef(
19
+ ({ className, value, defaultValue, onValueChange, disabled, required, orientation, ...props }, ref) => {
20
+ const invalid = props["aria-invalid"];
21
+ const { "aria-label": ariaLabel, ...rest } = props;
22
+ return /* @__PURE__ */ jsx(
23
+ AriaRadioGroup,
24
+ {
25
+ ref,
26
+ "data-slot": "radio-group",
27
+ className: cn("ui-choice-group", className),
28
+ value,
29
+ defaultValue,
30
+ onChange: onValueChange,
31
+ isDisabled: disabled,
32
+ isRequired: required,
33
+ isInvalid: invalid !== void 0 && invalid !== false && invalid !== "false",
34
+ orientation,
35
+ "aria-label": props["aria-labelledby"] ? void 0 : ariaLabel,
36
+ ...rest
37
+ }
38
+ );
17
39
  }
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,
40
+ );
41
+ RadioGroupRoot.displayName = "RadioGroup";
42
+ const RadioItem = React.forwardRef(
43
+ ({ className, disabled, value, ...props }, ref) => {
44
+ const { "data-field": fieldKey } = props;
45
+ const inputRef = useFieldKeyedInput(fieldKey);
46
+ return /* @__PURE__ */ jsx(
47
+ AriaRadio,
36
48
  {
37
- "data-slot": "radio-group-indicator",
38
- className: "ui-choice-indicator",
39
- children: /* @__PURE__ */ jsx(Circle, { className: "ui-radio-icon", "aria-hidden": "true" })
49
+ ref,
50
+ inputRef,
51
+ value,
52
+ isDisabled: disabled,
53
+ "data-slot": "radio-group-item",
54
+ className: cn(
55
+ // `.ui-radio:disabled, .ui-radio[data-disabled]` in styles/control.css already declares both
56
+ // and reads --disabled-opacity. The utility was layered after components, so it silently
57
+ // outranked that token — a service theme's --disabled-opacity never reached a radio.
58
+ // Byte-identical: --disabled-opacity defaults to 0.5.
59
+ //
60
+ // `inline-flex items-center justify-center` is load-bearing, for the reason Checkbox
61
+ // records: react-aria's root is a `<label>`, which is `display:inline`, so without it the
62
+ // 16px dot collapses to nothing. `data-[invalid]` replaces `aria-invalid:` because
63
+ // react-aria puts `aria-invalid` on the `<input>` and `data-invalid` on this box.
64
+ "ui-radio data-[invalid]:border-destructive inline-flex shrink-0 items-center justify-center shadow-xs transition-shadow outline-none",
65
+ className
66
+ ),
67
+ ...props,
68
+ render: (domProps, state) => /* @__PURE__ */ jsx("label", { ...domProps, "data-state": checkedState(state.isSelected) }),
69
+ 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
70
  }
41
- )
71
+ );
42
72
  }
43
- ));
44
- RadioItem.displayName = RadioGroupPrimitive.Item.displayName;
73
+ );
74
+ RadioItem.displayName = "Radio";
75
+ function RadioBarButton({
76
+ id,
77
+ option,
78
+ resolvedField
79
+ }) {
80
+ const inputRef = useFieldKeyedInput(resolvedField);
81
+ return /* @__PURE__ */ jsx(
82
+ AriaRadio,
83
+ {
84
+ id,
85
+ inputRef,
86
+ value: option.value,
87
+ isDisabled: option.disabled,
88
+ "data-slot": "radio-button",
89
+ className: "ui-radio-button ui-focus-ring",
90
+ render: (domProps, state) => /* @__PURE__ */ jsx("label", { ...domProps, "data-state": checkedState(state.isSelected) }),
91
+ children: /* @__PURE__ */ jsx("span", { className: "ui-radio-button-label", children: option.label })
92
+ }
93
+ );
94
+ }
45
95
  function RadioButtonBar({
46
96
  options,
47
- value,
48
97
  optionDomId,
49
98
  resolvedField
50
99
  }) {
51
100
  return /* @__PURE__ */ jsx(Fragment, { children: options.map((opt, index) => /* @__PURE__ */ jsx(
52
- RadioGroupPrimitive.Item,
101
+ RadioBarButton,
53
102
  {
54
103
  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 })
104
+ option: opt,
105
+ resolvedField
62
106
  },
63
107
  opt.value
64
108
  )) });
@@ -94,6 +138,7 @@ function RadioGroupOptions({
94
138
  disabled,
95
139
  name: resolvedName,
96
140
  id,
141
+ orientation,
97
142
  ...groupA11y,
98
143
  "data-field": resolvedField,
99
144
  "data-orientation": orientation,
@@ -108,7 +153,6 @@ function RadioGroupOptions({
108
153
  RadioButtonBar,
109
154
  {
110
155
  options,
111
- value,
112
156
  optionDomId,
113
157
  resolvedField
114
158
  }
@@ -147,6 +191,7 @@ function RadioGroupOptions({
147
191
  disabled,
148
192
  name: resolvedName,
149
193
  id,
194
+ orientation,
150
195
  ...groupA11y,
151
196
  "data-field": resolvedField,
152
197
  "data-orientation": orientation,
@@ -36,6 +36,7 @@ function SearchSelect(props) {
36
36
  size,
37
37
  status,
38
38
  variant,
39
+ width = "full",
39
40
  loading: loadingProp = false,
40
41
  open: openProp,
41
42
  defaultOpen = false,
@@ -334,9 +335,15 @@ function SearchSelect(props) {
334
335
  "data-field": resolvedField,
335
336
  "data-value": (multiple ? values.join(",") : value) || void 0,
336
337
  "data-mode": multiple ? "multiple" : void 0,
338
+ "data-width": width,
337
339
  className: cn(
338
340
  controlSurfaceTriggerClass,
339
- "w-full justify-start",
341
+ "justify-start",
342
+ // `bounded` deliberately emits NO width utility — its width is owned by the
343
+ // `[data-width="bounded"]` rule in control.css, and a utility here would win the
344
+ // layer order and make that token dead. Same reasoning as SelectTrigger.
345
+ width === "auto" && "w-auto",
346
+ width === "full" && "w-full",
340
347
  // Reserve trailing room for the single clear-or-chevron overlay rendered below.
341
348
  "ui-control-trigger-affixed"
342
349
  ),
@@ -298,6 +298,7 @@ function DataSelect(props) {
298
298
  size,
299
299
  status,
300
300
  variant,
301
+ width,
301
302
  loading,
302
303
  open,
303
304
  defaultOpen,
@@ -372,6 +373,7 @@ function DataSelect(props) {
372
373
  size,
373
374
  variant,
374
375
  status,
376
+ width,
375
377
  "aria-busy": loading || void 0,
376
378
  className: cn(
377
379
  (showClear || loading) && "ui-control-trigger-affixed",
@@ -1,9 +1,12 @@
1
1
  import * as React from "react";
2
- import * as SwitchPrimitive from "@radix-ui/react-switch";
3
2
  export type { SwitchProp, SwitchProp as SwitchProps } from "../../props/components/data-entry.prop.js";
4
- export declare const Switch: React.ForwardRefExoticComponent<Omit<SwitchPrimitive.SwitchProps & React.RefAttributes<HTMLButtonElement>, "ref"> & {
3
+ export declare const Switch: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "checked" | "defaultChecked" | "onChange" | "value"> & {
4
+ checked?: boolean;
5
+ defaultChecked?: boolean;
6
+ onCheckedChange?: (checked: boolean) => void;
7
+ required?: boolean;
5
8
  size?: "sm" | "md";
6
9
  loading?: boolean;
7
10
  checkedChildren?: React.ReactNode;
8
11
  unCheckedChildren?: React.ReactNode;
9
- } & React.RefAttributes<HTMLButtonElement>>;
12
+ } & React.RefAttributes<HTMLLabelElement>>;
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import * as SwitchPrimitive from "@radix-ui/react-switch";
4
+ import { Switch as AriaSwitch } from "react-aria-components";
5
5
  import { Loader2 } from "lucide-react";
6
6
  import { cn } from "../../lib/utils.js";
7
- import { useFieldIdentity } from "../../lib/field-a11y.js";
7
+ import { useFieldIdentity, useMirroredInputAttributes } from "../../lib/field-a11y.js";
8
8
  const Switch = React.forwardRef(
9
9
  ({
10
10
  className,
@@ -16,6 +16,8 @@ const Switch = React.forwardRef(
16
16
  loading = false,
17
17
  checkedChildren,
18
18
  unCheckedChildren,
19
+ disabled,
20
+ required,
19
21
  ...props
20
22
  }, ref) => {
21
23
  const identity = useFieldIdentity({
@@ -34,19 +36,42 @@ const Switch = React.forwardRef(
34
36
  }
35
37
  onCheckedChange?.(next);
36
38
  };
39
+ const ownField = props["data-field"];
40
+ const fieldKey = ownField ?? identity["data-field"];
41
+ const invalid = props["aria-invalid"];
42
+ const isInvalid = invalid !== void 0 && invalid !== false && invalid !== "false";
43
+ const inputRef = React.useRef(null);
44
+ useMirroredInputAttributes(inputRef, {
45
+ "aria-invalid": isInvalid ? "true" : void 0,
46
+ "aria-busy": loading ? "true" : void 0,
47
+ "aria-disabled": loading ? "true" : void 0,
48
+ // react-aria's `Switch` Omits `isRequired`, and a switch is never the target of native
49
+ // constraint validation in this library — the form layer owns that. The prop is accepted
50
+ // for shape and announced through `aria-required`.
51
+ "aria-required": required ? "true" : void 0,
52
+ "data-field": fieldKey
53
+ });
54
+ const state = isChecked ? "checked" : "unchecked";
37
55
  return /* @__PURE__ */ jsxs(Fragment, { children: [
38
56
  resolvedName ? /* @__PURE__ */ jsx("input", { type: "hidden", name: resolvedName, value: isChecked ? "1" : "0", readOnly: true }) : null,
39
57
  /* @__PURE__ */ jsxs(
40
- SwitchPrimitive.Root,
58
+ AriaSwitch,
41
59
  {
60
+ ...props,
42
61
  ref,
62
+ inputRef,
43
63
  "data-slot": "switch",
44
64
  "data-size": size,
65
+ "data-state": state,
45
66
  "data-loading": loading ? "true" : void 0,
46
- "aria-busy": loading || void 0,
47
- "aria-disabled": loading || void 0,
48
- checked: isChecked,
49
- onCheckedChange: handleCheckedChange,
67
+ isSelected: isChecked,
68
+ isDisabled: disabled,
69
+ onChange: handleCheckedChange,
70
+ onKeyDown: (event) => {
71
+ if (event.key !== "Enter") return;
72
+ event.preventDefault();
73
+ handleCheckedChange(!isChecked);
74
+ },
50
75
  className: cn(
51
76
  // `.ui-switch:disabled, .ui-switch[data-disabled]` in styles/control.css already
52
77
  // declares both and reads --disabled-opacity. The utility was layered after
@@ -56,11 +81,9 @@ const Switch = React.forwardRef(
56
81
  "peer ui-switch shadow-xs transition-all outline-none",
57
82
  className
58
83
  ),
59
- ...props,
60
- "data-field": identity["data-field"] ?? props["data-field"],
61
84
  children: [
62
85
  checkedChildren != null || unCheckedChildren != null ? /* @__PURE__ */ jsx("span", { "data-slot": "switch-content", className: "ui-switch-content", "aria-hidden": "true", children: isChecked ? checkedChildren : unCheckedChildren }) : null,
63
- /* @__PURE__ */ jsx(SwitchPrimitive.Thumb, { "data-slot": "switch-thumb", className: "ui-switch-thumb", children: loading ? /* @__PURE__ */ jsx(
86
+ /* @__PURE__ */ jsx("span", { "data-slot": "switch-thumb", "data-state": state, className: "ui-switch-thumb", children: loading ? /* @__PURE__ */ jsx(
64
87
  Loader2,
65
88
  {
66
89
  "data-slot": "switch-spinner",
@@ -74,7 +97,7 @@ const Switch = React.forwardRef(
74
97
  ] });
75
98
  }
76
99
  );
77
- Switch.displayName = SwitchPrimitive.Root.displayName;
100
+ Switch.displayName = "Switch";
78
101
  export {
79
102
  Switch
80
103
  };
@@ -18,4 +18,4 @@ export type { MobileShellProp, MobileShellProp as MobileShellProps, } from "../.
18
18
  * IS the app's primary navigation). They are the same strings in all three locales, and a fourth
19
19
  * identical copy under `layout.mobileShell.*` would be three files of duplication to keep in sync.
20
20
  */
21
- export declare function MobileShell({ statusBar, header, children, actions, tabBar, height, className, }: MobileShellProp): import("react").JSX.Element;
21
+ export declare function MobileShell({ statusBar, header, children, actions, tabBar, height, width, className, }: MobileShellProp): import("react").JSX.Element;
@@ -9,6 +9,7 @@ function MobileShell({
9
9
  actions,
10
10
  tabBar,
11
11
  height = "viewport",
12
+ width = "fill",
12
13
  className
13
14
  }) {
14
15
  const { t } = useTranslation();
@@ -17,6 +18,7 @@ function MobileShell({
17
18
  {
18
19
  "data-slot": "mobile-shell",
19
20
  "data-height": height === "viewport" ? void 0 : height,
21
+ "data-width": width === "fill" ? void 0 : width,
20
22
  className: cn("ui-mobile-shell", className),
21
23
  children: [
22
24
  (statusBar !== void 0 || header !== void 0) && /* @__PURE__ */ jsxs("header", { className: "ui-mobile-shell-chrome", "aria-label": t("layout.appShell.headerLabel"), children: [
@@ -1,9 +1,16 @@
1
1
  import * as React from "react";
2
2
  import * as MenubarPrimitive from "@radix-ui/react-menubar";
3
3
  export declare const Menubar: React.ForwardRefExoticComponent<MenubarPrimitive.MenubarProps & React.RefAttributes<HTMLDivElement>>;
4
- export declare const MenubarMenu: (props: MenubarPrimitive.MenubarMenuProps & {
5
- __scopeMenubar?: import("@radix-ui/react-context").Scope;
6
- }) => React.JSX.Element;
4
+ /**
5
+ * Annotated rather than inferred, and that is load-bearing.
6
+ *
7
+ * Radix types this one through `createContextScope`, so its INFERRED type reaches into
8
+ * `@radix-ui/react-context` for a `Scope` — and TypeScript then refuses to emit a declaration it
9
+ * cannot name portably (TS2883) unless that package is a DIRECT dependency. It was one, purely to
10
+ * satisfy this line: nothing in the tree ever imported it. Naming the props here severs that,
11
+ * which is what let the dependency go.
12
+ */
13
+ export declare const MenubarMenu: (props: React.ComponentProps<typeof MenubarPrimitive.Menu>) => React.ReactNode;
7
14
  export declare function MenubarTrigger(props: React.ComponentProps<typeof MenubarPrimitive.Trigger>): React.JSX.Element;
8
15
  export declare const MenubarContent: React.ForwardRefExoticComponent<Omit<MenubarPrimitive.MenubarContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
9
16
  export declare const MenubarItem: React.ForwardRefExoticComponent<Omit<MenubarPrimitive.MenubarItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
@@ -1,4 +1,4 @@
1
1
  import * as React from "react";
2
2
  import type { StepsProp } from "../../props/components/navigation.prop.js";
3
3
  export type { StepsProp, StepsProp as StepsProps, StepItemProp, StepStatusProp, StepsSeparatorProp, StepsTypeProp, } from "../../props/components/navigation.prop.js";
4
- export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement, separator, percent, onValueChange, className, }: StepsProp): React.JSX.Element;
4
+ export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement, separator, percent, onValueChange, className, ...props }: StepsProp): React.JSX.Element;
@@ -50,7 +50,8 @@ function Steps({
50
50
  separator = "chevron",
51
51
  percent,
52
52
  onValueChange,
53
- className
53
+ className,
54
+ ...props
54
55
  }) {
55
56
  const { t } = useTranslation();
56
57
  const base = defaultValue;
@@ -69,6 +70,7 @@ function Steps({
69
70
  "data-type": type,
70
71
  className: cn("flex w-full", inline ? "ui-steps-inline" : "ui-steps-list", className),
71
72
  "aria-label": t("navigation.steps.ariaLabel"),
73
+ ...props,
72
74
  children: items.map((item, index) => {
73
75
  const absoluteIndex = base + index;
74
76
  const stepStatus = resolveStepStatus(absoluteIndex, current, item.status, currentStatus);
@@ -34,3 +34,30 @@ export declare function syncActiveTabIntoView(list: HTMLElement | null, behavior
34
34
  * pre-fix behaviour instead of throwing.
35
35
  */
36
36
  export declare function useKeepActiveTabVisible(listRef: React.RefObject<HTMLElement | null>): void;
37
+ /**
38
+ * The values of the triggers that are NOT fully inside the strip's scrollport, in document order.
39
+ *
40
+ * Pure apart from the two rect reads — `values` is the `items` array's own order, and the triggers
41
+ * are matched to it by INDEX rather than by DOM id: `TabsTrigger` hands its `value` to React Aria
42
+ * as the collection KEY, and the rendered `id` attribute is RAC-generated, so the element carries
43
+ * no readable handle back to the item it came from.
44
+ *
45
+ * This is the measurement behind `overflow="menu"`. It is separated from the hook below so it can
46
+ * be exercised on plain numbers — jsdom lays nothing out, so the only honest unit assertions about
47
+ * it are the ones that hand it real geometry.
48
+ */
49
+ export declare function resolveHiddenTabValues(list: HTMLElement, values: readonly string[]): string[];
50
+ /**
51
+ * Keeps `onChange` fed with the values currently out of the scrollport, recomputing on the two
52
+ * things that can move them: the strip resizing (a viewport change, a font swap, a tab added or
53
+ * removed) and the strip scrolling (the user swiping, or `useKeepActiveTabVisible` re-pinning a
54
+ * trigger).
55
+ *
56
+ * `values` is joined into the effect key rather than passed as a dependency, so a consumer that
57
+ * rebuilds its `items` array on every render does not re-arm the observers on every render.
58
+ *
59
+ * The first measurement ALWAYS reports, even when nothing is hidden. Suppressing it looks harmless
60
+ * and is not: on a remount the local `last` resets while the caller's state does not, so a strip
61
+ * that had overflowed and then stopped would keep a stale menu forever.
62
+ */
63
+ export declare function useTabsOverflowValues(listRef: React.RefObject<HTMLElement | null>, values: readonly string[] | undefined, onChange: (hidden: string[]) => void): void;