@godxjp/ui 28.8.0 → 28.10.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 (271) 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 +76 -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 +86 -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 +15507 -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 +8422 -0
  200. package/agent/vocabulary.json +198 -0
  201. package/dist/components/data-entry/input.js +8 -1
  202. package/dist/components/layout/flex.d.ts +2 -2
  203. package/dist/components/layout/flex.js +2 -0
  204. package/dist/components/ui/tag-input.d.ts +10 -0
  205. package/dist/components/ui/tag-input.js +35 -2
  206. package/dist/contracts/measurement.json +1 -1
  207. package/dist/i18n/messages/en.json +23 -1
  208. package/dist/i18n/messages/ja.json +21 -1
  209. package/dist/i18n/messages/vi.json +21 -1
  210. package/dist/lib/variants.js +4 -1
  211. package/dist/props/components/data-entry.prop.d.ts +21 -2
  212. package/dist/props/components/layout.prop.d.ts +42 -0
  213. package/dist/props/registry.d.ts +9 -0
  214. package/dist/props/registry.js +6 -0
  215. package/dist/props/vocabulary/layout.prop.d.ts +1 -1
  216. package/dist/styles/base.css +47 -14
  217. package/dist/styles/card-layout.css +6 -6
  218. package/dist/styles/chart-layout.css +6 -6
  219. package/dist/styles/control.css +41 -6
  220. package/dist/styles/data-display-layout.css +21 -6
  221. package/dist/styles/density.css +2 -0
  222. package/dist/styles/dialog-layout.css +4 -1
  223. package/dist/styles/focus-ring.css +4 -1
  224. package/dist/styles/layout.css +30 -3
  225. package/dist/styles/navigation-layout.css +3 -1
  226. package/dist/styles/shell-layout.css +27 -21
  227. package/dist/styles/table-layout.css +50 -9
  228. package/dist/styles/text-layout.css +94 -23
  229. package/dist/tokens/components/activity.css +13 -4
  230. package/dist/tokens/components/attachments.css +1 -1
  231. package/dist/tokens/components/badge.css +1 -1
  232. package/dist/tokens/components/card.css +28 -7
  233. package/dist/tokens/components/chart.css +4 -1
  234. package/dist/tokens/components/chat-composer.css +4 -1
  235. package/dist/tokens/components/control.css +69 -30
  236. package/dist/tokens/components/conversations.css +4 -1
  237. package/dist/tokens/components/data-display.css +42 -15
  238. package/dist/tokens/components/data-entry.css +8 -2
  239. package/dist/tokens/components/descriptions.css +1 -1
  240. package/dist/tokens/components/feedback.css +8 -5
  241. package/dist/tokens/components/float-button.css +8 -2
  242. package/dist/tokens/components/legal-document.css +12 -3
  243. package/dist/tokens/components/logo.css +15 -6
  244. package/dist/tokens/components/mega-menu.css +14 -5
  245. package/dist/tokens/components/navigation.css +37 -13
  246. package/dist/tokens/components/segmented.css +9 -2
  247. package/dist/tokens/components/separator.css +4 -1
  248. package/dist/tokens/components/shell.css +96 -31
  249. package/dist/tokens/components/table.css +13 -6
  250. package/dist/tokens/components/thought-chain.css +4 -1
  251. package/dist/tokens/components/toggle.css +4 -1
  252. package/dist/tokens/components/tree.css +1 -1
  253. package/dist/tokens/components/upload.css +21 -9
  254. package/dist/tokens/foundation.css +24 -30
  255. package/dist/tokens/semantic/layout.css +19 -5
  256. package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
  257. package/docs/DESIGN-AUTHORITY.md +14 -0
  258. package/docs/DEVELOPMENT.md +81 -6
  259. package/docs/TOKENS.md +16 -1
  260. package/docs/data-entry/tag-input.tsx +37 -0
  261. package/docs/layout/flex.tsx +40 -0
  262. package/docs/roadmap/website-components.md +34 -0
  263. package/docs/showcase/case4-login.tsx +10 -2
  264. package/docs/showcase/case5-shift-calendar.tsx +1 -1
  265. package/docs/showcase/case6-agency-handy.tsx +6 -6
  266. package/docs/showcase/futurelastic-web.tsx +7 -9
  267. package/docs/showcase/marketing-page.tsx +61 -52
  268. package/docs/showcase/table-expandable-rows.tsx +4 -1
  269. package/docs/showcase/table-pagination.tsx +88 -18
  270. package/docs/showcase/theme-customization.tsx +25 -2
  271. package/package.json +8 -5
