@godxjp/ui 20.0.0 → 20.1.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 (250) hide show
  1. package/README.md +8 -2
  2. package/dist/app/theme-axes.d.ts +2 -0
  3. package/dist/app/theme-axes.js +45 -0
  4. package/dist/components/charts/chart-cartesian.d.ts +2 -1
  5. package/dist/components/charts/chart-cartesian.js +52 -3
  6. package/dist/components/charts/chart-category-axis.d.ts +55 -0
  7. package/dist/components/charts/chart-category-axis.js +93 -0
  8. package/dist/components/charts/chart-frame.d.ts +10 -1
  9. package/dist/components/charts/chart-frame.js +19 -3
  10. package/dist/components/charts/compact-bar-trend.d.ts +1 -1
  11. package/dist/components/charts/compact-bar-trend.js +2 -0
  12. package/dist/components/charts/pie-chart.d.ts +1 -1
  13. package/dist/components/charts/pie-chart.js +12 -1
  14. package/dist/components/charts/recharts-peer.d.ts +49 -0
  15. package/dist/components/charts/recharts-peer.js +45 -0
  16. package/dist/components/data-display/card.d.ts +12 -1
  17. package/dist/components/data-display/card.js +22 -4
  18. package/dist/components/data-display/code-block.js +6 -2
  19. package/dist/components/data-display/data-table.d.ts +39 -4
  20. package/dist/components/data-display/data-table.js +802 -270
  21. package/dist/components/data-display/descriptions.d.ts +27 -6
  22. package/dist/components/data-display/descriptions.js +58 -17
  23. package/dist/components/data-display/index.d.ts +2 -0
  24. package/dist/components/data-display/index.js +2 -0
  25. package/dist/components/data-display/list-row.d.ts +10 -1
  26. package/dist/components/data-display/list-row.js +7 -1
  27. package/dist/components/data-display/popover.d.ts +2 -2
  28. package/dist/components/data-display/popover.js +61 -11
  29. package/dist/components/data-display/range-timeline.d.ts +38 -0
  30. package/dist/components/data-display/range-timeline.js +161 -0
  31. package/dist/components/data-display/scroll-area.js +13 -2
  32. package/dist/components/data-display/service-launcher-card.js +10 -10
  33. package/dist/components/data-display/table.d.ts +11 -3
  34. package/dist/components/data-display/table.js +48 -18
  35. package/dist/components/data-display/tree-list.js +4 -9
  36. package/dist/components/data-entry/calendar.d.ts +2 -2
  37. package/dist/components/data-entry/calendar.js +12 -1
  38. package/dist/components/data-entry/cascader.d.ts +1 -1
  39. package/dist/components/data-entry/cascader.js +188 -24
  40. package/dist/components/data-entry/color-picker.d.ts +1 -1
  41. package/dist/components/data-entry/color-picker.js +17 -5
  42. package/dist/components/data-entry/control-appearance.d.ts +64 -0
  43. package/dist/components/data-entry/control-appearance.js +39 -0
  44. package/dist/components/data-entry/control-surface.d.ts +61 -0
  45. package/dist/components/data-entry/control-surface.js +39 -0
  46. package/dist/components/data-entry/date-picker.d.ts +1 -1
  47. package/dist/components/data-entry/date-picker.js +344 -113
  48. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  49. package/dist/components/data-entry/date-range-picker.js +278 -140
  50. package/dist/components/data-entry/form-field.d.ts +1 -1
  51. package/dist/components/data-entry/form-field.js +39 -4
  52. package/dist/components/data-entry/form.d.ts +4 -0
  53. package/dist/components/data-entry/form.js +4 -2
  54. package/dist/components/data-entry/index.d.ts +7 -3
  55. package/dist/components/data-entry/index.js +10 -1
  56. package/dist/components/data-entry/input-otp.d.ts +1 -1
  57. package/dist/components/data-entry/input.d.ts +12 -34
  58. package/dist/components/data-entry/input.js +92 -24
  59. package/dist/components/data-entry/month-picker.d.ts +1 -1
  60. package/dist/components/data-entry/month-picker.js +47 -10
  61. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  62. package/dist/components/data-entry/month-range-picker.js +51 -11
  63. package/dist/components/data-entry/number-input.d.ts +7 -0
  64. package/dist/components/data-entry/number-input.js +147 -98
  65. package/dist/components/data-entry/password-input.d.ts +1 -1
  66. package/dist/components/data-entry/radio.d.ts +1 -1
  67. package/dist/components/data-entry/radio.js +61 -13
  68. package/dist/components/data-entry/search-input.d.ts +1 -1
  69. package/dist/components/data-entry/search-input.js +16 -2
  70. package/dist/components/data-entry/search-select.d.ts +2 -2
  71. package/dist/components/data-entry/search-select.js +209 -78
  72. package/dist/components/data-entry/select.d.ts +22 -4
  73. package/dist/components/data-entry/select.js +215 -163
  74. package/dist/components/data-entry/slider.d.ts +9 -1
  75. package/dist/components/data-entry/slider.js +87 -9
  76. package/dist/components/data-entry/switch.d.ts +3 -0
  77. package/dist/components/data-entry/switch.js +29 -3
  78. package/dist/components/data-entry/textarea.d.ts +14 -56
  79. package/dist/components/data-entry/textarea.js +80 -33
  80. package/dist/components/data-entry/time-picker.d.ts +2 -2
  81. package/dist/components/data-entry/time-picker.js +333 -109
  82. package/dist/components/data-entry/time-range-picker.d.ts +5 -0
  83. package/dist/components/data-entry/time-range-picker.js +89 -0
  84. package/dist/components/data-entry/transfer.d.ts +1 -1
  85. package/dist/components/data-entry/transfer.js +86 -28
  86. package/dist/components/data-entry/tree-select.d.ts +1 -1
  87. package/dist/components/data-entry/tree-select.js +201 -113
  88. package/dist/components/data-entry/tree-utils.js +7 -14
  89. package/dist/components/data-entry/upload-files.d.ts +2 -0
  90. package/dist/components/data-entry/upload-files.js +31 -0
  91. package/dist/components/data-entry/upload-request.d.ts +4 -0
  92. package/dist/components/data-entry/upload-request.js +53 -0
  93. package/dist/components/data-entry/upload-types.d.ts +28 -0
  94. package/dist/components/data-entry/upload-types.js +2 -0
  95. package/dist/components/data-entry/upload.d.ts +2 -2
  96. package/dist/components/data-entry/upload.js +475 -115
  97. package/dist/components/feedback/dialog.js +16 -19
  98. package/dist/components/general/button.d.ts +1 -1
  99. package/dist/components/general/button.js +5 -1
  100. package/dist/components/general/index.d.ts +1 -0
  101. package/dist/components/general/index.js +2 -0
  102. package/dist/components/general/typography.d.ts +3 -0
  103. package/dist/components/general/typography.js +15 -1
  104. package/dist/components/general/visually-hidden.d.ts +3 -0
  105. package/dist/components/general/visually-hidden.js +9 -0
  106. package/dist/components/layout/app-launcher.d.ts +34 -0
  107. package/dist/components/layout/app-launcher.js +228 -0
  108. package/dist/components/layout/app-shell.d.ts +2 -0
  109. package/dist/components/layout/app-shell.js +20 -7
  110. package/dist/components/layout/breadcrumb.d.ts +14 -2
  111. package/dist/components/layout/breadcrumb.js +46 -4
  112. package/dist/components/layout/flex.d.ts +1 -1
  113. package/dist/components/layout/flex.js +23 -2
  114. package/dist/components/layout/index.d.ts +3 -0
  115. package/dist/components/layout/index.js +5 -1
  116. package/dist/components/layout/org-switcher.js +20 -1
  117. package/dist/components/layout/page-container.d.ts +1 -1
  118. package/dist/components/layout/page-container.js +5 -3
  119. package/dist/components/layout/responsive-grid.d.ts +16 -2
  120. package/dist/components/layout/responsive-grid.js +29 -2
  121. package/dist/components/layout/topbar-item.d.ts +3 -0
  122. package/dist/components/layout/topbar-item.js +18 -3
  123. package/dist/components/layout/topbar.d.ts +1 -1
  124. package/dist/components/layout/topbar.js +27 -6
  125. package/dist/components/navigation/dropdown-menu.d.ts +30 -2
  126. package/dist/components/navigation/dropdown-menu.js +68 -11
  127. package/dist/components/navigation/pagination.d.ts +2 -2
  128. package/dist/components/navigation/pagination.js +155 -83
  129. package/dist/components/navigation/steps.d.ts +2 -2
  130. package/dist/components/navigation/steps.js +29 -4
  131. package/dist/components/navigation/tabs.d.ts +5 -33
  132. package/dist/components/navigation/tabs.js +232 -64
  133. package/dist/components/ui/input-otp.d.ts +26 -28
  134. package/dist/components/ui/input-otp.js +50 -7
  135. package/dist/components/ui/password-input.d.ts +36 -2
  136. package/dist/components/ui/password-input.js +27 -7
  137. package/dist/components/ui/rating.d.ts +25 -0
  138. package/dist/components/ui/rating.js +67 -27
  139. package/dist/components/ui/segmented.d.ts +9 -0
  140. package/dist/components/ui/segmented.js +17 -2
  141. package/dist/components/ui/separator.d.ts +4 -0
  142. package/dist/components/ui/separator.js +13 -0
  143. package/dist/components/ui/tag-input.d.ts +45 -0
  144. package/dist/components/ui/tag-input.js +109 -27
  145. package/dist/form/form-context.d.ts +29 -0
  146. package/dist/form/form-context.js +44 -4
  147. package/dist/form/form-field-array.d.ts +22 -0
  148. package/dist/form/form-field-array.js +50 -0
  149. package/dist/form/form-field-control.d.ts +2 -1
  150. package/dist/form/form-field-control.js +116 -40
  151. package/dist/form/form-root.d.ts +2 -1
  152. package/dist/form/form-root.js +142 -23
  153. package/dist/form/index.d.ts +3 -1
  154. package/dist/form/index.js +12 -1
  155. package/dist/i18n/messages/en.json +56 -5
  156. package/dist/i18n/messages/ja.json +56 -5
  157. package/dist/i18n/messages/vi.json +56 -5
  158. package/dist/inertia/index.d.ts +20 -0
  159. package/dist/inertia/index.js +60 -1
  160. package/dist/lib/control-styles.d.ts +25 -3
  161. package/dist/lib/control-styles.js +9 -3
  162. package/dist/lib/datetime/picker-format.d.ts +8 -0
  163. package/dist/lib/datetime/picker-format.js +54 -0
  164. package/dist/props/components/charts.prop.d.ts +21 -0
  165. package/dist/props/components/data-display.prop.d.ts +55 -4
  166. package/dist/props/components/data-entry.prop.d.ts +687 -43
  167. package/dist/props/components/form.prop.d.ts +142 -4
  168. package/dist/props/components/general.prop.d.ts +17 -1
  169. package/dist/props/components/index.d.ts +2 -2
  170. package/dist/props/components/layout.prop.d.ts +163 -6
  171. package/dist/props/components/navigation.prop.d.ts +113 -3
  172. package/dist/props/registry.d.ts +376 -10
  173. package/dist/props/registry.js +493 -9
  174. package/dist/props/vocabulary/data.prop.d.ts +179 -1
  175. package/dist/props/vocabulary/index.d.ts +4 -4
  176. package/dist/props/vocabulary/interaction.prop.d.ts +51 -2
  177. package/dist/props/vocabulary/navigation.prop.d.ts +40 -0
  178. package/dist/props/vocabulary/shared.prop.d.ts +33 -0
  179. package/dist/styles/badge-layout.css +2 -1
  180. package/dist/styles/card-layout.css +26 -4
  181. package/dist/styles/chart-layout.css +15 -0
  182. package/dist/styles/control.css +515 -14
  183. package/dist/styles/data-display-layout.css +241 -6
  184. package/dist/styles/data-entry-layout.css +13 -0
  185. package/dist/styles/layout.css +221 -4
  186. package/dist/styles/navigation-layout.css +227 -0
  187. package/dist/styles/shell-layout.css +229 -5
  188. package/dist/styles/table-layout.css +95 -6
  189. package/dist/styles/text-layout.css +5 -0
  190. package/dist/tokens/base.css +1 -0
  191. package/dist/tokens/components/badge.css +1 -0
  192. package/dist/tokens/components/chart.css +6 -0
  193. package/dist/tokens/components/control.css +79 -2
  194. package/dist/tokens/components/data-display.css +9 -0
  195. package/dist/tokens/components/descriptions.css +11 -0
  196. package/dist/tokens/components/flex.css +8 -0
  197. package/dist/tokens/components/navigation.css +55 -0
  198. package/dist/tokens/components/shell.css +50 -2
  199. package/dist/tokens/components/table.css +14 -0
  200. package/dist/tokens/semantic/layout.css +9 -0
  201. package/docs/CONSUMER-RULES.md +13 -0
  202. package/docs/DESIGN-AUTHORITY.md +43 -6
  203. package/docs/DEVELOPMENT.md +4 -3
  204. package/docs/FORMS.md +151 -6
  205. package/docs/FRAME-COVERAGE-REPORT.md +29 -16
  206. package/docs/README.md +1 -1
  207. package/docs/STANDARDS-vocabulary-tokens.md +1 -1
  208. package/docs/TESTING.md +15 -6
  209. package/docs/charts/cjk-category-axis.tsx +81 -0
  210. package/docs/data-display/card/index.tsx +22 -0
  211. package/docs/data-display/data-table/examples/antd-parity.tsx +257 -0
  212. package/docs/data-display/data-table/index.tsx +23 -0
  213. package/docs/data-display/descriptions.tsx +41 -0
  214. package/docs/data-display/scroll-area.tsx +31 -25
  215. package/docs/data-display/table.tsx +104 -45
  216. package/docs/data-display/timeline.tsx +41 -0
  217. package/docs/data-entry/date-picker.tsx +85 -0
  218. package/docs/data-entry/date-range-picker.tsx +26 -0
  219. package/docs/data-entry/form-dynamic-fields.tsx +199 -0
  220. package/docs/data-entry/form.tsx +74 -4
  221. package/docs/data-entry/input-otp.tsx +24 -0
  222. package/docs/data-entry/input.tsx +46 -1
  223. package/docs/data-entry/month-range-picker.tsx +1 -1
  224. package/docs/data-entry/select.tsx +27 -0
  225. package/docs/data-entry/switch.tsx +41 -0
  226. package/docs/data-entry/textarea.tsx +38 -0
  227. package/docs/data-entry/time-picker.tsx +85 -1
  228. package/docs/data-entry/time-range-picker.tsx +55 -0
  229. package/docs/data-entry/transfer.tsx +17 -0
  230. package/docs/data-entry/upload.tsx +56 -12
  231. package/docs/feedback/tooltip.tsx +1 -1
  232. package/docs/general/activity.tsx +2 -2
  233. package/docs/general/button/index.tsx +14 -1
  234. package/docs/general/typography.tsx +14 -1
  235. package/docs/layout/app-launcher.tsx +151 -0
  236. package/docs/layout/app-shell.tsx +11 -0
  237. package/docs/layout/flex.tsx +50 -0
  238. package/docs/layout/responsive-grid.tsx +21 -1
  239. package/docs/layout/topbar.tsx +5 -9
  240. package/docs/navigation/app-setting-picker.tsx +11 -0
  241. package/docs/navigation/breadcrumb.tsx +48 -0
  242. package/docs/navigation/dropdown-menu.tsx +21 -0
  243. package/docs/navigation/pagination.tsx +52 -0
  244. package/docs/navigation/steps.tsx +37 -0
  245. package/docs/navigation/tabs.tsx +97 -1
  246. package/docs/query/button-refetch.tsx +1 -0
  247. package/package.json +19 -2
  248. package/scripts/_agent-setup.mjs +19 -2
  249. package/scripts/init-guinea-pig.mjs +26 -4
  250. package/scripts/ui-audit.mjs +243 -30
