@godxjp/ui 18.15.4 → 19.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 (145) hide show
  1. package/dist/app/date-format-labels.d.ts +1 -1
  2. package/dist/app/time-format-labels.d.ts +1 -1
  3. package/dist/components/data-display/badge.d.ts +1 -1
  4. package/dist/components/data-display/card.d.ts +4 -13
  5. package/dist/components/data-display/card.js +2 -15
  6. package/dist/components/data-display/data-table.d.ts +3 -8
  7. package/dist/components/data-display/data-table.js +106 -68
  8. package/dist/components/data-display/descriptions.js +34 -5
  9. package/dist/components/data-display/empty-state.js +1 -1
  10. package/dist/components/data-display/permission-matrix.js +7 -7
  11. package/dist/components/data-display/popover.js +2 -9
  12. package/dist/components/data-display/qr-code.d.ts +1 -1
  13. package/dist/components/data-display/scroll-area.d.ts +3 -1
  14. package/dist/components/data-display/scroll-area.js +150 -16
  15. package/dist/components/data-display/table.js +12 -3
  16. package/dist/components/data-entry/branch-scope-picker.js +4 -4
  17. package/dist/components/data-entry/calendar.js +15 -24
  18. package/dist/components/data-entry/cascader.js +67 -71
  19. package/dist/components/data-entry/checkbox.js +9 -1
  20. package/dist/components/data-entry/command.d.ts +6 -6
  21. package/dist/components/data-entry/date-picker.js +28 -15
  22. package/dist/components/data-entry/date-range-picker.js +10 -10
  23. package/dist/components/data-entry/form-field.js +7 -3
  24. package/dist/components/data-entry/index.d.ts +2 -1
  25. package/dist/components/data-entry/input.js +8 -19
  26. package/dist/components/data-entry/label.js +4 -2
  27. package/dist/components/data-entry/month-picker.js +16 -15
  28. package/dist/components/data-entry/month-range-picker.js +17 -16
  29. package/dist/components/data-entry/radio.js +6 -1
  30. package/dist/components/data-entry/search-select.js +29 -29
  31. package/dist/components/data-entry/select.js +15 -18
  32. package/dist/components/data-entry/switch.js +7 -1
  33. package/dist/components/data-entry/textarea.d.ts +56 -0
  34. package/dist/components/data-entry/textarea.js +103 -40
  35. package/dist/components/data-entry/time-picker.js +52 -52
  36. package/dist/components/data-entry/toggle-group.d.ts +1 -0
  37. package/dist/components/data-entry/toggle.d.ts +1 -1
  38. package/dist/components/data-entry/transfer.js +14 -15
  39. package/dist/components/data-entry/tree-select.js +16 -22
  40. package/dist/components/data-entry/upload-crop-dialog.js +4 -4
  41. package/dist/components/data-entry/upload.d.ts +1 -1
  42. package/dist/components/data-entry/upload.js +46 -46
  43. package/dist/components/feedback/alert.js +7 -14
  44. package/dist/components/feedback/dialog.d.ts +23 -32
  45. package/dist/components/feedback/dialog.js +16 -13
  46. package/dist/components/feedback/sheet.d.ts +1 -1
  47. package/dist/components/feedback/sheet.js +14 -14
  48. package/dist/components/feedback/skeleton.js +13 -10
  49. package/dist/components/feedback/sonner.d.ts +1 -1
  50. package/dist/components/feedback/sonner.js +17 -8
  51. package/dist/components/feedback/tooltip.js +1 -1
  52. package/dist/components/general/activity.d.ts +44 -0
  53. package/dist/components/general/activity.js +65 -0
  54. package/dist/components/general/button.d.ts +2 -2
  55. package/dist/components/general/button.js +11 -24
  56. package/dist/components/general/index.d.ts +2 -0
  57. package/dist/components/general/index.js +2 -0
  58. package/dist/components/layout/app-shell.js +5 -3
  59. package/dist/components/layout/auth-divider.d.ts +9 -2
  60. package/dist/components/layout/auth-divider.js +7 -11
  61. package/dist/components/layout/index.d.ts +1 -0
  62. package/dist/components/layout/page-container.d.ts +1 -1
  63. package/dist/components/layout/page-container.js +32 -20
  64. package/dist/components/layout/resizable.d.ts +12 -12
  65. package/dist/components/layout/separator.d.ts +1 -0
  66. package/dist/components/layout/service-role-panel.js +47 -42
  67. package/dist/components/layout/sidebar.js +13 -1
  68. package/dist/components/layout/split-pane.d.ts +12 -1
  69. package/dist/components/layout/split-pane.js +13 -4
  70. package/dist/components/navigation/app-setting-picker.js +5 -14
  71. package/dist/components/navigation/dropdown-menu.js +10 -16
  72. package/dist/components/navigation/menubar.d.ts +3 -6
  73. package/dist/components/navigation/pagination-utils.d.ts +15 -2
  74. package/dist/components/navigation/pagination-utils.js +27 -21
  75. package/dist/components/navigation/pagination.d.ts +4 -4
  76. package/dist/components/navigation/pagination.js +2 -2
  77. package/dist/components/navigation/steps.js +31 -67
  78. package/dist/components/navigation/tabs.js +17 -5
  79. package/dist/components/query/infinite-query-state.js +2 -2
  80. package/dist/components/ui/accordion.d.ts +1 -1
  81. package/dist/components/ui/avatar.d.ts +1 -1
  82. package/dist/components/ui/avatar.js +29 -10
  83. package/dist/components/ui/input-otp.d.ts +8 -6
  84. package/dist/components/ui/rating.js +1 -1
  85. package/dist/components/ui/separator.d.ts +27 -2
  86. package/dist/components/ui/separator.js +42 -11
  87. package/dist/components/ui/toggle-group.d.ts +15 -5
  88. package/dist/components/ui/toggle-group.js +43 -18
  89. package/dist/components/ui/toggle.d.ts +66 -5
  90. package/dist/components/ui/toggle.js +64 -9
  91. package/dist/i18n/messages/en.json +8 -0
  92. package/dist/i18n/messages/ja.json +8 -0
  93. package/dist/i18n/messages/vi.json +8 -0
  94. package/dist/lib/control-styles.d.ts +14 -0
  95. package/dist/lib/control-styles.js +2 -0
  96. package/dist/props/components/data-display.prop.d.ts +85 -3
  97. package/dist/props/components/data-entry.prop.d.ts +17 -0
  98. package/dist/props/components/general.prop.d.ts +52 -1
  99. package/dist/props/components/layout.prop.d.ts +137 -2
  100. package/dist/props/registry.d.ts +116 -5
  101. package/dist/props/registry.js +165 -4
  102. package/dist/props/vocabulary/index.d.ts +2 -2
  103. package/dist/props/vocabulary/interaction.prop.d.ts +15 -0
  104. package/dist/props/vocabulary/layout.prop.d.ts +8 -0
  105. package/dist/styles/alert-layout.css +69 -3
  106. package/dist/styles/badge-layout.css +2 -2
  107. package/dist/styles/base.css +6 -0
  108. package/dist/styles/card-layout.css +25 -8
  109. package/dist/styles/control.css +1150 -29
  110. package/dist/styles/data-display-layout.css +185 -11
  111. package/dist/styles/data-entry-layout.css +352 -1
  112. package/dist/styles/density.css +2 -2
  113. package/dist/styles/dialog-layout.css +203 -4
  114. package/dist/styles/focus-ring.css +155 -0
  115. package/dist/styles/form-layout.css +50 -5
  116. package/dist/styles/index.css +1 -0
  117. package/dist/styles/layout.css +412 -23
  118. package/dist/styles/motion.css +178 -0
  119. package/dist/styles/navigation-layout.css +398 -22
  120. package/dist/styles/shell-layout.css +137 -39
  121. package/dist/styles/table-layout.css +153 -22
  122. package/dist/styles/toggle.css +59 -0
  123. package/dist/tokens/base.css +5 -0
  124. package/dist/tokens/components/activity.css +64 -0
  125. package/dist/tokens/components/badge.css +7 -0
  126. package/dist/tokens/components/card.css +14 -10
  127. package/dist/tokens/components/chart.css +1 -1
  128. package/dist/tokens/components/control.css +358 -8
  129. package/dist/tokens/components/data-display.css +75 -0
  130. package/dist/tokens/components/data-entry.css +25 -0
  131. package/dist/tokens/components/descriptions.css +22 -0
  132. package/dist/tokens/components/feedback.css +143 -4
  133. package/dist/tokens/components/form.css +16 -0
  134. package/dist/tokens/components/legal-document.css +5 -1
  135. package/dist/tokens/components/navigation.css +109 -0
  136. package/dist/tokens/components/scroll-area.css +21 -0
  137. package/dist/tokens/components/separator.css +52 -0
  138. package/dist/tokens/components/shell.css +132 -20
  139. package/dist/tokens/components/table.css +52 -3
  140. package/dist/tokens/components/toggle.css +82 -0
  141. package/dist/tokens/components/upload.css +107 -0
  142. package/dist/tokens/foundation.css +171 -10
  143. package/dist/tokens/semantic/layout.css +120 -1
  144. package/package.json +84 -68
  145. package/scripts/ui-audit.mjs +51 -2
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { CalendarIcon } from "lucide-react";
4
+ import { CalendarIcon, X } from "lucide-react";
5
5
  import { usePickerLocales, useTranslation } from "../../i18n/use-translation.js";
