@godxjp/ui 5.0.2 → 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 -668
  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 -7343
  272. package/dist/components/composites.js.map +0 -1
  273. package/dist/components/primitives.d.ts +0 -2744
  274. package/dist/components/primitives.js +0 -7356
  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 -13062
  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,22 @@
1
+ import { cn } from "../../lib/utils";
2
+ import type { EmptyStateProp } from "../../props/components/data-display.prop";
3
+
4
+ export type {
5
+ EmptyStateProp,
6
+ EmptyStateProp as EmptyStateProps,
7
+ } from "../../props/components/data-display.prop";
8
+
9
+ export function EmptyState({ icon: Icon, title, description, action, className }: EmptyStateProp) {
10
+ return (
11
+ <div data-slot="empty-state" className={cn("ui-empty-state", className)}>
12
+ {Icon && (
13
+ <div className="ui-empty-state-icon">
14
+ <Icon className="text-muted-foreground size-6" aria-hidden="true" />
15
+ </div>
16
+ )}
17
+ <h3 className="ui-empty-state-title">{title}</h3>
18
+ {description && <p className="ui-empty-state-description">{description}</p>}
19
+ {action && <div>{action}</div>}
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,41 @@
1
+ export { Badge } from "./badge";
2
+ export type { BadgeProps } from "./badge";
3
+ export {
4
+ Card,
5
+ CardContent,
6
+ CardCover,
7
+ CardDescription,
8
+ CardFooter,
9
+ CardHeader,
10
+ CardTitle,
11
+ CardAction,
12
+ CardStat,
13
+ } from "./card";
14
+ export type { CardStatProps } from "./card";
15
+ export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table";
16
+ export { KeyValueGrid } from "./key-value-grid";
17
+ export { StatusBadge } from "./status-badge";
18
+ export { DataTable } from "./data-table";
19
+ export type { ColumnDef, Density as TableDensity } from "./data-table";
20
+ export { EmptyState } from "./empty-state";
21
+ export { CodeBadge } from "./code-badge";
22
+ export type { CodeBadgeKind, CodeBadgeProps } from "./code-badge";
23
+ export { ProgressMeter } from "./progress-meter";
24
+ export type { ProgressMeterProps, ProgressMeterTone } from "./progress-meter";
25
+ export { TreeList } from "./tree-list";
26
+ export type { TreeListItem, TreeListProps } from "./tree-list";
27
+ export { Timeline } from "./timeline";
28
+ export type { TimelineItem, TimelineProps } from "./timeline";
29
+ export { ScanPanel } from "./scan-panel";
30
+ export type { ScanPanelProps } from "./scan-panel";
31
+ export {
32
+ Popover,
33
+ PopoverAnchor,
34
+ PopoverContent,
35
+ PopoverDescription,
36
+ PopoverHeader,
37
+ PopoverTitle,
38
+ PopoverTrigger,
39
+ } from "./popover";
40
+ export { ScrollArea, ScrollBar } from "./scroll-area";
41
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent } from "./collapsible";
@@ -0,0 +1,46 @@
1
+ // KeyValueGrid — for detail-page metadata. Replaces hand-rolled <dl><dt><dd> layouts.
2
+ import * as React from "react";
3
+
4
+ import { cn } from "../../lib/utils";
5
+
6
+ interface KeyValueGridProps {
7
+ columns?: 1 | 2 | 3;
8
+ className?: string;
9
+ children: React.ReactNode;
10
+ }
11
+
12
+ export function KeyValueGrid({ columns = 2, className, children }: KeyValueGridProps) {
13
+ const colsClass =
14
+ columns === 1
15
+ ? "grid-cols-1"
16
+ : columns === 3
17
+ ? "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
18
+ : "grid-cols-1 sm:grid-cols-2";
19
+ return <dl className={cn("grid gap-x-6 gap-y-3", colsClass, className)}>{children}</dl>;
20
+ }
21
+
22
+ interface KeyValueGridItemProps {
23
+ label: React.ReactNode;
24
+ /** Use mono spacing for IDs, paths, JSON. */
25
+ mono?: boolean;
26
+ /** Span full row(s) when value is long. */
27
+ span?: 2 | 3;
28
+ className?: string;
29
+ children: React.ReactNode;
30
+ }
31
+
32
+ KeyValueGrid.Item = function KeyValueGridItem({
33
+ label,
34
+ mono,
35
+ span,
36
+ className,
37
+ children,
38
+ }: KeyValueGridItemProps) {
39
+ const spanClass = span === 2 ? "sm:col-span-2" : span === 3 ? "sm:col-span-2 lg:col-span-3" : "";
40
+ return (
41
+ <div className={cn(spanClass, className)}>
42
+ <dt className="text-muted-foreground text-xs">{label}</dt>
43
+ <dd className={cn("text-sm break-all", mono && "font-mono")}>{children}</dd>
44
+ </div>
45
+ );
46
+ };
@@ -0,0 +1,62 @@
1
+ import * as React from "react";
2
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ export function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>) {
6
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />;
7
+ }
8
+
9
+ export function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
10
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
11
+ }
12
+
13
+ export function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
14
+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
15
+ }
16
+
17
+ export const PopoverContent = React.forwardRef<
18
+ React.ComponentRef<typeof PopoverPrimitive.Content>,
19
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
20
+ >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
21
+ <PopoverPrimitive.Portal>
22
+ <PopoverPrimitive.Content
23
+ ref={ref}
24
+ data-slot="popover-content"
25
+ align={align}
26
+ sideOffset={sideOffset}
27
+ className={cn(
28
+ "bg-popover text-popover-foreground z-50 w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-md border p-4 shadow-md outline-none",
29
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
30
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
31
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
32
+ "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
33
+ className,
34
+ )}
35
+ {...props}
36
+ />
37
+ </PopoverPrimitive.Portal>
38
+ ));
39
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
40
+
41
+ export const PopoverHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
42
+ <div
43
+ data-slot="popover-header"
44
+ className={cn("flex flex-col gap-1 text-sm", className)}
45
+ {...props}
46
+ />
47
+ );
48
+
49
+ export const PopoverTitle = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
50
+ <div data-slot="popover-title" className={cn("font-medium", className)} {...props} />
51
+ );
52
+
53
+ export const PopoverDescription = ({
54
+ className,
55
+ ...props
56
+ }: React.HTMLAttributes<HTMLParagraphElement>) => (
57
+ <p
58
+ data-slot="popover-description"
59
+ className={cn("text-muted-foreground", className)}
60
+ {...props}
61
+ />
62
+ );
@@ -0,0 +1,20 @@
1
+ export type ProgressMeterTone = "success" | "warning";
2
+
3
+ export type ProgressMeterProps = {
4
+ value: number;
5
+ label?: string;
6
+ tone?: ProgressMeterTone;
7
+ };
8
+
9
+ export function ProgressMeter({ value, label, tone = "success" }: ProgressMeterProps) {
10
+ const boundedValue = Math.max(0, Math.min(100, value));
11
+
12
+ return (
13
+ <div className="ui-progress-meter" data-tone={tone}>
14
+ <div className="ui-progress-track">
15
+ <div className="ui-progress-bar" style={{ width: `${boundedValue}%` }} />
16
+ </div>
17
+ {label ? <div className="ui-progress-label">{label}</div> : null}
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,16 @@
1
+ import { ScanLine } from "lucide-react";
2
+
3
+ export type ScanPanelProps = {
4
+ title: string;
5
+ description?: string;
6
+ };
7
+
8
+ export function ScanPanel({ title, description }: ScanPanelProps) {
9
+ return (
10
+ <div className="ui-scan-panel">
11
+ <ScanLine aria-hidden="true" />
12
+ <div className="ui-scan-title">{title}</div>
13
+ {description ? <div className="ui-scan-description">{description}</div> : null}
14
+ </div>
15
+ );
16
+ }
@@ -0,0 +1,42 @@
1
+ import * as React from "react";
2
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
3
+
4
+ import { cn } from "../../lib/utils";
5
+
6
+ export const ScrollArea = React.forwardRef<
7
+ React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
9
+ >(({ className, children, ...props }, ref) => (
10
+ <ScrollAreaPrimitive.Root
11
+ ref={ref}
12
+ className={cn("relative overflow-hidden", className)}
13
+ {...props}
14
+ >
15
+ <ScrollAreaPrimitive.Viewport className="size-full rounded-[inherit]">
16
+ {children}
17
+ </ScrollAreaPrimitive.Viewport>
18
+ <ScrollBar />
19
+ <ScrollAreaPrimitive.Corner />
20
+ </ScrollAreaPrimitive.Root>
21
+ ));
22
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
23
+
24
+ export const ScrollBar = React.forwardRef<
25
+ React.ComponentRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
26
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
27
+ >(({ className, orientation = "vertical", ...props }, ref) => (
28
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
29
+ ref={ref}
30
+ orientation={orientation}
31
+ className={cn(
32
+ "flex touch-none transition-colors select-none",
33
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-px",
34
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-px",
35
+ className,
36
+ )}
37
+ {...props}
38
+ >
39
+ <ScrollAreaPrimitive.ScrollAreaThumb className="bg-border relative flex-1 rounded-full" />
40
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
41
+ ));
42
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
@@ -0,0 +1,83 @@
1
+ // StatusBadge — globally consistent status display. Adding a new status:
2
+ // extend STATUS_MAP below + add key to i18n JSON (`status.*`), never inline `<Badge>`.
3
+ import * as React from "react";
4
+ import {
5
+ AlertCircle,
6
+ CheckCircle2,
7
+ Circle,
8
+ Clock,
9
+ type LucideIcon,
10
+ Pause,
11
+ Play,
12
+ Trash2,
13
+ XCircle,
14
+ } from "lucide-react";
15
+
16
+ import { useTranslation } from "../../i18n/use-translation";
17
+ import { cn } from "../../lib/utils";
18
+ import {
19
+ toneDestructiveClass,
20
+ toneInfoClass,
21
+ toneNeutralClass,
22
+ toneSuccessClass,
23
+ toneWarningClass,
24
+ } from "../../lib/control-styles";
25
+
26
+ type Tone = "success" | "warning" | "destructive" | "info" | "neutral";
27
+
28
+ interface StatusDef {
29
+ tone: Tone;
30
+ icon: LucideIcon;
31
+ }
32
+
33
+ const STATUS_MAP: Record<string, StatusDef> = {
34
+ active: { tone: "success", icon: CheckCircle2 },
35
+ completed: { tone: "success", icon: CheckCircle2 },
36
+ delivered: { tone: "success", icon: CheckCircle2 },
37
+ done: { tone: "success", icon: CheckCircle2 },
38
+ permanent: { tone: "success", icon: CheckCircle2 },
39
+ succeeded: { tone: "success", icon: CheckCircle2 },
40
+ draft: { tone: "neutral", icon: Circle },
41
+ pending: { tone: "warning", icon: Clock },
42
+ scheduled: { tone: "info", icon: Clock },
43
+ sending: { tone: "info", icon: Play },
44
+ temporary: { tone: "warning", icon: Clock },
45
+ bounced: { tone: "destructive", icon: AlertCircle },
46
+ cancelled: { tone: "neutral", icon: Pause },
47
+ deleted: { tone: "destructive", icon: Trash2 },
48
+ failed: { tone: "destructive", icon: XCircle },
49
+ private: { tone: "neutral", icon: Circle },
50
+ internal: { tone: "info", icon: Circle },
51
+ public: { tone: "info", icon: Circle },
52
+ ASSIGNMENT_STATUS_ACTIVE: { tone: "success", icon: CheckCircle2 },
53
+ ASSIGNMENT_STATUS_SUSPENDED: { tone: "warning", icon: Pause },
54
+ ASSIGNMENT_STATUS_TERMINATED: { tone: "destructive", icon: XCircle },
55
+ };
56
+
57
+ const TONE_CLASSES: Record<Tone, string> = {
58
+ success: toneSuccessClass,
59
+ warning: toneWarningClass,
60
+ destructive: toneDestructiveClass,
61
+ info: toneInfoClass,
62
+ neutral: toneNeutralClass,
63
+ };
64
+
65
+ interface StatusBadgeProps {
66
+ status: string;
67
+ className?: string;
68
+ label?: React.ReactNode;
69
+ }
70
+
71
+ export function StatusBadge({ status, className, label: labelOverride }: StatusBadgeProps) {
72
+ const { t } = useTranslation();
73
+ const def = STATUS_MAP[status] ?? { tone: "neutral" as const, icon: Circle };
74
+ const Icon = def.icon;
75
+ const resolvedLabel = labelOverride ?? (status in STATUS_MAP ? t(`status.${status}`) : status);
76
+
77
+ return (
78
+ <span data-slot="status-badge" className={cn(TONE_CLASSES[def.tone], className)}>
79
+ <Icon data-slot="status-badge-icon" aria-hidden="true" />
80
+ <span>{resolvedLabel}</span>
81
+ </span>
82
+ );
83
+ }
@@ -0,0 +1,59 @@
1
+ import * as React from "react";
2
+ import { tableHeadHeightClass } from "../../lib/control-styles";
3
+ import { cn } from "../../lib/utils";
4
+
5
+ export const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
6
+ ({ className, ...props }, ref) => (
7
+ <div className="relative w-full overflow-auto">
8
+ <table ref={ref} className={cn("w-full caption-bottom text-sm", className)} {...props} />
9
+ </div>
10
+ ),
11
+ );
12
+ Table.displayName = "Table";
13
+
14
+ export const TableHeader = React.forwardRef<
15
+ HTMLTableSectionElement,
16
+ React.HTMLAttributes<HTMLTableSectionElement>
17
+ >(({ className, ...props }, ref) => (
18
+ <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
19
+ ));
20
+ TableHeader.displayName = "TableHeader";
21
+
22
+ export const TableBody = React.forwardRef<
23
+ HTMLTableSectionElement,
24
+ React.HTMLAttributes<HTMLTableSectionElement>
25
+ >(({ className, ...props }, ref) => (
26
+ <tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />
27
+ ));
28
+ TableBody.displayName = "TableBody";
29
+
30
+ export const TableRow = React.forwardRef<
31
+ HTMLTableRowElement,
32
+ React.HTMLAttributes<HTMLTableRowElement>
33
+ >(({ className, ...props }, ref) => (
34
+ <tr
35
+ ref={ref}
36
+ className={cn(
37
+ "ui-table-row hover:bg-accent/70 data-[state=selected]:bg-primary/[0.06] border-b transition-colors",
38
+ className,
39
+ )}
40
+ {...props}
41
+ />
42
+ ));
43
+ TableRow.displayName = "TableRow";
44
+
45
+ export const TableHead = React.forwardRef<
46
+ HTMLTableCellElement,
47
+ React.ThHTMLAttributes<HTMLTableCellElement>
48
+ >(({ className, ...props }, ref) => (
49
+ <th ref={ref} data-slot="table-head" className={cn(tableHeadHeightClass, className)} {...props} />
50
+ ));
51
+ TableHead.displayName = "TableHead";
52
+
53
+ export const TableCell = React.forwardRef<
54
+ HTMLTableCellElement,
55
+ React.TdHTMLAttributes<HTMLTableCellElement>
56
+ >(({ className, ...props }, ref) => (
57
+ <td ref={ref} data-slot="table-cell" className={cn(className)} {...props} />
58
+ ));
59
+ TableCell.displayName = "TableCell";
@@ -0,0 +1,42 @@
1
+ import type { ReactNode } from "react";
2
+ import { CheckCircle2, Plane } from "lucide-react";
3
+
4
+ export type TimelineItem = {
5
+ title: ReactNode;
6
+ location?: ReactNode;
7
+ time?: ReactNode;
8
+ note?: ReactNode;
9
+ current?: boolean;
10
+ };
11
+
12
+ export type TimelineProps = {
13
+ items: TimelineItem[];
14
+ };
15
+
16
+ export function Timeline({ items }: TimelineProps) {
17
+ return (
18
+ <div className="ui-timeline">
19
+ {items.map((item, index) => {
20
+ const Icon = item.current ? Plane : CheckCircle2;
21
+ return (
22
+ <div className="ui-timeline-item" key={index}>
23
+ <div className="ui-timeline-rail">
24
+ <span className="ui-timeline-dot" data-current={item.current ? "true" : undefined}>
25
+ <Icon aria-hidden="true" />
26
+ </span>
27
+ {index !== items.length - 1 ? <span className="ui-timeline-line" /> : null}
28
+ </div>
29
+ <div className="ui-timeline-body">
30
+ <div className="ui-timeline-head">
31
+ <span className="ui-timeline-title">{item.title}</span>
32
+ {item.time ? <span className="ui-timeline-time">{item.time}</span> : null}
33
+ </div>
34
+ {item.location ? <div className="ui-timeline-location">{item.location}</div> : null}
35
+ {item.note ? <p className="ui-timeline-note">{item.note}</p> : null}
36
+ </div>
37
+ </div>
38
+ );
39
+ })}
40
+ </div>
41
+ );
42
+ }
@@ -0,0 +1,42 @@
1
+ import type { ReactNode } from "react";
2
+ import { ChevronRight, Package } from "lucide-react";
3
+
4
+ import { Badge } from "./badge";
5
+
6
+ export type TreeListItem = {
7
+ id: string;
8
+ title: ReactNode;
9
+ description?: ReactNode;
10
+ depth?: number;
11
+ active?: boolean;
12
+ badge?: ReactNode;
13
+ };
14
+
15
+ export type TreeListProps = {
16
+ items: TreeListItem[];
17
+ };
18
+
19
+ export function TreeList({ items }: TreeListProps) {
20
+ return (
21
+ <div className="ui-tree-list">
22
+ {items.map((item) => (
23
+ <div
24
+ className="ui-tree-item"
25
+ data-active={item.active ? "true" : undefined}
26
+ data-depth={item.depth ?? 0}
27
+ key={item.id}
28
+ >
29
+ <ChevronRight aria-hidden="true" />
30
+ <Package aria-hidden="true" />
31
+ <div className="ui-tree-item-body">
32
+ <div className="ui-tree-item-title">{item.title}</div>
33
+ {item.description ? (
34
+ <div className="ui-tree-item-description">{item.description}</div>
35
+ ) : null}
36
+ </div>
37
+ {item.badge ? <Badge variant="secondary">{item.badge}</Badge> : null}
38
+ </div>
39
+ ))}
40
+ </div>
41
+ );
42
+ }
@@ -0,0 +1,80 @@
1
+ import type { TreeOptionProp } from "../../../props/components/data-entry.prop";
2
+
3
+ /** Demo data - VN/JP region hierarchy for preview examples and tests. */
4
+ export const REGION_OPTIONS: TreeOptionProp[] = [
5
+ {
6
+ value: "vn",
7
+ label: "Việt Nam",
8
+ children: [
9
+ {
10
+ value: "hcm",
11
+ label: "TP. Hồ Chí Minh",
12
+ children: [
13
+ { value: "q1", label: "Quận 1" },
14
+ { value: "q3", label: "Quận 3" },
15
+ ],
16
+ },
17
+ {
18
+ value: "hn",
19
+ label: "Hà Nội",
20
+ children: [
21
+ { value: "badinh", label: "Ba Đình" },
22
+ { value: "caugiay", label: "Cầu Giấy" },
23
+ ],
24
+ },
25
+ ],
26
+ },
27
+ {
28
+ value: "jp",
29
+ label: "日本",
30
+ children: [
31
+ {
32
+ value: "osaka",
33
+ label: "大阪府",
34
+ children: [
35
+ { value: "chuo", label: "中央区" },
36
+ { value: "kita", label: "北区" },
37
+ ],
38
+ },
39
+ {
40
+ value: "tokyo",
41
+ label: "東京都",
42
+ children: [
43
+ { value: "shinjuku", label: "新宿区" },
44
+ { value: "shibuya", label: "渋谷区" },
45
+ ],
46
+ },
47
+ ],
48
+ },
49
+ ];
50
+
51
+ export const ORG_TREE: TreeOptionProp[] = [
52
+ {
53
+ value: "godx",
54
+ label: "GODX",
55
+ children: [
56
+ {
57
+ value: "logistics",
58
+ label: "Logistics",
59
+ children: [
60
+ { value: "warehouse-osaka", label: "Kho Osaka" },
61
+ { value: "warehouse-hcm", label: "Kho HCM" },
62
+ ],
63
+ },
64
+ {
65
+ value: "platform",
66
+ label: "Platform",
67
+ children: [
68
+ { value: "identity", label: "Identity" },
69
+ { value: "media", label: "Media" },
70
+ ],
71
+ },
72
+ ],
73
+ },
74
+ ];
75
+
76
+ export const TRANSFER_MOCK = Array.from({ length: 12 }, (_, i) => ({
77
+ key: `user-${i + 1}`,
78
+ title: `NV-${String(i + 1).padStart(3, "0")}`,
79
+ description: i % 3 === 0 ? "Kho Osaka" : "Kho HCM",
80
+ }));