@godxjp/ui 28.9.0 → 28.12.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 (244) hide show
  1. package/agent/START-HERE.md +193 -0
  2. package/agent/anti-ai-tells.json +158 -0
  3. package/agent/components/Accordion.json +60 -0
  4. package/agent/components/AccountChip.json +59 -0
  5. package/agent/components/Actions.json +78 -0
  6. package/agent/components/Activity.json +80 -0
  7. package/agent/components/Affix.json +78 -0
  8. package/agent/components/Alert.json +65 -0
  9. package/agent/components/AlertDialog.json +109 -0
  10. package/agent/components/AlertDialogRoot.json +52 -0
  11. package/agent/components/Anchor.json +119 -0
  12. package/agent/components/AppLauncher.json +95 -0
  13. package/agent/components/AppProvider.json +105 -0
  14. package/agent/components/AppSettingPicker.json +88 -0
  15. package/agent/components/AppSettingToggle.json +70 -0
  16. package/agent/components/AppShell.json +159 -0
  17. package/agent/components/AreaChart.json +105 -0
  18. package/agent/components/AspectRatio.json +38 -0
  19. package/agent/components/Attachments.json +77 -0
  20. package/agent/components/AuthAccountSummary.json +64 -0
  21. package/agent/components/AuthDivider.json +35 -0
  22. package/agent/components/AuthFooter.json +48 -0
  23. package/agent/components/AuthIdentity.json +42 -0
  24. package/agent/components/AuthShell.json +108 -0
  25. package/agent/components/AuthStack.json +21 -0
  26. package/agent/components/Avatar.json +89 -0
  27. package/agent/components/Badge.json +96 -0
  28. package/agent/components/Banner.json +48 -0
  29. package/agent/components/BarChart.json +108 -0
  30. package/agent/components/BranchScopePicker.json +89 -0
  31. package/agent/components/Breadcrumb.json +54 -0
  32. package/agent/components/Button.json +133 -0
  33. package/agent/components/Calendar.json +259 -0
  34. package/agent/components/Callout.json +46 -0
  35. package/agent/components/Card.json +112 -0
  36. package/agent/components/CardBar.json +49 -0
  37. package/agent/components/CardContent.json +51 -0
  38. package/agent/components/Carousel.json +50 -0
  39. package/agent/components/Cascader.json +209 -0
  40. package/agent/components/CenteredShell.json +66 -0
  41. package/agent/components/ChatBubble.json +100 -0
  42. package/agent/components/ChatBubbleList.json +64 -0
  43. package/agent/components/ChatComposer.json +160 -0
  44. package/agent/components/ChatSuggestion.json +86 -0
  45. package/agent/components/Checkbox.json +68 -0
  46. package/agent/components/CheckboxGroup.json +96 -0
  47. package/agent/components/CodeBlock.json +64 -0
  48. package/agent/components/Collapsible.json +74 -0
  49. package/agent/components/ColorPicker.json +87 -0
  50. package/agent/components/Command.json +168 -0
  51. package/agent/components/CommandPalette.json +84 -0
  52. package/agent/components/CompactBarTrend.json +101 -0
  53. package/agent/components/Conversations.json +82 -0
  54. package/agent/components/CredentialReveal.json +93 -0
  55. package/agent/components/DataState.json +79 -0
  56. package/agent/components/DataTable.json +268 -0
  57. package/agent/components/DatePicker.json +275 -0
  58. package/agent/components/Descriptions.json +67 -0
  59. package/agent/components/Dialog.json +78 -0
  60. package/agent/components/DraggablePanel.json +106 -0
  61. package/agent/components/DropdownMenu.json +102 -0
  62. package/agent/components/EmptyState.json +83 -0
  63. package/agent/components/ErrorSurface.json +128 -0
  64. package/agent/components/FeatureList.json +43 -0
  65. package/agent/components/Field.json +64 -0
  66. package/agent/components/FilterBar.json +99 -0
  67. package/agent/components/Flex.json +153 -0
  68. package/agent/components/FloatButton.json +91 -0
  69. package/agent/components/Form.json +87 -0
  70. package/agent/components/FormErrors.json +51 -0
  71. package/agent/components/FormField.json +137 -0
  72. package/agent/components/FormFieldArray.json +39 -0
  73. package/agent/components/FormFieldControl.json +129 -0
  74. package/agent/components/FormRoot.json +122 -0
  75. package/agent/components/Heading.json +61 -0
  76. package/agent/components/HoverCard.json +55 -0
  77. package/agent/components/Icon.json +60 -0
  78. package/agent/components/InfiniteQueryState.json +58 -0
  79. package/agent/components/Input.json +122 -0
  80. package/agent/components/InputOTP.json +106 -0
  81. package/agent/components/Label.json +43 -0
  82. package/agent/components/LegalDocumentShell.json +102 -0
  83. package/agent/components/Legend.json +42 -0
  84. package/agent/components/LineChart.json +103 -0
  85. package/agent/components/Link.json +41 -0
  86. package/agent/components/ListRow.json +92 -0
  87. package/agent/components/Logo.json +85 -0
  88. package/agent/components/Marquee.json +91 -0
  89. package/agent/components/Masonry.json +82 -0
  90. package/agent/components/MasterDetail.json +95 -0
  91. package/agent/components/MegaMenu.json +120 -0
  92. package/agent/components/MobileShell.json +73 -0
  93. package/agent/components/NavList.json +63 -0
  94. package/agent/components/NumberInput.json +158 -0
  95. package/agent/components/OrgSwitcher.json +89 -0
  96. package/agent/components/OverlayPortalProvider.json +42 -0
  97. package/agent/components/PageContainer.json +181 -0
  98. package/agent/components/Pagination.json +132 -0
  99. package/agent/components/Paragraph.json +40 -0
  100. package/agent/components/PasswordInput.json +79 -0
  101. package/agent/components/PasswordStrength.json +51 -0
  102. package/agent/components/PermissionMatrix.json +81 -0
  103. package/agent/components/PieChart.json +99 -0
  104. package/agent/components/Popover.json +110 -0
  105. package/agent/components/PrefetchLink.json +65 -0
  106. package/agent/components/Progress.json +79 -0
  107. package/agent/components/Prose.json +57 -0
  108. package/agent/components/QrCode.json +62 -0
  109. package/agent/components/Radio.json +98 -0
  110. package/agent/components/RadioGroup.json +91 -0
  111. package/agent/components/RangeTimeline.json +80 -0
  112. package/agent/components/Rating.json +92 -0
  113. package/agent/components/ResizablePanel.json +69 -0
  114. package/agent/components/ResponsiveGrid.json +77 -0
  115. package/agent/components/Reveal.json +70 -0
  116. package/agent/components/ScrollArea.json +104 -0
  117. package/agent/components/SearchInput.json +98 -0
  118. package/agent/components/Segmented.json +96 -0
  119. package/agent/components/Select.json +397 -0
  120. package/agent/components/Separator.json +86 -0
  121. package/agent/components/ServiceCatalogCta.json +46 -0
  122. package/agent/components/ServiceLauncherCard.json +93 -0
  123. package/agent/components/ServiceRolePanel.json +83 -0
  124. package/agent/components/Sheet.json +85 -0
  125. package/agent/components/Sidebar.json +118 -0
  126. package/agent/components/Skeleton.json +57 -0
  127. package/agent/components/SkeletonArticle.json +71 -0
  128. package/agent/components/SkeletonAvatar.json +50 -0
  129. package/agent/components/SkeletonButton.json +57 -0
  130. package/agent/components/SkeletonForm.json +52 -0
  131. package/agent/components/SkeletonImage.json +37 -0
  132. package/agent/components/SkeletonInput.json +51 -0
  133. package/agent/components/SkeletonNode.json +42 -0
  134. package/agent/components/SkeletonRows.json +49 -0
  135. package/agent/components/SkeletonTable.json +45 -0
  136. package/agent/components/Slider.json +160 -0
  137. package/agent/components/SplitPane.json +66 -0
  138. package/agent/components/StatCard.json +83 -0
  139. package/agent/components/Steps.json +95 -0
  140. package/agent/components/Swatch.json +41 -0
  141. package/agent/components/Switch.json +81 -0
  142. package/agent/components/Table.json +112 -0
  143. package/agent/components/Tabs.json +158 -0
  144. package/agent/components/TagInput.json +105 -0
  145. package/agent/components/Text.json +201 -0
  146. package/agent/components/Textarea.json +126 -0
  147. package/agent/components/ThoughtChain.json +76 -0
  148. package/agent/components/Thumbnail.json +70 -0
  149. package/agent/components/TimePicker.json +200 -0
  150. package/agent/components/TimeRangePicker.json +90 -0
  151. package/agent/components/Timeline.json +47 -0
  152. package/agent/components/TimelineGrid.json +92 -0
  153. package/agent/components/Title.json +67 -0
  154. package/agent/components/Toaster.json +42 -0
  155. package/agent/components/Toggle.json +90 -0
  156. package/agent/components/ToggleGroup.json +102 -0
  157. package/agent/components/Toolbar.json +120 -0
  158. package/agent/components/Tooltip.json +110 -0
  159. package/agent/components/Topbar.json +83 -0
  160. package/agent/components/TopbarItem.json +79 -0
  161. package/agent/components/Transfer.json +141 -0
  162. package/agent/components/Tree.json +185 -0
  163. package/agent/components/TreeSelect.json +232 -0
  164. package/agent/components/TwoFactorSetup.json +79 -0
  165. package/agent/components/Typography.json +42 -0
  166. package/agent/components/Upload.json +221 -0
  167. package/agent/components/UploadCropDialog.json +60 -0
  168. package/agent/components/VisuallyHidden.json +20 -0
  169. package/agent/components/Welcome.json +65 -0
  170. package/agent/components/formatDate.json +46 -0
  171. package/agent/components/inertiaUpload.json +32 -0
  172. package/agent/components/useZodForm.json +39 -0
  173. package/agent/components-index.json +884 -0
  174. package/agent/components.json +15515 -0
  175. package/agent/index.json +56 -0
  176. package/agent/llms.txt +32 -0
  177. package/agent/patterns/account-recovery-settings.json +19 -0
  178. package/agent/patterns/async-data-state.json +20 -0
  179. package/agent/patterns/auth-recovery-panels.json +29 -0
  180. package/agent/patterns/badge-coloring.json +14 -0
  181. package/agent/patterns/common-fixes.json +16 -0
  182. package/agent/patterns/confirm-destructive.json +11 -0
  183. package/agent/patterns/data-table-page.json +18 -0
  184. package/agent/patterns/deferred-loading.json +12 -0
  185. package/agent/patterns/error-pages.json +28 -0
  186. package/agent/patterns/inertia-detail-page.json +13 -0
  187. package/agent/patterns/inertia-list-page.json +15 -0
  188. package/agent/patterns/inertia-persistent-layout.json +14 -0
  189. package/agent/patterns/organization-memberships.json +19 -0
  190. package/agent/patterns/page-sections.json +18 -0
  191. package/agent/patterns/settings-page-responsive.json +18 -0
  192. package/agent/patterns/settings-section-rows.json +23 -0
  193. package/agent/patterns/signup-form.json +13 -0
  194. package/agent/patterns/topbar-account-chip.json +18 -0
  195. package/agent/patterns/transactional-email.json +22 -0
  196. package/agent/patterns-index.json +323 -0
  197. package/agent/patterns.json +342 -0
  198. package/agent/rules.json +237 -0
  199. package/agent/tokens.json +8427 -0
  200. package/agent/vocabulary.json +198 -0
  201. package/dist/components/data-display/service-launcher-card.d.ts +19 -0
  202. package/dist/components/data-display/service-launcher-card.js +14 -1
  203. package/dist/components/data-entry/attachments.js +77 -33
  204. package/dist/components/data-entry/input.js +8 -1
  205. package/dist/components/layout/flex.d.ts +2 -2
  206. package/dist/components/layout/flex.js +2 -0
  207. package/dist/components/ui/avatar.d.ts +1 -18
  208. package/dist/components/ui/avatar.js +1 -36
  209. package/dist/components/ui/tag-input.d.ts +10 -0
  210. package/dist/components/ui/tag-input.js +35 -2
  211. package/dist/contracts/measurement.json +1 -1
  212. package/dist/i18n/messages/en.json +190 -1
  213. package/dist/i18n/messages/ja.json +188 -1
  214. package/dist/i18n/messages/vi.json +188 -1
  215. package/dist/lib/image-loading-status.d.ts +25 -0
  216. package/dist/lib/image-loading-status.js +41 -0
  217. package/dist/props/components/data-entry.prop.d.ts +21 -2
  218. package/dist/props/components/layout.prop.d.ts +42 -0
  219. package/dist/props/registry.d.ts +14 -1
  220. package/dist/props/registry.js +18 -1
  221. package/dist/styles/card-layout.css +10 -4
  222. package/dist/styles/control.css +33 -4
  223. package/dist/styles/data-display-layout.css +1 -1
  224. package/dist/styles/data-entry-layout.css +245 -2
  225. package/dist/styles/layout.css +17 -0
  226. package/dist/styles/navigation-layout.css +3 -1
  227. package/dist/styles/shell-layout.css +2 -0
  228. package/dist/styles/table-layout.css +50 -9
  229. package/dist/tokens/components/attachments.css +18 -9
  230. package/dist/tokens/components/segmented.css +7 -3
  231. package/dist/tokens/components/table.css +2 -1
  232. package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
  233. package/docs/DESIGN-AUTHORITY.md +52 -0
  234. package/docs/DEVELOPMENT.md +81 -6
  235. package/docs/assets/service-mark-rose.svg +6 -0
  236. package/docs/assets/service-mark-teal.svg +5 -0
  237. package/docs/data-display/service-launcher-card.tsx +232 -92
  238. package/docs/data-entry/tag-input.tsx +37 -0
  239. package/docs/layout/flex.tsx +40 -0
  240. package/docs/roadmap/website-components.md +34 -0
  241. package/docs/showcase/marketing-page.tsx +54 -45
  242. package/docs/showcase/table-pagination.tsx +99 -18
  243. package/docs/showcase/theme-customization.tsx +25 -2
  244. package/package.json +8 -5
