@godxjp/ui 5.0.1 → 6.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 (298) hide show
  1. package/README.md +101 -142
  2. package/package.json +124 -128
  3. package/scripts/ui-audit.mjs +179 -0
  4. package/src/app/__tests__/app-provider.test.tsx +232 -0
  5. package/src/app/__tests__/date-format-labels.test.ts +36 -0
  6. package/src/app/__tests__/date-formats.test.ts +44 -0
  7. package/src/app/__tests__/timezones.test.ts +65 -0
  8. package/src/app/app-provider.tsx +227 -0
  9. package/src/app/date-format-labels.ts +21 -0
  10. package/src/app/date-formats.ts +30 -0
  11. package/src/app/index.ts +40 -0
  12. package/src/app/locales.ts +32 -0
  13. package/src/app/request-headers.ts +31 -0
  14. package/src/app/storage.ts +44 -0
  15. package/src/app/time-format-labels.ts +19 -0
  16. package/src/app/time-formats.ts +15 -0
  17. package/src/app/timezones.ts +208 -0
  18. package/src/app/types.ts +39 -0
  19. package/src/app/use-formatting.ts +47 -0
  20. package/src/components/__tests__/accessibility-primitives.test.tsx +65 -0
  21. package/src/components/__tests__/docs-parity.test.ts +41 -0
  22. package/src/components/__tests__/shadcn-release-guardrails.test.ts +71 -0
  23. package/src/components/__tests__/theme-axes-integration.test.tsx +242 -0
  24. package/src/components/admin/index.ts +76 -0
  25. package/src/components/data-display/__tests__/card-table.test.tsx +328 -0
  26. package/src/components/data-display/__tests__/data-display.test.tsx +73 -0
  27. package/src/components/data-display/__tests__/data-table.test.tsx +84 -0
  28. package/src/components/data-display/__tests__/popover.test.tsx +92 -0
  29. package/src/components/data-display/__tests__/scroll-area-collapsible.test.tsx +66 -0
  30. package/src/components/data-display/badge.tsx +27 -0
  31. package/src/components/data-display/card.tsx +194 -0
  32. package/src/components/data-display/code-badge.tsx +28 -0
  33. package/src/components/data-display/collapsible.tsx +5 -0
  34. package/src/components/data-display/data-table.tsx +476 -0
  35. package/src/components/data-display/empty-state.tsx +22 -0
  36. package/src/components/data-display/index.ts +41 -0
  37. package/src/components/data-display/key-value-grid.tsx +46 -0
  38. package/src/components/data-display/popover.tsx +62 -0
  39. package/src/components/data-display/progress-meter.tsx +20 -0
  40. package/src/components/data-display/scan-panel.tsx +16 -0
  41. package/src/components/data-display/scroll-area.tsx +42 -0
  42. package/src/components/data-display/status-badge.tsx +83 -0
  43. package/src/components/data-display/table.tsx +59 -0
  44. package/src/components/data-display/timeline.tsx +42 -0
  45. package/src/components/data-display/tree-list.tsx +42 -0
  46. package/src/components/data-entry/__fixtures__/tree-options.ts +80 -0
  47. package/src/components/data-entry/__tests__/cascader-tree-transfer.test.tsx +417 -0
  48. package/src/components/data-entry/__tests__/checkbox-group.test.tsx +40 -0
  49. package/src/components/data-entry/__tests__/checkbox.test.tsx +20 -0
  50. package/src/components/data-entry/__tests__/date-autocomplete.test.tsx +94 -0
  51. package/src/components/data-entry/__tests__/form-field.test.tsx +49 -0
  52. package/src/components/data-entry/__tests__/input-textarea.test.tsx +38 -0
  53. package/src/components/data-entry/__tests__/label-select.test.tsx +62 -0
  54. package/src/components/data-entry/__tests__/pickers.test.tsx +74 -0
  55. package/src/components/data-entry/__tests__/radio.test.tsx +46 -0
  56. package/src/components/data-entry/__tests__/search-input.test.tsx +32 -0
  57. package/src/components/data-entry/__tests__/switch-field.test.tsx +52 -0
  58. package/src/components/data-entry/__tests__/upload.test.tsx +125 -0
  59. package/src/components/data-entry/autocomplete.tsx +91 -0
  60. package/src/components/data-entry/calendar.tsx +90 -0
  61. package/src/components/data-entry/cascader.tsx +305 -0
  62. package/src/components/data-entry/checkbox-group.tsx +90 -0
  63. package/src/components/data-entry/checkbox.tsx +30 -0
  64. package/src/components/data-entry/choice-field.tsx +27 -0
  65. package/src/components/data-entry/choice-option.ts +20 -0
  66. package/src/components/data-entry/color-picker.tsx +75 -0
  67. package/src/components/data-entry/command.tsx +56 -0
  68. package/src/components/data-entry/country-select.tsx +88 -0
  69. package/src/components/data-entry/date-picker.tsx +69 -0
  70. package/src/components/data-entry/date-range-picker.tsx +75 -0
  71. package/src/components/data-entry/form-field.tsx +59 -0
  72. package/src/components/data-entry/index.ts +62 -0
  73. package/src/components/data-entry/input.tsx +26 -0
  74. package/src/components/data-entry/label.tsx +25 -0
  75. package/src/components/data-entry/radio.tsx +109 -0
  76. package/src/components/data-entry/search-input.tsx +103 -0
  77. package/src/components/data-entry/select.tsx +149 -0
  78. package/src/components/data-entry/slider.tsx +38 -0
  79. package/src/components/data-entry/switch-field.tsx +91 -0
  80. package/src/components/data-entry/switch.tsx +24 -0
  81. package/src/components/data-entry/textarea.tsx +12 -0
  82. package/src/components/data-entry/time-picker.tsx +214 -0
  83. package/src/components/data-entry/transfer.tsx +231 -0
  84. package/src/components/data-entry/tree-select-strategy.ts +6 -0
  85. package/src/components/data-entry/tree-select.tsx +279 -0
  86. package/src/components/data-entry/tree-utils.ts +221 -0
  87. package/src/components/data-entry/upload-crop-dialog.tsx +109 -0
  88. package/src/components/data-entry/upload-types.ts +86 -0
  89. package/src/components/data-entry/upload.tsx +498 -0
  90. package/src/components/data-entry/use-upload-draft.ts +93 -0
  91. package/src/components/feedback/__tests__/alert.test.tsx +127 -0
  92. package/src/components/feedback/__tests__/dialog.test.tsx +290 -0
  93. package/src/components/feedback/__tests__/sheet.test.tsx +94 -0
  94. package/src/components/feedback/__tests__/skeleton.test.tsx +25 -0
  95. package/src/components/feedback/__tests__/toast.test.tsx +52 -0
  96. package/src/components/feedback/alert.tsx +167 -0
  97. package/src/components/feedback/dialog.tsx +325 -0
  98. package/src/components/feedback/index.ts +53 -0
  99. package/src/components/feedback/sheet.tsx +130 -0
  100. package/src/components/feedback/skeleton.tsx +95 -0
  101. package/src/components/feedback/sonner.tsx +54 -0
  102. package/src/components/feedback/toaster.tsx +1 -0
  103. package/src/components/feedback/use-toast.ts +62 -0
  104. package/src/components/general/__tests__/button.test.tsx +71 -0
  105. package/src/components/general/button.tsx +61 -0
  106. package/src/components/general/index.ts +2 -0
  107. package/src/components/layout/__tests__/page-container.test.tsx +69 -0
  108. package/src/components/layout/__tests__/page-inset.test.tsx +14 -0
  109. package/src/components/layout/__tests__/stack-inline.test.tsx +39 -0
  110. package/src/components/layout/app-shell.tsx +42 -0
  111. package/src/components/layout/breadcrumb.tsx +35 -0
  112. package/src/components/layout/index.ts +31 -0
  113. package/src/components/layout/inline.tsx +13 -0
  114. package/src/components/layout/menu.tsx +34 -0
  115. package/src/components/layout/mobile-frame.tsx +57 -0
  116. package/src/components/layout/page-container.tsx +81 -0
  117. package/src/components/layout/page-inset.tsx +16 -0
  118. package/src/components/layout/responsive-grid.tsx +14 -0
  119. package/src/components/layout/shell-app.tsx +30 -0
  120. package/src/components/layout/sidebar.tsx +98 -0
  121. package/src/components/layout/split-pane.tsx +16 -0
  122. package/src/components/layout/stack.tsx +13 -0
  123. package/src/components/layout/topbar.tsx +108 -0
  124. package/src/components/navigation/__tests__/app-pickers.test.tsx +118 -0
  125. package/src/components/navigation/__tests__/dropdown-menu.test.tsx +104 -0
  126. package/src/components/navigation/__tests__/navigation.test.tsx +61 -0
  127. package/src/components/navigation/__tests__/pagination-steps-tabs.test.tsx +76 -0
  128. package/src/components/navigation/date-format-picker.tsx +55 -0
  129. package/src/components/navigation/dropdown-menu.tsx +190 -0
  130. package/src/components/navigation/filter-bar.tsx +38 -0
  131. package/src/components/navigation/index.ts +28 -0
  132. package/src/components/navigation/locale-picker.tsx +49 -0
  133. package/src/components/navigation/page-header.tsx +50 -0
  134. package/src/components/navigation/pagination-utils.ts +35 -0
  135. package/src/components/navigation/pagination.tsx +168 -0
  136. package/src/components/navigation/steps.tsx +163 -0
  137. package/src/components/navigation/tabs-items.tsx +69 -0
  138. package/src/components/navigation/tabs.tsx +67 -0
  139. package/src/components/navigation/time-format-picker.tsx +55 -0
  140. package/src/components/navigation/timezone-picker.tsx +63 -0
  141. package/src/components/query/__tests__/data-state.test.tsx +214 -0
  142. package/src/components/query/__tests__/infinite-prefetch.test.tsx +105 -0
  143. package/src/components/query/__tests__/query-helpers.test.tsx +61 -0
  144. package/src/components/query/data-state.tsx +58 -0
  145. package/src/components/query/index.ts +10 -0
  146. package/src/components/query/infinite-query-state.tsx +99 -0
  147. package/src/components/query/mutation-feedback.tsx +31 -0
  148. package/src/components/query/prefetch-link.tsx +45 -0
  149. package/src/components/query/query-refetch-button.tsx +41 -0
  150. package/src/components/ui/alert-dialog.tsx +1 -0
  151. package/src/components/ui/alert.tsx +1 -0
  152. package/src/components/ui/autocomplete.tsx +1 -0
  153. package/src/components/ui/badge.tsx +1 -0
  154. package/src/components/ui/button.tsx +1 -0
  155. package/src/components/ui/calendar.tsx +1 -0
  156. package/src/components/ui/card.tsx +1 -0
  157. package/src/components/ui/checkbox.tsx +1 -0
  158. package/src/components/ui/color-picker.tsx +1 -0
  159. package/src/components/ui/command.tsx +1 -0
  160. package/src/components/ui/date-picker.tsx +1 -0
  161. package/src/components/ui/date-range-picker.tsx +1 -0
  162. package/src/components/ui/dialog.tsx +1 -0
  163. package/src/components/ui/dropdown-menu.tsx +1 -0
  164. package/src/components/ui/index.tsx +31 -0
  165. package/src/components/ui/input.tsx +1 -0
  166. package/src/components/ui/label.tsx +1 -0
  167. package/src/components/ui/pagination.tsx +1 -0
  168. package/src/components/ui/popover.tsx +1 -0
  169. package/src/components/ui/radio.tsx +1 -0
  170. package/src/components/ui/scroll-area.tsx +1 -0
  171. package/src/components/ui/select.tsx +1 -0
  172. package/src/components/ui/sheet.tsx +1 -0
  173. package/src/components/ui/slider.tsx +1 -0
  174. package/src/components/ui/sonner.tsx +1 -0
  175. package/src/components/ui/switch.tsx +1 -0
  176. package/src/components/ui/table.tsx +1 -0
  177. package/src/components/ui/tabs-items.tsx +1 -0
  178. package/src/components/ui/tabs.tsx +1 -0
  179. package/src/components/ui/textarea.tsx +1 -0
  180. package/src/components/ui/time-picker.tsx +1 -0
  181. package/src/components/ui/upload.tsx +1 -0
  182. package/src/form/__tests__/use-zod-form.test.tsx +97 -0
  183. package/src/form/form-field-control.tsx +44 -0
  184. package/src/form/form-root.tsx +29 -0
  185. package/src/form/index.ts +7 -0
  186. package/src/form/use-zod-form.ts +29 -0
  187. package/src/i18n/__tests__/translate.test.ts +23 -0
  188. package/src/i18n/index.ts +9 -0
  189. package/src/i18n/messages/en.json +171 -0
  190. package/src/i18n/messages/ja.json +171 -0
  191. package/src/i18n/messages/vi.json +171 -0
  192. package/src/i18n/translate.ts +74 -0
  193. package/src/i18n/use-translation.ts +53 -0
  194. package/src/index.ts +3 -0
  195. package/src/lib/__tests__/control-styles.test.ts +78 -0
  196. package/src/lib/__tests__/datetime.test.ts +77 -0
  197. package/src/lib/__tests__/format-date.test.ts +97 -0
  198. package/src/lib/__tests__/format.test.ts +62 -0
  199. package/src/lib/__tests__/theme-tokens-audit.test.ts +176 -0
  200. package/src/lib/__tests__/theme-tokens-css.test.ts +118 -0
  201. package/src/lib/__tests__/token-governance.test.ts +191 -0
  202. package/src/lib/__tests__/variants.test.ts +18 -0
  203. package/src/lib/control-styles.ts +33 -0
  204. package/src/lib/datetime/detect.ts +25 -0
  205. package/src/lib/datetime/format-date.ts +100 -0
  206. package/src/lib/datetime/format.ts +140 -0
  207. package/src/lib/datetime/index.ts +25 -0
  208. package/src/lib/datetime/parse.ts +51 -0
  209. package/src/lib/datetime/sync.ts +48 -0
  210. package/src/lib/format.ts +114 -0
  211. package/src/lib/hooks.ts +54 -0
  212. package/src/lib/utils.ts +6 -0
  213. package/src/lib/variants.ts +40 -0
  214. package/src/props/components/app.prop.ts +99 -0
  215. package/src/props/components/data-display.prop.ts +73 -0
  216. package/src/props/components/data-entry.prop.ts +334 -0
  217. package/src/props/components/feedback.prop.ts +80 -0
  218. package/src/props/components/form.prop.ts +46 -0
  219. package/src/props/components/general.prop.ts +18 -0
  220. package/src/props/components/index.ts +99 -0
  221. package/src/props/components/layout.prop.ts +130 -0
  222. package/src/props/components/navigation.prop.ts +88 -0
  223. package/src/props/components/query.prop.ts +94 -0
  224. package/src/props/index.ts +17 -0
  225. package/src/props/registry.ts +603 -0
  226. package/src/props/vocabulary/content.prop.ts +35 -0
  227. package/src/props/vocabulary/data.prop.ts +46 -0
  228. package/src/props/vocabulary/index.ts +73 -0
  229. package/src/props/vocabulary/interaction.prop.ts +42 -0
  230. package/src/props/vocabulary/layout.prop.ts +25 -0
  231. package/src/props/vocabulary/navigation.prop.ts +19 -0
  232. package/src/props/vocabulary/shared.prop.ts +59 -0
  233. package/src/styles/alert-layout.css +191 -0
  234. package/src/styles/badge-layout.css +22 -0
  235. package/src/styles/card-layout.css +373 -0
  236. package/src/styles/control.css +504 -0
  237. package/src/styles/data-display-layout.css +246 -0
  238. package/src/styles/density.css +43 -0
  239. package/src/styles/dialog-layout.css +84 -0
  240. package/src/styles/index.css +105 -0
  241. package/src/styles/layout.css +479 -0
  242. package/src/styles/shell-layout.css +604 -0
  243. package/src/styles/table-layout.css +109 -0
  244. package/src/test/__tests__/render-loop-guard.test.tsx +38 -0
  245. package/src/test/jest-dom.d.ts +4 -0
  246. package/src/test/render-loop-guard.tsx +50 -0
  247. package/src/test/render.tsx +29 -0
  248. package/src/test/theme-globals.test.ts +77 -0
  249. package/src/test/theme-globals.ts +134 -0
  250. package/src/test/theme-test-utils.tsx +67 -0
  251. package/src/theme/example.service.css +37 -0
  252. package/src/tokens/base.css +13 -0
  253. package/src/tokens/foundation.css +151 -0
  254. package/src/tokens/primitives/badge.css +13 -0
  255. package/src/tokens/primitives/card.css +29 -0
  256. package/src/tokens/primitives/control.css +55 -0
  257. package/src/tokens/primitives/feedback.css +17 -0
  258. package/src/tokens/primitives/layout.css +20 -0
  259. package/src/tokens/primitives/navigation.css +13 -0
  260. package/src/tokens/primitives/table.css +10 -0
  261. package/BRAND.md +0 -296
  262. package/CHANGELOG.md +0 -650
  263. package/config/eslint.js +0 -54
  264. package/config/prettier.cjs +0 -20
  265. package/config/tsconfig.base.json +0 -22
  266. package/config/vitest.base.ts +0 -26
  267. package/dist/MiniMonth-YAmPGEpC.d.ts +0 -143
  268. package/dist/Table.types-BbsxoIYE.d.ts +0 -352
  269. package/dist/color-DO0qqUAb.d.ts +0 -38
  270. package/dist/components/composites.d.ts +0 -963
  271. package/dist/components/composites.js +0 -7340
  272. package/dist/components/composites.js.map +0 -1
  273. package/dist/components/primitives.d.ts +0 -2736
  274. package/dist/components/primitives.js +0 -7353
  275. package/dist/components/primitives.js.map +0 -1
  276. package/dist/components/shell.d.ts +0 -182
  277. package/dist/components/shell.js +0 -774
  278. package/dist/components/shell.js.map +0 -1
  279. package/dist/hooks.d.ts +0 -100
  280. package/dist/hooks.js +0 -558
  281. package/dist/hooks.js.map +0 -1
  282. package/dist/i18n.d.ts +0 -61
  283. package/dist/i18n.js +0 -860
  284. package/dist/i18n.js.map +0 -1
  285. package/dist/index.d.ts +0 -33
  286. package/dist/index.js +0 -13059
  287. package/dist/index.js.map +0 -1
  288. package/dist/padding-DY0JV5Ja.d.ts +0 -16
  289. package/dist/preferences.d.ts +0 -132
  290. package/dist/preferences.js +0 -262
  291. package/dist/preferences.js.map +0 -1
  292. package/dist/props.d.ts +0 -86
  293. package/dist/props.js +0 -16
  294. package/dist/props.js.map +0 -1
  295. package/dist/size-CQwNvOWd.d.ts +0 -19
  296. package/dist/types-LTj-2bl-.d.ts +0 -30
  297. package/dist/useTableViews-D5NIAJ7h.d.ts +0 -154
  298. package/src/tokens/tailwind.css +0 -158