6
6
  import { parseDateInput, toIsoDate } from "../../lib/datetime/parse.js";
7
7
  import { useControlledLatch } from "../../lib/hooks.js";
@@ -48,6 +48,7 @@ function DatePicker({
48
48
  emit(void 0);
49
49
  setText("");
50
50
  };
51
+ const showClear = allowClear && text !== "" && !disabled;
51
52
  const commit = (raw) => {
52
53
  const trimmed = raw.trim();
53
54
  if (trimmed === "") {
@@ -76,19 +77,31 @@ function DatePicker({
76
77
  "aria-haspopup": "dialog",
77
78
  "aria-controls": open ? dialogId : void 0,
78
79
  ...fieldA11y,
79
- allowClear,
80
- onClear: clear,
81
- trailingIcon: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
82
- "button",
83
- {
84
- type: "button",
85
- disabled,
86
- tabIndex: -1,
87
- "aria-label": t("dataEntry.datePicker.openCalendar") ?? "Open calendar",
88
- className: "text-muted-foreground hover:text-foreground inline-flex size-5 items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100",
89
- children: /* @__PURE__ */ jsx(CalendarIcon, { className: "size-4", "aria-hidden": "true" })
90
- }
91
- ) }),
80
+ className: showClear ? "ui-control-inline-affix-pair-affixed" : void 0,
81
+ trailingIcon: /* @__PURE__ */ jsxs("span", { className: "ui-time-picker-affix", children: [
82
+ showClear ? /* @__PURE__ */ jsx(
83
+ "button",
84
+ {
85
+ type: "button",
86
+ tabIndex: -1,
87
+ "aria-label": t("common.clear") ?? "Clear",
88
+ onClick: clear,
89
+ className: "ui-control-inline-affix-action",
90
+ children: /* @__PURE__ */ jsx(X, { className: "ui-control-inline-affix-icon", "aria-hidden": "true" })
91
+ }
92
+ ) : null,
93
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
94
+ "button",
95
+ {
96
+ type: "button",
97
+ disabled,
98
+ tabIndex: -1,
99
+ "aria-label": t("dataEntry.datePicker.openCalendar") ?? "Open calendar",
100
+ className: "ui-control-inline-affix-action",
101
+ children: /* @__PURE__ */ jsx(CalendarIcon, { className: "ui-control-inline-affix-icon", "aria-hidden": "true" })
102
+ }
103
+ ) })
104
+ ] }),
92
105
  onClick: () => {
93
106
  if (!disabled) setOpen(true);
94
107
  },