@@ -0,0 +1,43 @@
1
+ {
2
+ "docPath": "data-display/feature-list.tsx",
3
+ "example": "import { FeatureList } from \"@godxjp/ui/data-display\";\n\n<FeatureList\n items={[\n { state: \"included\", label: \"SSO\", description: \"SAML と OIDC\" },\n { state: \"limited\", label: \"API 呼び出し\", description: \"月 10,000 回まで\" },\n { state: \"excluded\", label: \"監査ログのエクスポート\" },\n ]}\n/>",
4
+ "group": "data-display",
5
+ "importPath": "@godxjp/ui/data-display",
6
+ "name": "FeatureList",
7
+ "props": [
8
+ {
9
+ "description": "The lines, in reading order. `state` drives the glyph, its mark colour and a localized sr-only word, so colour is never the only carrier (WCAG 1.4.1). `description` is a muted second line that WRAPS.",
10
+ "name": "items",
11
+ "required": true,
12
+ "type": "{ state: \"included\" | \"excluded\" | \"limited\"; label: ReactNode; description?: ReactNode }[]"
13
+ },
14
+ {
15
+ "description": "Root class. The rhythm and the glyph metric live in the --feature-list-* tokens, not here.",
16
+ "name": "className",
17
+ "type": "string"
18
+ }
19
+ ],
20
+ "related": [
21
+ "ListRow — a single-line entity row (session, token, passkey) with a trailing action; it truncates and it rules a line between rows.",
22
+ "Timeline — the same glyph rail, but ordered in TIME, with a connector and done/current/pending.",
23
+ "Descriptions — a term/value grid. A feature line has no value column; the state IS the value, and it is drawn.",
24
+ "Legend — the key that says what a COLOUR means across other marks; a FeatureList makes statements of its own."
25
+ ],
26
+ "rules": [],
27
+ "storyPath": "data-display/FeatureList.stories.tsx",
28
+ "tagline": "A list of STATEMENTS, each with a leading state glyph (included ✓ / limited − / excluded ✗), an optional wrapping description, and the glyph aligned to the first text line.",
29
+ "usage": [
30
+ "DO import from `@godxjp/ui/data-display`: `import { FeatureList } from \"@godxjp/ui/data-display\";`",
31
+ "DO put a quantity INSIDE the label — there is no prop for it, because `<Text tone=\"muted\" tabular>` in the label is already legal and already audit-clean.",
32
+ "DO leave `excluded` lines un-emphasised. The component mutes the label and draws a plain ✗; a red cross per omission reads as a list of errors instead of a list of facts.",
33
+ "DON'T hand-roll it as a `<ul className=\"flex flex-col gap-2\">` with an `mt-0.5` icon nudge — that is three ui-audit errors (no-utility-layout, no-utility-spacing ×2), and the 2px is below --space-1 so no token step spells it.",
34
+ "DON'T reach for ListRow: it is a single-line ENTITY row with a divider between every row and a trailing action slot, and it truncates where a statement has to wrap.",
35
+ "DON'T reach for Timeline: it is an ORDERED event rail with a connector line and done/current/pending statuses — that is time, not inclusion."
36
+ ],
37
+ "useCases": [
38
+ "A pricing-plan card listing what the plan includes, what it limits and what it leaves out.",
39
+ "A comparison surface where each tier repeats the same rows with different states.",
40
+ "A submission review: which requirements the upload met, which it met partially, which it missed.",
41
+ "A capability or compatibility list — supported / partially supported / unsupported."
42
+ ]
43
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "example": "{`import { Field, Switch } from \"@godxjp/ui/data-entry\";\n\nexport function NotifyRow() {\n return (\n <Field id=\"notify\" label=\"メール通知\" description=\"重要な更新をメールで受け取る\">\n <Switch id=\"notify\" defaultChecked />\n </Field>\n );\n}`}",
3
+ "group": "data-entry",
4
+ "importPath": "@godxjp/ui/data-entry",
5
+ "name": "Field",
6
+ "props": [
7
+ {
8
+ "description": "id wired to the control via htmlFor; pass the same id to the child control.",
9
+ "name": "id",
10
+ "type": "string"
11
+ },
12
+ {
13
+ "description": "The field label, rendered as a <Label>.",
14
+ "name": "label",
15
+ "type": "ReactNode"
16
+ },
17
+ {
18
+ "description": "Content rendered BESIDE the label and OUTSIDE the <label> element — a help button, a Tooltip trigger, a status chip, a short text action. Same semantics as FormField.labelAddon. It must not go through `label`: Field's label is a real <label htmlFor> pointing at the checkbox/radio/switch, so a browser forwards a click anywhere inside it to that control and an interactive addon placed there TOGGLES the control when pressed (gh#812). A non-interactive mark (Badge as=\"span\") is still fine inside `label`.",
19
+ "name": "labelAddon",
20
+ "type": "ReactNode"
21
+ },
22
+ {
23
+ "description": "Muted hint rendered under the label, announced with the control via aria-describedby. Composes with `error` — both lines stack, they do not replace each other.",
24
+ "name": "description",
25
+ "type": "ReactNode"
26
+ },
27
+ {
28
+ "description": "Validation message under the description (role=alert). The message IS the invalid state: the control is cloned with aria-invalid, and the message id reaches it through aria-errormessage AND aria-describedby (react-aria's hidden input drops the former). This is how a validated boolean stays in Field — FormField must not wrap a bare Switch.",
29
+ "name": "error",
30
+ "type": "ReactNode"
31
+ },
32
+ {
33
+ "description": "The control (Checkbox/Radio/Switch) placed beside the label.",
34
+ "name": "children",
35
+ "type": "ReactNode"
36
+ },
37
+ {
38
+ "description": "Extra CSS classes on the wrapper.",
39
+ "name": "className",
40
+ "type": "string"
41
+ }
42
+ ],
43
+ "related": [
44
+ "FormField — block label/helper/required layout for text inputs; use it instead when the control stacks under its label. Field now carries `labelAddon` and `error` with FormField's semantics, so the two read as one idea (gh#812); what Field still does NOT have is `required`, `helper`, `name`/error-bag binding, layout/width knobs and `staticText`.",
45
+ "Switch / Checkbox / Radio — the controls Field typically wraps."
46
+ ],
47
+ "rules": [
48
+ 23
49
+ ],
50
+ "storyPath": "data-entry/Field.stories.tsx",
51
+ "tagline": "Label + optional description laid out beside a single checkbox/radio/switch control — the inline alternative to FormField's full block layout.",
52
+ "usage": [
53
+ "DO: Use Field to label a single boolean/choice control (Switch, Checkbox, Radio) in a compact two-column row — control beside label + description.",
54
+ "DO: Match the child control's id to Field's id so the label is correctly associated.",
55
+ "DO: Pass a pressable help affordance (Tooltip trigger, icon Button) through `labelAddon`, never through `label` — inside the <label> a press toggles the control (gh#812).",
56
+ "DO: Use `error` for a validated boolean instead of hand-wiring aria-invalid / aria-describedby or smuggling the message through `description`. Field wires aria-invalid, aria-errormessage and aria-describedby onto the control itself.",
57
+ "DON'T: Use Field for text inputs needing the full block label/required/helper layout — use FormField instead. There is no ChoiceField anymore; Field is the canonical name."
58
+ ],
59
+ "useCases": [
60
+ "A settings list of toggle rows (notifications, auto-save) where each Switch has a label + description.",
61
+ "A consent checkbox with an explanatory description beside it.",
62
+ "A radio option row in a preferences form."
63
+ ]
64
+ }
@@ -0,0 +1,99 @@
1
+ {
2
+ "example": "import { FilterBar } from \"@godxjp/ui/navigation\";\n\n<FilterBar\n search={{ value: query, onValueChange: setQuery, placeholder: \"Search records\" }}\n filters={[\n {\n value: \"status\",\n label: \"Status\",\n options: STATUS_OPTIONS,\n selected: status,\n onSelectedChange: setStatus,\n },\n ]}\n chips={appliedChips}\n onChipRemove={removeFilter}\n onClear={clearFilters}\n hasActiveFilters={hasFilters}\n resultCount={rows.length}\n actions={<Button onClick={openCreate}>Add member</Button>}\n/>",
3
+ "group": "navigation",
4
+ "importPath": "@godxjp/ui/navigation",
5
+ "name": "FilterBar",
6
+ "props": [
7
+ {
8
+ "description": "Filter controls and groups (composition form). In the typed-model form children remain valid as CUSTOM filters, rendered after the typed `filters`.",
9
+ "name": "children",
10
+ "type": "ReactNode"
11
+ },
12
+ {
13
+ "description": "Consumer-owned clear/reset action — also the chips' clear-all.",
14
+ "name": "onClear",
15
+ "type": "() => void"
16
+ },
17
+ {
18
+ "defaultValue": "true",
19
+ "description": "Shows clear only when filters are active.",
20
+ "name": "hasActiveFilters",
21
+ "type": "boolean"
22
+ },
23
+ {
24
+ "defaultValue": "false",
25
+ "description": "Uses the token-owned sticky presentation.",
26
+ "name": "sticky",
27
+ "type": "boolean"
28
+ },
29
+ {
30
+ "defaultValue": "'wrap'",
31
+ "description": "Responsive overflow strategy. 'wrap': stacked column below 640px, wrapping rows above. 'scroll': one bounded inline-scrolling row above 640px (still stacked below) with the clear-all action pinned at the inline end. The geometry is entirely token/CSS owned — never re-implement it in the page.",
32
+ "name": "overflow",
33
+ "type": "'wrap' | 'scroll'"
34
+ },
35
+ {
36
+ "description": "Typed model: search slot, first in the strip, token-owned width (--filter-bar-search-width). Presence of ANY model prop (search/filters/chips/onChipRemove/actions/resultCount/loading/disabled/error) activates the model layout; without them the composition form renders unchanged.",
37
+ "name": "search",
38
+ "type": "FilterBarSearchProp"
39
+ },
40
+ {
41
+ "description": "Typed model: labelled Select filters ({ value, label, options, selected/defaultSelected/onSelectedChange, placeholder, disabled }) — label becomes the control's real <label>. Width knob: --filter-bar-filter-width.",
42
+ "name": "filters",
43
+ "type": "FilterBarFilterProp[]"
44
+ },
45
+ {
46
+ "description": "Typed model: applied-filter chips ({ value, label, disabled }) in a labelled row. Add = include in the array; remove = onChipRemove(value); clear-all = onClear.",
47
+ "name": "chips",
48
+ "type": "FilterBarChipProp[]"
49
+ },
50
+ {
51
+ "description": "Per-chip remove handler; required for the × remove buttons to render.",
52
+ "name": "onChipRemove",
53
+ "type": "(value: string) => void"
54
+ },
55
+ {
56
+ "description": "Typed model: trailing action slot at the inline end, after reset in DOM/keyboard order.",
57
+ "name": "actions",
58
+ "type": "ReactNode"
59
+ },
60
+ {
61
+ "description": "Typed model: localized CLDR-pluralized count in a polite role='status' line; 0 is the rendered empty state.",
62
+ "name": "resultCount",
63
+ "type": "number"
64
+ },
65
+ {
66
+ "description": "Typed model: aria-busy strip + data-loading root while results (re)load.",
67
+ "name": "loading",
68
+ "type": "boolean"
69
+ },
70
+ {
71
+ "description": "Typed model: disables all model-rendered controls.",
72
+ "name": "disabled",
73
+ "type": "boolean"
74
+ },
75
+ {
76
+ "description": "Typed model: role='alert' error line replacing the result count.",
77
+ "name": "error",
78
+ "type": "ReactNode"
79
+ },
80
+ {
81
+ "description": "Optional structural class override.",
82
+ "name": "className",
83
+ "type": "string"
84
+ }
85
+ ],
86
+ "rules": [],
87
+ "storyPath": "navigation/FilterBar.stories.tsx",
88
+ "subParts": [
89
+ "FilterBarGroup"
90
+ ],
91
+ "tagline": "Domain-neutral list-page filter toolbar with optional clear action and labelled groups.",
92
+ "usage": [
93
+ "Prefer the typed model for a canonical list page: pass `search` + `filters` + `chips` + `resultCount` and the bar owns the layout, widths, chip lifecycle, keyboard order (search → filters → children → reset → actions → chip removes) and responsive stacking through --filter-bar-* tokens. Everything stays consumer data — the bar renders state, never owns it.",
94
+ "Compose real controls as children when the model doesn't fit; filter state and URL synchronization remain consumer-owned. Children also render INSIDE the model layout (after the typed filters) for one-off custom controls like a date-range picker.",
95
+ "Give each FilterBarGroup a `controlId` matching its single control's `id` so the visible caption is that control's real <label>; otherwise the control is nameless to a screen reader. Typed `filters` wire this automatically.",
96
+ "Reach for `overflow='scroll'` on filter-heavy list pages so a long JA/EN/VI label set never grows the strip into multiple rows and pushes the table below the fold.",
97
+ "A mobile sheet presentation is a COMPOSITION, not a prop: put the same typed FilterBar inside a Sheet triggered from a compact toolbar when a screen wants drawer-style filters. The bar's own responsive behavior is stack (below 640px) + wrap/scroll (above), token-owned."
98
+ ]
99
+ }
@@ -0,0 +1,153 @@
1
+ {
2
+ "example": "import { Flex } from \"@godxjp/ui/layout\";\nimport { Button } from \"@godxjp/ui/general\";\n\n<Flex direction=\"row\" gap=\"sm\" align=\"center\" justify=\"between\" wrap>\n <SearchSummary />\n <Flex direction=\"row\" gap=\"xs\" align=\"center\" wrap>\n <Button variant=\"outline\">リセット</Button>\n <Button>適用</Button>\n </Flex>\n</Flex>",
3
+ "group": "layout",
4
+ "importPath": "@godxjp/ui/layout",
5
+ "name": "Flex",
6
+ "props": [
7
+ {
8
+ "description": "Floating child actions shown on parent hover/focus-within; always visible on touch.",
9
+ "name": "reveal",
10
+ "type": "\"hover\""
11
+ },
12
+ {
13
+ "description": "Negative inline inset on the token scale.",
14
+ "name": "bleed",
15
+ "type": "GapProp"
16
+ },
17
+ {
18
+ "description": "Lightweight surface, without Card structure.",
19
+ "name": "surface",
20
+ "type": "\"muted\" | \"popover\" | \"warning\""
21
+ },
22
+ {
23
+ "description": "false prevents shrinking.",
24
+ "name": "shrink",
25
+ "type": "boolean"
26
+ },
27
+ {
28
+ "description": "Grow into remaining flex space.",
29
+ "name": "grow",
30
+ "type": "boolean"
31
+ },
32
+ {
33
+ "description": "Semantic element. A list keeps its marker and indentation unless marker=\"none\" says otherwise.",
34
+ "name": "as",
35
+ "type": "\"div\" | \"span\" | \"ul\" | \"ol\" | \"li\""
36
+ },
37
+ {
38
+ "defaultValue": "as=\"ul\" → \"disc\", as=\"ol\" → \"decimal\"",
39
+ "description": "Marker for a LIST element; ignored by every other tag. marker=\"none\" emits NO data-list, so the list keeps its <ul>/<ol>, its <li> semantics and its gap token, and loses the bullet AND the --space-5 indent — that is the container a short entity list needs: <Flex as=\"ul\" marker=\"none\" direction=\"col\" gap=\"none\"> with <ListRow as=\"li\"> rows. Before it, the bullet was unconditional and .ui-flex[data-list] > li { display: list-item } outranked [data-slot=\"list-row\"] { display: flex }, so rows fell out of flex layout (94.58px tall instead of 69.98px) and the only move left was a raw <ul>, which carries no gap token (gh#714). disc/decimal stay available so an <ol> can be bulleted, or a <ul> numbered, without a list-style-type in a className.",
40
+ "name": "marker",
41
+ "type": "\"disc\" | \"decimal\" | \"none\""
42
+ },
43
+ {
44
+ "description": "Responsive axis; omitted steps inherit.",
45
+ "name": "direction",
46
+ "type": "\"row\" | \"col\" | {base?: \"row\" | \"col\"; sm?: \"row\" | \"col\"; md?: \"row\" | \"col\"; lg?: \"row\" | \"col\"; xl?: \"row\" | \"col\"}"
47
+ },
48
+ {
49
+ "defaultValue": "\"md\"",
50
+ "description": "Token gap between children, shared with other layout primitives. \"none\" is a DELIBERATE zero for two lines that read as one block — a name over its role, a weekday over its date, a tab bar with no seam — not a way to opt out of the token scale.",
51
+ "name": "gap",
52
+ "type": "\"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\""
53
+ },
54
+ {
55
+ "description": "ESCAPE HATCH: a gap in pixels, off every step of the scale. Real designs land on 2px, 5px, 6px, 10px, 14px — rounding to the nearest named step drifts the layout, and writing the literal in a className is blocked by ui-audit. Spend the ten named steps FIRST: `gap={3}` is 12px and follows the user's --scaling, gapRaw does not. It wins over `gap` (which then emits no class, so the two cannot fight over specificity) and leaves data-gap-raw on the DOM, so every escape stays countable.",
56
+ "name": "gapRaw",
57
+ "type": "number"
58
+ },
59
+ {
60
+ "description": "Inner padding on the token scale — one step for all four sides, or an object keyed by LOGICAL side. Exists because a missing padding prop produced 42 of 51 ui-audit errors in one real consumer (gh#408): `<Flex className=\"p-3\">` was the only move left.",
61
+ "name": "pad",
62
+ "type": "number | { inline?, block?, inlineStart?, inlineEnd?, blockStart?, blockEnd? }"
63
+ },
64
+ {
65
+ "description": "Raw-pixel padding for values off the scale, same contract and same price as gapRaw: it leaves data-pad-raw on the DOM so each escape is countable. Overrides `pad` PER SIDE, not as a whole.",
66
+ "name": "padRaw",
67
+ "type": "number | { inline?, block?, inlineStart?, inlineEnd?, blockStart?, blockEnd? }"
68
+ },
69
+ {
70
+ "defaultValue": "false",
71
+ "description": "Take the space the siblings leave — the ELASTIC column of a `fixed | elastic | fixed` row (name · meter · figures). Also sets min-inline-size: 0, which is what lets a truncating child ellipse instead of pushing the row wider. Without this axis the only move was className=\"flex-1 min-w-0\", which ui-audit blocks (gh#405 §2).",
72
+ "name": "fill",
73
+ "type": "boolean"
74
+ },
75
+ {
76
+ "description": "A FIXED column: number = px, string = any CSS length. Comes with flex: none — a width a sibling can still squeeze is a suggestion, and stacked rows would start at different x offsets. Leaves data-width-raw on the DOM so the measurement stays countable, like gapRaw/padRaw.",
77
+ "name": "width",
78
+ "type": "number | string"
79
+ },
80
+ {
81
+ "description": "CENTRE this box and CAP it at a page measure (--page-measure-narrow 42rem / -medium 48rem / -wide 72rem): margin-inline:auto + inline-size:100% + max-inline-size. THE public answer to \"full-bleed outside, measured column inside\" — a marketing <section> paints edge to edge and its inner Flex measure=\"wide\" holds the content on the page measure. Deliberately NOT on PageContainer: that owns page padding and a header/toolbar/footer scaffold, so a full-bleed page cannot consume it, and the refusal left the centred column owned by nobody — three showcases each hand-wrote the same four declarations (gh#839). It adds NO gutter: the page inset is pad={{ inline: 6 }}, and one band with two padding owners drifts. A MAX, so nothing binds below the cap at 390px. An explicit `width` wins. Do not give a READING column \"wide\" — prose stays narrow/medium, which bracket Bringhurst's 45-75 characters. Omitted emits no attribute at all.",
82
+ "name": "measure",
83
+ "type": "\"narrow\" | \"medium\" | \"wide\""
84
+ },
85
+ {
86
+ "description": "Cross-axis alignment, emitted as a data attribute for the layout CSS.",
87
+ "name": "align",
88
+ "type": "\"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\""
89
+ },
90
+ {
91
+ "description": "Main-axis distribution, emitted as a data attribute for the layout CSS.",
92
+ "name": "justify",
93
+ "type": "\"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\""
94
+ },
95
+ {
96
+ "defaultValue": "false",
97
+ "description": "Allows children to wrap onto additional flex lines.",
98
+ "name": "wrap",
99
+ "type": "boolean"
100
+ },
101
+ {
102
+ "description": "Drop this region below a canonical breakpoint step (sm 40rem, md 48rem, lg 64rem, xl 80rem). THE public way to make a region responsive without a page-local media query — a public header hides its anchor navigation with hideBelow=\"md\". Omitted by default, so no attribute is emitted and no rule matches. display:none also removes it from the a11y tree, so keep its destinations reachable elsewhere (a footer nav) at that width.",
103
+ "name": "hideBelow",
104
+ "type": "\"sm\" | \"md\" | \"lg\" | \"xl\""
105
+ },
106
+ {
107
+ "description": "Inverse of hideBelow — drop the region FROM that step upwards, i.e. keep it only on the narrow side (a compact-only affordance).",
108
+ "name": "hideFrom",
109
+ "type": "\"sm\" | \"md\" | \"lg\" | \"xl\""
110
+ },
111
+ {
112
+ "description": "ESCAPE HATCH: the same drop, at a width in PIXELS off every step of the scale — the gapRaw/padRaw contract on the breakpoint axis (gh#528). An off-scale breakpoint is a LOCAL EXCEPTION, not a new tier: sm/md/lg/xl are shared with collapseBelow and --master-detail-collapse-below, so two regions asked to fold at the same step really do fold together, while a raw width folds one region only. Spend the four steps first; reach here when the canonical design specifies a width the scale has not got (a nav that becomes a hamburger at 900px). It leaves data-hide-below-raw on the DOM so every escape stays countable, and prints one deduped media rule per distinct width (a media query cannot read a var()). It wins over hideBelow, which then emits nothing.",
113
+ "name": "hideBelowRaw",
114
+ "type": "number"
115
+ },
116
+ {
117
+ "description": "Inverse of hideBelowRaw, same contract and same price (data-hide-from-raw). THE SEAM: hideBelowRaw hides while width < N and hideFromRaw hides while width >= N — the same comparisons as the token steps, so the pair is exact complements and at exactly N the hideBelowRaw region is the visible one. Never hand-roll this with an inclusive max-width: `<= N` paired with `>= N` hides BOTH at exactly N, the 1px hole a consumer measured on a hand-rolled max-[900px]: pair.",
118
+ "name": "hideFromRaw",
119
+ "type": "number"
120
+ }
121
+ ],
122
+ "related": [
123
+ "Flex `direction='col'` — the standard pattern for ordinary vertical block spacing; use explicit `align`, `justify`, or `wrap` props when you need more control.",
124
+ "Flex `direction='row'` — the standard pattern for simple horizontal groups; add `wrap` and `align='center'` for the typical row with wrapped centered items.",
125
+ "ResponsiveGrid — use for equal-width, multi-column tile layouts. Flex arranges children on one flex axis and does not provide column-count behavior.",
126
+ "PageContainer — page scaffold and padding context. Flex is an inner layout primitive used inside page sections, cards, dialogs, and toolbars. PageContainer `measure` bounds an APP page (header and body together, narrow | medium); Flex `measure` bounds ONE centred column inside a full-bleed band, and is the only one that reaches the marketing `wide` step."
127
+ ],
128
+ "rules": [
129
+ 2,
130
+ 40
131
+ ],
132
+ "storyPath": "layout/Flex.stories.tsx",
133
+ "tagline": "Token-spaced flex primitive with explicit direction, alignment, justification, and wrapping controls.",
134
+ "usage": [
135
+ "DO import from `@godxjp/ui/layout` and reach for Flex when the axis, alignment, justification, or wrap behavior is part of the component contract: `import { Flex } from \"@godxjp/ui/layout\"`.",
136
+ "DO keep spacing on the `gap` prop instead of raw `gap-*`, `space-*`, or padding utilities. Flex uses the same token scale as other layout primitives, so spacing remains tied to the design system.",
137
+ "DO use `direction=\"row\"` with `wrap` for responsive control rows, chip clusters, and action groups that need more control than simple row composition.",
138
+ "DO use `direction=\"col\"` for vertical groupings that need explicit `align` or `justify` behavior. For pure vertical stacking without alignment control, `direction=\"col\"` is sufficient.",
139
+ "DON'T override the axis with `className` after choosing a direction prop. Keep the layout intent in props so catalog guidance and data attributes stay accurate.",
140
+ "FULL-BLEED BAND: a marketing/landing section is a `<section>` carrying the edge-to-edge paint (background, border, halo) around ONE `<Flex direction=\"col\" measure=\"wide\" pad={{ inline: 6, block: 20 }}>`. The measure centres and caps the column, `pad` owns the gutter and the band rhythm (`block: 20` = 80px, `block: 24` = 96px — the two band steps). Never a page-local `max-width: 1200px` + `margin-inline: auto` class, and never an inline style saying the same thing: that four-declaration idiom is what this prop replaced (gh#839).",
141
+ "Flex is a plain div with React.HTMLAttributes<HTMLDivElement>; pass `id`, `role`, `aria-*`, `data-*`, and structural className values as needed, but do not use it as a semantic form or button wrapper. When the parent only accepts phrasing content — a TabsTrigger, PopoverTrigger or Button, all of which render a <button> — pass `as=\"span\"` rather than reaching for a raw `<span className=\"flex …\">`.",
142
+ "SEMANTIC LIST: a list of rows is `<Flex as=\"ul\" marker=\"none\" direction=\"col\" gap=\"none\">` with `<ListRow as=\"li\">` children — never a raw `<ul>` (no gap token), never `<div role=\"list\">` + `<div role=\"listitem\">` (ARIA re-describing markup HTML already has), and never a wrapper around each row: the divider is `:not(:last-child)` among SIBLINGS, so a row alone in its own wrapper loses it silently. `ui-audit` flags all three as `no-hand-rolled-list`.",
143
+ "NAMED FLEX = GROUP: a role-less div may not carry a naming attribute (axe aria-allowed-attr), so a Flex given `aria-label`/`aria-labelledby` — e.g. by FormField wrapping a composite range/年月 field — automatically renders `role='group'`, folds `aria-errormessage` into `aria-describedby`, and drops the widget-only `aria-required`/`aria-invalid`. Passing an explicit `role` opts out of all of this and the caller owns the attribute set."
144
+ ],
145
+ "useCases": [
146
+ "Toolbar internals where controls should sit in a row, wrap on narrow widths, and stay vertically centered.",
147
+ "Card headers that need title content on the left and actions on the right via `justify='between'` without hand-rolling flex utility classes.",
148
+ "Empty-state or loading blocks that center content on both axes using `align='center'` and `justify='center'`.",
149
+ "Form sub-sections where a vertical group needs stretched children or centered helper content beyond what a plain column Flex provides.",
150
+ "Badge, chip, or tag clusters where wrapping is required but the caller also needs explicit gap control.",
151
+ "Low-level layout composition inside custom components where raw flex classes would duplicate the primitive."
152
+ ]
153
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "docPath": "general/float-button.tsx",
3
+ "example": "import { FloatButton } from \"@godxjp/ui/general\";\nimport { MessageCircle, Share2 } from \"lucide-react\";\n\n<FloatButton.Group trigger=\"click\" type=\"primary\" icon={<MessageCircle />} aria-label=\"Trợ lý\">\n <FloatButton tooltip=\"Hỏi trợ lý\" badge={{ count: 3 }} />\n <FloatButton tooltip=\"Chia sẻ\" icon={<Share2 />} />\n <FloatButton.BackTop />\n</FloatButton.Group>",
4
+ "group": "general",
5
+ "importPath": "@godxjp/ui/general",
6
+ "name": "FloatButton",
7
+ "props": [
8
+ {
9
+ "description": "The glyph. Falls back to a document mark when the button carries neither an icon nor `content`, which is antd's own default.",
10
+ "name": "icon",
11
+ "type": "ReactNode"
12
+ },
13
+ {
14
+ "description": "A short caption UNDER the glyph. Legal only with shape=\"square\" — a circle has no room, and asking for one logs antd's dev warning.",
15
+ "name": "content",
16
+ "type": "ReactNode"
17
+ },
18
+ {
19
+ "description": "antd 6's deprecated spelling of `content`, kept so an antd call site compiles unchanged. `content` wins when both are given. Prefer `content` in new code.",
20
+ "name": "description",
21
+ "type": "ReactNode"
22
+ },
23
+ {
24
+ "defaultValue": "\"default\"",
25
+ "description": "antd's word for the FILL, not the native button type — `default` is the surface-with-hairline mark, `primary` the brand fill. The native attribute is `htmlType`.",
26
+ "name": "type",
27
+ "type": "\"default\" | \"primary\""
28
+ },
29
+ {
30
+ "defaultValue": "\"circle\"",
31
+ "description": "Round mark or rounded square. A FloatButton inside a Group inherits the group's shape, so a stack can never mix the two.",
32
+ "name": "shape",
33
+ "type": "\"circle\" | \"square\""
34
+ },
35
+ {
36
+ "description": "Hover/focus label. When it is a STRING and no aria-label is given it also becomes the button's accessible name — an icon-only control needs a name, and a tooltip alone never reaches a touch user (WCAG 4.1.2).",
37
+ "name": "tooltip",
38
+ "type": "ReactNode | { title, side, align, sideOffset }"
39
+ },
40
+ {
41
+ "description": "Renders an <a> instead of a <button>, exactly as antd does.",
42
+ "name": "href",
43
+ "type": "string"
44
+ },
45
+ {
46
+ "description": "Anchor target; only meaningful beside `href`.",
47
+ "name": "target",
48
+ "type": "string"
49
+ },
50
+ {
51
+ "description": "The corner count/dot mark. `overflowCount` defaults to 99 and `showZero` to false, both antd's. antd's `offset` and `size` are deliberately not ported — `offset` is a raw px tuple no token step spells.",
52
+ "name": "badge",
53
+ "type": "{ count?: number; dot?: boolean; overflowCount?: number; showZero?: boolean; color?: string }"
54
+ },
55
+ {
56
+ "description": "Non-interactive. A Group's trigger inherits it. Remember the house rule: the REASON a control is disabled is visible text, never the tooltip.",
57
+ "name": "disabled",
58
+ "type": "boolean"
59
+ },
60
+ {
61
+ "defaultValue": "\"button\"",
62
+ "description": "The native button type, since `type` is taken by the fill. antd 5.21+.",
63
+ "name": "htmlType",
64
+ "type": "\"button\" | \"submit\" | \"reset\""
65
+ }
66
+ ],
67
+ "related": [
68
+ "Button — the same control IN the layout flow. A float button is a Button that gave up its place in the page and took a corner instead.",
69
+ "Popover — anchors a panel to a trigger; it does not place the trigger.",
70
+ "Banner — a full-bleed strip, not a corner mark.",
71
+ "Toolbar / PageContainer extra — where an action belongs when it IS part of this page's content."
72
+ ],
73
+ "rules": [],
74
+ "storyPath": "general/FloatButton.stories.tsx",
75
+ "tagline": "Ant Design's corner action, ported whole — a control pinned to the viewport above the page, with FloatButton.Group (a stack, or a menu behind one trigger) and FloatButton.BackTop. The corner insets are tokens, so a service moves the mark without a media query.",
76
+ "usage": [
77
+ "DO import from `@godxjp/ui/general`: `import { FloatButton } from \"@godxjp/ui/general\";`",
78
+ "DO give every float button a name — `tooltip` as a plain string is enough, since it becomes the aria-label too.",
79
+ "DO move the mark with the `--float-button-offset-block-end` / `--float-button-offset-inline-end` tokens when a sticky action bar or a mobile tab bar is already in that corner.",
80
+ "DO pass `target={() => element}` to FloatButton.BackTop inside a shell that owns its own scroll region, so it watches that region rather than the document.",
81
+ "DON'T hand-roll it as a Button with `position: fixed` at the call site — that is page-local CSS the audit blocks, and it is the exact gap gh#558 was filed for.",
82
+ "DON'T reach for Popover: it anchors a PANEL to a trigger, so it answers where the panel goes, not where the trigger goes.",
83
+ "DON'T put a caption on a circle. `content` needs shape=\"square\"."
84
+ ],
85
+ "useCases": [
86
+ "An assistant or composer that must stay reachable while the reader works — a chat you may want open is not a one-shot trigger in a bar.",
87
+ "A stack of secondary actions collapsed behind one corner trigger (share / export / print).",
88
+ "Back to top on a long document or a long list.",
89
+ "A corner action carrying an unread count, via `badge`."
90
+ ]
91
+ }
@@ -0,0 +1,87 @@
1
+ {
2
+ "example": "import { Form, FormField, Input } from \"@godxjp/ui/data-entry\";\n\n<Form layout=\"horizontal\" labelWidth={120} columns={2} onSubmit={onSubmit}>\n <FormField id=\"first\" label=\"姓\"><Input id=\"first\" /></FormField>\n <FormField id=\"last\" label=\"名\"><Input id=\"last\" /></FormField>\n <FormField id=\"address\" label=\"住所\" colSpan={2}><Input id=\"address\" /></FormField>\n</Form>",
3
+ "group": "data-entry",
4
+ "importPath": "@godxjp/ui/data-entry",
5
+ "name": "Form",
6
+ "props": [
7
+ {
8
+ "defaultValue": "\"vertical\"",
9
+ "description": "Label position relative to control; applied to all FormFields.",
10
+ "name": "layout",
11
+ "type": "\"vertical\" | \"horizontal\" | \"inline\""
12
+ },
13
+ {
14
+ "description": "Label column width in horizontal layout (number→px). e.g. 120 or '8rem'.",
15
+ "name": "labelWidth",
16
+ "type": "number | string"
17
+ },
18
+ {
19
+ "description": "Cap the control width (number→px). Omit to fill the column.",
20
+ "name": "controlWidth",
21
+ "type": "number | string"
22
+ },
23
+ {
24
+ "defaultValue": "\"end\"",
25
+ "description": "Horizontal alignment of the label within its column.",
26
+ "name": "labelAlign",
27
+ "type": "\"start\" | \"end\""
28
+ },
29
+ {
30
+ "defaultValue": "\"md\"",
31
+ "description": "Breakpoint below which horizontal collapses to vertical (mobile-first). false = always horizontal.",
32
+ "name": "collapseBelow",
33
+ "type": "\"sm\" | \"md\" | \"lg\" | \"xl\" | false"
34
+ },
35
+ {
36
+ "description": "Lay fields out in a responsive grid (reuses ResponsiveGrid; 1 col on small).",
37
+ "name": "columns",
38
+ "type": "number | { sm?: number; md?: number; lg?: number }"
39
+ },
40
+ {
41
+ "description": "Server validation error bag (e.g. Inertia's form.errors). Each FormField name='…' inside resolves its own message from the bag and CLAIMS its key; <FormErrors /> renders the unclaimed remainder (errors on hidden/derived fields). Works in both <form> and asChild modes.",
42
+ "name": "errors",
43
+ "type": "Partial<Record<string, string | string[]>>"
44
+ },
45
+ {
46
+ "description": "Apply a density to controls inside the form.",
47
+ "name": "density",
48
+ "type": "\"compact\" | \"default\" | \"comfortable\""
49
+ },
50
+ {
51
+ "description": "Disable controls inside FormField, including nested native controls.",
52
+ "name": "disabled",
53
+ "type": "boolean"
54
+ },
55
+ {
56
+ "description": "Show required marks, hide them, or mark optional fields.",
57
+ "name": "requiredMark",
58
+ "type": "boolean | \"optional\""
59
+ }
60
+ ],
61
+ "related": [
62
+ "FormField — the per-field wrapper (label + control + helper/error) that reads Form's layout context; use one per control inside a Form.",
63
+ "FormErrors — renders the error-bag entries no mounted FormField claims (validation errors on hidden/derived fields); place inside a `Form errors={…}`.",
64
+ "ResponsiveGrid — Form `columns` reuses it; use ResponsiveGrid directly for non-form card grids."
65
+ ],
66
+ "rules": [
67
+ 23,
68
+ 24
69
+ ],
70
+ "storyPath": "data-entry/Form.stories.tsx",
71
+ "tagline": "Ant-style layout container — renders <form> and pushes layout (vertical/horizontal), labelWidth/controlWidth, label alignment, responsive collapse, and multi-column grid down to every FormField (overridable per field).",
72
+ "usage": [
73
+ "DO set `layout`, `labelWidth`, `controlWidth` ONCE on `<Form>` — every `<FormField>` inside inherits them. Override a single field by passing the same prop on that `<FormField>` (Form → FormField priority).",
74
+ "DO rely on mobile-first collapse: `layout='horizontal'` automatically stacks to vertical below `collapseBelow` (default `md`). Pass `collapseBelow={false}` only when a field MUST stay label-beside-control even on phones.",
75
+ "DO use `columns` for multi-field forms (e.g. `columns={2}`) — it reuses ResponsiveGrid (1 column on small screens, more on md/lg). Span a wide field across columns with `<FormField colSpan={2}>`.",
76
+ "ROW RHYTHM IS THE SAME ON EVERY PATH: a grid of FormFields hands its row spacing to the grid's own `row-gap` (--form-grid-row-gap, defaulting to --form-field-row-gap) instead of the per-field margin a stacked Form uses. So `columns={1}` — and any `columns={n}` form once a narrow container collapses it to one column — is pixel-identical to a Form with no `columns`, and a hand-written `<ResponsiveGrid columns={2}>` of FormFields inside a `CardContent` (what a form with several titled Card sections has to write) matches `columns={2}` exactly. Retune --form-field-row-gap to move every path together; --form-grid-row-gap / --form-grid-column-gap to retune the grid rows / gutter alone. Never add margin to a FormField to space a grid row: a per-item margin inside a grid misaligns row 1 and double-counts the track gap.",
77
+ "DON'T hand-roll a `<form>` + Flex stack for spacing — `<Form>` provides the vertical rhythm and the layout context FormField reads. Wire react-hook-form by spreading `onSubmit={handleSubmit(...)}` onto `<Form>`.",
78
+ "SERVER ERROR BAG: pass `errors={form.errors}` (Inertia) ONCE on `<Form>`, give each field a `name`, and put `<FormErrors />` at the top of the form. A named field resolves its message from the bag automatically (no per-field `error={errors.x}`), and FormErrors catches validation errors on hidden/derived keys (`action_mode`, `page`, a source-record id) that no visible field could display — without it those submits fail SILENTLY.",
79
+ "SIBLING FORMS: an edit screen split into several Card+Form sections shares ONE bag by wrapping the region in `<FormErrorsProvider errors={form.errors}>` instead of passing `errors` to each Form — the section Forms (without their own `errors`) join the shared registry, and one `<FormErrors />` anywhere in the region renders the unclaimed remainder."
80
+ ],
81
+ "useCases": [
82
+ "A settings page form where every label sits in a fixed 120px column to the left of its control (horizontal), collapsing to stacked labels on mobile.",
83
+ "A two-column entity-edit form (`columns={2}`) where the address field spans both columns (`colSpan={2}`).",
84
+ "A compact filter form (`layout='horizontal' density='compact'`) above a DataTable.",
85
+ "An Inertia edit screen passing `errors={form.errors}` so every `FormField name='…'` self-binds its server validation message and `<FormErrors />` surfaces the hidden-field remainder."
86
+ ]
87
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "example": "import { Form, FormErrors, FormField, Input } from \"@godxjp/ui/data-entry\";\nimport { useForm } from \"@inertiajs/react\";\n\nconst form = useForm({ customer_nm: \"\", action_mode: \"regist\" });\n\n<Form asChild layout=\"horizontal\" labelWidth={140} errors={form.errors}>\n <form onSubmit={submit}>\n <FormErrors />\n <FormField name=\"customer_nm\" label=\"顧客名\" required>\n <Input value={form.data.customer_nm} onChange={(e) => form.setData(\"customer_nm\", e.target.value)} />\n </FormField>\n </form>\n</Form>",
3
+ "group": "data-entry",
4
+ "importPath": "@godxjp/ui/data-entry",
5
+ "name": "FormErrors",
6
+ "props": [
7
+ {
8
+ "description": "Explicit error bag — overrides the surrounding Form's `errors`. Use when the component sits outside a Form (e.g. inside FormRoot); field claiming still applies when a Form provides the registry.",
9
+ "name": "errors",
10
+ "type": "Partial<Record<string, string | string[]>>"
11
+ },
12
+ {
13
+ "description": "Heading above the messages. Defaults to the localized 'please review your input' title (dataEntry.formErrors.title).",
14
+ "name": "title",
15
+ "type": "ReactNode"
16
+ },
17
+ {
18
+ "description": "Root class override.",
19
+ "name": "className",
20
+ "type": "string"
21
+ }
22
+ ],
23
+ "related": [
24
+ "Form — provides the error bag (`errors`) and the claim registry FormErrors reads; FormErrors must sit inside it (or receive `errors` explicitly).",
25
+ "FormErrorsProvider — the shared registry for a REGION of sibling Forms (multi-Card edit screens): wrap the region with it, leave `errors` off the section Forms, and one FormErrors covers the whole screen.",
26
+ "FormField — `name` claims a bag key and self-binds its message; the claimed key never re-appears in FormErrors.",
27
+ "Alert — the underlying destructive banner; use Alert directly for non-validation notices."
28
+ ],
29
+ "rules": [
30
+ 23
31
+ ],
32
+ "storyPath": "data-entry/FormErrors.stories.tsx",
33
+ "subParts": [
34
+ "FormErrorsProvider"
35
+ ],
36
+ "tagline": "The 'no field to stand on' error summary — renders the entries of the surrounding Form's server error bag that no mounted FormField name='…' claims: validation errors on hidden/derived fields (action_mode, page, a source-record id) that would otherwise fail silently. Composed on Alert tone='destructive' (role='alert'); renders nothing while every entry is claimed or the bag is empty.",
37
+ "usage": [
38
+ "DO pass the WHOLE bag to `<Form errors={form.errors}>` and place `<FormErrors />` at the top of the form — never hand-filter the bag per page. Fields with `name` claim their keys automatically; FormErrors shows only the remainder, so the consumer never maintains an except-list.",
39
+ "DO give every visible field its `name` when adopting `Form errors` on a screen. A field that keeps a manual `error={errors.x}` WITHOUT `name` does not claim its key, and FormErrors will show that message twice.",
40
+ "DON'T hand-roll a destructive Alert bound to `errors.hidden_key` per page — that is exactly the per-page listing this component exists to remove, and it goes stale the moment the server adds a new derived-field rule.",
41
+ "DON'T use FormErrors as a generic mutation-failure banner — that is `Alert.QueryError` / toast territory. FormErrors is scoped to the VALIDATION bag of the surrounding form.",
42
+ "INSIDE FormRoot: `<FormRoot form={form} onSubmit={(v) => m.mutateAsync(v)} errors={serverErrors(m.error)}><FormErrors /><AlertMutationFeedback mutation={m} />…</FormRoot>` — FormRoot mounts the claim registry, so `<FormErrors />` needs no `errors` of its own and shows only unclaimed keys. A 422 renders exactly once: claimed keys under their fields, unclaimed keys here, no AlertMutationFeedback alert (gh#690) and no FormRoot submitFailed banner (gh#698).",
43
+ "ARRAY ENTRIES: a `string[]` bag value lists every message in the banner; a claimed field shows only the FIRST message of its array (Laravel `$errors->first()` semantics).",
44
+ "SIBLING FORMS: when the screen is split into several Card+Form sections, wrap the REGION in `<FormErrorsProvider errors={form.errors}>` and give NO `errors` to the section Forms — they join the shared registry and one `<FormErrors />` covers the whole screen. A nested Form WITH its own `errors` deliberately starts a separate (shadowed) registry."
45
+ ],
46
+ "useCases": [
47
+ "An Inertia edit screen whose Laravel FormRequest validates hidden/derived inputs (`action_mode`, `page`, `source_slip_cd`) — the user pressed save and previously saw NOTHING because those keys have no visible field.",
48
+ "A ported legacy screen where the server rejects a stale edit-lock or a missing source record under a key that only exists server-side.",
49
+ "A create form where a Laravel `RuleObject` attaches a cross-field error to a synthetic key (e.g. `combination`) rather than to one input."
50
+ ]
51
+ }