@@ -0,0 +1,69 @@
1
+ import * as React from "react";
2
+ import { CalendarIcon } from "lucide-react";
3
+ import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
4
+ import { formatDate } from "../../lib/datetime";
5
+ import { cn } from "../../lib/utils";
6
+ import { Button } from "../general/button";
7
+ import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover";
8
+ import { Calendar } from "./calendar";
9
+ import type { DatePickerProp } from "../../props/components/data-entry.prop";
10
+
11
+ export type {
12
+ DatePickerProp,
13
+ DatePickerProp as DatePickerProps,
14
+ } from "../../props/components/data-entry.prop";
15
+
16
+ export function DatePicker({
17
+ value,
18
+ onChange,
19
+ placeholder,
20
+ disabled,
21
+ className,
22
+ id,
23
+ locale: localeProp,
24
+ fromDate,
25
+ toDate,
26
+ }: DatePickerProp) {
27
+ const { t } = useTranslation();
28
+ const { dayPickerLocale } = usePickerLocales(localeProp);
29
+ const [open, setOpen] = React.useState(false);
30
+ const resolvedPlaceholder = placeholder ?? t("dataEntry.datePicker.placeholder");
31
+
32
+ return (
33
+ <Popover open={open} onOpenChange={setOpen}>
34
+ <PopoverTrigger asChild>
35
+ <Button
36
+ id={id}
37
+ type="button"
38
+ variant="outline"
39
+ disabled={disabled}
40
+ className={cn(
41
+ "w-full justify-start text-left font-normal",
42
+ !value && "text-muted-foreground",
43
+ className,
44
+ )}
45
+ >
46
+ <CalendarIcon className="mr-2 size-4 shrink-0" aria-hidden="true" />
47
+ {value ? formatDate(value, { kind: "calendar" }) : resolvedPlaceholder}
48
+ </Button>
49
+ </PopoverTrigger>
50
+ <PopoverContent className="w-auto p-0" align="start">
51
+ <Calendar
52
+ mode="single"
53
+ selected={value}
54
+ onSelect={(date) => {
55
+ onChange?.(date);
56
+ setOpen(false);
57
+ }}
58
+ locale={dayPickerLocale}
59
+ disabled={[
60
+ ...(fromDate ? [{ before: fromDate }] : []),
61
+ ...(toDate ? [{ after: toDate }] : []),
62
+ ]}
63
+ startMonth={fromDate}
64
+ endMonth={toDate}
65
+ />
66
+ </PopoverContent>
67
+ </Popover>
68
+ );
69
+ }
@@ -0,0 +1,75 @@
1
+ import * as React from "react";
2
+ import { CalendarIcon } from "lucide-react";
3
+ import type { DateRange } from "react-day-picker";
4
+ import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
5
+ import { formatDate } from "../../lib/datetime";
6
+ import { cn } from "../../lib/utils";
7
+ import { Button } from "../general/button";
8
+ import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover";
9
+ import { Calendar } from "./calendar";
10
+ import type { DateRangePickerProp } from "../../props/components/data-entry.prop";
11
+
12
+ export type {
13
+ DateRangePickerProp,
14
+ DateRangePickerProp as DateRangePickerProps,
15
+ } from "../../props/components/data-entry.prop";
16
+
17
+ function formatRange(range: DateRange | undefined): string {
18
+ if (!range?.from) return "";
19
+ const fmt = (d: Date) => formatDate(d, { kind: "calendar" });
20
+ if (!range.to) return fmt(range.from);
21
+ return `${fmt(range.from)} – ${fmt(range.to)}`;
22
+ }
23
+
24
+ export function DateRangePicker({
25
+ value,
26
+ onChange,
27
+ placeholder,
28
+ disabled,
29
+ className,
30
+ id,
31
+ locale: localeProp,
32
+ fromDate,
33
+ toDate,
34
+ }: DateRangePickerProp) {
35
+ const { t } = useTranslation();
36
+ const { dayPickerLocale } = usePickerLocales(localeProp);
37
+ const [open, setOpen] = React.useState(false);
38
+ const label = formatRange(value);
39
+ const resolvedPlaceholder = placeholder ?? t("dataEntry.dateRangePicker.placeholder");
40
+
41
+ return (
42
+ <Popover open={open} onOpenChange={setOpen}>
43
+ <PopoverTrigger asChild>
44
+ <Button
45
+ id={id}
46
+ type="button"
47
+ variant="outline"
48
+ disabled={disabled}
49
+ className={cn(
50
+ "w-full justify-start text-left font-normal",
51
+ !label && "text-muted-foreground",
52
+ className,
53
+ )}
54
+ >
55
+ <CalendarIcon className="mr-2 size-4 shrink-0" aria-hidden="true" />
56
+ {label || resolvedPlaceholder}
57
+ </Button>
58
+ </PopoverTrigger>
59
+ <PopoverContent className="w-auto p-0" align="start">
60
+ <Calendar
61
+ mode="range"
62
+ selected={value}
63
+ onSelect={onChange}
64
+ locale={dayPickerLocale}
65
+ disabled={[
66
+ ...(fromDate ? [{ before: fromDate }] : []),
67
+ ...(toDate ? [{ after: toDate }] : []),
68
+ ]}
69
+ startMonth={fromDate}
70
+ endMonth={toDate}
71
+ />
72
+ </PopoverContent>
73
+ </Popover>
74
+ );
75
+ }
@@ -0,0 +1,59 @@
1
+ import * as React from "react";
2
+
3
+ import { Label } from "../data-entry/label";
4
+ import { cn } from "../../lib/utils";
5
+ import type { FormFieldProp } from "../../props/components/data-entry.prop";
6
+
7
+ export type {
8
+ FormFieldProp,
9
+ FormFieldProp as FormFieldProps,
10
+ } from "../../props/components/data-entry.prop";
11
+
12
+ export function FormField({
13
+ id,
14
+ label,
15
+ required,
16
+ helper,
17
+ error,
18
+ labelAddon,
19
+ className,
20
+ children,
21
+ }: FormFieldProp) {
22
+ const helperId = helper && !error ? `${id}-helper` : undefined;
23
+ const errorId = error ? `${id}-error` : undefined;
24
+ const describedBy = errorId ?? helperId;
25
+
26
+ const childWithA11y = React.isValidElement(children)
27
+ ? React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {
28
+ "aria-describedby": describedBy,
29
+ "aria-required": required ? true : undefined,
30
+ "aria-invalid": !!error || undefined,
31
+ })
32
+ : children;
33
+
34
+ return (
35
+ <div className={cn("ui-stack-sm", className)}>
36
+ <div className="flex items-center gap-1">
37
+ <Label htmlFor={id} className="ui-inline-xs">
38
+ <span>{label}</span>
39
+ {required && (
40
+ <span aria-hidden="true" className="text-destructive">
41
+ *
42
+ </span>
43
+ )}
44
+ </Label>
45
+ {labelAddon}
46
+ </div>
47
+ {childWithA11y}
48
+ {error ? (
49
+ <p id={errorId} role="alert" className="text-destructive text-xs">
50
+ {error}
51
+ </p>
52
+ ) : helper ? (
53
+ <p id={helperId} className="text-muted-foreground text-xs">
54
+ {helper}
55
+ </p>
56
+ ) : null}
57
+ </div>
58
+ );
59
+ }
@@ -0,0 +1,62 @@
1
+ export { Input } from "./input";
2
+ export type { InputProps } from "./input";
3
+ export { Label } from "./label";
4
+ export {
5
+ Select,
6
+ SelectContent,
7
+ SelectGroup,
8
+ SelectItem,
9
+ SelectLabel,
10
+ SelectScrollDownButton,
11
+ SelectScrollUpButton,
12
+ SelectSeparator,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ } from "./select";
16
+ export { Checkbox } from "./checkbox";
17
+ export { CheckboxGroup } from "./checkbox-group";
18
+ export { Radio, RadioGroup, RadioItem, RadioGroupRoot } from "./radio";
19
+ export { Textarea } from "./textarea";
20
+ export type { TextareaProps } from "./textarea";
21
+ export { FormField } from "./form-field";
22
+ export { SwitchField } from "./switch-field";
23
+ export type { SwitchFieldProps } from "./switch-field";
24
+ export { CountrySelect, CountryOptionLabel } from "./country-select";
25
+ export type {
26
+ CountrySelectProps,
27
+ CountryOptionProp,
28
+ CountryOptionLabelProp,
29
+ } from "./country-select";
30
+ export { SearchInput } from "./search-input";
31
+ export { Switch } from "./switch";
32
+ export type { SwitchProps } from "./switch";
33
+ export { Slider } from "./slider";
34
+ export type { SliderProps } from "./slider";
35
+ export { Calendar } from "./calendar";
36
+ export type { CalendarProps } from "./calendar";
37
+ export { DatePicker } from "./date-picker";
38
+ export type { DatePickerProps } from "./date-picker";
39
+ export { DateRangePicker } from "./date-range-picker";
40
+ export type { DateRangePickerProps } from "./date-range-picker";
41
+ export { TimePicker } from "./time-picker";
42
+ export type { TimePickerProps } from "./time-picker";
43
+ export { ColorPicker } from "./color-picker";
44
+ export type { ColorPickerProps } from "./color-picker";
45
+ export { Autocomplete } from "./autocomplete";
46
+ export type { AutocompleteProps } from "./autocomplete";
47
+ export { Upload, collectUploadCommitActions, createUploadItem, useUploadDraft } from "./upload";
48
+ export type { UploadProps, UploadFileItem, UploadVariant, UploadCommitAction } from "./upload";
49
+ export { Cascader } from "./cascader";
50
+ export type { CascaderProps, TreeOption, TreeFieldNames } from "./cascader";
51
+ export { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select";
52
+ export type { TreeSelectProps } from "./tree-select";
53
+ export { Transfer } from "./transfer";
54
+ export type { TransferProps, TransferItemProp } from "./transfer";
55
+ export {
56
+ Command,
57
+ CommandEmpty,
58
+ CommandGroup,
59
+ CommandInput,
60
+ CommandItem,
61
+ CommandList,
62
+ } from "./command";
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
5
+
6
+ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
7
+ ({ className, type, ...props }, ref) => (
8
+ <input
9
+ type={type}
10
+ data-slot="input"
11
+ ref={ref}
12
+ className={cn(
13
+ "ui-control border-input bg-background w-full min-w-0 rounded-md border px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none md:text-sm",
14
+ "selection:bg-primary selection:text-primary-foreground",
15
+ "file:text-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium",
16
+ "placeholder:text-muted-foreground",
17
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
18
+ "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
19
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ ),
25
+ );
26
+ Input.displayName = "Input";
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+ import * as LabelPrimitive from "@radix-ui/react-label";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "../../lib/utils";
5
+
6
+ const labelVariants = cva(
7
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
8
+ );
9
+
10
+ export const Label = React.forwardRef<
11
+ React.ComponentRef<typeof LabelPrimitive.Root>,
12
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
13
+ >(({ className, ...props }, ref) => (
14
+ <LabelPrimitive.Root
15
+ ref={ref}
16
+ data-slot="label"
17
+ className={cn(
18
+ "flex items-center gap-2 select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
19
+ labelVariants(),
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ ));
25
+ Label.displayName = LabelPrimitive.Root.displayName;
@@ -0,0 +1,109 @@
1
+ import * as React from "react";
2
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
3
+ import { Circle } from "lucide-react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+ import { ChoiceField } from "./choice-field";
7
+ import { choiceGroupClassName, type ChoiceOption } from "./choice-option";
8
+ import type { RadioGroupProp } from "../../props/components/data-entry.prop";
9
+
10
+ export type {
11
+ RadioGroupProp,
12
+ RadioGroupProp as RadioGroupProps,
13
+ } from "../../props/components/data-entry.prop";
14
+
15
+ const RadioGroupRoot = React.forwardRef<
16
+ React.ComponentRef<typeof RadioGroupPrimitive.Root>,
17
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
18
+ >(({ className, ...props }, ref) => (
19
+ <RadioGroupPrimitive.Root
20
+ ref={ref}
21
+ data-slot="radio-group"
22
+ className={cn("ui-choice-group", className)}
23
+ {...props}
24
+ />
25
+ ));
26
+ RadioGroupRoot.displayName = RadioGroupPrimitive.Root.displayName;
27
+
28
+ const RadioItem = React.forwardRef<
29
+ React.ComponentRef<typeof RadioGroupPrimitive.Item>,
30
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
31
+ >(({ className, ...props }, ref) => (
32
+ <RadioGroupPrimitive.Item
33
+ ref={ref}
34
+ data-slot="radio-group-item"
35
+ className={cn(
36
+ "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",
37
+ className,
38
+ )}
39
+ {...props}
40
+ >
41
+ <RadioGroupPrimitive.Indicator
42
+ data-slot="radio-group-indicator"
43
+ className="ui-choice-indicator"
44
+ >
45
+ <Circle className="ui-radio-icon" aria-hidden="true" />
46
+ </RadioGroupPrimitive.Indicator>
47
+ </RadioGroupPrimitive.Item>
48
+ ));
49
+ RadioItem.displayName = RadioGroupPrimitive.Item.displayName;
50
+
51
+ function RadioGroupOptions({
52
+ value,
53
+ defaultValue,
54
+ onValueChange,
55
+ options,
56
+ orientation = "vertical",
57
+ disabled,
58
+ name,
59
+ className,
60
+ children,
61
+ }: RadioGroupProp) {
62
+ const reactId = React.useId();
63
+
64
+ if (options && options.length > 0) {
65
+ return (
66
+ <RadioGroupRoot
67
+ value={value}
68
+ defaultValue={defaultValue}
69
+ onValueChange={onValueChange}
70
+ disabled={disabled}
71
+ name={name}
72
+ data-orientation={orientation}
73
+ className={choiceGroupClassName(orientation, className)}
74
+ >
75
+ {options.map((opt: ChoiceOption, index) => {
76
+ const id = `${reactId}-${opt.value}-${index}`;
77
+ return (
78
+ <ChoiceField key={opt.value} id={id} label={opt.label} description={opt.description}>
79
+ <RadioItem id={id} value={opt.value} disabled={opt.disabled} />
80
+ </ChoiceField>
81
+ );
82
+ })}
83
+ </RadioGroupRoot>
84
+ );
85
+ }
86
+
87
+ return (
88
+ <RadioGroupRoot
89
+ value={value}
90
+ defaultValue={defaultValue}
91
+ onValueChange={onValueChange}
92
+ disabled={disabled}
93
+ name={name}
94
+ data-orientation={orientation}
95
+ className={choiceGroupClassName(orientation, className)}
96
+ >
97
+ {children}
98
+ </RadioGroupRoot>
99
+ );
100
+ }
101
+
102
+ /** Single radio — use inside `Radio.Group` / `RadioGroupRoot`, or via `options` API. */
103
+ export const Radio = Object.assign(RadioItem, {
104
+ Root: RadioGroupRoot,
105
+ Group: RadioGroupOptions,
106
+ Item: RadioItem,
107
+ });
108
+
109
+ export { RadioGroupRoot, RadioItem, RadioGroupOptions as RadioGroup };
@@ -0,0 +1,103 @@
1
+ import * as React from "react";
2
+ import { Search, X } from "lucide-react";
3
+
4
+ import { useTranslation } from "../../i18n/use-translation";
5
+ import { Input } from "../data-entry/input";
6
+ import { Label } from "../data-entry/label";
7
+ import { cn } from "../../lib/utils";
8
+ import { useDebouncedValue } from "../../lib/hooks";
9
+
10
+ interface SearchInputProps {
11
+ value?: string;
12
+ defaultValue?: string;
13
+ placeholder?: string;
14
+ debounce?: number;
15
+ onSearch: (q: string) => void;
16
+ label?: React.ReactNode;
17
+ ariaLabel?: string;
18
+ className?: string;
19
+ inputClassName?: string;
20
+ id?: string;
21
+ disabled?: boolean;
22
+ }
23
+
24
+ export function SearchInput({
25
+ value: controlledValue,
26
+ defaultValue = "",
27
+ placeholder,
28
+ debounce = 250,
29
+ onSearch,
30
+ label,
31
+ ariaLabel,
32
+ className,
33
+ inputClassName,
34
+ id,
35
+ disabled = false,
36
+ }: SearchInputProps) {
37
+ const { t } = useTranslation();
38
+ const isControlled = controlledValue !== undefined;
39
+ const [internal, setInternal] = React.useState(defaultValue);
40
+ const value = isControlled ? controlledValue : internal;
41
+ const debounced = useDebouncedValue(value, debounce);
42
+ const reactId = React.useId();
43
+ const inputId = id ?? `search-${reactId}`;
44
+ const resolvedPlaceholder = placeholder ?? t("dataEntry.searchInput.placeholder");
45
+ const resolvedAriaLabel = ariaLabel ?? t("common.search");
46
+
47
+ const onSearchRef = React.useRef(onSearch);
48
+ React.useEffect(() => {
49
+ onSearchRef.current = onSearch;
50
+ });
51
+ React.useEffect(() => {
52
+ onSearchRef.current(debounced);
53
+ }, [debounced]);
54
+
55
+ const setValue = (v: string) => {
56
+ if (!isControlled) setInternal(v);
57
+ };
58
+
59
+ return (
60
+ <div className={cn("ui-search-input", className)}>
61
+ {label !== undefined ? (
62
+ <Label htmlFor={inputId} className="ui-search-input-label">
63
+ {label}
64
+ </Label>
65
+ ) : (
66
+ <Label htmlFor={inputId} className="sr-only">
67
+ {resolvedAriaLabel}
68
+ </Label>
69
+ )}
70
+ <div className="ui-search-input-field">
71
+ <Search className="ui-search-input-icon" aria-hidden="true" />
72
+ <Input
73
+ id={inputId}
74
+ type="text"
75
+ role="searchbox"
76
+ value={value}
77
+ onChange={(e) => {
78
+ setValue(e.target.value);
79
+ }}
80
+ placeholder={resolvedPlaceholder}
81
+ aria-label={resolvedAriaLabel}
82
+ className={cn(
83
+ "ui-search-input-control !pr-[var(--search-input-end-padding)] !pl-[var(--search-input-start-padding)]",
84
+ inputClassName,
85
+ )}
86
+ disabled={disabled}
87
+ />
88
+ {value && !disabled && (
89
+ <button
90
+ type="button"
91
+ onClick={() => {
92
+ setValue("");
93
+ }}
94
+ aria-label={t("common.clearSearch")}
95
+ className="ui-search-input-clear"
96
+ >
97
+ <X />
98
+ </button>
99
+ )}
100
+ </div>
101
+ </div>
102
+ );
103
+ }
@@ -0,0 +1,149 @@
1
+ import * as React from "react";
2
+ import * as SelectPrimitive from "@radix-ui/react-select";
3
+ import { ChevronDown, ChevronUp } from "lucide-react";
4
+ import { cn } from "../../lib/utils";
5
+ import { controlTriggerClass } from "../../lib/control-styles";
6
+
7
+ export function Select(props: React.ComponentProps<typeof SelectPrimitive.Root>) {
8
+ return <SelectPrimitive.Root data-slot="select" {...props} />;
9
+ }
10
+
11
+ export function SelectGroup(props: React.ComponentProps<typeof SelectPrimitive.Group>) {
12
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />;
13
+ }
14
+
15
+ export function SelectValue(props: React.ComponentProps<typeof SelectPrimitive.Value>) {
16
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />;
17
+ }
18
+
19
+ export const SelectTrigger = React.forwardRef<
20
+ React.ComponentRef<typeof SelectPrimitive.Trigger>,
21
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
22
+ size?: "sm" | "default";
23
+ }
24
+ >(({ className, children, size = "default", ...props }, ref) => (
25
+ <SelectPrimitive.Trigger
26
+ ref={ref}
27
+ data-slot="select-trigger"
28
+ data-size={size}
29
+ className={cn(
30
+ controlTriggerClass,
31
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground w-fit gap-2 whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
32
+ className,
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <SelectPrimitive.Icon asChild>
38
+ <ChevronDown className="size-4 shrink-0 opacity-50" aria-hidden="true" />
39
+ </SelectPrimitive.Icon>
40
+ </SelectPrimitive.Trigger>
41
+ ));
42
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
43
+
44
+ export const SelectScrollUpButton = React.forwardRef<
45
+ React.ComponentRef<typeof SelectPrimitive.ScrollUpButton>,
46
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
47
+ >(({ className, ...props }, ref) => (
48
+ <SelectPrimitive.ScrollUpButton
49
+ ref={ref}
50
+ data-slot="select-scroll-up-button"
51
+ className={cn("flex cursor-default items-center justify-center py-1", className)}
52
+ {...props}
53
+ >
54
+ <ChevronUp className="size-4" aria-hidden="true" />
55
+ </SelectPrimitive.ScrollUpButton>
56
+ ));
57
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
58
+
59
+ export const SelectScrollDownButton = React.forwardRef<
60
+ React.ComponentRef<typeof SelectPrimitive.ScrollDownButton>,
61
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
62
+ >(({ className, ...props }, ref) => (
63
+ <SelectPrimitive.ScrollDownButton
64
+ ref={ref}
65
+ data-slot="select-scroll-down-button"
66
+ className={cn("flex cursor-default items-center justify-center py-1", className)}
67
+ {...props}
68
+ >
69
+ <ChevronDown className="size-4" aria-hidden="true" />
70
+ </SelectPrimitive.ScrollDownButton>
71
+ ));
72
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
73
+
74
+ export const SelectContent = React.forwardRef<
75
+ React.ComponentRef<typeof SelectPrimitive.Content>,
76
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
77
+ >(({ className, children, position = "popper", ...props }, ref) => (
78
+ <SelectPrimitive.Portal>
79
+ <SelectPrimitive.Content
80
+ ref={ref}
81
+ data-slot="select-content"
82
+ className={cn(
83
+ "bg-popover text-popover-foreground data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border shadow-md",
84
+ position === "popper" && "translate-y-1",
85
+ className,
86
+ )}
87
+ position={position}
88
+ {...props}
89
+ >
90
+ <SelectScrollUpButton />
91
+ <SelectPrimitive.Viewport
92
+ data-slot="select-viewport"
93
+ className={cn(
94
+ "p-1",
95
+ position === "popper" &&
96
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
97
+ )}
98
+ >
99
+ {children}
100
+ </SelectPrimitive.Viewport>
101
+ <SelectScrollDownButton />
102
+ </SelectPrimitive.Content>
103
+ </SelectPrimitive.Portal>
104
+ ));
105
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
106
+
107
+ export const SelectLabel = React.forwardRef<
108
+ React.ComponentRef<typeof SelectPrimitive.Label>,
109
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
110
+ >(({ className, ...props }, ref) => (
111
+ <SelectPrimitive.Label
112
+ ref={ref}
113
+ data-slot="select-label"
114
+ className={cn("px-2 py-1.5 text-sm font-medium", className)}
115
+ {...props}
116
+ />
117
+ ));
118
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
119
+
120
+ export const SelectItem = React.forwardRef<
121
+ React.ComponentRef<typeof SelectPrimitive.Item>,
122
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
123
+ >(({ className, children, ...props }, ref) => (
124
+ <SelectPrimitive.Item
125
+ ref={ref}
126
+ data-slot="select-item"
127
+ className={cn(
128
+ "focus:bg-accent focus:text-accent-foreground data-[state=checked]:bg-accent data-[state=checked]:text-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm px-2.5 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[state=checked]:font-medium [&_svg]:pointer-events-none [&_svg]:shrink-0",
129
+ className,
130
+ )}
131
+ {...props}
132
+ >
133
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
134
+ </SelectPrimitive.Item>
135
+ ));
136
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
137
+
138
+ export const SelectSeparator = React.forwardRef<
139
+ React.ComponentRef<typeof SelectPrimitive.Separator>,
140
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
141
+ >(({ className, ...props }, ref) => (
142
+ <SelectPrimitive.Separator
143
+ ref={ref}
144
+ data-slot="select-separator"
145
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
146
+ {...props}
147
+ />
148
+ ));
149
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;