@@ -116,7 +129,7 @@ function DatePicker({
116
129
  id: dialogId,
117
130
  role: "dialog",
118
131
  "aria-label": t("dataEntry.datePicker.openCalendar") ?? "Calendar",
119
- className: "w-auto p-0",
132
+ className: "ui-control-panel-flush",
120
133
  align: "start",
121
134
  onOpenAutoFocus: (event) => event.preventDefault(),
122
135
  children: /* @__PURE__ */ jsx(
@@ -70,7 +70,7 @@ function DateRangePicker({
70
70
  }
71
71
  }
72
72
  };
73
- const innerInputClass = "min-w-0 flex-1 bg-transparent tabular-nums outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed";
73
+ const innerInputClass = "ui-month-picker-input";
74
74
  return /* @__PURE__ */ jsx(Popover, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
75
75
  "div",
76
76
  {
@@ -79,12 +79,11 @@ function DateRangePicker({
79
79
  ...groupA11y,
80
80
  "aria-disabled": disabled ? true : void 0,
81
81
  className: cn(
82
- // One input-styled shell for the whole range — mirrors Input's control
83
- // tokens (border/radius/ring) so it reads as a single form field.
84
- "ui-control border-input bg-background flex w-full min-w-0 items-center gap-2 rounded-[var(--control-radius)] transition-[color,box-shadow] outline-none",
82
+ // One input-styled shell for the whole range — the shared composite-field box, so
83
+ // this and the month range picker cannot drift into two slightly different fields.
84
+ "ui-control ui-control-composite-field",
85
85
  "focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
86
86
  open && "border-ring ring-ring/50 ring-[3px]",
87
- disabled && "pointer-events-none cursor-not-allowed opacity-50",
88
87
  className
89
88
  ),
90
89
  onClick: () => {
@@ -114,7 +113,7 @@ function DateRangePicker({
114
113
  }
115
114
  }
116
115
  ),
117
- /* @__PURE__ */ jsx(ArrowRight, { className: "text-muted-foreground size-3.5 shrink-0", "aria-hidden": "true" }),
116
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ui-month-picker-separator-icon", "aria-hidden": "true" }),
118
117
  /* @__PURE__ */ jsx(
119
118
  "input",
120
119
  {
@@ -149,9 +148,10 @@ function DateRangePicker({
149
148
  event.stopPropagation();
150
149
  clear();
151
150
  },
152
- children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
151
+ children: /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
153
152
  }
154
- ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
153
+ ) : null,
154
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
155
155
  "button",
156
156
  {
157
157
  type: "button",
@@ -159,13 +159,13 @@ function DateRangePicker({
159
159
  tabIndex: -1,
160
160
  "aria-label": t("dataEntry.dateRangePicker.openCalendar") ?? "Open calendar",
161
161
  className: "text-muted-foreground hover:text-foreground shrink-0",
162
- children: /* @__PURE__ */ jsx(CalendarIcon, { className: "size-4 shrink-0", "aria-hidden": "true" })
162
+ children: /* @__PURE__ */ jsx(CalendarIcon, { className: "ui-month-picker-icon", "aria-hidden": "true" })
163
163
  }
164
164
  ) }),
165
165
  /* @__PURE__ */ jsx(
166
166
  PopoverContent,
167
167
  {
168
- className: "w-auto p-0",
168
+ className: "ui-control-panel-flush",
169
169
  align: "start",
170
170
  onOpenAutoFocus: (event) => event.preventDefault(),
171
171
  children: /* @__PURE__ */ jsx(
@@ -52,8 +52,12 @@ function FormField({
52
52
  const mergeIds = mergeAriaIds;
53
53
  const childWithA11y = isStatic ? (
54
54
  // Byte-for-byte the same value typography as `Descriptions.Item`'s `dd` (gh#294), so a
55
- // read-only FormField row and a Descriptions value are indistinguishable when mixed.
56
- /* @__PURE__ */ jsx("span", { className: "text-sm break-all", children: staticText })
55
+ // read-only FormField row and a Descriptions value are indistinguishable when mixed. That
56
+ // used to be two copies of the literal `text-sm` kept in sync by this comment; both call sites
57
+ // now read the SAME token pair (#319), so a service retunes both from one place and they
58
+ // cannot drift. --descriptions-value-line-height is the mandatory companion: `text-sm` also
59
+ // set Tailwind's `--text-sm--line-height`, which the theme never remaps (the gh#260 bug).
60
+ /* @__PURE__ */ jsx("span", { className: "text-[length:var(--descriptions-value-font-size)] leading-[var(--descriptions-value-line-height)] break-all", children: staticText })
57
61
  ) : React.isValidElement(children) ? React.cloneElement(children, {
58
62
  // The label is associated via aria-labelledby (not <label for>): composite
59
63
  // controls (Radio.Group, checkbox lists, range pairs) have no labelable root,
@@ -83,7 +87,7 @@ function FormField({
83
87
  style["--form-label-width"] = toCssLength(labelWidth);
84
88
  if (controlWidth != null)
85
89
  style["--form-control-width"] = toCssLength(controlWidth);
86
- if (colSpan != null) style.gridColumn = `span ${colSpan}`;
90
+ if (colSpan != null) style["--form-field-col-span"] = String(colSpan);
87
91
  return /* @__PURE__ */ jsxs(
88
92
  "div",
89
93
  {
@@ -21,8 +21,9 @@ export { SearchInput } from "./search-input.js";
21
21
  export { Switch } from "./switch.js";
22
22
  export type { SwitchProps } from "./switch.js";
23
23
  export { Toggle } from "./toggle.js";
24
- export type { ToggleProps } from "./toggle.js";
24
+ export type { ToggleProp, ToggleProps } from "./toggle.js";
25
25
  export { ToggleGroup, ToggleGroupItem } from "./toggle-group.js";
26
+ export type { ToggleGroupItemProp, ToggleGroupItemProps } from "./toggle-group.js";
26
27
  export { Slider } from "./slider.js";
27
28
  export type { SliderProps } from "./slider.js";
28
29
  export { Calendar } from "./calendar.js";
@@ -6,12 +6,9 @@ import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { useFieldNameFallback } from "../../lib/field-a11y.js";
7
7
  import { cn } from "../../lib/utils.js";
8
8
  const inputBaseClass = [
9
- "ui-control border-input bg-background w-full min-w-0 rounded-[var(--control-radius)] transition-[color,box-shadow] outline-none",
9
+ "ui-control ui-input border-input bg-background w-full rounded-[var(--control-radius)] transition-[color,box-shadow] outline-none",
10
10
  "selection:bg-primary selection:text-primary-foreground",
11
- "file:text-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium",
12
11
  "placeholder:text-muted-foreground",
13
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
14
- "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
15
12
  "aria-invalid:border-destructive aria-invalid:ring-destructive/20"
16
13
  ];
17
14
  const Input = React.forwardRef(
@@ -89,20 +86,12 @@ const Input = React.forwardRef(
89
86
  tabIndex: -1,
90
87
  "aria-label": t("common.clear") ?? "Clear",
91
88
  onClick: clear,
92
- className: "text-muted-foreground hover:text-foreground inline-flex size-5 items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:opacity-100",
93
- children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
89
+ className: "ui-control-inline-affix-action",
90
+ children: /* @__PURE__ */ jsx(X, { className: "ui-control-inline-affix-icon", "aria-hidden": "true" })
94
91
  }
95
92
  ) : trailingIcon;
96
- return /* @__PURE__ */ jsxs("span", { "data-slot": "input-affix-wrapper", className: "relative inline-flex w-full items-center", children: [
97
- leadingIcon != null ? /* @__PURE__ */ jsx(
98
- "span",
99
- {
100
- "data-slot": "input-leading",
101
- "aria-hidden": "true",
102
- className: "text-muted-foreground pointer-events-none absolute inset-y-0 start-2 inline-flex items-center",
103
- children: leadingIcon
104
- }
105
- ) : null,
93
+ return /* @__PURE__ */ jsxs("span", { "data-slot": "input-affix-wrapper", className: "ui-input-affix-wrapper", children: [
94
+ leadingIcon != null ? /* @__PURE__ */ jsx("span", { "data-slot": "input-leading", "aria-hidden": "true", className: "ui-input-leading", children: leadingIcon }) : null,
106
95
  /* @__PURE__ */ jsx(
107
96
  "input",
108
97
  {
@@ -114,15 +103,15 @@ const Input = React.forwardRef(
114
103
  onChange: handleChange,
115
104
  className: cn(
116
105
  inputBaseClass,
117
- leadingIcon != null && "ps-9",
118
- (showClear || trailingIcon != null) && "pe-9",
106
+ leadingIcon != null && "ui-input--leading-affix",
107
+ (showClear || trailingIcon != null) && "ui-input--trailing-affix",
119
108
  className
120
109
  ),
121
110
  ...props,
122
111
  ...nameFallback
123
112
  }
124
113
  ),
125
- trailing != null ? /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 end-2 inline-flex items-center", children: trailing }) : null
114
+ trailing != null ? /* @__PURE__ */ jsx("span", { className: "ui-input-trailing", children: trailing }) : null
126
115
  ] });
127
116
  }
128
117
  );
@@ -5,7 +5,7 @@ import * as LabelPrimitive from "@radix-ui/react-label";
5
5
  import { cva } from "class-variance-authority";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  const labelVariants = cva(
8
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
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
10
  const Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
11
11
  LabelPrimitive.Root,
@@ -13,7 +13,9 @@ const Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
13
13
  ref,
14
14
  "data-slot": "label",
15
15
  className: cn(
16
- "flex items-center gap-2 select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
16
+ // A label inside a disabled group reads at the SYSTEM disabled alpha (--disabled-opacity),
17
+ // the same one .ui-checkbox/.ui-radio/.ui-switch:disabled use — one knob, not a second one.
18
+ "ui-label group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-[var(--disabled-opacity)]",
17
19
  labelVariants(),
18
20
  className
19
21
  ),
@@ -61,11 +61,11 @@ function MonthPicker({
61
61
  return /* @__PURE__ */ jsx(Popover, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
62
62
  "div",
63
63
  {
64
+ "data-disabled": disabled ? "" : void 0,
64
65
  className: cn(
65
- "ui-control border-input bg-background flex w-full min-w-0 items-center gap-2 rounded-[var(--control-radius)] transition-[color,box-shadow] outline-none",
66
+ "ui-control ui-control-composite-field",
66
67
  "focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
67
68
  open && "border-ring ring-ring/50 ring-[3px]",
68
- disabled && "pointer-events-none cursor-not-allowed opacity-50",
69
69
  className
70
70
  ),
71
71
  onClick: () => {
@@ -87,7 +87,7 @@ function MonthPicker({
87
87
  "aria-haspopup": "dialog",
88
88
  "aria-controls": open ? dialogId : void 0,
89
89
  ...fieldA11y,
90
- className: "placeholder:text-muted-foreground min-w-0 flex-1 bg-transparent tabular-nums outline-none disabled:cursor-not-allowed",
90
+ className: "ui-month-picker-input",
91
91
  onKeyDown: (event) => {
92
92
  if (event.key === "ArrowDown") {
93
93
  event.preventDefault();
@@ -117,9 +117,10 @@ function MonthPicker({
117
117
  emit(void 0);
118
118
  setText("");
119
119
  },
120
- children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
120
+ children: /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
121
121
  }
122
- ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
122
+ ) : null,
123
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
123
124
  "button",
124
125
  {
125
126
  type: "button",
@@ -127,7 +128,7 @@ function MonthPicker({
127
128
  tabIndex: -1,
128
129
  "aria-label": t("dataEntry.monthPicker.openGrid") ?? "Open month grid",
129
130
  className: "text-muted-foreground hover:text-foreground shrink-0",
130
- children: /* @__PURE__ */ jsx(CalendarIcon, { className: "size-4 shrink-0", "aria-hidden": "true" })
131
+ children: /* @__PURE__ */ jsx(CalendarIcon, { className: "ui-month-picker-icon", "aria-hidden": "true" })
131
132
  }
132
133
  ) }),
133
134
  /* @__PURE__ */ jsxs(
@@ -136,12 +137,12 @@ function MonthPicker({
136
137
  id: dialogId,
137
138
  role: "dialog",
138
139
  "aria-label": t("dataEntry.monthPicker.openGrid") ?? "Month grid",
139
- className: "w-auto p-3",
140
+ className: "ui-month-picker-panel",
140
141
  align: "start",
141
142
  onOpenAutoFocus: (event) => event.preventDefault(),
142
143
  onClick: (event) => event.stopPropagation(),
143
144
  children: [
144
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
145
+ /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-nav", children: [
145
146
  /* @__PURE__ */ jsx(
146
147
  Button,
147
148
  {
@@ -150,12 +151,12 @@ function MonthPicker({
150
151
  size: "icon-sm",
151
152
  disabled: prevDisabled,
152
153
  "aria-label": t("dataEntry.monthPicker.previousYear") ?? "Previous year",
153
- className: "bg-transparent opacity-70 hover:opacity-100",
154
+ className: "ui-month-picker-nav-button",
154
155
  onClick: () => setViewYear((y) => y - 1),
155
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4", "aria-hidden": "true" })
156
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "ui-month-picker-icon", "aria-hidden": "true" })
156
157
  }
157
158
  ),
158
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", "aria-live": "polite", children: viewYear }),
159
+ /* @__PURE__ */ jsx("span", { className: "ui-month-picker-nav-label", "aria-live": "polite", children: viewYear }),
159
160
  /* @__PURE__ */ jsx(
160
161
  Button,
161
162
  {
@@ -164,13 +165,13 @@ function MonthPicker({
164
165
  size: "icon-sm",
165
166
  disabled: nextDisabled,
166
167
  "aria-label": t("dataEntry.monthPicker.nextYear") ?? "Next year",
167
- className: "bg-transparent opacity-70 hover:opacity-100",
168
+ className: "ui-month-picker-nav-button",
168
169
  onClick: () => setViewYear((y) => y + 1),
169
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4", "aria-hidden": "true" })
170
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "ui-month-picker-icon", "aria-hidden": "true" })
170
171
  }
171
172
  )
172
173
  ] }),
173
- /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "mt-3 grid grid-cols-3 gap-1", children: monthLabels.map((label, i) => {
174
+ /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "ui-month-picker-grid", children: monthLabels.map((label, i) => {
174
175
  const selected = value?.getFullYear() === viewYear && value?.getMonth() === i;
175
176
  return /* @__PURE__ */ jsx(
176
177
  Button,
@@ -179,7 +180,7 @@ function MonthPicker({
179
180
  variant: selected ? "default" : "ghost",
180
181
  size: "sm",
181
182
  "aria-pressed": selected,
182
- className: "px-4 font-normal",
183
+ className: "ui-month-picker-cell",
183
184
  onClick: () => {
184
185
  emit(new Date(viewYear, i, 1));
185
186
  setOpen(false);
@@ -99,7 +99,7 @@ function MonthRangePicker({
99
99
  }
100
100
  }
101
101
  };
102
- const innerInputClass = "min-w-0 flex-1 bg-transparent tabular-nums outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed";
102
+ const innerInputClass = "ui-month-picker-input";
103
103
  const resolvedPlaceholder = placeholder ?? t("dataEntry.monthPicker.placeholder") ?? YM_HINT;
104
104
  return /* @__PURE__ */ jsx(Popover, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
105
105
  "div",
@@ -108,11 +108,11 @@ function MonthRangePicker({
108
108
  id: groupId,
109
109
  ...groupA11y,
110
110
  "aria-disabled": disabled ? true : void 0,
111
+ "data-disabled": disabled ? "" : void 0,
111
112
  className: cn(
112
- "ui-control border-input bg-background flex w-full min-w-0 items-center gap-2 rounded-[var(--control-radius)] transition-[color,box-shadow] outline-none",
113
+ "ui-control ui-control-composite-field",
113
114
  "focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
114
115
  open && "border-ring ring-ring/50 ring-[3px]",
115
- disabled && "pointer-events-none cursor-not-allowed opacity-50",
116
116
  className
117
117
  ),
118
118
  onClick: () => {
@@ -139,7 +139,7 @@ function MonthRangePicker({
139
139
  onBlur: () => setFromText(toYmText(value?.from))
140
140
  }
141
141
  ),
142
- /* @__PURE__ */ jsx(ArrowRight, { className: "text-muted-foreground size-3.5 shrink-0", "aria-hidden": "true" }),
142
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ui-month-picker-separator-icon", "aria-hidden": "true" }),
143
143
  /* @__PURE__ */ jsx(
144
144
  "input",
145
145
  {
@@ -171,9 +171,10 @@ function MonthRangePicker({
171
171
  event.stopPropagation();
172
172
  clear();
173
173
  },
174
- children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
174
+ children: /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
175
175
  }
176
- ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
176
+ ) : null,
177
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
177
178
  "button",
178
179
  {
179
180
  type: "button",
@@ -181,18 +182,18 @@ function MonthRangePicker({
181
182
  tabIndex: -1,
182
183
  "aria-label": t("dataEntry.monthPicker.openGrid") ?? "Open month grid",
183
184
  className: "text-muted-foreground hover:text-foreground shrink-0",
184
- children: /* @__PURE__ */ jsx(CalendarIcon, { className: "size-4 shrink-0", "aria-hidden": "true" })
185
+ children: /* @__PURE__ */ jsx(CalendarIcon, { className: "ui-month-picker-icon", "aria-hidden": "true" })
185
186
  }
186
187
  ) }),
187
188
  /* @__PURE__ */ jsxs(
188
189
  PopoverContent,
189
190
  {
190
- className: "w-auto p-3",
191
+ className: "ui-month-picker-panel",
191
192
  align: "start",
192
193
  onOpenAutoFocus: (event) => event.preventDefault(),
193
194
  onClick: (event) => event.stopPropagation(),
194
195
  children: [
195
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
196
+ /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-nav", children: [
196
197
  /* @__PURE__ */ jsx(
197
198
  Button,
198
199
  {
@@ -201,12 +202,12 @@ function MonthRangePicker({
201
202
  size: "icon-sm",
202
203
  disabled: prevDisabled,
203
204
  "aria-label": t("dataEntry.monthPicker.previousYear") ?? "Previous year",
204
- className: "bg-transparent opacity-70 hover:opacity-100",
205
+ className: "ui-month-picker-nav-button",
205
206
  onClick: () => setViewYear((y) => y - 1),
206
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4", "aria-hidden": "true" })
207
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "ui-month-picker-icon", "aria-hidden": "true" })
207
208
  }
208
209
  ),
209
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", "aria-live": "polite", children: viewYear }),
210
+ /* @__PURE__ */ jsx("span", { className: "ui-month-picker-nav-label", "aria-live": "polite", children: viewYear }),
210
211
  /* @__PURE__ */ jsx(
211
212
  Button,
212
213
  {
@@ -215,13 +216,13 @@ function MonthRangePicker({
215
216
  size: "icon-sm",
216
217
  disabled: nextDisabled,
217
218
  "aria-label": t("dataEntry.monthPicker.nextYear") ?? "Next year",
218
- className: "bg-transparent opacity-70 hover:opacity-100",
219
+ className: "ui-month-picker-nav-button",
219
220
  onClick: () => setViewYear((y) => y + 1),
220
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4", "aria-hidden": "true" })
221
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "ui-month-picker-icon", "aria-hidden": "true" })
221
222
  }
222
223
  )
223
224
  ] }),
224
- /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "mt-3 grid grid-cols-3 gap-1", children: monthLabels.map((label, i) => {
225
+ /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "ui-month-picker-grid", children: monthLabels.map((label, i) => {
225
226
  const cell = new Date(viewYear, i, 1);
226
227
  const cellIdx = ymIndex(cell);
227
228
  const fromIdx = value?.from ? ymIndex(value.from) : void 0;
@@ -235,7 +236,7 @@ function MonthRangePicker({
235
236
  variant: isEdge ? "default" : inRange ? "secondary" : "ghost",
236
237
  size: "sm",
237
238
  "aria-pressed": isEdge,
238
- className: "px-4 font-normal",
239
+ className: "ui-month-picker-cell",
239
240
  onClick: () => pickMonth(cell),
240
241
  children: label
241
242
  },
@@ -23,7 +23,12 @@ const RadioItem = React.forwardRef(({ className, ...props }, ref) => /* @__PURE_
23
23
  ref,
24
24
  "data-slot": "radio-group-item",
25
25
  className: cn(
26
- "ui-radio focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 shrink-0 shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
26
+ // `disabled:cursor-not-allowed disabled:opacity-50` DELETED, not moved (#319):
27
+ // `.ui-radio:disabled, .ui-radio[data-disabled]` in styles/control.css already declares both
28
+ // and reads --disabled-opacity. The utility was layered after components, so it silently
29
+ // outranked that token — a service theme's --disabled-opacity never reached a radio.
30
+ // Byte-identical: --disabled-opacity defaults to 0.5.
31
+ "ui-radio aria-invalid:border-destructive aria-invalid:ring-destructive/20 shrink-0 shadow-xs transition-shadow outline-none",
27
32
  className
28
33
  ),
29
34
  ...props,
@@ -258,13 +258,13 @@ function SearchSelect({
258
258
  "w-full justify-start font-normal",
259
259
  controlOpenRingClass,
260
260
  // Reserve trailing room for the single clear-or-chevron overlay rendered below.
261
- "pe-9"
261
+ "ui-control-trigger-affixed"
262
262
  ),
263
263
  children: /* @__PURE__ */ jsx(
264
264
  "span",
265
265
  {
266
266
  className: cn(
267
- "flex min-w-0 items-center gap-2 text-start",
267
+ "ui-search-select-option-body text-start",
268
268
  !value && "text-muted-foreground"
269
269
  ),
270
270
  children: value && labelRender ? labelRender({ value, label: currentLabel, option: selectedOption ?? void 0 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -284,9 +284,9 @@ function SearchSelect({
284
284
  align: "start",
285
285
  sideOffset: 4,
286
286
  collisionPadding: 12,
287
- className: "flex max-h-[var(--radix-popover-content-available-height)] w-max max-w-[min(32rem,calc(100vw-1.5rem))] min-w-[var(--radix-popover-trigger-width)] flex-col p-0",
288
- children: /* @__PURE__ */ jsxs(Command, { value, shouldFilter: false, className: "flex min-h-0 flex-col", children: [
289
- /* @__PURE__ */ jsx("div", { className: "border-border shrink-0 border-b", children: /* @__PURE__ */ jsx(
287
+ className: "ui-search-select-panel",
288
+ children: /* @__PURE__ */ jsxs(Command, { value, shouldFilter: false, className: "ui-search-select-command", children: [
289
+ /* @__PURE__ */ jsx("div", { className: "ui-search-select-search", children: /* @__PURE__ */ jsx(
290
290
  Input,
291
291
  {
292
292
  autoFocus: true,
@@ -298,7 +298,7 @@ function SearchSelect({
298
298
  onChange: (event) => setQuery(event.target.value),
299
299
  onKeyDown,
300
300
  placeholder: searchPlaceholder ?? t("dataEntry.searchSelect.search"),
301
- className: "rounded-none border-0 shadow-none focus-visible:ring-0"
301
+ className: "ui-search-select-search-input"
302
302
  }
303
303
  ) }),
304
304
  /* @__PURE__ */ jsxs(
@@ -307,7 +307,7 @@ function SearchSelect({
307
307
  id: listId,
308
308
  role: "listbox",
309
309
  "aria-busy": loading,
310
- className: "max-h-none min-h-0 flex-1 overflow-y-auto p-1",
310
+ className: "ui-search-select-list",
311
311
  onScroll,
312
312
  children: [
313
313
  grouped.map((group) => {
@@ -331,11 +331,11 @@ function SearchSelect({
331
331
  onMouseEnter: () => !option.disabled && setActiveIndex(index),
332
332
  onMouseDown: (event) => event.preventDefault(),
333
333
  onClick: () => select(option),
334
- children: renderOption ? /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: renderOption(option) }) : /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
334
+ children: renderOption ? /* @__PURE__ */ jsx("div", { className: "ui-search-select-option-slot", children: renderOption(option) }) : /* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-body", children: [
335
335
  option.icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", "aria-hidden": "true", children: option.icon }) : null,
336
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col", children: [
337
- /* @__PURE__ */ jsx("span", { className: "truncate text-sm", children: option.label }),
338
- option.sublabel ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-xs", children: option.sublabel }) : null
336
+ /* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-text", children: [
337
+ /* @__PURE__ */ jsx("span", { className: "ui-search-select-option-label", children: option.label }),
338
+ option.sublabel ? /* @__PURE__ */ jsx("span", { className: "ui-search-select-option-sublabel", children: option.sublabel }) : null
339
339
  ] })
340
340
  ] })
341
341
  },
@@ -343,17 +343,10 @@ function SearchSelect({
343
343
  ));
344
344
  return group.heading ? /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: rows }, group.heading) : /* @__PURE__ */ jsx(React.Fragment, { children: rows }, "__ungrouped");
345
345
  }),
346
- loading ? /* @__PURE__ */ jsxs(
347
- "div",
348
- {
349
- role: "status",
350
- className: "text-muted-foreground flex items-center gap-2 px-2 py-3 text-sm",
351
- children: [
352
- /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" }),
353
- loadingMessage ?? t("dataEntry.searchSelect.loading")
354
- ]
355
- }
356
- ) : error ? renderError ? renderError({
346
+ loading ? /* @__PURE__ */ jsxs("div", { role: "status", className: "ui-search-select-status", children: [
347
+ /* @__PURE__ */ jsx(Loader2, { className: "ui-search-select-spinner animate-spin", "aria-hidden": "true" }),
348
+ loadingMessage ?? t("dataEntry.searchSelect.loading")
349
+ ] }) : error ? renderError ? renderError({
357
350
  message: errorMessage ?? t("dataEntry.searchSelect.error"),
358
351
  // Retry always reloads from the first page (a defined, predictable recovery —
359
352
  // NOT a resume of a failed page-N append, which would need to re-derive state).
@@ -364,7 +357,8 @@ function SearchSelect({
364
357
  role: "option",
365
358
  "aria-disabled": "true",
366
359
  "aria-selected": false,
367
- className: "text-destructive px-2 py-6 text-center text-sm",
360
+ className: "ui-search-select-placeholder",
361
+ "data-tone": "destructive",
368
362
  children: errorMessage ?? t("dataEntry.searchSelect.error")
369
363
  }
370
364
  ) : loaded.length === 0 ? /* @__PURE__ */ jsx(
@@ -373,7 +367,7 @@ function SearchSelect({
373
367
  role: "option",
374
368
  "aria-disabled": "true",
375
369
  "aria-selected": false,
376
- className: "text-muted-foreground px-2 py-6 text-center text-sm",
370
+ className: "ui-search-select-placeholder",
377
371
  children: emptyMessage ?? t("dataEntry.searchSelect.empty")
378
372
  }
379
373
  ) : null
@@ -383,7 +377,7 @@ function SearchSelect({
383
377
  renderLoadMore && hasMore ? /* @__PURE__ */ jsx(
384
378
  "div",
385
379
  {
386
- className: "border-border shrink-0 border-t p-1",
380
+ className: "ui-search-select-footer",
387
381
  onKeyDown: (event) => event.stopPropagation(),
388
382
  children: renderLoadMore({
389
383
  hasMore,
@@ -400,17 +394,23 @@ function SearchSelect({
400
394
  ]
401
395
  }
402
396
  ),
403
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 end-2 flex items-center", children: showClear ? /* @__PURE__ */ jsx(
397
+ /* @__PURE__ */ jsx("div", { className: "ui-control-affix", children: showClear ? /* @__PURE__ */ jsx(
404
398
  "button",
405
399
  {
406
400
  type: "button",
407
401
  "aria-label": clearLabel ?? t("dataEntry.searchSelect.clear"),
408
402
  "data-testid": optionTestId("clear"),
409
- className: "pointer-events-auto flex size-6 items-center justify-center rounded-sm opacity-50 hover:opacity-100 focus-visible:opacity-100",
403
+ className: "ui-control-affix-action",
410
404
  onClick: clear,
411
- children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
405
+ children: /* @__PURE__ */ jsx(X, { className: "ui-control-affix-icon", "aria-hidden": "true" })
406
+ }
407
+ ) : /* @__PURE__ */ jsx(
408
+ ChevronsUpDown,
409
+ {
410
+ className: "ui-control-affix-icon ui-control-affix-indicator",
411
+ "aria-hidden": "true"
412
412
  }
413
- ) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" }) })
413
+ ) })
414
414
  ] });
415
415
  }
416
416
  export {