@@ -33,7 +33,13 @@ import { DatePicker } from "./date-picker.js";
33
33
  import { DateRangePicker } from "./date-range-picker.js";
34
34
  import { TimePicker } from "./time-picker.js";
35
35
  import { ColorPicker } from "./color-picker.js";
36
- import { Upload, collectUploadCommitActions, createUploadItem, useUploadDraft } from "./upload.js";
36
+ import {
37
+ UPLOAD_LIST_IGNORE,
38
+ Upload,
39
+ collectUploadCommitActions,
40
+ createUploadItem,
41
+ useUploadDraft
42
+ } from "./upload.js";
37
43
  import { Cascader } from "./cascader.js";
38
44
  import { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select.js";
39
45
  import { Transfer } from "./transfer.js";
@@ -53,6 +59,7 @@ import { Rating } from "./rating.js";
53
59
  import { TagInput } from "./tag-input.js";
54
60
  import { Segmented } from "./segmented.js";
55
61
  import { BranchScopePicker } from "./branch-scope-picker.js";
62
+ import { TimeRangePicker } from "./time-range-picker.js";
56
63
  export {
57
64
  BranchScopePicker,
58
65
  Calendar,
@@ -110,11 +117,13 @@ export {
110
117
  TagInput,
111
118
  Textarea,
112
119
  TimePicker,
120
+ TimeRangePicker,
113
121
  Toggle,
114
122
  ToggleGroup,
115
123
  ToggleGroupItem,
116
124
  Transfer,
117
125
  TreeSelect,
126
+ UPLOAD_LIST_IGNORE,
118
127
  Upload,
119
128
  collectUploadCommitActions,
120
129
  createUploadItem,
@@ -1,2 +1,2 @@
1
1
  export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "../ui/input-otp.js";
2
- export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, } from "../ui/input-otp.js";
2
+ export type { InputOTPAlignProp, InputOTPMaskProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, InputOTPProp, InputOTPProps, } from "../ui/input-otp.js";
@@ -1,39 +1,17 @@
1
1
  import * as React from "react";
2
- export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
3
- /** Control height tier: `md` (default) or `sm`, the same tiers as SelectTrigger and NumberInput. */
4
- size?: "sm" | "md";
5
- /** Show an inline that clears the field while it holds text (default false). */
6
- allowClear?: boolean;
7
- /** Called after the field is cleared via the inline ✕. */
2
+ export type { InputProp, InputProp as InputProps } from "../../props/components/data-entry.prop.js";
3
+ export declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "prefix" | "size"> & {
4
+ onValueChange?: (value: string) => void;
5
+ size?: "sm" | "md" | "lg";
6
+ status?: import("../../props/index.js").ControlStatusProp;
7
+ variant?: import("../../props/index.js").ControlVariantProp;
8
+ allowClear?: import("../../props/index.js").AllowClearProp;
8
9
  onClear?: () => void;
9
- /**
10
- * A leading affordance pinned inside the start of the field — e.g. a mail or search icon (the
11
- * common auth/search pattern). Decorative by default; the input keeps the keyboard focus.
12
- */
13
10
  leadingIcon?: React.ReactNode;
14
- /**
15
- * A trailing affordance pinned inside the field — e.g. a calendar / clock popover trigger
16
- * button. ONE trailing icon shows at a time: when `allowClear` and the field holds a value the
17
- * clear ✕ REPLACES this icon; otherwise this icon shows.
18
- */
19
- trailingIcon?: React.ReactNode;
20
- };
21
- export declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
22
- /** Control height tier: `md` (default) or `sm`, the same tiers as SelectTrigger and NumberInput. */
23
- size?: "sm" | "md";
24
- /** Show an inline ✕ that clears the field while it holds text (default false). */
25
- allowClear?: boolean;
26
- /** Called after the field is cleared via the inline ✕. */
27
- onClear?: () => void;
28
- /**
29
- * A leading affordance pinned inside the start of the field — e.g. a mail or search icon (the
30
- * common auth/search pattern). Decorative by default; the input keeps the keyboard focus.
31
- */
32
- leadingIcon?: React.ReactNode;
33
- /**
34
- * A trailing affordance pinned inside the field — e.g. a calendar / clock popover trigger
35
- * button. ONE trailing icon shows at a time: when `allowClear` and the field holds a value the
36
- * clear ✕ REPLACES this icon; otherwise this icon shows.
37
- */
38
11
  trailingIcon?: React.ReactNode;
12
+ prefix?: React.ReactNode;
13
+ suffix?: React.ReactNode;
14
+ addonBefore?: React.ReactNode;
15
+ addonAfter?: React.ReactNode;
16
+ count?: import("../../props/components/data-entry.prop.js").ControlCountProp;
39
17
  } & React.RefAttributes<HTMLInputElement>>;
@@ -5,24 +5,40 @@ import { X } from "lucide-react";
5
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { useFieldIdentity, useFieldNameFallback } from "../../lib/field-a11y.js";
7
7
  import { cn } from "../../lib/utils.js";
8
+ import {
9
+ CONTROL_STATUS_CHROME_CLASS,
10
+ CONTROL_VARIANT_CHROME_CLASS,
11
+ controlAppearanceAttributes,
12
+ resolveControlCount
13
+ } from "./control-appearance.js";
14
+ import { resolveAllowClear } from "./control-surface.js";
8
15
  const inputBaseClass = [
9
- "ui-control ui-input border-input bg-background w-full rounded-[var(--control-radius)] transition-[color,box-shadow] outline-none",
16
+ "ui-control ui-input w-full rounded-[var(--control-radius)] transition-[color,box-shadow] outline-none",
10
17
  "selection:bg-primary selection:text-primary-foreground",
11
18
  "placeholder:text-muted-foreground",
12
- "aria-invalid:border-destructive"
19
+ "aria-invalid:border-destructive",
20
+ CONTROL_STATUS_CHROME_CLASS
13
21
  ];
14
22
  const Input = React.forwardRef(
15
23
  ({
16
24
  size,
25
+ status,
26
+ variant = "outlined",
17
27
  className,
18
28
  type,
19
- allowClear = false,
29
+ allowClear,
20
30
  onClear,
21
31
  leadingIcon,
22
32
  trailingIcon,
33
+ prefix,
34
+ suffix,
35
+ addonBefore,
36
+ addonAfter,
37
+ count,
23
38
  value,
24
39
  defaultValue,
25
40
  onChange,
41
+ onValueChange,
26
42
  ...props
27
43
  }, ref) => {
28
44
  const { t } = useTranslation();
@@ -35,6 +51,11 @@ const Input = React.forwardRef(
35
51
  name: props.name,
36
52
  "data-field": props["data-field"]
37
53
  });
54
+ const appearance = controlAppearanceAttributes({
55
+ status,
56
+ variant,
57
+ "aria-invalid": props["aria-invalid"]
58
+ });
38
59
  const innerRef = React.useRef(null);
39
60
  const setRefs = React.useCallback(
40
61
  (node) => {
@@ -44,15 +65,15 @@ const Input = React.forwardRef(
44
65
  },
45
66
  [ref]
46
67
  );
47
- const [hasText, setHasText] = React.useState(
48
- () => String(value ?? defaultValue ?? "").length > 0
49
- );
68
+ const [text, setText] = React.useState(() => String(value ?? defaultValue ?? ""));
69
+ const hasText = text.length > 0;
50
70
  React.useEffect(() => {
51
- if (value !== void 0) setHasText(String(value).length > 0);
71
+ if (value !== void 0) setText(String(value));
52
72
  }, [value]);
53
73
  const handleChange = (event) => {
54
- if (value === void 0) setHasText(event.target.value.length > 0);
74
+ if (value === void 0) setText(event.target.value);
55
75
  onChange?.(event);
76
+ if (onChange !== onValueChange) onValueChange?.(event.target.value);
56
77
  };
57
78
  const clear = () => {
58
79
  const el = innerRef.current;
@@ -65,11 +86,17 @@ const Input = React.forwardRef(
65
86
  el.dispatchEvent(new Event("input", { bubbles: true }));
66
87
  el.focus();
67
88
  }
68
- setHasText(false);
89
+ setText("");
69
90
  onClear?.();
70
91
  };
71
- if (!allowClear && trailingIcon == null && leadingIcon == null) {
72
- return /* @__PURE__ */ jsx(
92
+ const clearControl = resolveAllowClear(allowClear, false, t("common.clear") ?? "Clear");
93
+ const chrome = CONTROL_VARIANT_CHROME_CLASS[variant];
94
+ const counter = resolveControlCount(count, text);
95
+ const leading = prefix ?? leadingIcon;
96
+ const leadingIsDecorative = prefix == null;
97
+ const hasAddon = addonBefore != null || addonAfter != null;
98
+ if (!clearControl.enabled && leading == null && trailingIcon == null && suffix == null && !counter) {
99
+ const bare = /* @__PURE__ */ jsx(
73
100
  "input",
74
101
  {
75
102
  type,
@@ -78,28 +105,38 @@ const Input = React.forwardRef(
78
105
  ref: setRefs,
79
106
  value,
80
107
  defaultValue,
81
- onChange,
82
- className: cn(inputBaseClass, className),
108
+ onChange: handleChange,
109
+ className: cn(inputBaseClass, chrome, className),
83
110
  ...props,
84
111
  ...nameFallback,
85
- ...identity
112
+ ...identity,
113
+ ...appearance
86
114
  }
87
115
  );
116
+ return hasAddon ? withAddons(bare, addonBefore, addonAfter, size, appearance) : bare;
88
117
  }
89
- const showClear = allowClear && hasText && !props.disabled && !props.readOnly;
118
+ const showClear = clearControl.enabled && hasText && !props.disabled && !props.readOnly;
90
119
  const trailing = showClear ? /* @__PURE__ */ jsx(
91
120
  "button",
92
121
  {
93
122
  type: "button",
94
123
  tabIndex: -1,
95
- "aria-label": t("common.clear") ?? "Clear",
124
+ "aria-label": clearControl.label,
96
125
  onClick: clear,
97
126
  className: "ui-control-inline-affix-action",
98
- children: /* @__PURE__ */ jsx(X, { className: "ui-control-inline-affix-icon", "aria-hidden": "true" })
127
+ children: clearControl.clearIcon ?? /* @__PURE__ */ jsx(X, { className: "ui-control-inline-affix-icon", "aria-hidden": "true" })
99
128
  }
100
- ) : trailingIcon;
101
- return /* @__PURE__ */ jsxs("span", { "data-slot": "input-affix-wrapper", className: "ui-input-affix-wrapper", children: [
102
- leadingIcon != null ? /* @__PURE__ */ jsx("span", { "data-slot": "input-leading", "aria-hidden": "true", className: "ui-input-leading", children: leadingIcon }) : null,
129
+ ) : suffix ?? trailingIcon;
130
+ const affixed = /* @__PURE__ */ jsxs("span", { "data-slot": "input-affix-wrapper", className: "ui-input-affix-wrapper", children: [
131
+ leading != null ? /* @__PURE__ */ jsx(
132
+ "span",
133
+ {
134
+ "data-slot": "input-leading",
135
+ "aria-hidden": leadingIsDecorative ? "true" : void 0,
136
+ className: "ui-input-leading",
137
+ children: leading
138
+ }
139
+ ) : null,
103
140
  /* @__PURE__ */ jsx(
104
141
  "input",
105
142
  {
@@ -112,20 +149,51 @@ const Input = React.forwardRef(
112
149
  onChange: handleChange,
113
150
  className: cn(
114
151
  inputBaseClass,
115
- leadingIcon != null && "ui-input--leading-affix",
116
- (showClear || trailingIcon != null) && "ui-input--trailing-affix",
152
+ chrome,
153
+ leading != null && "ui-input--leading-affix",
154
+ (trailing != null || counter !== null) && "ui-input--trailing-affix",
117
155
  className
118
156
  ),
119
157
  ...props,
120
158
  ...nameFallback,
121
- ...identity
159
+ ...identity,
160
+ ...appearance
122
161
  }
123
162
  ),
124
- trailing != null ? /* @__PURE__ */ jsx("span", { className: "ui-input-trailing", children: trailing }) : null
163
+ trailing != null || counter !== null ? /* @__PURE__ */ jsxs("span", { className: "ui-input-trailing", children: [
164
+ counter ? /* @__PURE__ */ jsx(
165
+ "span",
166
+ {
167
+ "data-slot": "input-count",
168
+ "data-exceeded": counter.exceeded ? "true" : void 0,
169
+ className: "ui-control-count",
170
+ "aria-hidden": "true",
171
+ children: counter.content
172
+ }
173
+ ) : null,
174
+ trailing
175
+ ] }) : null
125
176
  ] });
177
+ return hasAddon ? withAddons(affixed, addonBefore, addonAfter, size, appearance) : affixed;
126
178
  }
127
179
  );
128
180
  Input.displayName = "Input";
181
+ function withAddons(field, addonBefore, addonAfter, size, appearance) {
182
+ return /* @__PURE__ */ jsxs(
183
+ "span",
184
+ {
185
+ "data-slot": "input-group",
186
+ "data-size": size,
187
+ "data-status": appearance["data-status"],
188
+ className: "ui-input-group",
189
+ children: [
190
+ addonBefore != null ? /* @__PURE__ */ jsx("span", { "data-slot": "input-addon-before", className: "ui-input-addon", children: addonBefore }) : null,
191
+ field,
192
+ addonAfter != null ? /* @__PURE__ */ jsx("span", { "data-slot": "input-addon-after", className: "ui-input-addon", children: addonAfter }) : null
193
+ ]
194
+ }
195
+ );
196
+ }
129
197
  export {
130
198
  Input
131
199
  };
@@ -7,4 +7,4 @@ export type { MonthPickerProp, MonthPickerProp as MonthPickerProps, } from "../.
7
7
  * The field stays a real, typeable input (form-submittable via `name` as
8
8
  * `yyyy-MM`); the grid is the visual affordance.
9
9
  */
10
- export declare function MonthPicker({ value: valueProp, defaultValue, onValueChange, placeholder, disabled, className, id, name, fromYear, toYear, allowClear, ...ariaProps }: MonthPickerProp): React.JSX.Element;
10
+ export declare function MonthPicker({ value: valueProp, defaultValue, onValueChange, placeholder, disabled, className, id, name, fromYear, toYear, allowClear, open: openProp, defaultOpen, onOpenChange, status, variant, size, inputReadOnly, preserveInvalidOnBlur, placement, renderExtraFooter, ref, ...ariaProps }: MonthPickerProp): React.JSX.Element;
@@ -6,6 +6,8 @@ import { usePickerLocales, useTranslation } from "../../i18n/use-translation.js"
6
6
  import { useControlledLatch } from "../../lib/hooks.js";
7
7
  import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
8
8
  import { cn } from "../../lib/utils.js";
9
+ import { resolveAllowClear, resolveAriaInvalid } from "./control-surface.js";
10
+ import { CONTROL_STATUS_CHROME_CLASS, CONTROL_VARIANT_CHROME_CLASS } from "./control-appearance.js";
9
11
  import { Button } from "../general/button.js";
10
12
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
11
13
  const YM_HINT = "yyyy/mm";
@@ -28,12 +30,33 @@ function MonthPicker({
28
30
  name,
29
31
  fromYear,
30
32
  toYear,
31
- allowClear = true,
33
+ allowClear,
34
+ open: openProp,
35
+ defaultOpen = false,
36
+ onOpenChange,
37
+ status,
38
+ variant,
39
+ size,
40
+ inputReadOnly,
41
+ preserveInvalidOnBlur,
42
+ placement = "bottom-start",
43
+ renderExtraFooter,
44
+ ref,
32
45
  ...ariaProps
33
46
  }) {
34
47
  const { t } = useTranslation();
35
48
  const { locale } = usePickerLocales();
36
- const [open, setOpen] = React.useState(false);
49
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
50
+ const isOpenControlled = openProp !== void 0;
51
+ const open = isOpenControlled ? openProp : internalOpen;
52
+ const setOpen = React.useCallback(
53
+ (next) => {
54
+ if (next && inputReadOnly) return;
55
+ if (!isOpenControlled) setInternalOpen(next);
56
+ onOpenChange?.(next);
57
+ },
58
+ [isOpenControlled, onOpenChange, inputReadOnly]
59
+ );
37
60
  const autoId = React.useId();
38
61
  const inputId = id ?? autoId;
39
62
  const dialogId = `${inputId}-dialog`;
@@ -56,7 +79,8 @@ function MonthPicker({
56
79
  if (!isControlled) setInternalValue(next);
57
80
  onValueChange?.(next);
58
81
  };
59
- const showClear = allowClear && Boolean(value) && !disabled;
82
+ const clearControl = resolveAllowClear(allowClear, true, t("common.clear") ?? "Clear");
83
+ const showClear = clearControl.enabled && Boolean(value) && !disabled && !inputReadOnly;
60
84
  const prevDisabled = fromYear !== void 0 && viewYear <= fromYear;
61
85
  const nextDisabled = toYear !== void 0 && viewYear >= toYear;
62
86
  return /* @__PURE__ */ jsx(Popover, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
@@ -64,9 +88,14 @@ function MonthPicker({
64
88
  {
65
89
  "data-disabled": disabled ? "" : void 0,
66
90
  "data-state": open ? "open" : "closed",
91
+ "data-size": size,
92
+ "data-status": status,
93
+ "data-variant": variant,
67
94
  className: cn(
68
95
  "ui-control ui-control-composite-field",
69
96
  "aria-invalid:border-destructive",
97
+ CONTROL_VARIANT_CHROME_CLASS[variant ?? "outlined"],
98
+ CONTROL_STATUS_CHROME_CLASS,
70
99
  className
71
100
  ),
72
101
  onClick: () => {
@@ -76,11 +105,13 @@ function MonthPicker({
76
105
  /* @__PURE__ */ jsx(
77
106
  "input",
78
107
  {
108
+ ref,
79
109
  id: inputId,
80
110
  name: name ?? identity.name,
81
111
  "data-field": fieldA11y["data-field"] ?? identity["data-field"],
82
112
  value: text,
83
113
  disabled,
114
+ readOnly: inputReadOnly,
84
115
  placeholder: placeholder ?? t("dataEntry.monthPicker.placeholder") ?? YM_HINT,
85
116
  inputMode: "numeric",
86
117
  autoComplete: "off",
@@ -88,7 +119,9 @@ function MonthPicker({
88
119
  "aria-expanded": open,
89
120
  "aria-haspopup": "dialog",
90
121
  "aria-controls": open ? dialogId : void 0,
122
+ "aria-readonly": inputReadOnly || void 0,
91
123
  ...fieldA11y,
124
+ "aria-invalid": resolveAriaInvalid(fieldA11y["aria-invalid"], status),
92
125
  className: "ui-month-picker-input",
93
126
  onKeyDown: (event) => {
94
127
  if (event.key === "ArrowDown") {
@@ -104,7 +137,9 @@ function MonthPicker({
104
137
  if (parsed) emit(parsed);
105
138
  else if (event.target.value.trim() === "") emit(void 0);
106
139
  },
107
- onBlur: () => setText(toYmText(value))
140
+ onBlur: () => {
141
+ if (!preserveInvalidOnBlur) setText(toYmText(value));
142
+ }
108
143
  }
109
144
  ),
110
145
  showClear ? /* @__PURE__ */ jsx(
@@ -112,17 +147,17 @@ function MonthPicker({
112
147
  {
113
148
  type: "button",
114
149
  tabIndex: -1,
115
- "aria-label": t("common.clear") ?? "Clear",
150
+ "aria-label": clearControl.label,
116
151
  className: "text-muted-foreground hover:text-foreground shrink-0",
117
152
  onClick: (event) => {
118
153
  event.stopPropagation();
154
+ event.currentTarget.closest("div")?.querySelector("input:not([type=hidden])")?.focus();
119
155
  emit(void 0);
120
156
  setText("");
121
157
  },
122
- children: /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
158
+ children: clearControl.clearIcon ?? /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
123
159
  }
124
- ) : null,
125
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
160
+ ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
126
161
  "button",
127
162
  {
128
163
  type: "button",
@@ -140,7 +175,8 @@ function MonthPicker({
140
175
  role: "dialog",
141
176
  "aria-label": t("dataEntry.monthPicker.openGrid") ?? "Month grid",
142
177
  className: "ui-month-picker-panel",
143
- align: "start",
178
+ side: placement.startsWith("top") ? "top" : "bottom",
179
+ align: placement.endsWith("end") ? "end" : "start",
144
180
  onOpenAutoFocus: (event) => event.preventDefault(),
145
181
  onClick: (event) => event.stopPropagation(),
146
182
  children: [
@@ -191,7 +227,8 @@ function MonthPicker({
191
227
  },
192
228
  label
193
229
  );
194
- }) })
230
+ }) }),
231
+ renderExtraFooter?.()
195
232
  ]
196
233
  }
197
234
  )
@@ -7,4 +7,4 @@ export type { MonthRangePickerProp, MonthRangePickerProp as MonthRangePickerProp
7
7
  * `yyyy/MM` inputs (form-submittable via `${name}_from` / `${name}_to`); the Ant-style month grid
8
8
  * is the visual affordance.
9
9
  */
10
- export declare function MonthRangePicker({ value: valueProp, defaultValue, onValueChange, placeholder, disabled, className, id, name, fromYear, toYear, allowClear, ...ariaProps }: MonthRangePickerProp): React.JSX.Element;
10
+ export declare function MonthRangePicker({ value: valueProp, defaultValue, onValueChange, placeholder, disabled, className, id, name, fromYear, toYear, allowClear, open: openProp, defaultOpen, onOpenChange, status, variant, size, inputReadOnly, preserveInvalidOnBlur, placement, renderExtraFooter, ref, ...ariaProps }: MonthRangePickerProp): React.JSX.Element;
@@ -6,6 +6,8 @@ import { usePickerLocales, useTranslation } from "../../i18n/use-translation.js"
6
6
  import { useControlledLatch } from "../../lib/hooks.js";
7
7
  import { pickGroupFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
8
8
  import { cn } from "../../lib/utils.js";
9
+ import { resolveAllowClear } from "./control-surface.js";
10
+ import { CONTROL_STATUS_CHROME_CLASS, CONTROL_VARIANT_CHROME_CLASS } from "./control-appearance.js";
9
11
  import { Button } from "../general/button.js";
10
12
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
11
13
  const YM_HINT = "yyyy/mm";
@@ -29,12 +31,33 @@ function MonthRangePicker({
29
31
  name,
30
32
  fromYear,
31
33
  toYear,
32
- allowClear = true,
34
+ allowClear,
35
+ open: openProp,
36
+ defaultOpen = false,
37
+ onOpenChange,
38
+ status,
39
+ variant,
40
+ size,
41
+ inputReadOnly,
42
+ preserveInvalidOnBlur,
43
+ placement = "bottom-start",
44
+ renderExtraFooter,
45
+ ref,
33
46
  ...ariaProps
34
47
  }) {
35
48
  const { t } = useTranslation();
36
49
  const { locale } = usePickerLocales();
37
- const [open, setOpen] = React.useState(false);
50
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
51
+ const isOpenControlled = openProp !== void 0;
52
+ const open = isOpenControlled ? openProp : internalOpen;
53
+ const setOpen = React.useCallback(
54
+ (next) => {
55
+ if (next && inputReadOnly) return;
56
+ if (!isOpenControlled) setInternalOpen(next);
57
+ onOpenChange?.(next);
58
+ },
59
+ [isOpenControlled, onOpenChange, inputReadOnly]
60
+ );
38
61
  const groupA11y = pickGroupFieldA11y(ariaProps);
39
62
  const autoId = React.useId();
40
63
  const groupId = id ?? autoId;
@@ -62,7 +85,8 @@ function MonthRangePicker({
62
85
  if (!isControlled) setInternalValue(next);
63
86
  onValueChange?.(next);
64
87
  };
65
- const showClear = allowClear && Boolean(value?.from || value?.to) && !disabled;
88
+ const clearControl = resolveAllowClear(allowClear, true, t("common.clear") ?? "Clear");
89
+ const showClear = clearControl.enabled && Boolean(value?.from || value?.to) && !disabled && !inputReadOnly;
66
90
  const prevDisabled = fromYear !== void 0 && viewYear <= fromYear;
67
91
  const nextDisabled = toYear !== void 0 && viewYear >= toYear;
68
92
  const clear = () => {
@@ -114,9 +138,14 @@ function MonthRangePicker({
114
138
  "aria-disabled": disabled ? true : void 0,
115
139
  "data-disabled": disabled ? "" : void 0,
116
140
  "data-state": open ? "open" : "closed",
141
+ "data-size": size,
142
+ "data-status": status,
143
+ "data-variant": variant,
117
144
  className: cn(
118
145
  "ui-control ui-control-composite-field",
119
146
  "aria-invalid:border-destructive",
147
+ CONTROL_VARIANT_CHROME_CLASS[variant ?? "outlined"],
148
+ CONTROL_STATUS_CHROME_CLASS,
120
149
  className
121
150
  ),
122
151
  onClick: () => {
@@ -126,11 +155,14 @@ function MonthRangePicker({
126
155
  /* @__PURE__ */ jsx(
127
156
  "input",
128
157
  {
158
+ ref,
129
159
  id: fromId,
130
160
  "data-field": rangeField ? `${rangeField}_from` : void 0,
131
161
  name: rangeName ? `${rangeName}_from` : void 0,
132
162
  value: fromText,
133
163
  disabled,
164
+ readOnly: inputReadOnly,
165
+ "aria-readonly": inputReadOnly || void 0,
134
166
  placeholder: resolvedPlaceholder,
135
167
  inputMode: "numeric",
136
168
  autoComplete: "off",
@@ -141,7 +173,9 @@ function MonthRangePicker({
141
173
  setFromText(event.target.value);
142
174
  commitEdge("from", event.target.value);
143
175
  },
144
- onBlur: () => setFromText(toYmText(value?.from))
176
+ onBlur: () => {
177
+ if (!preserveInvalidOnBlur) setFromText(toYmText(value?.from));
178
+ }
145
179
  }
146
180
  ),
147
181
  /* @__PURE__ */ jsx(ArrowRight, { className: "ui-month-picker-separator-icon", "aria-hidden": "true" }),
@@ -153,6 +187,8 @@ function MonthRangePicker({
153
187
  name: rangeName ? `${rangeName}_to` : void 0,
154
188
  value: toText,
155
189
  disabled,
190
+ readOnly: inputReadOnly,
191
+ "aria-readonly": inputReadOnly || void 0,
156
192
  placeholder: resolvedPlaceholder,
157
193
  inputMode: "numeric",
158
194
  autoComplete: "off",
@@ -163,7 +199,9 @@ function MonthRangePicker({
163
199
  setToText(event.target.value);
164
200
  commitEdge("to", event.target.value);
165
201
  },
166
- onBlur: () => setToText(toYmText(value?.to))
202
+ onBlur: () => {
203
+ if (!preserveInvalidOnBlur) setToText(toYmText(value?.to));
204
+ }
167
205
  }
168
206
  ),
169
207
  showClear ? /* @__PURE__ */ jsx(
@@ -171,16 +209,16 @@ function MonthRangePicker({
171
209
  {
172
210
  type: "button",
173
211
  tabIndex: -1,
174
- "aria-label": t("common.clear") ?? "Clear",
212
+ "aria-label": clearControl.label,
175
213
  className: "text-muted-foreground hover:text-foreground shrink-0",
176
214
  onClick: (event) => {
177
215
  event.stopPropagation();
216
+ event.currentTarget.closest("div")?.querySelector("input:not([type=hidden])")?.focus();
178
217
  clear();
179
218
  },
180
- children: /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
219
+ children: clearControl.clearIcon ?? /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
181
220
  }
182
- ) : null,
183
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
221
+ ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
184
222
  "button",
185
223
  {
186
224
  type: "button",
@@ -195,7 +233,8 @@ function MonthRangePicker({
195
233
  PopoverContent,
196
234
  {
197
235
  className: "ui-month-picker-panel",
198
- align: "start",
236
+ side: placement.startsWith("top") ? "top" : "bottom",
237
+ align: placement.endsWith("end") ? "end" : "start",
199
238
  onOpenAutoFocus: (event) => event.preventDefault(),
200
239
  onClick: (event) => event.stopPropagation(),
201
240
  children: [
@@ -248,7 +287,8 @@ function MonthRangePicker({
248
287
  },
249
288
  label
250
289
  );
251
- }) })
290
+ }) }),
291
+ renderExtraFooter?.()
252
292
  ]
253
293
  }
254
294
  )
@@ -12,6 +12,13 @@ export declare const NumberInput: React.ForwardRefExoticComponent<import("../../
12
12
  max?: number;
13
13
  step?: number;
14
14
  precision?: number;
15
+ formatter?: (value: number | null) => string;
16
+ parser?: (display: string) => number | null;
17
+ keyboard?: boolean;
18
+ changeOnWheel?: boolean;
19
+ controls?: boolean;
20
+ status?: import("../../props/index.js").ControlStatusProp;
21
+ variant?: import("../../props/index.js").ControlVariantProp;
15
22
  disabled?: import("../../props/index.js").DisabledProp;
16
23
  readOnly?: boolean;
17
24
  size?: import("../../props/index.js").SizeProp;