@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,268 @@
1
+ {
2
+ "absorbed": [
3
+ "DataGrid"
4
+ ],
5
+ "example": "import { useState } from \"react\";\nimport { Badge, DataTable, type ColumnDef } from \"@godxjp/ui/data-display\";\nimport { EmptyState } from \"@godxjp/ui/data-display\";\n\ntype Invoice = {\n id: string;\n customer: string;\n amount: number;\n status: \"paid\" | \"pending\" | \"overdue\";\n};\n\nconst columns: ColumnDef<Invoice>[] = [\n { key: \"id\", header: \"Invoice #\", width: \"w-32\" },\n { key: \"customer\", header: \"Customer\" },\n {\n key: \"status\",\n header: \"Status\",\n render: (row) => (\n <Badge\n variant={\n row.status === \"paid\" ? \"success\" : row.status === \"overdue\" ? \"destructive\" : \"secondary\"\n }\n >\n {row.status}\n </Badge>\n ),\n },\n { key: \"amount\", header: \"Amount\", align: \"right\", sortable: true },\n];\n\nexport default function InvoiceList({\n invoices,\n loading,\n}: {\n invoices: Invoice[];\n loading: boolean;\n}) {\n const [selected, setSelected] = useState<Set<string>>(new Set());\n const [sort, setSort] = useState<{ key: string; direction: \"asc\" | \"desc\" } | undefined>();\n\n return (\n <DataTable\n data={invoices}\n columns={columns}\n getRowId={(row) => row.id}\n selectable\n selected={selected}\n onSelectChange={setSelected}\n sort={sort}\n onSortChange={setSort}\n loading={loading}\n empty={\n <EmptyState\n title=\"No invoices found\"\n description=\"Adjust your filters or create a new invoice.\"\n />\n }\n >\n <DataTable.Toolbar>\n <DataTable.BulkActions>\n <button type=\"button\" onClick={() => setSelected(new Set())}>\n Mark paid\n </button>\n </DataTable.BulkActions>\n <DataTable.DensityToggle />\n </DataTable.Toolbar>\n </DataTable>\n );\n}",
6
+ "group": "data-display",
7
+ "importPath": "@godxjp/ui/data-display",
8
+ "name": "DataTable",
9
+ "props": [
10
+ {
11
+ "description": "Array of row data. When empty and loading is false, a built-in EmptyState renders automatically inside the table body — no external guard needed.",
12
+ "name": "data",
13
+ "required": true,
14
+ "type": "T[]"
15
+ },
16
+ {
17
+ "description": "Lean column definitions (adapted to TanStack internally — `meta.lean` is the declared home for every custom column option, so `priority` needs no second TanStack channel). Each column: { key: string; header: ReactNode; ariaLabel?: string; render?: (row: T) => ReactNode; sortable?: boolean; width?: string; align?: 'left'|'center'|'right'; hideBelow?: 'sm'|'md'|'lg'|'xl' (same contract as Flex hideBelow — stamped as data-hide-below on th/td); hiddenOnMobile?: boolean (alias for hideBelow:'md'); enableHiding?: boolean; pin?: 'end'; priority?: 'primary'|'secondary'|'meta'|'actions' }. priority is the column-priority contract read by preset=\"action-collection\" — DataTable stamps it as data-priority on the <th> AND every <td> of the column, so the preset can allocate the narrow-frame measure; leave the free-text column unmarked (it takes the remaining space), and prefer priority over width under the preset because an explicit width utility wins the cascade and defeats the measure. If render is omitted, the raw value at row[key] is rendered as a string. sortable opts the column into the sort cycle (client-side by default, or server-side via sort+onSortChange). enableHiding (default true) lists the column in DataTable.ViewOptions; set false to keep a key/actions column always visible. pin:'end' sticks the column (typically row actions) to the inline-end edge on horizontal scroll with a separating shadow — pin at most one column. ariaLabel gives a VISUALLY-EMPTY header (header='' — an action or selection column) a screen-reader name (e.g. 'Actions'/'Select'): it renders as an sr-only label inside the <th> so the column is never nameless (axe: empty-table-header). ANT DESIGN PARITY on the same column: fixed:'start'|'end' freezes the column against a scroll edge (logical, so it mirrors in RTL; the stacking offsets are MEASURED from the rendered header, so several adjacent frozen columns are correct at any width — pin:'end' is the older spelling of fixed:'end'). ellipsis holds the cell to one line and keeps the full value as its title (it also switches the table to table-layout: fixed, without which no ellipsis truncates anything). sorter is antd's richer `sortable`: true | (a, b) => number | { compare, multiple }, where multiple is the MULTI-column sort priority (highest sorts first). sortOrder / defaultSortOrder / sortDirections control and shape the cycle per column, and showSorterTooltip explains the next step. filters + onFilter + filteredValue / defaultFilteredValue / filterMultiple add a real filter menu to the header (filterMultiple: false makes it single-choice); omit onFilter for a server filter and drive it from the table's onFilterChange.",
18
+ "name": "columns",
19
+ "required": true,
20
+ "type": "ColumnDef<T>[]"
21
+ },
22
+ {
23
+ "defaultValue": "(row) => String(row.id)",
24
+ "description": "Extracts a stable unique string key per row. Required when selectable is true or rows lack an 'id' field. Falls back to row.id cast to string.",
25
+ "name": "getRowId",
26
+ "type": "(row: T) => string"
27
+ },
28
+ {
29
+ "description": "Human name of a row — what its selection checkbox (or radio) is announced as: `Select row {label}`. Default: the text of the `priority: \"primary\"` column, else of the first column, when that value is a string or number; the row id only as a last resort, because an id is a KEY and announced it reads a UUID aloud. Set it when the first column is not the row's name (an avatar, a status badge, an id). `rowSelection.getCheckboxProps` `aria-label` still overrides a single row.",
30
+ "name": "getRowLabel",
31
+ "type": "(row: T) => string"
32
+ },
33
+ {
34
+ "defaultValue": "false",
35
+ "description": "Adds a checkbox column and a SelectAll header checkbox. Use with selected + onSelectChange for controlled selection, or omit both for uncontrolled.",
36
+ "name": "selectable",
37
+ "type": "boolean"
38
+ },
39
+ {
40
+ "description": "Controlled set of selected row IDs. Pair with onSelectChange. Omit for uncontrolled.",
41
+ "name": "selected",
42
+ "type": "Set<string>"
43
+ },
44
+ {
45
+ "description": "Per-row STATE — a leading-edge rail plus a weak wash, in the same six tone names Card `accent` uses, so a row needing attention and a card needing attention are one vocabulary. Return undefined for an ordinary row. This is the supported alternative to painting rows through rowClassName: ui-audit treats any prop whose name ends in `className` as a class expression, so a `border-l-4 bg-amber-50` inside that arrow is an error in a consumer. NEVER the only signal — colour alone cannot carry meaning (WCAG 1.4.1), so keep the reason in a cell (a Badge, a status column) and let the rail make that cell findable in a long table. The rail reads the --mark-* tier, which is held at 3:1 against its own row by src/tokens/__tests__/tone-mark-contrast.test.ts.",
46
+ "name": "rowTone",
47
+ "type": "(row: T) => \"primary\" | \"success\" | \"warning\" | \"info\" | \"attention\" | \"destructive\" | undefined"
48
+ },
49
+ {
50
+ "description": "Called with the full new selection set after any checkbox interaction.",
51
+ "name": "onSelectChange",
52
+ "type": "(next: Set<string>) => void"
53
+ },
54
+ {
55
+ "description": "Makes rows clickable for navigation. Row click is suppressed when the user clicks an interactive descendant (button, a, input, select, textarea, [role=menuitem]).",
56
+ "name": "onRowClick",
57
+ "type": "(row: T) => void"
58
+ },
59
+ {
60
+ "defaultValue": "'compact'",
61
+ "description": "Controlled row density across all three tiers (compact 28 / default 36 / comfortable 48) — drive it from a 表示密度 radio. Omit to let DataTable manage it internally (DataTable.DensityToggle flips compact↔comfortable).",
62
+ "name": "density",
63
+ "type": "'compact' | 'default' | 'comfortable'"
64
+ },
65
+ {
66
+ "description": "Called when the user toggles density. Only needed when density is controlled.",
67
+ "name": "onDensityChange",
68
+ "type": "(density: 'compact' | 'default' | 'comfortable') => void"
69
+ },
70
+ {
71
+ "description": "Zebra rows: every EVEN LOGICAL record paints --table-row-striped-background (default --muted at 0.8 alpha — every text role on it stays at AA, computed at the row so dark mode and scoped themes follow). Parity is by record, not DOM row — an expanded detail row is skipped when counting and wears its own record's stripe; on a paged table the count restarts per rendered page. Frozen (`fixed`) cells wear the stripe over their opaque base; hover, selection, `rowClassName` and `rowTone` all still read on a striped row. OMIT to inherit the theme default (`--table-row-striped-alpha`, 0% unless the service set it); `true` / `false` override it for this table. Element Plus `stripe` / Bootstrap `.table-striped`; antd has no prop.",
72
+ "name": "striped",
73
+ "type": "boolean"
74
+ },
75
+ {
76
+ "defaultValue": "false",
77
+ "description": "Highlight a row on hover even when it is not clickable. onRowClick already implies hover; use this for read-only tables that still want the hover affordance.",
78
+ "name": "hoverable",
79
+ "type": "boolean"
80
+ },
81
+ {
82
+ "defaultValue": "true",
83
+ "description": "Pin the header to the top while the body scrolls (ヘッダ追従). Set false to let it scroll away with the rows.",
84
+ "name": "stickyHeader",
85
+ "type": "boolean"
86
+ },
87
+ {
88
+ "defaultValue": "'default'",
89
+ "description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: below collapseBelow the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*) under table-layout: fixed, cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL.",
90
+ "name": "preset",
91
+ "type": "'default' | 'action-collection'"
92
+ },
93
+ {
94
+ "defaultValue": "'sm'",
95
+ "description": "Step at which preset=\"action-collection\" switches to the compact priority measures, measured against the TABLE'S OWN container (a container query on sm 40rem · md 48rem · lg 64rem · xl 80rem), not the viewport — a table inside a master rail collapses before the page does. Ignored while preset is 'default'.",
96
+ "name": "collapseBelow",
97
+ "type": "'sm' | 'md' | 'lg' | 'xl'"
98
+ },
99
+ {
100
+ "description": "Accessible name for the horizontal-scroll REGION — the tabindex=\"0\" wrapper a keyboard user lands on to scroll a table wider than its container — NOT for the <table> element (pass aria-label for that; it still reaches the table). OPTIONAL: left out, the region takes the localized dataTable.scrollRegion default (\"Scrollable table\"), so no consumer has to invent a name for every table. Pass a plain string when the page can say WHICH table; a non-string node cannot be an aria-label and falls back to the default. The wrapper carries role=\"group\" (not \"region\" — a named region is a LANDMARK, and several tables on one page would then collide under axe landmark-unique) and is emitted ONLY while the region actually has overflow to reach, measured at runtime: a table that fits adds no tab stop, no role and no name. (gh#817)",
101
+ "name": "label",
102
+ "type": "LabelProp"
103
+ },
104
+ {
105
+ "description": "Active sort state (controlled/server surface). When provided alongside onSortChange, sortable columns show directional arrow icons and clicking the active column twice clears sort (calls onSortChange(undefined)). Omit both sort and onSortChange to sort client-side via TanStack.",
106
+ "name": "sort",
107
+ "type": "{ key: string; direction: 'asc' | 'desc' }"
108
+ },
109
+ {
110
+ "description": "Called when a sortable column header is clicked. Receives undefined when sort is cleared (third click on same column). Providing sort or onSortChange opts into the controlled (server) sort surface; omit both and the table sorts client-side via TanStack.",
111
+ "name": "onSortChange",
112
+ "type": "(sort: { key: string; direction: 'asc' | 'desc' } | undefined) => void"
113
+ },
114
+ {
115
+ "description": "Global search term surfaced by DataTable.Search. Omit both for client-side filtering; pass them to drive a server query (with manualFiltering).",
116
+ "name": "globalFilter / onGlobalFilterChange",
117
+ "type": "string / (next: string) => void"
118
+ },
119
+ {
120
+ "description": "Pagination state. THREE shapes: the TanStack `{ pageIndex, pageSize }` (surfaced by a composed DataTable.Pagination); antd's TablePaginationConfig `{ total, current (1-based), pageSize, pageSizeOptions, showSizeChanger, showTotal, position, onChange(page, pageSize) }`; or `false` (no pager, rows unsliced). The antd object WITHOUT a composed DataTable.Pagination renders the table's own footer — the real Pagination (total beside the page numbers) at `position` (TablePaginationPositionProp[], logical: topStart|topCenter|topEnd|bottomStart|bottomCenter|bottomEnd|none; default ['bottomEnd'] = antd bottomRight), `size=\"sm\"` on a compact table so it matches the toolbar's sm controls. Server-paged: `pagination={{ total, current, pageSize, onChange }}` with `data` = the current page — a total larger than data.length with ≤ pageSize rows reads as server paging (antd's rule), giving ceil(total / pageSize) pages. A composed DataTable.Pagination keeps its own footer (never two pagers). `hideOnSinglePage` defaults to FALSE here (antd's table default), so a list filtered down to one page keeps its total and rows-per-page select; pass `hideOnSinglePage: true` for the standalone Pagination's behaviour.",
121
+ "name": "pagination / onPaginationChange / rowCount",
122
+ "type": "{ pageIndex: number; pageSize: number } | TablePaginationProp | false / OnChangeFn / number"
123
+ },
124
+ {
125
+ "description": "Column show/hide state surfaced by DataTable.ViewOptions ('set view'). Internal if omitted.",
126
+ "name": "columnVisibility / onColumnVisibilityChange",
127
+ "type": "VisibilityState / OnChangeFn<VisibilityState>"
128
+ },
129
+ {
130
+ "defaultValue": "false",
131
+ "description": "Default false so the simple data+columns case sorts/filters/paginates in-browser. Set the relevant flag true and drive the matching state from your query for server-side behaviour.",
132
+ "name": "manualSorting / manualFiltering / manualPagination",
133
+ "type": "boolean"
134
+ },
135
+ {
136
+ "defaultValue": "false",
137
+ "description": "When true, swaps the body for SHAPED skeleton rows rendered inside the table's own grid (one border, aligned columns) — never a separate <SkeletonTable> in a Card (that double-borders). With React Query keepPreviousData, drive this off isPlaceholderData (pagination/search) || isLoading (first load), NOT isLoading alone. Suppresses the empty state while true.",
138
+ "name": "loading",
139
+ "type": "boolean"
140
+ },
141
+ {
142
+ "description": "Custom content rendered inside the table body when data is empty and loading is false. Defaults to a built-in EmptyState with a localised 'No data' message. Pass a custom <EmptyState title='...' description='...' action={...}/> to tailor the message.",
143
+ "name": "empty",
144
+ "type": "ReactNode"
145
+ },
146
+ {
147
+ "description": "FAILURE state. Pass error={isError} — `true` renders the built-in localized destructive EmptyState ('Couldn't load this list') announced with role='alert'; any other node REPLACES that copy (e.g. an <Alert> carrying an error code + request id). `false`/`undefined` means the read succeeded. NEVER pass a raw Error object (it is not renderable).",
148
+ "name": "error",
149
+ "type": "ReactNode"
150
+ },
151
+ {
152
+ "description": "PERMISSION-DENIED state — the read was REFUSED (403), not failed. `true` renders the built-in localized warning EmptyState ('You don't have access to this list') with NO retry, announced politely (aria-live) because a permission boundary is expected information, not a fault. Takes precedence over `error`. Any other node replaces the copy.",
153
+ "name": "denied",
154
+ "type": "ReactNode"
155
+ },
156
+ {
157
+ "description": "Retry handler surfaced as a Retry button inside the BUILT-IN error state only. Omit it to render the error without a retry affordance; it is intentionally never offered for `denied` (repeating a 403 cannot succeed).",
158
+ "name": "onRetry",
159
+ "type": "() => void"
160
+ },
161
+ {
162
+ "description": "Full row-selection configuration (antd rowSelection). Supersedes — and can be mixed with — selectable/selected/onSelectChange, which drive the same state. type:'radio' makes the column single-choice (no header checkbox at all). getCheckboxProps is the declared home for 'this row cannot be selected' (disabled) and for a per-row accessible name. preserveSelectedRowKeys keeps a key selected after its row leaves `data` (server paging / a filter), which is the only way a select-across-pages bulk action can be correct. selections adds bulk entries under the header checkbox (true = the built-in all · invert · none).",
163
+ "name": "rowSelection",
164
+ "type": "{ type?: 'checkbox'|'radio'; selectedRowKeys?: string[]; defaultSelectedRowKeys?: string[]; onChange?: (keys, rows) => void; getCheckboxProps?: (row) => { disabled?, 'aria-label'? }; preserveSelectedRowKeys?: boolean; selections?: true | { key, text, onSelect }[]; hideSelectAll?: boolean; columnTitle?: ReactNode }"
165
+ },
166
+ {
167
+ "description": "Expandable detail rows (antd expandable). Supplying expandedRowRender adds a leading expand column before the selection column and renders the panel in a real <tr> spanning every column, so the table's grid semantics survive. rowExpandable gates the affordance per row; expandedRowKeys + onExpandedRowsChange make it controlled.",
168
+ "name": "expandable",
169
+ "type": "{ expandedRowRender?: (row, index, expanded) => ReactNode; rowExpandable?: (row) => boolean; defaultExpandAllRows?: boolean; expandedRowKeys?: string[]; onExpandedRowsChange?: (keys) => void; expandRowByClick?: boolean; columnTitle?: ReactNode }"
170
+ },
171
+ {
172
+ "description": "Footer totals row (antd summary), rendered in a real <tfoot> so it keeps the column widths and the screen-reader row navigation. Receives the rows currently rendered (post sort/filter/page), so a page total and a grand total are both expressible. Compose the return with <TableRow>/<TableCell> from the Table primitive.",
173
+ "name": "summary",
174
+ "type": "(rows: readonly T[]) => ReactNode"
175
+ },
176
+ {
177
+ "description": "Scroll envelope (antd scroll). x is the table's MINIMUM inline size — it scrolls horizontally past it; y is the body's MAXIMUM block size — it scrolls vertically past it, with the sticky header staying put. Both are published as --table-scroll-inline-size / --table-scroll-block-size, so the lengths stay data and the geometry stays in the stylesheet. Setting x also switches the table to `table-layout: fixed`, which is what makes column widths (and `ellipsis`) authoritative.",
178
+ "name": "scroll",
179
+ "type": "{ x?: number | string; y?: number | string }"
180
+ },
181
+ {
182
+ "description": "Sticky header (antd sticky). Supersedes stickyHeader when given. The object form carries the offset a page-level fixed topbar needs, published as --table-sticky-offset.",
183
+ "name": "sticky",
184
+ "type": "boolean | { offsetHeader?: number | string }"
185
+ },
186
+ {
187
+ "description": "Per-row DOM props merged onto the <tr> (antd onRow) — a context menu, a drag handle, a data attribute for an E2E hook. The returned onClick/onKeyDown/className COMPOSE with the built-in row-click and row-tint behaviour rather than replacing it. For plain row navigation prefer onRowClick, which already handles the keyboard and the interactive-descendant guard.",
188
+ "name": "onRow",
189
+ "type": "(row: T, index: number) => React.HTMLAttributes<HTMLTableRowElement>"
190
+ },
191
+ {
192
+ "defaultValue": "false",
193
+ "description": "Draw the vertical rules between columns (antd bordered), forwarded to the Table primitive. The surface keeps drawing the outer frame, so the two never stack. Reach for it when the table carries merged cells or a dense numeric grid.",
194
+ "name": "bordered",
195
+ "type": "boolean"
196
+ },
197
+ {
198
+ "defaultValue": "false",
199
+ "description": "Explain the NEXT sort step in a tooltip on every sortable header (antd showSorterTooltip). Defaults to false, not antd's true, so an existing table gains no hover chrome; a column's own showSorterTooltip overrides it either way.",
200
+ "name": "showSorterTooltip",
201
+ "type": "boolean"
202
+ },
203
+ {
204
+ "defaultValue": "['asc', 'desc']",
205
+ "description": "Table-wide sort cycle (antd sortDirections, in this library's asc/desc spelling). The cycle runs through the listed directions and then clears, so ['desc','asc'] sorts descending first — the right default for a date or amount column. A column's own sortDirections wins.",
206
+ "name": "sortDirections",
207
+ "type": "('asc' | 'desc')[]"
208
+ },
209
+ {
210
+ "description": "Column filters changed, keyed by column — this library's split of the `filters` argument antd passes to the table-level onChange. Pair it with a column's filteredValue to drive filtering from a server query; omit both and the column filters client-side through its onFilter.",
211
+ "name": "onFilterChange",
212
+ "type": "(filters: Record<string, (string | number | boolean)[]>) => void"
213
+ },
214
+ {
215
+ "description": "Extra classes applied to the root wrapper div (ui-data-table-root).",
216
+ "name": "className",
217
+ "type": "string"
218
+ },
219
+ {
220
+ "description": "Compound sub-parts: DataTable.Toolbar, DataTable.Search (global filter), DataTable.ViewOptions (column show/hide), DataTable.SelectAll, DataTable.BulkActions (ReactNode children OR a (count)=>node render-prop), DataTable.DensityToggle, DataTable.Pagination (cursor first/next when given cursor+hasMore+onChange, else numbered page-size form), DataTable.RowActions (kebab trigger), DataTable.Content. If no DataTable.Content is present in children, one is auto-rendered.",
221
+ "name": "children",
222
+ "type": "ReactNode"
223
+ }
224
+ ],
225
+ "related": [
226
+ "Table — raw primitive (TableHeader/TableBody/TableRow/TableCell). Use DataTable instead; only reach for Table directly when you need a non-standard layout that DataTable cannot express.",
227
+ "SkeletonTable — standalone skeleton placeholder rendered before any DataTable mounts (e.g. in a Suspense fallback or deferred-prop skeleton slot). DataTable.loading covers in-table loading; SkeletonTable covers pre-mount skeletons.",
228
+ "EmptyState — standalone empty state for non-table lists. DataTable already embeds EmptyState in its body; only use bare EmptyState for card content, non-tabular lists, or zero-state pages outside a DataTable.",
229
+ "LineChart / BarChart / AreaChart / PieChart (@godxjp/ui/charts) — when the SHAPE or trend of aggregated data matters more than exact per-row figures, visualize it with a chart instead of (or alongside) the table; keep DataTable when users need to read, sort, or act on individual rows.",
230
+ "DataState / InfiniteQueryState — TanStack Query lifecycle widgets from @godxjp/ui/query. Prefer these over DataTable when your list is driven by useQuery/useInfiniteQuery and you want automatic skeleton/empty/error handling at the query level rather than at the table level."
231
+ ],
232
+ "rules": [
233
+ 24,
234
+ 31,
235
+ 35,
236
+ 37
237
+ ],
238
+ "storyPath": "data-display/DataTable.stories.tsx",
239
+ "tagline": "The one TanStack-powered compound admin list — sticky header, sorting, global search, column visibility ('set view'), bulk selection, BOTH cursor and numbered pagination, density, and built-in empty/loading states. Keep the SIMPLE `data` + lean `columns` (ColumnDef) API for the common case; opt into the full grid chrome via the compound parts. Internally driven by @tanstack/react-table (a real dependency). Lives on @godxjp/ui/data-display only (it is NOT on the runtime-neutral root/admin barrel because it pulls TanStack).",
240
+ "usage": [
241
+ "DO use `striped` on dense list tables (many columns, a row the eye must follow across the width). To stripe EVERY Table and DataTable in a service, set it ONCE in the theme — `:root { --table-row-striped-alpha: 100%; }` — instead of passing `striped` at each call site; `striped={false}` then opts one table out. Retint with `--table-row-striped-background`, never with a `rowClassName` utility or `:nth-child` page CSS (those count DOM rows, so an expanded detail row shifts every stripe after it).",
242
+ "DO pass loading={isFetching} during data fetches — it renders a loading row in the table body and suppresses the empty state. Never show a spinner outside DataTable while the table is visible.",
243
+ "DO NOT add a data.length===0 conditional around DataTable. When data is empty and loading is false, the built-in EmptyState renders automatically. Pass empty={<EmptyState title='...'/>} only when you need a custom message.",
244
+ "SIX STATES, ZERO HAND-ROLLING: loading (`loading`), empty (automatic / `empty`), error (`error` + optional `onRetry`), denied (`denied`), pagination (`DataTable.Pagination`), row actions (`DataTable.RowActions`). Wire them straight off the query — `<DataTable loading={isPending} error={isError} denied={status === 403} onRetry={refetch} …/>` — and never branch the page around the table to render your own alert/empty/forbidden block. Precedence is loading > denied > error > empty > rows, so exactly one state ever shows.",
245
+ "DO provide getRowId when selectable is true or when rows do not have a string/number 'id' field — the default falls back to row.id and silently returns '' for missing IDs, which breaks selection.",
246
+ "DO use DataTable.Toolbar as the immediate child that wraps search/filter controls on the left and DataTable.DensityToggle/action buttons on the right. DataTable.BulkActions inside the toolbar auto-hides when selection count is 0; it accepts either plain ReactNode children (built-in 'N selected' status bar) or a (count)=>node render-prop (you own the whole bar).",
247
+ "DO reach for the grid chrome (DataTable.Search, DataTable.ViewOptions, DataTable.Pagination pageSizeOptions) when you need global search, a column 'set view' picker, or numbered pagination — these are the merged former-DataGrid features, now on the one DataTable. Drive them client-side by default; pass the matching state + manual* flag for a server query.",
248
+ "DataTable.Pagination OWNS ITS OWN INSET. The footer is a self-contained slot: it declares `padding-block` + `padding-inline` from `--table-pagination-padding-{y,x}` (block default = `--space-stack-sm`, inline default = `--table-cell-space-x`, so the 'rows per page' label lands on the same optical axis as the first column's text). Before the fix it declared `padding-top` only, so inside the documented flush container (`<Card><CardContent flush><DataTable/>`) the label and page-size Select sat flush against the container edge and border. DON'T ship a local `.ui-data-table-pagination { padding: … }` override in an app — retune the two tokens in your theme instead.",
249
+ "RESPONSIVE APPROVAL / ACTION QUEUE: reach for `preset=\"action-collection\"` when a dense five-column queue (requester · target · reason · requested date · row actions) must stay readable at 390px, and give every column a `priority` on its ColumnDef — `primary` (the row subject), `secondary` (its target), `meta` (a timestamp/id), `actions` (the row-action affordance, whose measure is reserved FIRST so it can never be pushed off-screen). Leave the free-text column unmarked; it takes the remaining space. This is the SAME contract, the SAME `--table-action-collection-*` tokens and the SAME container query as `Table preset=\"action-collection\"` — there is no separate DataTable family. Never add a consumer width, a hidden column, `hiddenOnMobile` or a page-local breakpoint to make a table fit: retune the tokens instead.",
250
+ "DON'T set `width` on a column that also has a `priority` — an explicit width utility wins the cascade over the priority measure and re-opens the horizontal scroll. Under the preset, `pin: 'end'` is also redundant: nothing scrolls sideways, so the actions column is already in frame.",
251
+ "The DataTable surface's narrow-viewport width floor is `--table-surface-min-inline-size` (default 640px, released at the `sm` viewport step). It used to be a hard-coded `min-w-[640px] sm:min-w-0` utility pair on the surface — the literal that forced the horizontal scroll at 390. Retune (or zero) the token in your theme; `preset=\"action-collection\"` already opts out of it.",
252
+ "DO use ColumnDef.render for custom cell content (Badge, Link, RowActions). For plain string/number fields render can be omitted — DataTable falls back to String(row[key]).",
253
+ "DO give every visually-empty column an accessible header via `ariaLabel` — a row-actions column (`header: ''`, `pin: 'end'`) sets `ariaLabel: t('actions')`, so screen readers announce the column and axe reports no `empty-table-header`. DataTable dev-warns any column that renders a `<th>` with neither visible text nor an `ariaLabel`. The selection column added by `selectable` is already named by its SelectAll checkbox — no `ariaLabel` needed there.",
254
+ "COLUMN SEMANTICS + KEYBOARD: a `sortable` header renders as a real <button> inside the <th> with `aria-sort` (ascending/descending/none) on the <th>; it is Tab-reachable and toggles asc → desc → cleared on Enter/Space/click. A selection column exposes a header 'select all' Checkbox (indeterminate when a subset is selected) and a per-row Checkbox, each keyboard-operable with Space. An action column is visually empty but carries an `ariaLabel`; its per-row controls (kebab menu / buttons) own their own accessible names and keyboard behavior. Row click (`onRowClick`) is suppressed when the user activates an interactive descendant.",
255
+ "DO NOT nest DataTable.Content in a conditional — it is already guarded internally. If you need to override the table body slot, drop exactly one <DataTable.Content /> in children; DataTable auto-detects it by displayName and skips the default."
256
+ ],
257
+ "useCases": [
258
+ "Admin list pages (invoices, customers, orders, accounts) where rows are clickable for detail navigation via onRowClick.",
259
+ "Bulk-action workflows (e.g. mark invoices paid, export selected rows) — use selectable + DataTable.BulkActions to show contextual action buttons only when something is selected.",
260
+ "Server-side sorted tables: pass sort + onSortChange and update the data prop after the API call; DataTable renders asc/desc/neutral icons on the header automatically.",
261
+ "Cursor-paginated lists: add DataTable.Pagination with cursor + hasMore + onChange inside children to get First/Next navigation without offset arithmetic. For page-size + numbered prev/next instead, use DataTable.Pagination with pageSizeOptions (no cursor/onChange) driven by the internal TanStack pagination.",
262
+ "Server-paged table (antd `Table pagination`): `pagination={{ total, current, pageSize, onChange }}` with `data` = the rows of the current page — the table renders its own footer with the real Pagination (total + page numbers, bottom-end, sized from density). Add `showTotal: true` or `(total, [from, to]) => …` for the total label; `position: ['topEnd']` to move it.",
263
+ "Full grid screens (global search + column 'set view' + numbered pagination): compose DataTable.Search, DataTable.ViewOptions, DataTable.DensityToggle in the toolbar and DataTable.Pagination pageSizeOptions={[…]} — client-side by default, or server-side by passing globalFilter/pagination/sort state with the matching manual* flag.",
264
+ "Responsive admin tables where columns should drop at specific viewport steps — set hideBelow on each ColumnDef (sm/md/lg/xl, same ladder as Flex hideBelow); hiddenOnMobile: true remains an alias for hideBelow:\"md\". ColumnDef.priority is ONLY for preset=\"action-collection\" width allocation, not for hiding. When every column must stay DISCOVERABLE at 390 (an approval/action queue), use preset=\"action-collection\" + priority rather than hideBelow.",
265
+ "Access-approval / action queues at 390px (SCR-105): preset=\"action-collection\" + a priority on each ColumnDef keeps requester · target · reason · requested date · row actions inside the initial narrow frame with no page-local CSS, no consumer width, no hidden column and no horizontal scroll — see the DataTable 'Approval queue' example page.",
266
+ "Loading skeletons during initial page load or filter change: set loading={true} alongside an empty data={[]} to show the loading row without flashing an empty state."
267
+ ]
268
+ }
@@ -0,0 +1,275 @@
1
+ {
2
+ "absorbed": [
3
+ "DateRangePicker"
4
+ ],
5
+ "example": "import { useState } from \"react\";\nimport { DatePicker, FormField } from \"@godxjp/ui/data-entry\";\nimport { Flex } from \"@godxjp/ui/layout\";\nimport type { DateRange } from \"react-day-picker\";\n\n// One component, three shapes: a day, a month, and a range.\nexport function BillingFields() {\n const [dueDate, setDueDate] = useState<Date | undefined>(undefined);\n const [month, setMonth] = useState<Date | undefined>(undefined);\n const [period, setPeriod] = useState<DateRange | undefined>(undefined);\n\n return (\n <Flex direction=\"col\" gap=\"md\">\n <FormField id=\"due-date\" label=\"支払期日\" required>\n <DatePicker\n id=\"due-date\"\n name=\"due_date\"\n value={dueDate}\n onValueChange={setDueDate}\n minDate={new Date()}\n />\n </FormField>\n\n <FormField id=\"billing-month\" label=\"請求年月\">\n <DatePicker\n id=\"billing-month\"\n name=\"billing_month\"\n picker=\"month\"\n value={month}\n onValueChange={setMonth}\n />\n </FormField>\n\n <FormField id=\"period\" label=\"会計期間\">\n <DatePicker id=\"period\" name=\"period\" range value={period} onValueChange={setPeriod} />\n </FormField>\n </Flex>\n );\n}",
6
+ "group": "data-entry",
7
+ "importPath": "@godxjp/ui/data-entry",
8
+ "name": "DatePicker",
9
+ "props": [
10
+ {
11
+ "description": "Controlled panel visibility.",
12
+ "name": "open",
13
+ "type": "boolean"
14
+ },
15
+ {
16
+ "description": "Initial panel visibility.",
17
+ "name": "defaultOpen",
18
+ "type": "boolean"
19
+ },
20
+ {
21
+ "description": "Panel visibility changes.",
22
+ "name": "onOpenChange",
23
+ "type": "(open: boolean) => void"
24
+ },
25
+ {
26
+ "description": "Validation appearance; error announces invalid state.",
27
+ "name": "status",
28
+ "type": "\"error\" | \"warning\""
29
+ },
30
+ {
31
+ "description": "Shared control surface.",
32
+ "name": "variant",
33
+ "type": "\"outlined\" | \"filled\" | \"borderless\" | \"underlined\""
34
+ },
35
+ {
36
+ "description": "Shared control sizing.",
37
+ "name": "size",
38
+ "type": "\"sm\" | \"md\" | \"lg\""
39
+ },
40
+ {
41
+ "description": "antd `inputReadOnly` — sets the readonly attribute on the input so the mobile virtual keyboard stays down. The PANEL STILL OPENS: this is 'pick from the panel, do not type', not a second `disabled`. Use `disabled` to make the control inert.",
42
+ "name": "inputReadOnly",
43
+ "type": "boolean"
44
+ },
45
+ {
46
+ "description": "Keep invalid draft text on blur; never submit it as a committed value.",
47
+ "name": "preserveInvalidOnBlur",
48
+ "type": "boolean"
49
+ },
50
+ {
51
+ "description": "Logical popup placement.",
52
+ "name": "placement",
53
+ "type": "\"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\""
54
+ },
55
+ {
56
+ "description": "Additional panel footer content.",
57
+ "name": "renderExtraFooter",
58
+ "type": "() => ReactNode"
59
+ },
60
+ {
61
+ "description": "Ref to the editable input (with `range`, the start edge).",
62
+ "name": "ref",
63
+ "type": "Ref<HTMLInputElement>"
64
+ },
65
+ {
66
+ "description": "Display using date-fns patterns, Intl options (Japanese era supported), or a callback. Submission stays ISO.",
67
+ "name": "format",
68
+ "type": "string | Intl.DateTimeFormatOptions | ((date: Date) => string)"
69
+ },
70
+ {
71
+ "description": "Parser for custom or era display. Complete ISO input always works.",
72
+ "name": "parseFormat",
73
+ "type": "(text: string) => Date | undefined"
74
+ },
75
+ {
76
+ "description": "Earliest selectable date, inclusive, enforced on BOTH routes into the value — the panel greys the cell out and a typed date is rejected. The ONLY lower bound: the removed MonthPicker's `fromYear` is `minDate={new Date(year, 0, 1)}`. Alias of fromDate.",
77
+ "name": "minDate",
78
+ "type": "Date"
79
+ },
80
+ {
81
+ "description": "Latest selectable date, inclusive, enforced on both routes. `toYear={y}` becomes `maxDate={new Date(y, 11, 31)}`. Alias of toDate.",
82
+ "name": "maxDate",
83
+ "type": "Date"
84
+ },
85
+ {
86
+ "description": "Show week numbers.",
87
+ "name": "showWeek",
88
+ "type": "boolean"
89
+ },
90
+ {
91
+ "description": "Which period the PANEL opens on, independently of the value (antd `defaultPickerValue`), and — as in antd — RE-APPLIED every time the panel opens, not only at mount. Use it for 'open on the fiscal year's start month' or 'open on the month of the row being edited'.",
92
+ "name": "defaultPickerValue",
93
+ "type": "Date"
94
+ },
95
+ {
96
+ "description": "Controlled panel period (antd `pickerValue`). Wins over `defaultPickerValue` and over the value, and freezes the panel's own navigation — the parent owns which period is shown.",
97
+ "name": "pickerValue",
98
+ "type": "Date"
99
+ },
100
+ {
101
+ "description": "Stage choices until confirmed; defaults on with showTime.",
102
+ "name": "needConfirm",
103
+ "type": "boolean"
104
+ },
105
+ {
106
+ "description": "Include time editing; object takes TimePicker steps/hour-cycle/disabledTime. Not available with `range` or `multiple`.",
107
+ "name": "showTime",
108
+ "type": "boolean | object"
109
+ },
110
+ {
111
+ "description": "Quick choices resolved on click and checked against the constraints. `T` follows the cardinality: `Date` normally, `DateRange` with `range`.",
112
+ "name": "presets",
113
+ "type": "{ label: ReactNode; value: T | (() => T) }[]"
114
+ },
115
+ {
116
+ "description": "GRANULARITY of one selection (antd `picker`). `date`/`week` show the day grid; `month`/`quarter`/`year` show a period grid. The value is always the START of the chosen period, and `name` submits the matching reduced ISO form.",
117
+ "name": "picker",
118
+ "type": "\"date\" | \"week\" | \"month\" | \"quarter\" | \"year\""
119
+ },
120
+ {
121
+ "description": "CARDINALITY: two endpoints in one control (antd `DatePicker.RangePicker`). value/defaultValue and the callback become `DateRange`; the pair submits as `${name}_from` / `${name}_to`. Composes with `picker`, so a month range is `<DatePicker range picker=\"month\" />`. Never place two DatePickers side by side to fake this.",
122
+ "name": "range",
123
+ "type": "boolean"
124
+ },
125
+ {
126
+ "description": "Select several dates; value/defaultValue and the callback use Date[]. Incompatible with showTime and with range.",
127
+ "name": "multiple",
128
+ "type": "boolean"
129
+ },
130
+ {
131
+ "description": "With `range`: which of the two endpoints may stay empty (antd `allowEmpty`). Default `[true, true]`.",
132
+ "name": "allowEmpty",
133
+ "type": "[boolean, boolean]"
134
+ },
135
+ {
136
+ "defaultValue": "true",
137
+ "description": "Normalise the selection into ascending order (antd `order`). ONE rule seen through two value shapes: a `range` picked backwards is SWAPPED, a `multiple` selection is SORTED. Pass false to keep pick order.",
138
+ "name": "order",
139
+ "type": "boolean"
140
+ },
141
+ {
142
+ "description": "Decorate a day cell — 祝日, a booked day, a deadline. It WRAPS the library's own day button rather than replacing it: `originNode` already carries the selection state, `aria-selected`, the disabled handling and its place in the grid's roving tabindex. Write `<>{originNode}<Badge …/></>` — decorate, never rebuild, or every marker re-derives all of that and most get it wrong.",
143
+ "name": "cellRender",
144
+ "type": "(date: Date, info: { originNode: ReactNode }) => ReactNode"
145
+ },
146
+ {
147
+ "defaultValue": "true",
148
+ "description": "Rule the popup's DAY grid — forwarded to `Calendar bordered`, on by default like it. Applies to `picker=\"date\"` / `\"week\"` in single, `multiple` and `range`; the month / quarter / year period grid has no day cells and ignores it. `bordered={false}` opts out. Line colour: `--calendar-grid-border-color` (default `hsl(var(--border))`, the same tier as the popover edge and a table row rule — lightened from `hsl(var(--input) / 0.5)` in gh#730). The caption→weekday-row step is ONE token now, `--calendar-grid-space-block-start` (space-3, measured 32px → 12px): it used to stack with the month column gap.",
149
+ "name": "bordered",
150
+ "type": "boolean"
151
+ },
152
+ {
153
+ "description": "Forbid individual dates by predicate — a business rule the `minDate`/`maxDate` window cannot express (holidays, blackout days, a 開始 date already chosen). A forbidden day is refused on BOTH routes into the value: it cannot be clicked and it is rejected when typed.",
154
+ "name": "disabledDate",
155
+ "type": "(date: Date) => boolean"
156
+ },
157
+ {
158
+ "defaultValue": "false",
159
+ "description": "Footer action that jumps to the current month and selects today (single), fills the open end of the range (range) or adds today (multiple). Disabled when today is outside minDate / maxDate or matches `disabledDate`.",
160
+ "name": "showToday",
161
+ "type": "boolean"
162
+ },
163
+ {
164
+ "defaultValue": "false",
165
+ "description": "Footer action that calls `onClose`.",
166
+ "name": "showClose",
167
+ "type": "boolean"
168
+ },
169
+ {
170
+ "description": "Invoked by the showClose footer action.",
171
+ "name": "onClose",
172
+ "type": "() => void"
173
+ },
174
+ {
175
+ "description": "Controlled selection. `Date` by default, `Date[]` with `multiple`, `DateRange` with `range`. The input text and the panel stay in sync with it.",
176
+ "name": "value",
177
+ "type": "Date | Date[] | DateRange | undefined"
178
+ },
179
+ {
180
+ "description": "Uncontrolled initial selection, same shape as `value`.",
181
+ "name": "defaultValue",
182
+ "type": "Date | Date[] | DateRange | undefined"
183
+ },
184
+ {
185
+ "description": "Fires when the user commits — a panel pick, a complete typed entry, Enter, or a clear (undefined). The argument type follows the cardinality, so no narrowing is needed at the call site.",
186
+ "name": "onValueChange",
187
+ "type": "(value: Date | Date[] | DateRange | undefined) => void"
188
+ },
189
+ {
190
+ "description": "Form field name. Submits ISO-8601 at the precision `picker` selects: `2026-03-01` for date/week, `2026-03` for month and quarter, `2026` for year. With `range` the pair submits as `${name}_from` / `${name}_to`.",
191
+ "name": "name",
192
+ "type": "string"
193
+ },
194
+ {
195
+ "description": "HTML `id` for the input (the group, with `range`). Auto-generated when omitted, so the field always has one.",
196
+ "name": "id",
197
+ "type": "string"
198
+ },
199
+ {
200
+ "description": "Placeholder shown while empty. Defaults to the i18n string for the cardinality and granularity in play.",
201
+ "name": "placeholder",
202
+ "type": "string"
203
+ },
204
+ {
205
+ "defaultValue": "false",
206
+ "description": "Makes the control inert: the input, the clear button and the panel trigger all go dead, and a controlled `open` cannot force the panel up. With `range`, the TUPLE form `[from, to]` locks ONE endpoint and leaves the other editable (antd's RangePicker `disabled`) — the locked edge keeps its committed value whatever is typed or picked, and the ✕ is withdrawn because clearing would wipe it.",
207
+ "name": "disabled",
208
+ "type": "boolean | [boolean, boolean]"
209
+ },
210
+ {
211
+ "description": "Extra CSS classes on the outermost wrapper. Use for width/margin overrides.",
212
+ "name": "className",
213
+ "type": "string"
214
+ },
215
+ {
216
+ "description": "Locale object (from `date-fns/locale`) forwarded to the panel. Controls month/day names and the week start. The input always accepts the ISO form regardless of locale.",
217
+ "name": "locale",
218
+ "type": "DayPickerProps[\"locale\"]"
219
+ },
220
+ {
221
+ "description": "Alias of minDate, kept for the antd v4 spelling.",
222
+ "name": "fromDate",
223
+ "type": "Date"
224
+ },
225
+ {
226
+ "description": "Alias of maxDate, kept for the antd v4 spelling.",
227
+ "name": "toDate",
228
+ "type": "Date"
229
+ },
230
+ {
231
+ "defaultValue": "true",
232
+ "description": "Inline ✕ that resets the value when one is set (antd `allowClear`). The object form replaces the icon and/or the accessible label. Pass `false` on a required field.",
233
+ "name": "allowClear",
234
+ "type": "boolean | { clearIcon?: ReactNode; label?: string }"
235
+ },
236
+ {
237
+ "description": "Accessible NAME of the calendar button. The default (「カレンダーを開く」 / 'Open calendar') is right for one picker on a page and useless for three: a screen-reader user hears the same sentence three times with nothing to say which field each button belongs to (WCAG 2.2 SC 2.4.6). Name it after the FIELD — `開始日のカレンダーを開く`. Same axis and same reason as Select's `clearLabel`.",
238
+ "name": "triggerLabel",
239
+ "type": "string"
240
+ }
241
+ ],
242
+ "related": [
243
+ "TimePicker — companion for HH:mm time selection; same form-submittable-input pattern with a `name` prop.",
244
+ "Calendar — the bare calendar grid used inside DatePicker; reach for it only when you need an always-visible month grid with no input.",
245
+ "MonthPicker / MonthRangePicker / DateRangePicker were separate components until 22.0.0. They are now `picker=\"month\"`, `range picker=\"month\"` and `range` on this component."
246
+ ],
247
+ "rules": [
248
+ 3,
249
+ 6,
250
+ 13,
251
+ 31
252
+ ],
253
+ "storyPath": "data-entry/DatePicker.stories.tsx",
254
+ "tagline": "ONE date control: `picker` sets the granularity (day · week · month · quarter · year), `range` makes it a two-endpoint field, `multiple` a set. A real typeable input holds the value and submits ISO-8601 at the picker's own precision; the panel is the visual-only affordance.",
255
+ "usage": [
256
+ "ONE component covers every date-shaped field. `picker` is the granularity axis, `range` and `multiple` are the cardinality axis, and they compose: `<DatePicker range picker=\"month\" />` is a month range.",
257
+ "DO use `name` to make the field form-submittable — it emits ISO-8601 at the picker's own precision (`2026-03` for a month, `2026` for a year, `2026-03-01` for a day). No hidden input of your own is needed.",
258
+ "DO wrap it in FormField like every other labelled control; FormField injects the id/aria wiring onto the input.",
259
+ "DO pass `triggerLabel` whenever a screen has MORE THAN ONE date field. Without it every calendar button on the page carries the identical name and a screen-reader user cannot tell which field each one opens.",
260
+ "DO test by filling the input directly: `await user.type(screen.getByRole('combobox'), '2026-04-15')`. With `range`, the two edges are named textboxes (From / To). The panel is secondary and not required for testing.",
261
+ "DO use `minDate` / `maxDate` to restrict what is selectable — they are enforced on the panel AND on typed entry, so the keyboard is not a way around the rule the mouse obeys.",
262
+ "DON'T place two DatePickers side by side to fake a from~to pair — that is `range`, which is one control, one shell and one value.",
263
+ "DO use `defaultPickerValue` when the panel should open somewhere other than the value — it is the only way to say it, and it re-applies on every open.",
264
+ "DON'T reach for `inputReadOnly` to switch the control off; it only sets the input's readonly attribute (the panel still opens). `disabled` is the inert one.",
265
+ "DON'T hand-roll a date text input + calendar popover — this component IS that pattern at WAI-ARIA combobox spec level."
266
+ ],
267
+ "useCases": [
268
+ "Invoice due-date field in an accounting form — attach `name='due_date'` and submit natively.",
269
+ "A 対象月 / 締め年月 field where the day is meaningless — `picker=\"month\"`, which submits `2026-03`.",
270
+ "A 会計期間 from~to pair — `range`, submitting `period_from` / `period_to`.",
271
+ "A quarterly or fiscal-year reporting filter — `picker=\"quarter\"` or `picker=\"year\"`.",
272
+ "Restricting a closing date to future days with `minDate={new Date()}`.",
273
+ "Locale-aware picker in a multi-language admin panel — pass a `date-fns` locale for the panel while the input keeps its ISO form."
274
+ ]
275
+ }