@@ -0,0 +1,209 @@
1
+ {
2
+ "example": "{`import { Cascader } from \"@godxjp/ui/data-entry\";\n\nconst REGIONS = [\n {\n value: \"jp\",\n label: \"日本\",\n content: [\n {\n value: \"tokyo\",\n label: \"東京都\",\n content: [\n { value: \"shinjuku\", label: \"新宿区\" },\n { value: \"shibuya\", label: \"渋谷区\" },\n ],\n },\n ],\n },\n {\n value: \"vn\",\n label: \"Việt Nam\",\n content: [\n {\n value: \"hcm\",\n label: \"TP. Hồ Chí Minh\",\n content: [\n { value: \"q1\", label: \"Quận 1\" },\n { value: \"q3\", label: \"Quận 3\" },\n ],\n },\n ],\n },\n];\n\n// Controlled single-path\nfunction RegionPicker() {\n const [path, setPath] = React.useState<string[]>([]);\n\n return (\n <Cascader\n options={REGIONS}\n value={path}\n onValueChange={(v) => setPath(v as string[])}\n showSearch\n placeholder=\"Select region…\"\n />\n );\n}\n\n// Multi-path (multiple selection)\nfunction MultiRegionPicker() {\n const [paths, setPaths] = React.useState<string[][]>([]);\n\n return (\n <Cascader\n options={REGIONS}\n multiple\n value={paths}\n onValueChange={(v) => setPaths(v as string[][])}\n showSearch\n />\n );\n}\n\n// With custom field names (data uses 'name'/'id'/'nodes')\n<Cascader\n options={rawApiData}\n fieldNames={{ label: \"name\", value: \"id\", content: \"nodes\" }}\n defaultValue={[\"dept-1\", \"team-3\"]}\n/>\n\n// changeOnSelect: lets user pick a branch node (not only leaves)\n<Cascader\n options={REGIONS}\n changeOnSelect\n onValueChange={(v) => console.log(\"path\", v)}\n/>\n`}",
3
+ "group": "data-entry",
4
+ "importPath": "@godxjp/ui/data-entry",
5
+ "name": "Cascader",
6
+ "props": [
7
+ {
8
+ "description": "Native form field name; repeated values for multiple paths.",
9
+ "name": "name",
10
+ "type": "string"
11
+ },
12
+ {
13
+ "description": "Prevent edits while preserving the displayed value.",
14
+ "name": "readOnly",
15
+ "type": "boolean"
16
+ },
17
+ {
18
+ "description": "The hierarchical option tree. Each node has { value: string; label: ReactNode; disabled?: boolean; isLeaf?: boolean; children?: TreeOptionProp[] }. Normalised internally via fieldNames.",
19
+ "name": "options",
20
+ "required": true,
21
+ "type": "TreeOptionProp[]"
22
+ },
23
+ {
24
+ "description": "Controlled value. Single mode: string[] path (e.g. ['vn','hcm','q1']). Multiple mode: string[][] array of paths.",
25
+ "name": "value",
26
+ "type": "string[] | string[][]"
27
+ },
28
+ {
29
+ "description": "Initial value for uncontrolled mode. Same shape as value.",
30
+ "name": "defaultValue",
31
+ "type": "string[] | string[][]"
32
+ },
33
+ {
34
+ "description": "Fires when selection changes. First arg is the selected path(s); second is the matching node objects. On clear, called with [].",
35
+ "name": "onValueChange",
36
+ "type": "(value: string[] | string[][], selectedOptions?: TreeOptionProp[] | TreeOptionProp[][]) => void"
37
+ },
38
+ {
39
+ "defaultValue": "false",
40
+ "description": "Enable multi-path selection. Renders checkboxes in columns and search results. Panel stays open on each pick. value/defaultValue become string[][].",
41
+ "name": "multiple",
42
+ "type": "boolean"
43
+ },
44
+ {
45
+ "defaultValue": "false",
46
+ "description": "When true, clicking any node (including branch nodes with children) commits that path immediately instead of waiting for a leaf selection.",
47
+ "name": "changeOnSelect",
48
+ "type": "boolean"
49
+ },
50
+ {
51
+ "defaultValue": "false",
52
+ "description": "Renders a CommandInput at the top of the popover. Filters to matching leaf paths across the whole tree when a query is typed; reverts to cascade columns when the query is cleared.",
53
+ "name": "showSearch",
54
+ "type": "boolean"
55
+ },
56
+ {
57
+ "description": "Trigger button placeholder text when no value is selected. Defaults to the i18n key dataEntry.cascader.placeholder.",
58
+ "name": "placeholder",
59
+ "type": "string"
60
+ },
61
+ {
62
+ "defaultValue": "false",
63
+ "description": "Disables the trigger button and prevents the popover from opening.",
64
+ "name": "disabled",
65
+ "type": "boolean"
66
+ },
67
+ {
68
+ "defaultValue": "\"click\"",
69
+ "description": "How child columns are expanded. 'hover' expands on mouseenter and collapses back on mouseleave.",
70
+ "name": "expandTrigger",
71
+ "type": "\"click\" | \"hover\""
72
+ },
73
+ {
74
+ "description": "Remap custom data keys: { label?: string; value?: string; children?: string }. Use when your data uses e.g. 'name' and 'id' instead of 'label' and 'value'.",
75
+ "name": "fieldNames",
76
+ "type": "TreeFieldNamesProp"
77
+ },
78
+ {
79
+ "defaultValue": "true",
80
+ "description": "Shows an X icon on the trigger when a value is selected. Clicking it calls onChange([]) and resets to placeholder. On by default, as antd Cascader; pass `false` on a required field.",
81
+ "name": "allowClear",
82
+ "type": "boolean"
83
+ },
84
+ {
85
+ "description": "Extra Tailwind classes applied to the trigger button.",
86
+ "name": "className",
87
+ "type": "string"
88
+ },
89
+ {
90
+ "description": "HTML id forwarded to the trigger button. Use to associate a <label htmlFor>.",
91
+ "name": "id",
92
+ "type": "string"
93
+ },
94
+ {
95
+ "description": "antd `status`. `error` recolours the control AND sets aria-invalid (a colour-only error fails WCAG 2.2 SC 1.4.1); `warning` recolours only. An aria-invalid injected by FormField always wins.",
96
+ "name": "status",
97
+ "type": "\"error\" | \"warning\""
98
+ },
99
+ {
100
+ "description": "antd `variant` — the control surface. Default `outlined`. Drawn from --control-{surface,filled,borderless}-* tokens, so a theme retunes all three at once.",
101
+ "name": "variant",
102
+ "type": "\"outlined\" | \"filled\" | \"borderless\""
103
+ },
104
+ {
105
+ "description": "antd `loading` — the FIELD is in flight: the trailing chevron becomes a spinner and the trigger reports aria-busy. Distinct from the list-level spinner an async loadOptions shows inside the popup.",
106
+ "name": "loading",
107
+ "type": "boolean"
108
+ },
109
+ {
110
+ "description": "antd `size` — height tier on the shared --control-height ladder.",
111
+ "name": "size",
112
+ "type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
113
+ },
114
+ {
115
+ "description": "antd `open` — controlled panel state. The consumer is the authority: nothing internal closes a pinned panel.",
116
+ "name": "open",
117
+ "type": "boolean"
118
+ },
119
+ {
120
+ "description": "antd `defaultOpen` — uncontrolled initial panel state.",
121
+ "name": "defaultOpen",
122
+ "type": "boolean"
123
+ },
124
+ {
125
+ "description": "antd `onOpenChange` — fires for both controlled and uncontrolled panels.",
126
+ "name": "onOpenChange",
127
+ "type": "(open: boolean) => void"
128
+ },
129
+ {
130
+ "description": "antd `maxTagCount` — how many selected values stay visible before the rest collapse into the overflow node. antd's `\"responsive\"` is not supported (see the parity PR).",
131
+ "name": "maxTagCount",
132
+ "type": "number"
133
+ },
134
+ {
135
+ "description": "antd `maxTagPlaceholder` — the node standing in for what maxTagCount hid. Defaults to a localized `+N`.",
136
+ "name": "maxTagPlaceholder",
137
+ "type": "React.ReactNode | ((omitted: { value: string; label: React.ReactNode }[]) => React.ReactNode)"
138
+ },
139
+ {
140
+ "description": "antd `notFoundContent` — the node shown when the popup has nothing to list. Outranks the string-only emptyMessage.",
141
+ "name": "notFoundContent",
142
+ "type": "React.ReactNode"
143
+ },
144
+ {
145
+ "description": "antd `autoClearSearchValue` (default true) — clear the search box after a pick / on close. Set false to resume the same filtered list on the next open.",
146
+ "name": "autoClearSearchValue",
147
+ "type": "boolean"
148
+ },
149
+ {
150
+ "description": "antd `showCheckedStrategy` (multiple only). SHOW_PARENT collapses a fully-checked branch into the branch itself; SHOW_CHILD (default) lists the leaves.",
151
+ "name": "showCheckedStrategy",
152
+ "type": "\"SHOW_CHILD\" | \"SHOW_PARENT\""
153
+ },
154
+ {
155
+ "description": "antd `loadData` — lazy children. Called ONCE per branch that has no children and isLeaf !== true, the first time it is expanded; push the fetched children into options.",
156
+ "name": "loadData",
157
+ "type": "(selectedOptions: TreeOptionProp[]) => void | Promise<void>"
158
+ },
159
+ {
160
+ "description": "antd `displayRender` — owns the trigger label built from the selected path.",
161
+ "name": "displayRender",
162
+ "type": "(labels: string[], selectedOptions?: TreeOptionProp[]) => React.ReactNode"
163
+ },
164
+ {
165
+ "description": "antd `optionRender` — owns a column row's body. The checkbox, check mark and chevron stay with the component.",
166
+ "name": "optionRender",
167
+ "type": "(option: TreeOptionProp) => React.ReactNode"
168
+ },
169
+ {
170
+ "description": "Controlled search query (antd `showSearch.searchValue`).",
171
+ "name": "search",
172
+ "type": "string"
173
+ },
174
+ {
175
+ "description": "Search query change (antd `showSearch.onSearch`).",
176
+ "name": "onSearchChange",
177
+ "type": "(query: string) => void"
178
+ }
179
+ ],
180
+ "related": [
181
+ "TreeSelect — use when the hierarchy is a collapsible tree (expand/collapse nodes) rather than side-by-side columns, and when a single flat value string (node key) is sufficient instead of a full ancestor path. TreeSelect also supports treeCheckable for multi-select.",
182
+ "Select — use for a flat (non-hierarchical) list of options. Cascader is only needed when items have meaningful parent–child levels.",
183
+ "Transfer — use when the user needs to shuttle multiple items between two panels; not for hierarchical path selection."
184
+ ],
185
+ "rules": [
186
+ 3,
187
+ 6,
188
+ 23,
189
+ 31
190
+ ],
191
+ "storyPath": "data-entry/Cascader.stories.tsx",
192
+ "tagline": "Multi-level hierarchical path picker (Popover + cascading columns); value is always a string[] path, never a flat ID — passing a bare string breaks it.",
193
+ "usage": [
194
+ "DO pass a string[] path as value in single mode (e.g. ['country','region','city']). DON'T pass a flat string ID — the component treats value as an ordered path array and will render nothing if you pass a bare string.",
195
+ "DO use value + onChange together for controlled mode, or defaultValue alone for uncontrolled. DON'T mix both — providing value without onChange makes the field read-only (the internal state won't update).",
196
+ "DO set multiple={true} and pass value as string[][] (array of paths) for multi-selection. onChange receives string[][] in that mode. Mixing single-mode shape with multiple={true} silently produces no selection.",
197
+ "DON'T hand-roll a search input next to Cascader. Use showSearch={true} — it adds a built-in CommandInput that filters leaf paths across the full tree and reverts to cascade columns when cleared.",
198
+ "DO use fieldNames to remap data keys ({label:'name', value:'id', children:'nodes'}) rather than pre-transforming your API data. This keeps options in their original shape.",
199
+ "For form submission, Cascader has no 'name' prop. Wrap in a controlled pattern and store the path array in your form state (useForm/useState). For Inertia useForm, keep the field as an array (e.g. data.categoryPath = ['a','b','c'])."
200
+ ],
201
+ "useCases": [
202
+ "Geographic drilldown (Country → Prefecture → City) for address or branch-office pickers in accounting or logistics forms.",
203
+ "Expense category selection (e.g. Operating Expenses → Marketing → Digital Ads) where the full classification path is required for the general ledger.",
204
+ "Product taxonomy navigation (Department → Category → Sub-category) in inventory or invoice line-item entry.",
205
+ "Organisational unit picker (Company → Division → Department) in budget allocation or approval-routing configurations.",
206
+ "Multi-region filter in a report or dashboard filter bar, using multiple={true} to allow selecting several leaf locations at once.",
207
+ "Any deeply nested classification where the relationship between levels is meaningful and must be captured — not just the leaf value."
208
+ ]
209
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "example": "import { CenteredShell, Topbar, Flex } from \"@godxjp/ui/layout\";\nimport { AppSettingPicker } from \"@godxjp/ui/navigation\";\nimport { Avatar, AvatarFallback, Card, CardContent, CardHeader, CardTitle } from \"@godxjp/ui/data-display\";\nimport { Button, Text } from \"@godxjp/ui/general\";\n\nexport function MyPage() {\n return (\n <CenteredShell\n width=\"md\"\n topbar={\n <Topbar\n start={\n <Avatar className=\"rounded-md\">\n <AvatarFallback className=\"bg-primary text-primary-foreground font-bold\">G</AvatarFallback>\n </Avatar>\n }\n end={\n <>\n <AppSettingPicker kind=\"locale\" />\n <Button variant=\"ghost\" size=\"sm\">田中 太郎</Button>\n </>\n }\n />\n }\n footer={<Text size=\"xs\" tone=\"muted\">© 2026 GodX</Text>}\n >\n <Flex direction=\"col\" gap=\"lg\">\n <Card>\n <CardHeader><CardTitle level={1}>マイページ</CardTitle></CardHeader>\n <CardContent>アカウントの概要。</CardContent>\n </Card>\n </Flex>\n </CenteredShell>\n );\n}",
3
+ "group": "layout",
4
+ "importPath": "@godxjp/ui/layout",
5
+ "name": "CenteredShell",
6
+ "props": [
7
+ {
8
+ "description": "Centred column content — page sections (identity hero, org picker, service grid, team list). Top-aligned and scrolls; NOT vertically centred like AuthShell's card.",
9
+ "name": "children",
10
+ "required": true,
11
+ "type": "ReactNode"
12
+ },
13
+ {
14
+ "description": "Top bar slot (banner) — a <Topbar> with brand + real actions (AppSettingPicker, user menu, sign-out). Wrapped in the SAME padded chrome as AppShell's topbar (inline padding, border, backdrop) WITHOUT a sidebar; omit → no banner. Never hand-roll a bar (the bare Topbar ships no padding — the .ui-topbar zero-inset footgun).",
15
+ "name": "topbar",
16
+ "type": "ReactNode"
17
+ },
18
+ {
19
+ "description": "Footer slot (contentinfo) pinned to the bottom (legal links, locale switch, support). Omit → no footer.",
20
+ "name": "footer",
21
+ "type": "ReactNode"
22
+ },
23
+ {
24
+ "description": "Max-width of the centred column: sm ~32rem, md (default) ~46rem, lg ~64rem — all wider than AuthShell's 24rem auth card. A service retunes each tier via --centered-shell-width-*.",
25
+ "name": "width",
26
+ "type": "\"sm\" | \"md\" | \"lg\""
27
+ },
28
+ {
29
+ "description": "Block alignment of the centred column inside the 100dvh shell. \"start\" (default) keeps the top-aligned flowing/scrolling page shape. Overflowing content still scrolls from the top, so a long localized message is never clipped.",
30
+ "name": "align",
31
+ "type": "\"start\" | \"center\""
32
+ },
33
+ {
34
+ "defaultValue": "\"default\"",
35
+ "description": "Whole-page shell contract. \"default\" emits no attribute and keeps the exact box. \"public-landing\" owns the PUBLIC landing geometry: ONE content measure shared by the header bar, the centred column and the footer (--centered-shell-landing-max-width, 67.5rem), the section rhythm between page sections, the flat elevation-free card chrome (--centered-shell-landing-card-shadow: none) and the hero h1 tier — plus the compact step at 40rem. A landing composition therefore needs no page-local CSS, no max-width wrapper and no descendant selector against shell internals.",
36
+ "name": "preset",
37
+ "type": "\"default\" | \"public-landing\""
38
+ }
39
+ ],
40
+ "related": [
41
+ "AppShell — the shell for authenticated app pages WITH a sidebar nav rail. CenteredShell is its no-sidebar sibling (same padded topbar chrome, a centred column instead of a full-bleed main).",
42
+ "AuthShell — the UNAUTHENTICATED root shell (login/mfa/reset): a ~24rem card centred vertically, with its own banner `actions` slot and a `measure=\"wide\"` split-login measure. CenteredShell is the AUTHENTICATED centred-page counterpart. Never nest the two.",
43
+ "Topbar — compose it into `topbar`; CenteredShell supplies the padded chrome the bare Topbar lacks.",
44
+ "PageContainer — for a titled section INSIDE the column; or compose <Card>/<ResponsiveGrid> sections directly."
45
+ ],
46
+ "rules": [
47
+ 23
48
+ ],
49
+ "storyPath": "layout/CenteredShell.stories.tsx",
50
+ "tagline": "Authenticated, no-sidebar, centred-column page shell (hosted-ID My Page / account / standalone settings) — padded topbar with real actions + a width-tiered centred column, zero custom CSS.",
51
+ "usage": [
52
+ "DO use CenteredShell for an AUTHENTICATED page that has a topbar with actions but NO sidebar — the hosted-ID 'My Page', an account / self-service surface, a standalone settings page. It is the third shell: AppShell (needs a sidebar) · AuthShell (unauthenticated narrow card) · CenteredShell (authenticated centred column).",
53
+ "DO put a <Topbar start={<brand/>} end={<actions/>}/> in `topbar` — CenteredShell wraps it in the padded `.app-topbar` chrome, so you get inline padding + border + backdrop with zero custom CSS. Do NOT hand-roll a bar with raw `padding-inline` — the bare Topbar primitive ships no inset (the .ui-topbar zero-padding footgun) and content sits flush to the edge.",
54
+ "DO pick `width` by content: `sm` (~32rem) for a single settings form, `md` (default, ~46rem) for a My Page of stacked sections, `lg` (~64rem) for a service-launcher grid. All are wider than AuthShell's 24rem card.",
55
+ "DO wrap an individual section in <Reveal> for entrance motion — CenteredShell stays layout-only and delegates prefers-reduced-motion handling to Reveal (same as AuthShell).",
56
+ "DO NOT use AuthShell for an authenticated page (it is the UNAUTHENTICATED root and imposes auth-card geometry), and DO NOT force AppShell with an empty sidebar — use CenteredShell. Conversely, do NOT reach for CenteredShell to build a login page: AuthShell has its own `actions` slot for the locale/theme controls and a `measure=\"wide\"` for the split brand-panel login. Never nest it inside AppShell/AuthShell (or vice-versa); it is a ROOT shell.",
57
+ "DO use `align=\"center\"` (+ `width=\"sm\"`) for a SYSTEM-level standalone page — a 500/503 error surface, a maintenance notice. It centres the column in the 100dvh shell at 1440/1024/390 with no consumer `min-h-dvh` / flex CSS and no className; the knob is --centered-shell-column-offset-block. For an actual 403/404/500/503 page do NOT wire this by hand — use `ErrorSurface`, which renders this shell itself in `mode=\"system\"`."
58
+ ],
59
+ "useCases": [
60
+ "Hosted GoDX ID 'My Page': <CenteredShell topbar={<Topbar start={<Brand/>} end={<><AppSettingPicker kind=\"locale\"/><UserMenu/></>}/>} footer={<Footer/>} width=\"md\"> with an identity hero, an org picker, a service-launcher grid and a team list.",
61
+ "Account / self-service settings surface (no admin sidebar): stacked <Card> sections (profile, security, sessions) in a centred `md` column under a topbar with a user menu.",
62
+ "Standalone single settings page: `width=\"sm\"` with one <Card> of <Field>s and a save action.",
63
+ "System error / maintenance page (500 · 503): `<CenteredShell align=\"center\" width=\"sm\">` around the canonical error body (status code <Text mono tabular> + <EmptyState icon tone title description action> + optional request-ID / maintenance line). See the `error-pages` pattern.",
64
+ "Service launcher / app picker after sign-in: `width=\"lg\"` with a <ResponsiveGrid> of app cards under the brand topbar."
65
+ ]
66
+ }
@@ -0,0 +1,100 @@
1
+ {
2
+ "docPath": "data-display/chat-bubble.tsx",
3
+ "example": "import { Avatar, AvatarFallback, ChatBubble } from \"@godxjp/ui/data-display\";\nimport { formatAppTime } from \"@godxjp/ui/datetime\";\n\n<ChatBubble\n placement=\"start\"\n variant=\"filled\"\n avatar={<Avatar aria-hidden=\"true\"><AvatarFallback>AI</AvatarFallback></Avatar>}\n header={t(\"chat.bubble.assistant\")}\n footer={formatAppTime(message.sentAt)}\n typing={{ step: 2, interval: 24 }}\n>\n {message.text}\n</ChatBubble>\n\n// The reply has been requested and has not arrived yet.\n<ChatBubble placement=\"start\" header={t(\"chat.bubble.assistant\")} loading />\n\n// A failed send: colour AND a localized sr-only word, never colour alone.\n<ChatBubble placement=\"end\" tone=\"destructive\" header={t(\"chat.bubble.you\")}>\n {draft}\n</ChatBubble>",
4
+ "group": "data-display",
5
+ "importPath": "@godxjp/ui/data-display",
6
+ "name": "ChatBubble",
7
+ "props": [
8
+ {
9
+ "description": "The message. Pass a plain STRING to make `typing` animatable — a ReactNode has no character count to reveal, so it always renders whole.",
10
+ "name": "children",
11
+ "type": "ReactNode"
12
+ },
13
+ {
14
+ "defaultValue": "\"start\"",
15
+ "description": "Side of the conversation, on the LOGICAL inline axis: \"start\" is the other party, \"end\" is the reader's own message. It flips automatically under dir=\"rtl\" — never branch on locale.",
16
+ "name": "placement",
17
+ "type": "\"start\" | \"end\""
18
+ },
19
+ {
20
+ "defaultValue": "\"filled\"",
21
+ "description": "Structural treatment of the body. Ant Design X's `shadow` is absent on purpose: this is a 1px-border system with no drop shadows. Status colour is `tone`, never `variant`.",
22
+ "name": "variant",
23
+ "type": "\"filled\" | \"borderless\" | \"outlined\""
24
+ },
25
+ {
26
+ "description": "The author's mark — a real <Avatar>, never a styled div. Mark it aria-hidden when `header` already names the author.",
27
+ "name": "avatar",
28
+ "type": "ReactNode"
29
+ },
30
+ {
31
+ "description": "Line above the body naming the turn. When present it becomes the bubble's ACCESSIBLE NAME (aria-labelledby), so keep it text.",
32
+ "name": "header",
33
+ "type": "ReactNode"
34
+ },
35
+ {
36
+ "description": "Line below the body — a timestamp (format it with formatAppTime from @godxjp/ui/datetime), per-message actions, a token count.",
37
+ "name": "footer",
38
+ "type": "ReactNode"
39
+ },
40
+ {
41
+ "defaultValue": "false",
42
+ "description": "The reply was requested and has not arrived: renders Skeleton bars (aria-hidden, they are decorative) plus a localized sr-only line, and sets aria-busy on the article.",
43
+ "name": "loading",
44
+ "type": "boolean"
45
+ },
46
+ {
47
+ "defaultValue": "false",
48
+ "description": "Stream the text in. `true` = 1 character every 50ms; the object form retunes it. While streaming the half-typed text is aria-hidden and the article is aria-busy, so the log announces the finished message ONCE. Under prefers-reduced-motion: reduce the full text renders immediately and no timer starts.",
49
+ "name": "typing",
50
+ "type": "boolean | { step?: number; interval?: number }"
51
+ },
52
+ {
53
+ "defaultValue": "\"md\"",
54
+ "description": "Type step and inner inset. Never \"default\".",
55
+ "name": "size",
56
+ "type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
57
+ },
58
+ {
59
+ "defaultValue": "\"default\"",
60
+ "description": "Status intent for a message that is not ordinary conversation (a failed send, a rate-limit warning). Washes the role colour like Alert AND renders a localized sr-only tone word — never colour alone.",
61
+ "name": "tone",
62
+ "type": "\"default\" | \"info\" | \"success\" | \"warning\" | \"destructive\""
63
+ },
64
+ {
65
+ "description": "DOM id forwarded to the bubble's `<article>`. Supply one when something outside the bubble has to point at it (an `aria-describedby` from a retry button, a deep link to a single message); otherwise leave it off and let React generate the ids the header/label wiring needs.",
66
+ "name": "id",
67
+ "type": "string"
68
+ }
69
+ ],
70
+ "related": [
71
+ "ChatBubbleList — the feed. Use it whenever there is more than one message: it owns stick-to-bottom, the jump-to-latest affordance and the single aria-live region.",
72
+ "ListRow — a single-line entity row for short lists (sessions, tokens). Use ListRow for records, ChatBubble for conversation turns.",
73
+ "Alert — a page-level status banner. A toned ChatBubble is a status INSIDE a conversation; an Alert is a status about the screen.",
74
+ "Prose — use it inside `children` when the assistant returns rendered Markdown; ChatBubble owns the container, Prose owns the typography of the body.",
75
+ "Skeleton — what `loading` renders. Do not compose it yourself around a bubble; the prop also sets aria-busy and the localized status line."
76
+ ],
77
+ "rules": [
78
+ 6,
79
+ 23,
80
+ 44,
81
+ 45
82
+ ],
83
+ "storyPath": "data-display/ChatBubble.stories.tsx",
84
+ "tagline": "One message in a conversation: an author mark, a header naming the turn, the body, and a footer. Renders as an <article> inside ChatBubbleList's log; `loading` shows Skeleton and `typing` streams a string in, dropping the animation entirely under prefers-reduced-motion.",
85
+ "usage": [
86
+ "DO let ChatBubbleList own the feed. A lone ChatBubble is for a single quoted message; a conversation is `items` + `roles` on the list, which is where stick-to-bottom and the one aria-live region live.",
87
+ "DO pass a real <Avatar> to `avatar` and a plain string to `header`. The header is the accessible name of the bubble, so a decorative avatar next to it should be aria-hidden.",
88
+ "DO pass `typing` on the streaming message only, and only when its body is a string. Do not re-mount the bubble on every token — change `children` and let the component reveal it.",
89
+ "DO format timestamps for `footer` with formatAppTime / formatAppDate from @godxjp/ui/datetime (Intl.DateTimeFormat + the AppProvider timezone). Never hand-build a date string.",
90
+ "DO NOT put an aria-live region inside a bubble. The feed owns exactly one; one per bubble floods a screen reader on every token.",
91
+ "DO NOT reach for `variant` to signal an error — that is `tone`. `variant` is structural (filled/borderless/outlined) and has no status values.",
92
+ "DO NOT hand-roll the bubble as a rounded div with a background: placement, the RTL flip, the max measure and the tone wash are all tokens on this component (--chat-bubble-*)."
93
+ ],
94
+ "useCases": [
95
+ "An AI assistant panel — assistant turns on the start side, the operator's own prompts on the end side, with the in-flight reply streaming via `typing`.",
96
+ "A support-inbox thread where an agent reads the customer's history: the same feed renders both parties and marks a failed send with tone=\"destructive\".",
97
+ "A tool/agent transcript where a system note (tone=\"info\", variant=\"outlined\") sits between conversational turns.",
98
+ "A quoted single message inside a Card — a reported message in a moderation screen, rendered as one ChatBubble with a footer carrying the report time."
99
+ ]
100
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "docPath": "data-display/chat-bubble.tsx",
3
+ "example": "import { ChatBubbleList, type ChatMessageProp } from \"@godxjp/ui/data-display\";\n\nconst messages: ChatMessageProp[] = [\n { id: \"m1\", role: \"assistant\", content: \"How can I help?\" },\n { id: \"m2\", role: \"user\", content: \"Summarise yesterday's invoices.\" },\n { id: \"m3\", role: \"assistant\", content: reply, typing: true },\n];\n\n<ChatBubbleList\n label={t(\"chat.list.label\")}\n items={messages}\n roles={{\n assistant: { placement: \"start\", variant: \"filled\", avatar: assistantAvatar },\n user: { placement: \"end\", variant: \"outlined\" },\n system: { placement: \"start\", variant: \"borderless\", tone: \"info\", size: \"sm\" },\n }}\n/>",
4
+ "group": "data-display",
5
+ "importPath": "@godxjp/ui/data-display",
6
+ "name": "ChatBubbleList",
7
+ "props": [
8
+ {
9
+ "description": "`{ id, role?, content?, ...ChatBubbleProp }` in conversation order, oldest first. `id` is the React key AND the rendered <article>'s DOM id.",
10
+ "name": "items",
11
+ "required": true,
12
+ "type": "ChatMessageProp[]"
13
+ },
14
+ {
15
+ "description": "Per-role bubble defaults, merged UNDER each message's own props: `{ assistant: { placement: \"start\" }, user: { placement: \"end\", variant: \"outlined\" } }`. Written once instead of on every message.",
16
+ "name": "roles",
17
+ "type": "Record<string, Partial<ChatBubbleProp>>"
18
+ },
19
+ {
20
+ "defaultValue": "true",
21
+ "description": "Keep the newest message in view WHILE the reader is at the bottom. It is not \"scroll to the bottom when content arrives\": scrolling up revokes the pin until the reader asks for it back, because yanking them down mid-sentence is a change of context they did not request (WCAG 3.2.5).",
22
+ "name": "autoScroll",
23
+ "type": "boolean"
24
+ },
25
+ {
26
+ "description": "Accessible name of the feed; lands on aria-label. Defaults to the localized t(\"chat.list.label\").",
27
+ "name": "label",
28
+ "type": "string"
29
+ },
30
+ {
31
+ "description": "DOM id forwarded to the feed's scroll container (`role=\"log\"`). Supply one when a control outside the feed must reference it — an `aria-controls` on a \"jump to latest\" button of your own, or a skip link that moves focus into the transcript.",
32
+ "name": "id",
33
+ "type": "string"
34
+ }
35
+ ],
36
+ "related": [
37
+ "ChatBubble — one message; this list renders them and supplies per-role defaults.",
38
+ "ScrollArea — the scrolling primitive underneath. Use ScrollArea anchor=\"bottom\" directly for a non-conversational live stream (an audit log); use ChatBubbleList when the rows are conversation turns.",
39
+ "Timeline — an ordered event rail with no scale and no live region. Use Timeline for a record's history, ChatBubbleList for a dialogue.",
40
+ "DataTable — for many rows that need sorting, filtering and pagination. A conversation is neither sorted nor paged."
41
+ ],
42
+ "rules": [
43
+ 6,
44
+ 23,
45
+ 44,
46
+ 45
47
+ ],
48
+ "storyPath": "data-display/ChatBubbleList.stories.tsx",
49
+ "tagline": "The message feed. It owns STICK-TO-BOTTOM (auto-scroll only while the reader is already at the bottom; the moment they scroll up the pin is revoked and a focusable jump-to-latest button appears), one aria-live=\"polite\" role=\"log\" region for the whole conversation, and per-role bubble defaults.",
50
+ "usage": [
51
+ "DO drive the feed from `items` and shape it with `roles`. Mapping ChatBubbles yourself inside a ScrollArea loses stick-to-bottom, the jump affordance and the single live region — the three reasons this component exists.",
52
+ "DO give the list a DEFINITE height — `className=\"h-96\"`, or a flex/grid parent that hands it a track. It scrolls inside itself; measured at 1280px, a list with no height grew to its content (1,952px) inside a 256px Card, scrollHeight === clientHeight, and stick-to-bottom had nothing to anchor.",
53
+ "DO keep `id` stable per message. It is the React key, so a regenerated id re-mounts the bubble and restarts any typing animation.",
54
+ "DO leave autoScroll on for a live conversation and turn it off for an archived transcript, where landing on the newest message is not what the reader wants.",
55
+ "DO NOT add your own aria-live region inside the feed, and do not put one on a bubble. This list already declares role=\"log\" with aria-live=\"polite\"; a second region double-announces every message.",
56
+ "DO NOT scroll the viewport yourself on every append. That is the defect stick-to-bottom exists to prevent; the reader's scroll position is the only thing that grants the pin."
57
+ ],
58
+ "useCases": [
59
+ "The transcript pane of an AI assistant screen, above a ChatComposer, streaming the assistant's reply into the last bubble.",
60
+ "A support conversation in an admin detail page, where an operator scrolls up to re-read an earlier message while new ones keep arriving.",
61
+ "An agent/tool run log rendered as a conversation, with system notes as toned bubbles between turns.",
62
+ "An archived thread opened read-only from a report (autoScroll={false}), so the reader lands where the citation is rather than at the end."
63
+ ]
64
+ }
@@ -0,0 +1,160 @@
1
+ {
2
+ "docPath": "data-entry/chat-composer.tsx",
3
+ "example": "import { ChatComposer } from \"@godxjp/ui/data-entry\";\n\nconst [draft, setDraft] = useState(\"\");\nconst [streaming, setStreaming] = useState(false);\n\n<ChatComposer\n value={draft}\n onValueChange={setDraft}\n onSubmit={(text) => { send(text); setDraft(\"\"); }}\n loading={streaming}\n onCancel={() => setStreaming(false)}\n placeholder={t(\"chat.placeholder\")}\n footer={t(\"dataEntry.chatComposer.hintEnter\")}\n/>",
4
+ "group": "data-entry",
5
+ "importPath": "@godxjp/ui/data-entry",
6
+ "name": "ChatComposer",
7
+ "props": [
8
+ {
9
+ "description": "Controlled draft text. Pair with onValueChange or the box freezes.",
10
+ "name": "value",
11
+ "type": "string"
12
+ },
13
+ {
14
+ "description": "Uncontrolled initial draft text.",
15
+ "name": "defaultValue",
16
+ "type": "string"
17
+ },
18
+ {
19
+ "description": "Draft-text change handler; fires on every keystroke, including during an IME conversion.",
20
+ "name": "onValueChange",
21
+ "type": "(value: string) => void"
22
+ },
23
+ {
24
+ "description": "Send the draft. Never fires for empty or whitespace-only text (unless allowEmptySubmit, which passes \"\"), nor while loading/disabled/readOnly.",
25
+ "name": "onSubmit",
26
+ "type": "(value: string) => void"
27
+ },
28
+ {
29
+ "description": "Stop the in-flight response. Only reachable while loading.",
30
+ "name": "onCancel",
31
+ "type": "() => void"
32
+ },
33
+ {
34
+ "defaultValue": "false",
35
+ "description": "A response is streaming: the trailing action BECOMES cancel. Send and cancel never render together.",
36
+ "name": "loading",
37
+ "type": "boolean"
38
+ },
39
+ {
40
+ "defaultValue": "\"enter\"",
41
+ "description": "\"enter\": Enter sends, Shift+Enter breaks the line. \"shiftEnter\": the inverse, for long deliberate drafts. \"modEnter\" (library extension; antd X Sender has only the first two): ⌘+Enter on Apple platforms, Ctrl+Enter elsewhere sends, while Enter and Shift+Enter both break the line — the record-comment convention (GitHub, Jira, Linear).",
42
+ "name": "submitType",
43
+ "type": "\"enter\" | \"shiftEnter\" | \"modEnter\""
44
+ },
45
+ {
46
+ "defaultValue": "false",
47
+ "description": "Let an empty or whitespace-only draft be sent when header/footer carry payload of their own (a status change on a record). The send button stays enabled and the button and keyboard submit call onSubmit(\"\"). Still blocked while loading/disabled/readOnly.",
48
+ "name": "allowEmptySubmit",
49
+ "type": "boolean"
50
+ },
51
+ {
52
+ "description": "Empty-state text of the draft box — pass it through t() at the call site.",
53
+ "name": "placeholder",
54
+ "type": "string"
55
+ },
56
+ {
57
+ "defaultValue": "false",
58
+ "description": "Disable the composer and every action.",
59
+ "name": "disabled",
60
+ "type": "boolean"
61
+ },
62
+ {
63
+ "defaultValue": "false",
64
+ "description": "Show the draft without allowing an edit; still focusable.",
65
+ "name": "readOnly",
66
+ "type": "boolean"
67
+ },
68
+ {
69
+ "description": "Slot ABOVE the draft row — attachments, a reply-to banner, a model picker.",
70
+ "name": "header",
71
+ "type": "React.ReactNode"
72
+ },
73
+ {
74
+ "description": "Slot at the inline START of the draft row — an attach Button, an Avatar.",
75
+ "name": "prefix",
76
+ "type": "React.ReactNode"
77
+ },
78
+ {
79
+ "description": "Slot BELOW the draft row — a hint line, a token counter.",
80
+ "name": "footer",
81
+ "type": "React.ReactNode"
82
+ },
83
+ {
84
+ "description": "Extra trailing actions, rendered BEFORE the send/cancel action.",
85
+ "name": "actions",
86
+ "type": "React.ReactNode"
87
+ },
88
+ {
89
+ "defaultValue": "\"md\"",
90
+ "description": "Height tier on the shared --control-height ladder; moves both auto-grow bounds together.",
91
+ "name": "size",
92
+ "type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
93
+ },
94
+ {
95
+ "description": "Hard ceiling on the draft length, forwarded to the textarea.",
96
+ "name": "maxLength",
97
+ "type": "number"
98
+ },
99
+ {
100
+ "description": "Validation state the frame paints. error also reports aria-invalid (colour alone fails WCAG 1.4.1).",
101
+ "name": "status",
102
+ "type": "\"error\" | \"warning\""
103
+ },
104
+ {
105
+ "description": "Accessible name override for the send action (localized default otherwise).",
106
+ "name": "submitLabel",
107
+ "type": "string"
108
+ },
109
+ {
110
+ "description": "Accessible name override for the cancel action (localized default otherwise).",
111
+ "name": "cancelLabel",
112
+ "type": "string"
113
+ },
114
+ {
115
+ "description": "Keydown on the draft box — how ChatSuggestion drives its list. A handler that calls preventDefault() owns the key, and the composer will not treat it as a send.",
116
+ "name": "onKeyDown",
117
+ "type": "React.KeyboardEventHandler<HTMLTextAreaElement>"
118
+ },
119
+ {
120
+ "description": "Native form name, forwarded to the textarea.",
121
+ "name": "name",
122
+ "type": "string"
123
+ },
124
+ {
125
+ "description": "DOM id of the textarea (the semantic focus target FormField labels).",
126
+ "name": "id",
127
+ "type": "string"
128
+ }
129
+ ],
130
+ "related": [
131
+ "Textarea — the primitive underneath. Use it directly for an ordinary multi-line form field with no send action.",
132
+ "ChatSuggestion — wraps ChatComposer to add trigger-character (/) autocomplete.",
133
+ "ChatBubbleList — the feed the composer sends into.",
134
+ "SearchInput — a single-line query field; a composer is multi-line and holds a draft."
135
+ ],
136
+ "rules": [
137
+ 2,
138
+ 6,
139
+ 43,
140
+ 45
141
+ ],
142
+ "storyPath": "data-entry/ChatComposer.stories.tsx",
143
+ "tagline": "The message input of a conversation (Ant Design X Sender): an auto-growing Textarea plus exactly ONE trailing action — send, or cancel while a response streams. Enter / Shift+Enter / ⌘-or-Ctrl+Enter is configurable and never fires during an IME conversion.",
144
+ "usage": [
145
+ "DO pair a controlled `value` with `onValueChange` — a controlled value with no synchronised handler is the classic frozen-input bug, and it freezes the whole conversation.",
146
+ "DO wrap it in FormField when the composer is a labelled field; the label/helper/error contract lands on the <textarea>, which is the semantic focus target (ref goes there too).",
147
+ "DON'T hand-roll Enter-to-send. An IME conversion (ja/vi) fires a real Enter to ACCEPT a candidate; ChatComposer already guards compositionstart/compositionend, and skipping that guard makes Japanese and Vietnamese input impossible.",
148
+ "DON'T render your own stop button beside the send button — set `loading` and the trailing action becomes cancel. Exactly one trailing action exists at a time (the picker trailing-action discipline).",
149
+ "DO put a hint in `footer` (t('dataEntry.chatComposer.hintEnter') / 'hintShiftEnter') when you flip `submitType` — the keystroke contract is invisible otherwise. For submitType=\"modEnter\" use t('dataEntry.chatComposer.hintModEnter', { modifier: isApplePlatform() ? '⌘' : 'Ctrl' }) with isApplePlatform from @godxjp/ui/lib/utils — the same platform test the composer uses to pick metaKey vs ctrlKey.",
150
+ "DO set `allowEmptySubmit` (not a hidden fake draft) when the composer also submits field changes from `header`/`footer`; your onSubmit receives \"\" and decides whether anything changed.",
151
+ "DON'T size it with a className height: the box grows between --chat-composer-min-height and --chat-composer-max-height, both derived from the --control-height tier. Use `size`, or re-tune the two tokens in your theme."
152
+ ],
153
+ "useCases": [
154
+ "The message box of an AI assistant or support chat, under a ChatBubbleList feed.",
155
+ "A comment composer on a record detail screen (prefix = attach Button, footer = character counter).",
156
+ "A long-form reply box where Enter must break the line: submitType=\"shiftEnter\".",
157
+ "A comment bar on an issue/record where Enter breaks the line and ⌘/Ctrl+Enter posts, and a status change may be posted without text: submitType=\"modEnter\" + allowEmptySubmit.",
158
+ "A streaming answer the user can stop: loading + onCancel."
159
+ ]
160
+ }