@godxjp/ui 28.12.0 → 29.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/START-HERE.md +29 -10
- package/agent/components/Anchor.json +6 -1
- package/agent/components/AppLauncher.json +10 -0
- package/agent/components/AppShell.json +1 -1
- package/agent/components/AreaChart.json +19 -1
- package/agent/components/Attachments.json +26 -1
- package/agent/components/BarChart.json +11 -1
- package/agent/components/BranchScopePicker.json +10 -0
- package/agent/components/Cascader.json +6 -1
- package/agent/components/Checkbox.json +6 -0
- package/agent/components/CompactBarTrend.json +1 -1
- package/agent/components/CredentialReveal.json +21 -0
- package/agent/components/DataState.json +1 -1
- package/agent/components/DataTable.json +4 -4
- package/agent/components/FormField.json +1 -1
- package/agent/components/InfiniteQueryState.json +1 -1
- package/agent/components/Input.json +1 -1
- package/agent/components/InputOTP.json +35 -0
- package/agent/components/LineChart.json +20 -2
- package/agent/components/ListRow.json +1 -1
- package/agent/components/Masonry.json +1 -1
- package/agent/components/MasterDetail.json +1 -1
- package/agent/components/PasswordStrength.json +1 -1
- package/agent/components/PermissionMatrix.json +6 -1
- package/agent/components/SearchInput.json +5 -0
- package/agent/components/Select.json +1 -0
- package/agent/components/ServiceRolePanel.json +5 -0
- package/agent/components/Sidebar.json +1 -1
- package/agent/components/Switch.json +6 -0
- package/agent/components/Table.json +8 -3
- package/agent/components/Tabs.json +10 -0
- package/agent/components/ThemeScope.json +49 -0
- package/agent/components/TimeRangePicker.json +5 -0
- package/agent/components/Topbar.json +1 -0
- package/agent/components/TopbarItem.json +2 -1
- package/agent/components/Transfer.json +6 -1
- package/agent/components/TreeSelect.json +1 -1
- package/agent/components/Upload.json +5 -0
- package/agent/components/UploadCropDialog.json +1 -1
- package/agent/components/formatDate.json +1 -1
- package/agent/components-index.json +5 -0
- package/agent/components.json +297 -31
- package/agent/index.json +19 -9
- package/agent/llms.txt +10 -10
- package/agent/patterns/tenant-brand-color.json +28 -0
- package/agent/patterns-index.json +27 -0
- package/agent/patterns.json +28 -0
- package/agent/rules.json +15 -0
- package/agent/tokens.json +4965 -970
- package/dist/app/index.d.ts +3 -0
- package/dist/app/index.js +3 -0
- package/dist/app/tenant-theme.d.ts +80 -0
- package/dist/app/tenant-theme.js +154 -0
- package/dist/app/theme-axes.d.ts +14 -1
- package/dist/app/theme-axes.js +24 -31
- package/dist/components/charts/chart-cartesian.d.ts +5 -1
- package/dist/components/charts/chart-cartesian.js +15 -8
- package/dist/components/data-display/badge.d.ts +1 -1
- package/dist/components/data-display/badge.js +20 -2
- package/dist/components/data-display/carousel.js +4 -4
- package/dist/components/data-display/data-table.js +13 -2
- package/dist/components/data-display/permission-matrix.js +1 -1
- package/dist/components/data-display/table.d.ts +11 -2
- package/dist/components/data-display/table.js +18 -2
- package/dist/components/data-entry/control-appearance.d.ts +12 -6
- package/dist/components/data-entry/control-appearance.js +1 -1
- package/dist/components/data-entry/select.js +4 -3
- package/dist/components/feedback/dialog.js +6 -3
- package/dist/components/feedback/overlay-header-tone.d.ts +7 -0
- package/dist/components/feedback/overlay-header-tone.js +4 -4
- package/dist/components/feedback/sheet.d.ts +1 -1
- package/dist/components/feedback/sheet.js +6 -9
- package/dist/components/feedback/sonner.js +16 -3
- package/dist/components/general/button.js +22 -5
- package/dist/components/layout/affix.js +15 -1
- package/dist/components/layout/sidebar.js +7 -1
- package/dist/components/navigation/anchor.d.ts +1 -1
- package/dist/components/navigation/anchor.js +5 -4
- package/dist/components/navigation/app-setting-picker.js +1 -1
- package/dist/components/navigation/pagination.js +1 -1
- package/dist/components/navigation/tabs.js +15 -2
- package/dist/components/query/infinite-query-state.d.ts +22 -6
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +0 -697
- package/dist/i18n/messages/ja.json +0 -691
- package/dist/i18n/messages/vi.json +0 -691
- package/dist/lib/control-styles.d.ts +31 -11
- package/dist/lib/control-styles.js +6 -6
- package/dist/lib/overlay-portal.d.ts +20 -0
- package/dist/lib/overlay-portal.js +93 -0
- package/dist/props/components/app.prop.d.ts +12 -0
- package/dist/props/components/charts.prop.d.ts +30 -0
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/navigation.prop.d.ts +21 -2
- package/dist/props/components/query.prop.d.ts +36 -2
- package/dist/props/registry.d.ts +46 -1
- package/dist/props/registry.js +38 -3
- package/dist/styles/alert-layout.css +34 -14
- package/dist/styles/badge-layout.css +10 -6
- package/dist/styles/base.css +14 -5
- package/dist/styles/card-layout.css +19 -8
- package/dist/styles/chart-layout.css +22 -3
- package/dist/styles/control.css +165 -59
- package/dist/styles/data-display-layout.css +129 -36
- package/dist/styles/data-entry-layout.css +23 -87
- package/dist/styles/dialog-layout.css +49 -19
- package/dist/styles/float-button-layout.css +5 -5
- package/dist/styles/focus-ring.css +9 -5
- package/dist/styles/layout.css +42 -15
- package/dist/styles/logo-layout.css +1 -1
- package/dist/styles/motion.css +1 -1
- package/dist/styles/navigation-layout.css +90 -29
- package/dist/styles/shell-layout.css +63 -40
- package/dist/styles/table-layout.css +56 -17
- package/dist/styles/text-layout.css +13 -4
- package/dist/styles/toggle.css +8 -2
- package/dist/tokens/components/actions.css +1 -1
- package/dist/tokens/components/attachments.css +4 -4
- package/dist/tokens/components/badge.css +4 -4
- package/dist/tokens/components/callout.css +1 -1
- package/dist/tokens/components/card.css +9 -4
- package/dist/tokens/components/chart.css +10 -1
- package/dist/tokens/components/chat-bubble.css +1 -1
- package/dist/tokens/components/control.css +28 -10
- package/dist/tokens/components/conversations.css +2 -1
- package/dist/tokens/components/data-display.css +12 -7
- package/dist/tokens/components/descriptions.css +1 -1
- package/dist/tokens/components/draggable-panel.css +1 -1
- package/dist/tokens/components/feedback.css +28 -8
- package/dist/tokens/components/float-button.css +1 -1
- package/dist/tokens/components/legal-document.css +1 -1
- package/dist/tokens/components/logo.css +1 -1
- package/dist/tokens/components/mega-menu.css +5 -3
- package/dist/tokens/components/navigation.css +21 -7
- package/dist/tokens/components/segmented.css +8 -3
- package/dist/tokens/components/shell.css +19 -5
- package/dist/tokens/components/table.css +9 -1
- package/dist/tokens/components/thought-chain.css +1 -1
- package/dist/tokens/components/toggle.css +2 -0
- package/dist/tokens/components/tree.css +3 -1
- package/dist/tokens/components/upload.css +6 -6
- package/dist/tokens/components/welcome.css +1 -1
- package/dist/tokens/foundation.css +28 -1
- package/docs/COMPOSITION-VS-COMPONENT.md +31 -0
- package/docs/CUSTOMER-THEMING.md +637 -1
- package/docs/DESIGN-AUTHORITY.md +13 -0
- package/docs/FRAME-COVERAGE-REPORT.md +3 -2
- package/docs/GLASSMORPHISM-STANDARD.md +196 -0
- package/docs/THEME-API-COVERAGE.md +538 -0
- package/docs/TOKEN-RESOLUTION.md +195 -0
- package/docs/TOKENS.md +63 -24
- package/docs/asset-modules.d.ts +7 -0
- package/docs/data-display/charts.tsx +80 -0
- package/docs/data-display/data-table/index.tsx +30 -0
- package/docs/data-display/popover.tsx +1 -1
- package/docs/data-display/table.tsx +52 -0
- package/docs/feedback/sheet.tsx +10 -10
- package/docs/foundation/density.tsx +4 -4
- package/docs/i18n/messages/en.json +1201 -0
- package/docs/i18n/messages/ja.json +1195 -0
- package/docs/i18n/messages/vi.json +1195 -0
- package/docs/layout/account-chip.tsx +2 -2
- package/docs/layout/responsive-grid.tsx +1 -1
- package/docs/navigation/toolbar.tsx +20 -12
- package/docs/providers/theme-scope.tsx +186 -0
- package/docs/showcase/caimono-price-comparison.tsx +911 -0
- package/docs/showcase/case4-login.tsx +2 -2
- package/docs/showcase/marketing-page.tsx +3 -2
- package/docs/showcase/permission-matrix.tsx +13 -5
- package/docs/showcase/table-pagination.tsx +2 -1
- package/docs/showcase/tenant-brand-color.tsx +338 -0
- package/docs/showcase/theme-customization.tsx +2 -1
- package/docs/showcase/theme-lab.tsx +2125 -0
- package/docs/themes/flat.css +462 -0
- package/docs/themes/glassmorphism.css +958 -0
- package/docs/themes/index.ts +200 -0
- package/package.json +4 -3
- package/scripts/explain-token.mjs +382 -0
package/agent/components.json
CHANGED
|
@@ -441,6 +441,11 @@
|
|
|
441
441
|
"name": "allowEmpty",
|
|
442
442
|
"type": "[boolean,boolean]"
|
|
443
443
|
},
|
|
444
|
+
{
|
|
445
|
+
"description": "A PAIR, one per endpoint — TimePicker's single-string `placeholder` is omitted from this type on purpose, because a range has two empty fields and one string would label both of them the same. Route both through t().",
|
|
446
|
+
"name": "placeholder",
|
|
447
|
+
"type": "[string,string]"
|
|
448
|
+
},
|
|
444
449
|
{
|
|
445
450
|
"description": "Native names are name_from and name_to.",
|
|
446
451
|
"name": "name",
|
|
@@ -1011,7 +1016,7 @@
|
|
|
1011
1016
|
]
|
|
1012
1017
|
},
|
|
1013
1018
|
{
|
|
1014
|
-
"example": "import { AppShell, Sidebar } from \"@godxjp/ui/layout\";\nimport { LayoutDashboard, Users } from \"lucide-react\";\nimport { router } from \"@inertiajs/react\";\n\nconst sidebar = (\n <Sidebar\n activeId=\"/dashboard\"\n onSelect={(id) => router.visit(id)}\n sections={[{ items: [\n { id: \"/dashboard\", label: \"ダッシュボード\", icon: LayoutDashboard },\n { id: \"/users\", label: \"ユーザー\", icon: Users },\n ] }]}\n product={{ name: \"JOVY CRM\", role: \"本部\", color: \"var(--color-primary)\" }}\n />\n);\n\nexport function CrmLayout({ children }: {
|
|
1019
|
+
"example": "import { AppShell, Sidebar } from \"@godxjp/ui/layout\";\nimport { LayoutDashboard, Users } from \"lucide-react\";\nimport { router } from \"@inertiajs/react\";\n\nconst sidebar = (\n <Sidebar\n activeId=\"/dashboard\"\n onSelect={(id) => router.visit(id)}\n sections={[{ items: [\n { id: \"/dashboard\", label: \"ダッシュボード\", icon: LayoutDashboard },\n { id: \"/users\", label: \"ユーザー\", icon: Users },\n ] }]}\n product={{ name: \"JOVY CRM\", role: \"本部\", color: \"var(--color-primary)\" }}\n />\n);\n\nexport function CrmLayout({ children }: { children: React.ReactNode }) {\n return <AppShell sidebar={sidebar}>{children}</AppShell>;\n}",
|
|
1015
1020
|
"group": "layout",
|
|
1016
1021
|
"importPath": "@godxjp/ui/layout",
|
|
1017
1022
|
"name": "AppShell",
|
|
@@ -1417,7 +1422,7 @@
|
|
|
1417
1422
|
]
|
|
1418
1423
|
},
|
|
1419
1424
|
{
|
|
1420
|
-
"example": "\n{`import { useState } from \"react\";\nimport { LayoutDashboard, FileText, Users, Shield, CreditCard, BookOpen } from \"lucide-react\";\nimport { Link } from \"react-router-dom\";\nimport { AppShell, createSidebarLink } from \"@godxjp/ui/layout\";\nimport { Sidebar, type
|
|
1425
|
+
"example": "\n{`import { useState } from \"react\";\nimport { LayoutDashboard, FileText, Users, Shield, CreditCard, BookOpen } from \"lucide-react\";\nimport { Link } from \"react-router-dom\";\nimport { AppShell, createSidebarLink } from \"@godxjp/ui/layout\";\nimport { Sidebar, type SidebarSectionProp } from \"@godxjp/ui/layout\";\nimport { Topbar, TopbarItem } from \"@godxjp/ui/layout\";\n\n// The WHOLE router integration: pass the element type, the library composes every row\n// (icon · label · badge · active · collapsed rail). Inertia: inertiaSidebarLink(Link) from\n// \"@godxjp/ui/inertia\". Next.js: createSidebarLink(Link).\nconst NavLink = createSidebarLink(Link, \"to\");\n\nconst sections: SidebarSectionProp[] = [\n {\n label: \"Accounting\",\n items: [\n { id: \"dashboard\", label: \"Dashboard\", icon: LayoutDashboard, href: \"/dashboard\" },\n {\n id: \"ledger\",\n label: \"Ledger\",\n icon: BookOpen,\n children: [\n { id: \"journal\", label: \"Journal\", icon: FileText, href: \"/ledger/journal\" },\n { id: \"chart-of-accounts\", label: \"Chart of Accounts\", icon: CreditCard, href: \"/ledger/coa\" },\n ],\n },\n ],\n },\n {\n label: \"Administration\",\n items: [\n { id: \"users\", label: \"Users\", icon: Users, href: \"/users\" },\n { id: \"roles\", label: \"Roles\", icon: Shield, href: \"/roles\", disabled: true },\n ],\n },\n];\n\nexport default function Shell() {\n const [activeId, setActiveId] = useState(\"dashboard\");\n const [collapsed, setCollapsed] = useState(false);\n\n return (\n <AppShell\n sidebarCollapsed={collapsed}\n sidebar={\n <Sidebar\n activeId={activeId}\n collapsed={collapsed}\n onSelect={setActiveId}\n sections={sections}\n linkComponent={NavLink}\n product={{ name: \"CoreBooks\", role: \"Admin Console\", color: \"hsl(var(--primary))\" }}\n onProductClick={() => {/* open entity switcher */}}\n footer={\n <div className=\"text-muted-foreground text-xs\">\n <div className=\"text-foreground font-medium\">Satoshi Yamamoto</div>\n <div>Online · Tokyo branch</div>\n </div>\n }\n />\n }\n topbar={\n <Topbar\n start={\n <>\n {/* A bar cell is a TopbarItem, never a Button: a Button in a bar is a\n --control-height pill floating in a taller strip, with its own hover\n fill and its own focus ring. */}\n <TopbarItem aria-label=\"メニュー\" onClick={() => setCollapsed((c) => !c)}>\n <PanelLeft />\n </TopbarItem>\n <Logo mark=\"godx\" label=\"CoreBooks\" />\n </>\n }\n end={<TopbarItem aria-label=\"検索\" onClick={() => {}}><Search /></TopbarItem>}\n />\n }\n >\n <>{/* page content */}</>\n </AppShell>\n );\n}`}\n",
|
|
1421
1426
|
"group": "layout",
|
|
1422
1427
|
"importPath": "@godxjp/ui/layout",
|
|
1423
1428
|
"name": "Sidebar",
|
|
@@ -1604,6 +1609,7 @@
|
|
|
1604
1609
|
"DO compose the bar yourself: a brand mark (an `Avatar`) + sidebar toggle in `start`, a search trigger in `center`, settings pickers + notifications + user menu in `end`. The shell only positions; it never decides WHICH controls exist.",
|
|
1605
1610
|
"DO build the sidebar toggle as a `TopbarItem` with a `PanelLeftClose`/`PanelLeftOpen` icon and your own `t()` aria-label, wired to AppShell's `sidebarCollapsed`. There is no baked toggle — but there IS a bar CELL, and it is not a Button: a Button in a slot is a --control-height pill floating in a taller bar, with its own hover fill and a ring drawn around the pill instead of the cell. The same holds for the notifications bell and the account trigger.",
|
|
1606
1611
|
"DO put a locale/theme switcher in `end` using `AppSettingPicker` (or your own control) — icon-only vs labelled, bordered vs not, is THAT component's prop, not Topbar's. Topbar does not ship or force a language picker.",
|
|
1612
|
+
"DON'T wrap two+ TopbarItems in a `<Flex>` to group them in one slot (gh#883) — the slot is already a flex line with its own gap, and a `<Flex>` wrapper collapses to 16px and takes every item inside it down with it. Use a fragment (`<>…</>`, no DOM node) or pass an array instead; see TopbarItem's own DON'T for the measured before/after.",
|
|
1607
1613
|
"DON'T look for `product`/`project`/`onSearchOpen`/`onNotificationsOpen`/`collapsed` props — they were removed. A chrome control only exists if YOU put it in a slot, so there is never a dead dropdown / empty search with nothing behind it.",
|
|
1608
1614
|
"DO render Topbar inside `AppShell`'s `topbar` slot (or any `<header>`). For a non-three-cluster layout, pass `children` and lay it out yourself.",
|
|
1609
1615
|
"DO decide, explicitly, what happens to the `center` slot at 1100px and below. It is REMOVED there by default (`--topbar-center-compact-display: none`) so it cannot cover the start or end clusters when a 16rem sidebar is docked — which also means a global search trigger in `center` is gone on tablets AND phones. This default arrived in 18.6.0 and changed behaviour for consumers who touched nothing but their lockfile. If your center content already has a compact presentation (an icon-only search trigger), opt back in globally with `:root { --topbar-center-compact-display: flex; }`; if it does not, move the trigger into `end` for compact widths. Never re-create either behaviour with a page-local media query.",
|
|
@@ -1688,7 +1694,8 @@
|
|
|
1688
1694
|
"DO wrap it in a DropdownMenuTrigger asChild for a user menu; the open state lights the cell via [data-state=open].",
|
|
1689
1695
|
"DON'T set a height: the cell stretches to whatever the bar is (AppShell's grid row, --topbar-height, or the coarse-pointer bar), which is why there is no height knob.",
|
|
1690
1696
|
"DO collapse a cell by breakpoint with its own props, never by hand-wrapping the glyph: `<TopbarItem asChild icon={<Target />} labelHideBelow=\"sm\"><a href=\"/goals\">Goals</a></TopbarItem>` replaces `<Flex hideFrom=\"sm\"><Icon as={Target} size=\"md\" /></Flex>` — icon-only below sm, the label still the accessible name. Add `iconHideFrom` for a label-only cell from a step up (gh#726).",
|
|
1691
|
-
"DON'T reach for it outside a Topbar — a full-bleed cell needs a bar to bleed to. Use Button anywhere else."
|
|
1697
|
+
"DON'T reach for it outside a Topbar — a full-bleed cell needs a bar to bleed to. Use Button anywhere else.",
|
|
1698
|
+
"DON'T wrap two or more TopbarItems in a `<Flex>` to place them together in one slot (gh#883) — a `<Flex>` is a REAL element with `align-self: auto` by default, so it sits between the item and the slot, collapses to its own content height, and every item inside it stretches only to THAT (measured: a 47px bar down to 16px). The slot (`.ui-topbar-start`/`-center`/`-end`) is already a flex line with its own `gap`, so it needs no wrapper at all: put a JSX FRAGMENT `<>…</>` around the items (renders no DOM node, so each item is still the slot's DIRECT child) or pass an array — `end={[<TopbarItem key=\"notifications\" …/>, <TopbarItem key=\"account\" …/>]}` — either way every item keeps its own `align-self: stretch` reaching the real bar height. Reach for `<Flex>` there only when you deliberately want the group NOT full height (rare in a topbar)."
|
|
1692
1699
|
],
|
|
1693
1700
|
"useCases": [
|
|
1694
1701
|
"Account / user-menu trigger in the topbar end slot",
|
|
@@ -1760,7 +1767,7 @@
|
|
|
1760
1767
|
]
|
|
1761
1768
|
},
|
|
1762
1769
|
{
|
|
1763
|
-
"example": "import { MasterDetail } from \"@godxjp/ui/layout\";\n\n// Canonical: fluid list + fixed 320px detail rail (stacks below 40rem).\n<MasterDetail\n masterLabel=\"Teams\"\n detailLabel=\"Selected team\"\n detailId=\"team-detail\"\n master={<TeamTable onRowClick={select} detailId=\"team-detail\" />}\n>\n <TeamDetail team={selected} />\n</MasterDetail>\n\n// Leading navigator rail instead.\n<MasterDetail
|
|
1770
|
+
"example": "import { MasterDetail } from \"@godxjp/ui/layout\";\n\n// Canonical: fluid list + fixed 320px detail rail (stacks below 40rem).\n<MasterDetail\n masterLabel=\"Teams\"\n detailLabel=\"Selected team\"\n detailId=\"team-detail\"\n master={<TeamTable onRowClick={select} detailId=\"team-detail\" />}\n>\n <TeamDetail team={selected} />\n</MasterDetail>\n\n// Leading navigator rail instead.\n<MasterDetail\n rail=\"master\"\n railWidth=\"compact\"\n masterLabel=\"Categories\"\n master={<CategoryNav activeId={activeCategory} onSelect={setActiveCategory} />}\n>\n <SettingsForm />\n</MasterDetail>\n\n// A long real collection: bound the master so it scrolls in place and the detail\n// stays near the top of a stacked mobile page.\n<MasterDetail\n masterViewport=\"compact\"\n masterLabel=\"Members\"\n detailLabel=\"Selected member\"\n master={<MemberList onRowClick={select} />}\n>\n <MemberDetail member={selected} />\n</MasterDetail>",
|
|
1764
1771
|
"group": "layout",
|
|
1765
1772
|
"importPath": "@godxjp/ui/layout",
|
|
1766
1773
|
"name": "MasterDetail",
|
|
@@ -3285,7 +3292,7 @@
|
|
|
3285
3292
|
"absorbed": [
|
|
3286
3293
|
"DataGrid"
|
|
3287
3294
|
],
|
|
3288
|
-
"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
|
|
3295
|
+
"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 tone={\n row.status === \"paid\" ? \"success\" : row.status === \"overdue\" ? \"destructive\" : \"warning\"\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}",
|
|
3289
3296
|
"group": "data-display",
|
|
3290
3297
|
"importPath": "@godxjp/ui/data-display",
|
|
3291
3298
|
"name": "DataTable",
|
|
@@ -3369,13 +3376,13 @@
|
|
|
3369
3376
|
},
|
|
3370
3377
|
{
|
|
3371
3378
|
"defaultValue": "'default'",
|
|
3372
|
-
"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.",
|
|
3379
|
+
"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. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a bordered key-value card (--table-stacked-collection-*). Each cell then carries its column's header inline above the value, DERIVED from the same ColumnDef.header the <th> uses — so the card cannot drift from the table, and a column with a deliberately empty header names itself with ariaLabel (gh#864). The labels are aria-hidden: the real <th> is still in the DOM and remains the accessible-name source, so screen-reader table navigation is unchanged at every width. Reach for this instead of building a parallel Card tree beside the table; two trees for one dataset means two sets of labels to keep in sync.",
|
|
3373
3380
|
"name": "preset",
|
|
3374
|
-
"type": "'default' | 'action-collection'"
|
|
3381
|
+
"type": "'default' | 'action-collection' | 'stacked-record-collection'"
|
|
3375
3382
|
},
|
|
3376
3383
|
{
|
|
3377
3384
|
"defaultValue": "'sm'",
|
|
3378
|
-
"description": "Step at which preset=\"action-collection\" switches to the compact priority measures,
|
|
3385
|
+
"description": "Step at which preset=\"action-collection\" switches to the compact priority measures, or preset=\"stacked-record-collection\" folds its rows into cards. 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, and the same table folds by the width it is GIVEN. Ignored while preset is 'default'.",
|
|
3379
3386
|
"name": "collapseBelow",
|
|
3380
3387
|
"type": "'sm' | 'md' | 'lg' | 'xl'"
|
|
3381
3388
|
},
|
|
@@ -4227,7 +4234,7 @@
|
|
|
4227
4234
|
]
|
|
4228
4235
|
},
|
|
4229
4236
|
{
|
|
4230
|
-
"example": "import { Card, CardContent, CardHeader, CardTitle, ListRow, Badge } from \"@godxjp/ui/data-display\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Smartphone } from \"lucide-react\";\nimport { Flex } from \"@godxjp/ui/layout\";\n\n// The three scenarios below are three CARDS on one page, so they are wrapped in a Flex — the gap\n// between sibling cards belongs to the stack, never to the cards (audit: sibling-cards-need-flex).\n<Flex direction=\"col\" gap=\"lg\">\n<Card>\n <CardHeader>\n <CardTitle>アクティブなセッション</CardTitle>\n </CardHeader>\n <CardContent flush>\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"iPhone 15 · Tokyo\"\n description=\"最終アクセス 2分前\"\n trailing={<Badge status=\"active\" />}\n />\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"MacBook Pro · Osaka\"\n description=\"最終アクセス 3日前\"\n trailing={<Button size=\"xs\" variant=\"outline\">ログアウト</Button>}\n />\n </CardContent>\n</Card>\n\n// Notifications — unread dot + emphasized surface, wrapping title, two inline actions\n<Card>\n <CardContent flush>\n <ListRow\n unread\n align=\"start\"\n overflow=\"wrap\"\n title=\"組織「グローバル・トランスフォーメーション推進本部」への招待が届いています\"\n description=\"2026-07-30 09:12 JST\"\n trailing={\n <>\n <Button size=\"xs\" variant=\"ghost\">既読にする</Button>\n <Button size=\"xs\" variant=\"outline\">開く</Button>\n </>\n }\n />\n <ListRow unread={false} align=\"start\" overflow=\"wrap\" title=\"請求書が発行されました\" description=\"2026-07-28 18:40 JST\" />\n </CardContent>\n</Card>\n\n
|
|
4237
|
+
"example": "import { Card, CardContent, CardHeader, CardTitle, ListRow, Badge } from \"@godxjp/ui/data-display\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Smartphone } from \"lucide-react\";\nimport { Flex } from \"@godxjp/ui/layout\";\n\n// The three scenarios below are three CARDS on one page, so they are wrapped in a Flex — the gap\n// between sibling cards belongs to the stack, never to the cards (audit: sibling-cards-need-flex).\n<Flex direction=\"col\" gap=\"lg\">\n<Card>\n <CardHeader>\n <CardTitle>アクティブなセッション</CardTitle>\n </CardHeader>\n <CardContent flush>\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"iPhone 15 · Tokyo\"\n description=\"最終アクセス 2分前\"\n trailing={<Badge status=\"active\" />}\n />\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"MacBook Pro · Osaka\"\n description=\"最終アクセス 3日前\"\n trailing={<Button size=\"xs\" variant=\"outline\">ログアウト</Button>}\n />\n </CardContent>\n</Card>\n\n// Notifications — unread dot + emphasized surface, wrapping title, two inline actions\n<Card>\n <CardContent flush>\n <ListRow\n unread\n align=\"start\"\n overflow=\"wrap\"\n title=\"組織「グローバル・トランスフォーメーション推進本部」への招待が届いています\"\n description=\"2026-07-30 09:12 JST\"\n trailing={\n <>\n <Button size=\"xs\" variant=\"ghost\">既読にする</Button>\n <Button size=\"xs\" variant=\"outline\">開く</Button>\n </>\n }\n />\n <ListRow unread={false} align=\"start\" overflow=\"wrap\" title=\"請求書が発行されました\" description=\"2026-07-28 18:40 JST\" />\n </CardContent>\n</Card>\n\n{/* A list of LINKS — `as=\"li\"` gives the list item, `asChild` gives the whole-row link, and the\n item carries the divider. Never wrap the row in your own li, and never reach for a raw ul:\n `marker=\"none\"` is the semantic list container (no bullet, no indent, gap token). */}\n<Card>\n <CardContent flush>\n <Flex as=\"ul\" marker=\"none\" direction=\"col\" gap=\"none\">\n {projects.map((project) => (\n <ListRow key={project.key} as=\"li\" asChild title={project.name} description={project.key}>\n <Link href={`/projects/${project.key}`} />\n </ListRow>\n ))}\n </Flex>\n </CardContent>\n</Card>\n</Flex>",
|
|
4231
4238
|
"group": "data-display",
|
|
4232
4239
|
"importPath": "@godxjp/ui/data-display",
|
|
4233
4240
|
"name": "ListRow",
|
|
@@ -4366,12 +4373,23 @@
|
|
|
4366
4373
|
"name": "onAcknowledge",
|
|
4367
4374
|
"type": "() => void"
|
|
4368
4375
|
},
|
|
4376
|
+
{
|
|
4377
|
+
"description": "Copy on the button `onAcknowledge` creates. Defaults to a localized \"I've saved it\" — override it when the confirmation claims something more specific than having read the secret (\"保管しました\", \"Stored in 1Password\"). Consumer-owned wording: route it through t().",
|
|
4378
|
+
"name": "acknowledgeLabel",
|
|
4379
|
+
"type": "React.ReactNode"
|
|
4380
|
+
},
|
|
4369
4381
|
{
|
|
4370
4382
|
"defaultValue": "false",
|
|
4371
4383
|
"description": "Offer a download-as-file button.",
|
|
4372
4384
|
"name": "downloadable",
|
|
4373
4385
|
"type": "boolean"
|
|
4374
4386
|
},
|
|
4387
|
+
{
|
|
4388
|
+
"defaultValue": "\"credential.txt\"",
|
|
4389
|
+
"description": "Name of the file `downloadable` writes. The default is deliberately anonymous; set it when the user will hold several at once (`api-key-prod.txt`) so the saved files are still telling apart in a downloads folder.",
|
|
4390
|
+
"name": "downloadFileName",
|
|
4391
|
+
"type": "string"
|
|
4392
|
+
},
|
|
4375
4393
|
{
|
|
4376
4394
|
"defaultValue": "\"md\"",
|
|
4377
4395
|
"description": "Action button size tier.",
|
|
@@ -4383,6 +4401,16 @@
|
|
|
4383
4401
|
"description": "Caution banner severity.",
|
|
4384
4402
|
"name": "tone",
|
|
4385
4403
|
"type": "\"warning\" | \"destructive\" | \"info\""
|
|
4404
|
+
},
|
|
4405
|
+
{
|
|
4406
|
+
"description": "DOM id on the root. Useful here because the surface is usually inside a Dialog: it is what a `aria-describedby` on the dialog, or a deep link back to the issued credential, can point at.",
|
|
4407
|
+
"name": "id",
|
|
4408
|
+
"type": "string"
|
|
4409
|
+
},
|
|
4410
|
+
{
|
|
4411
|
+
"description": "Accessible name for the credential region when `label` is not set or is a non-string node. `label` is the VISIBLE caption and already names the region when it is a plain string, so reach for this only when the caption is rich content or when the surrounding dialog title is the only thing saying which secret this is.",
|
|
4412
|
+
"name": "aria-label",
|
|
4413
|
+
"type": "string"
|
|
4386
4414
|
}
|
|
4387
4415
|
],
|
|
4388
4416
|
"related": [
|
|
@@ -5020,15 +5048,20 @@
|
|
|
5020
5048
|
"name": "striped",
|
|
5021
5049
|
"type": "boolean"
|
|
5022
5050
|
},
|
|
5051
|
+
{
|
|
5052
|
+
"description": "On TableRow (gh#876): the row's STATE — a leading-edge rail plus a weak wash, the SAME six tones and meanings `DataTable rowTone` already paints. Writes `data-tone`, the attribute the paint is keyed on — `<TableRow data-tone=\"warning\">` still works unchanged, `tone` is just the typed, discoverable route to it. Never the only signal (WCAG 1.4.1): keep the reason in a cell (a Badge, a status column) and let the rail make that cell findable. DO NOT reach for a `bg-<status>/…` utility on a TableRow instead — that bypasses the token-owned wash and the theme can no longer retune it (gh#872).",
|
|
5053
|
+
"name": "tone",
|
|
5054
|
+
"type": "\"primary\" | \"success\" | \"warning\" | \"info\" | \"attention\" | \"destructive\""
|
|
5055
|
+
},
|
|
5023
5056
|
{
|
|
5024
5057
|
"defaultValue": "\"default\"",
|
|
5025
|
-
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440.",
|
|
5058
|
+
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a bordered key-value card (--table-stacked-collection-*). Give each TableCell a `label` — its column header — because the <th> association it normally reads from is the thing that just went away; DataTable derives that label from ColumnDef.header for you (gh#864). The label is aria-hidden and the real <th> stays in the DOM, so the accessible name and table navigation are unchanged.",
|
|
5026
5059
|
"name": "preset",
|
|
5027
|
-
"type": "\"default\" | \"action-collection\""
|
|
5060
|
+
"type": "\"default\" | \"action-collection\" | \"stacked-record-collection\""
|
|
5028
5061
|
},
|
|
5029
5062
|
{
|
|
5030
5063
|
"defaultValue": "\"sm\"",
|
|
5031
|
-
"description": "Step at which preset=\"action-collection\" switches to the compact priority measures,
|
|
5064
|
+
"description": "Step at which preset=\"action-collection\" switches to the compact priority measures, or preset=\"stacked-record-collection\" folds its rows into cards. Measured against the TABLE'S OWN container (a container query), not the viewport — a table inside a master rail collapses before the page does. Ignored while preset is \"default\".",
|
|
5032
5065
|
"name": "collapseBelow",
|
|
5033
5066
|
"type": "\"sm\" | \"md\" | \"lg\" | \"xl\""
|
|
5034
5067
|
},
|
|
@@ -5075,7 +5108,7 @@
|
|
|
5075
5108
|
]
|
|
5076
5109
|
},
|
|
5077
5110
|
{
|
|
5078
|
-
"example": "import { DataState } from \"@godxjp/ui/query\";\n\n<DataState query={membersQuery} skeleton={<SkeletonTable />} isEmpty={(d) => d.items.length === 0} empty={<EmptyState title=\"会員なし\" />}>\n {(d) => <MemberTable items={d.items} />}\n</DataState>",
|
|
5111
|
+
"example": "import { useQuery } from \"@tanstack/react-query\";\nimport { DataState } from \"@godxjp/ui/query\";\n\ntype MembersPage = { items: { id: string; name: string }[] };\n\n// T flows from the query, so `d` is MembersPage in BOTH callbacks. If you find yourself\n// annotating them, the query is untyped — fix that, never write `(d: any)`.\nconst membersQuery = useQuery<MembersPage>({ queryKey: [\"members\"], queryFn: fetchMembers });\n\n<DataState query={membersQuery} skeleton={<SkeletonTable />} isEmpty={(d) => d.items.length === 0} empty={<EmptyState title=\"会員なし\" />}>\n {(d) => <MemberTable items={d.items} />}\n</DataState>",
|
|
5079
5112
|
"group": "data-display",
|
|
5080
5113
|
"importPath": "@godxjp/ui/query",
|
|
5081
5114
|
"name": "DataState",
|
|
@@ -5154,7 +5187,7 @@
|
|
|
5154
5187
|
]
|
|
5155
5188
|
},
|
|
5156
5189
|
{
|
|
5157
|
-
"example": "import { InfiniteQueryState, flattenItemPages } from \"@godxjp/ui/query\";\n\n<InfiniteQueryState query={q} skeleton={<SkeletonRows />} flatten={flattenItemPages} isEmpty={(it) => it.length === 0}>\n {(items) => items.map((a) => <ActivityRow key={a.id} activity={a} />)}\n</InfiniteQueryState>",
|
|
5190
|
+
"example": "import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { InfiniteQueryState, flattenItemPages } from \"@godxjp/ui/query\";\n\ntype Activity = { id: string; label: string };\n\n// `flattenItemPages` constrains the page to `{ items: TItem[] }`, so the query must be typed:\n// an untyped one makes the page `unknown`, which cannot satisfy that constraint.\nconst q = useInfiniteQuery<{ items: Activity[]; cursor?: string }>({\n queryKey: [\"activity\"],\n queryFn: fetchActivityPage,\n initialPageParam: undefined,\n getNextPageParam: (last) => last.cursor,\n});\n\n<InfiniteQueryState query={q} skeleton={<SkeletonRows />} flatten={flattenItemPages} isEmpty={(it) => it.length === 0}>\n {(items) => items.map((a) => <ActivityRow key={a.id} activity={a} />)}\n</InfiniteQueryState>",
|
|
5158
5191
|
"group": "data-display",
|
|
5159
5192
|
"importPath": "@godxjp/ui/query",
|
|
5160
5193
|
"name": "InfiniteQueryState",
|
|
@@ -5299,7 +5332,7 @@
|
|
|
5299
5332
|
]
|
|
5300
5333
|
},
|
|
5301
5334
|
{
|
|
5302
|
-
"example": "import { FormField, Input } from \"@godxjp/ui/data-entry\";\n\n<FormField id=\"coupon-name\" label=\"クーポン名\" required error={errors.name} helper=\"最大50文字\">\n <Input id=\"coupon-name\" placeholder=\"春の花粉症対策15%OFF\" value={name} onValueChange={(
|
|
5335
|
+
"example": "import { FormField, Input } from \"@godxjp/ui/data-entry\";\n\n<FormField id=\"coupon-name\" label=\"クーポン名\" required error={errors.name} helper=\"最大50文字\">\n <Input id=\"coupon-name\" placeholder=\"春の花粉症対策15%OFF\" value={name} onValueChange={(v) => setName(v)} />\n</FormField>",
|
|
5303
5336
|
"group": "data-entry",
|
|
5304
5337
|
"importPath": "@godxjp/ui/data-entry",
|
|
5305
5338
|
"name": "FormField",
|
|
@@ -5487,7 +5520,7 @@
|
|
|
5487
5520
|
]
|
|
5488
5521
|
},
|
|
5489
5522
|
{
|
|
5490
|
-
"example": "import { Input } from \"@godxjp/ui/data-entry\";\n\n<Input id=\"qty\" type=\"number\" placeholder=\"例: 500\" value={value} onValueChange={(
|
|
5523
|
+
"example": "import { Input } from \"@godxjp/ui/data-entry\";\n\n<Input id=\"qty\" type=\"number\" placeholder=\"例: 500\" value={value} onValueChange={(v) => setValue(v)} />",
|
|
5491
5524
|
"group": "data-entry",
|
|
5492
5525
|
"importPath": "@godxjp/ui/data-entry",
|
|
5493
5526
|
"name": "Input",
|
|
@@ -5835,6 +5868,11 @@
|
|
|
5835
5868
|
"description": "Disable search input and clearing.",
|
|
5836
5869
|
"name": "disabled",
|
|
5837
5870
|
"type": "boolean"
|
|
5871
|
+
},
|
|
5872
|
+
{
|
|
5873
|
+
"description": "Class on the `<input>` itself. SearchInput renders a WRAPPER (label, icon, clear button, input), so `className` lands on that wrapper and never reaches the field — this is the second handle, for the case where the field and its chrome need different treatment. Layout and colour still belong to tokens; use it for the rare geometry a token cannot reach.",
|
|
5874
|
+
"name": "inputClassName",
|
|
5875
|
+
"type": "string"
|
|
5838
5876
|
}
|
|
5839
5877
|
],
|
|
5840
5878
|
"related": [
|
|
@@ -6237,6 +6275,7 @@
|
|
|
6237
6275
|
"usage": [
|
|
6238
6276
|
"DO use the data-driven API (options/loadOptions) for straightforward selects — it handles grouping, search, async, and custom rendering automatically. Only reach for the compound API when you need to inject arbitrary content into the trigger or listbox.",
|
|
6239
6277
|
"DO pass name= on the data-driven Select so the value is submitted with a native form or Inertia useForm. Without name= the value is React-only and will not appear in form data.",
|
|
6278
|
+
"NAMING A SELECT WITH NO VISIBLE LABEL: put `aria-label` on `<Select>`, NOT on `<SelectTrigger>`. Both render the identical button attribute — Select forwards its name down through SelectFieldA11yContext and the trigger writes last — but only the root spelling also names the react-aria field, and a root that cannot see a name warns once per render (gh#869). Inside a FormField or Field, pass nothing: the label id reaches both levels on its own. `aria-labelledby` pointing at your own element works on either level and is the right spelling when a visible heading already names the control.",
|
|
6240
6279
|
"READING THE SELECTED CODE FROM THE DOM: the trigger publishes `data-value` = the selected VALUE, alongside the `data-field` key it inherits from FormField. Use that in e2e tests and screen automation — the trigger's visible text is the option LABEL (東京本社), and the only other place the code lives is the aria-hidden, 1px-clipped native <select> react-aria renders so a native submit (and browser autofill) carries the value. `data-value` is absent while nothing is selected, and it tracks uncontrolled picks too.",
|
|
6241
6280
|
"DO use loadOptions + selectedLabel together for async selects: selectedLabel prevents a flash of the raw id string while the first page loads.",
|
|
6242
6281
|
"A Select is safe inside a draggable element (a Kanban card with draggable=true) and inside a `contain: paint` / `transform` app region: the aria-hidden native <select> fallback is held at its static position beside the trigger (position: absolute, 1px clipped), so the browser's drag image stays the card's own box instead of reaching to the region's corner (gh#708). No wrapper or consumer CSS is needed.",
|
|
@@ -6310,6 +6349,12 @@
|
|
|
6310
6349
|
"name": "id",
|
|
6311
6350
|
"type": "string"
|
|
6312
6351
|
},
|
|
6352
|
+
{
|
|
6353
|
+
"defaultValue": "false",
|
|
6354
|
+
"description": "ANNOUNCES the requirement; it does not enforce it. react-aria's Switch omits `isRequired`, and a switch is never the target of native constraint validation in this library, so this writes `aria-required=\"true\"` onto the real input and stops there. The form layer (FormField / your schema) still owns whether an unflipped switch blocks submit — pairing this with nothing that validates is how a screen reader ends up promising a check the form never makes.",
|
|
6355
|
+
"name": "required",
|
|
6356
|
+
"type": "boolean"
|
|
6357
|
+
},
|
|
6313
6358
|
{
|
|
6314
6359
|
"defaultValue": "false",
|
|
6315
6360
|
"description": "Disable the toggle.",
|
|
@@ -6542,6 +6587,12 @@
|
|
|
6542
6587
|
"name": "id",
|
|
6543
6588
|
"type": "string"
|
|
6544
6589
|
},
|
|
6590
|
+
{
|
|
6591
|
+
"defaultValue": "false",
|
|
6592
|
+
"description": "Keeps its HTML spelling here and becomes react-aria's `isRequired`, so unlike Switch — where the same prop only ANNOUNCES the requirement — this is real constraint validation on the underlying input: an unchecked box blocks native form submission. The consent checkbox is the case it exists for. It does not render an asterisk; the required MARK belongs to FormField's label.",
|
|
6593
|
+
"name": "required",
|
|
6594
|
+
"type": "boolean"
|
|
6595
|
+
},
|
|
6545
6596
|
{
|
|
6546
6597
|
"description": "antd `<Checkbox>label</Checkbox>` — the INLINE label: box first, label at inline-end on the same line, the same markup as a `Checkbox.Group` option. A real `<label for>`: clicking the text toggles the box and the text is its accessible name. `className` then styles the labelled row.",
|
|
6547
6598
|
"name": "children",
|
|
@@ -7682,6 +7733,16 @@
|
|
|
7682
7733
|
"description": "Ant Design `onTabScroll`, fired whenever the trigger strip's own scrollport moves — a swipe, a wheel, or the component re-pinning the active trigger (antd reports its own re-pins too). LOGICAL VALUES instead of antd's `left | right | top | bottom`: two of those four are just the other axis of the same event, and upstream's pair is read off the sign of an inner transform, so in an RTL strip its `left` means the opposite of what it means in LTR. `start`/`end` say the same thing on whichever axis and in whichever direction the strip is written. Only fires for the `items` API, which is the path that owns the strip element.",
|
|
7683
7734
|
"name": "onTabScroll",
|
|
7684
7735
|
"type": "(info: { direction: \"start\" | \"end\" }) => void"
|
|
7736
|
+
},
|
|
7737
|
+
{
|
|
7738
|
+
"description": "Class on the TRIGGER STRIP (`TabsList`) under the `items` API — the handle that composing the tree manually gives you as `<TabsList className>`. `className` reaches only the root, which holds the strip AND the panels, so anything meant for the bar alone belongs here. Almost always unnecessary: placement, size, centring and the card rail are props and `--tabs-*` tokens.",
|
|
7739
|
+
"name": "listClassName",
|
|
7740
|
+
"type": "string"
|
|
7741
|
+
},
|
|
7742
|
+
{
|
|
7743
|
+
"description": "Class on EVERY panel (`TabsContent`) under the `items` API. It is written so it can WIN: the joined card body travels to CSS as `data-bodied` on the root rather than as a class, precisely so a consumer class on the panel is not fighting a utility the component already claimed (gh#762). Reach for the `bodied` prop and the `--tabs-panel-*` tokens first — this is for the geometry no token exposes.",
|
|
7744
|
+
"name": "contentClassName",
|
|
7745
|
+
"type": "string"
|
|
7685
7746
|
}
|
|
7686
7747
|
],
|
|
7687
7748
|
"related": [
|
|
@@ -8319,9 +8380,58 @@
|
|
|
8319
8380
|
]
|
|
8320
8381
|
},
|
|
8321
8382
|
{
|
|
8322
|
-
"example": "import {
|
|
8383
|
+
"example": "import { ThemeScope, tenantTheme } from \"@godxjp/ui/app\";\n\n// The customer's colour on one region — and on every overlay that region opens.\n<ThemeScope style={tenantTheme(customer.brandHex).vars} data-tenant={customer.slug}>\n <Dialog>\n <DialogTrigger asChild><Button>Review</Button></DialogTrigger>\n <DialogContent><DialogTitle>Review</DialogTitle></DialogContent>\n </Dialog>\n</ThemeScope>",
|
|
8323
8384
|
"group": "providers",
|
|
8324
8385
|
"importPath": "@godxjp/ui/app",
|
|
8386
|
+
"name": "ThemeScope",
|
|
8387
|
+
"props": [
|
|
8388
|
+
{
|
|
8389
|
+
"description": "The themed region. Overlays opened anywhere below it follow its tokens.",
|
|
8390
|
+
"name": "children",
|
|
8391
|
+
"required": true,
|
|
8392
|
+
"type": "ReactNode"
|
|
8393
|
+
},
|
|
8394
|
+
{
|
|
8395
|
+
"description": "Classes on the scope element — this is where `dark` goes when a REGION is dark rather than the whole page.",
|
|
8396
|
+
"name": "className",
|
|
8397
|
+
"required": false,
|
|
8398
|
+
"type": "string"
|
|
8399
|
+
},
|
|
8400
|
+
{
|
|
8401
|
+
"description": "DOM id of the scope element.",
|
|
8402
|
+
"name": "id",
|
|
8403
|
+
"required": false,
|
|
8404
|
+
"type": "string"
|
|
8405
|
+
}
|
|
8406
|
+
],
|
|
8407
|
+
"related": [
|
|
8408
|
+
"OverlayPortalProvider — the other half of the same statement: it decides WHERE an overlay lands, ThemeScope decides which TOKENS it inherits there. They compose; a ThemeScope inside one hosts itself in that container.",
|
|
8409
|
+
"tenantTheme — the function that turns a customer hex into the declarations you put ON a ThemeScope (`style={tenantTheme(hex).vars}`). It computes the colours; ThemeScope is what carries them past the portal boundary.",
|
|
8410
|
+
"AppProvider — page-level theme axes (theme/brand/density/fontSize). Use it for the whole app; ThemeScope is for one region that differs from it."
|
|
8411
|
+
],
|
|
8412
|
+
"rules": [
|
|
8413
|
+
5
|
|
8414
|
+
],
|
|
8415
|
+
"storyPath": "app/ThemeScope.stories.tsx",
|
|
8416
|
+
"tagline": "Makes a themed REGION reach the overlays it opens. Every overlay portals to document.body, so custom-property inheritance stops at the portal boundary and a tenant-themed region's Dialog, Select listbox, Popover and Toast paint the package defaults. Wrap the region in ThemeScope and they carry the region's tokens.",
|
|
8417
|
+
"usage": [
|
|
8418
|
+
"DO wrap the region, then theme it the way you already do — `style={tenantTheme(hex).vars}`, `data-tenant=\"acme\"`, `className=\"dark\"`, or a stylesheet rule that never mentions React. ThemeScope reads the COMPUTED tokens at its own element, so all of those paths behave identically; it has no theme prop and needs none.",
|
|
8419
|
+
"DO nest it. An inner ThemeScope inside an outer one wins for the overlays opened below it, because its scope already inherits the outer's tokens and it diffs against the document root.",
|
|
8420
|
+
"DO mount it inside an OverlayPortalProvider when you have one. It puts its host INSIDE that container, so a shadow-rooted app can be tenant-themed as well — the two providers compose rather than compete.",
|
|
8421
|
+
"DON'T reach for `OverlayPortalProvider container={themedWrapper}` to solve this. It works until the wrapper sits inside an `overflow: hidden`, a `transform` or a `contain` ancestor, and then the region clips its own overlays — a colour bug traded for a layout bug that is harder to see. `container` stays for the shadow-DOM case it was built for.",
|
|
8422
|
+
"DON'T expect it on a page with no scoped theme. With nothing themed it carries nothing, and with no ThemeScope at all every overlay behaves exactly as before.",
|
|
8423
|
+
"DON'T assume a CLASS-keyed rule travels. What crosses the boundary is the custom-property delta — the tokens. A rule written as `.dark .my-thing { background: #111 }` in app CSS is not a token and does not follow the overlay."
|
|
8424
|
+
],
|
|
8425
|
+
"useCases": [
|
|
8426
|
+
"A multi-tenant screen where one region wears a customer's brand colour from `tenantTheme(hex)` — the button was already right, and this is what makes the dialog it opens right too.",
|
|
8427
|
+
"A dark region on a light page (a preview pane, an editor canvas): `className=\"dark\"` on the region, and its Select listbox and Popover stay dark instead of flashing the page's light popover surface.",
|
|
8428
|
+
"A `[data-tenant]` theme written entirely in the consumer's own stylesheet, with no React theming provider anywhere — the documented way to theme a region in this package, and the case the design was chosen to cover."
|
|
8429
|
+
]
|
|
8430
|
+
},
|
|
8431
|
+
{
|
|
8432
|
+
"example": "import { formatDate } from \"@godxjp/ui/datetime\";\n\nformatDate(coupon.validFrom); // \"2026-05-01\"\nformatDate(order.createdAt, { kind: \"relative\" }); // \"3日前\"",
|
|
8433
|
+
"group": "providers",
|
|
8434
|
+
"importPath": "@godxjp/ui/datetime",
|
|
8325
8435
|
"name": "formatDate",
|
|
8326
8436
|
"props": [
|
|
8327
8437
|
{
|
|
@@ -8565,7 +8675,7 @@
|
|
|
8565
8675
|
]
|
|
8566
8676
|
},
|
|
8567
8677
|
{
|
|
8568
|
-
"example": "{`import { Cascader } from \"@godxjp/ui/data-entry\";\n\nconst REGIONS = [\n {\n value: \"jp\",\n label: \"日本\",\n
|
|
8678
|
+
"example": "{`import { Cascader } from \"@godxjp/ui/data-entry\";\n\nconst REGIONS = [\n {\n value: \"jp\",\n label: \"日本\",\n children: [\n {\n value: \"tokyo\",\n label: \"東京都\",\n children: [\n { value: \"shinjuku\", label: \"新宿区\" },\n { value: \"shibuya\", label: \"渋谷区\" },\n ],\n },\n ],\n },\n {\n value: \"vn\",\n label: \"Việt Nam\",\n children: [\n {\n value: \"hcm\",\n label: \"TP. Hồ Chí Minh\",\n children: [\n { value: \"q1\", label: \"Quận 1\" },\n { value: \"q3\", label: \"Quận 3\" },\n ],\n },\n ],\n },\n];\n\n// Controlled single-path\nfunction RegionPicker() {\n const [path, setPath] = React.useState<string[]>([]);\n\n return (\n <Cascader\n options={REGIONS}\n value={path}\n onValueChange={(v) => setPath(v as string[])}\n showSearch\n placeholder=\"Select region…\"\n />\n );\n}\n\n// Multi-path (multiple selection)\nfunction MultiRegionPicker() {\n const [paths, setPaths] = React.useState<string[][]>([]);\n\n return (\n <Cascader\n options={REGIONS}\n multiple\n value={paths}\n onValueChange={(v) => setPaths(v as string[][])}\n showSearch\n />\n );\n}\n\n// With custom field names (data uses 'name'/'id'/'nodes')\n<Cascader\n options={rawApiData}\n fieldNames={{ label: \"name\", value: \"id\", children: \"nodes\" }}\n defaultValue={[\"dept-1\", \"team-3\"]}\n/>\n\n// changeOnSelect: lets user pick a branch node (not only leaves)\n<Cascader\n options={REGIONS}\n changeOnSelect\n onValueChange={(v) => console.log(\"path\", v)}\n/>\n`}",
|
|
8569
8679
|
"group": "data-entry",
|
|
8570
8680
|
"importPath": "@godxjp/ui/data-entry",
|
|
8571
8681
|
"name": "Cascader",
|
|
@@ -8741,6 +8851,11 @@
|
|
|
8741
8851
|
"description": "Search query change (antd `showSearch.onSearch`).",
|
|
8742
8852
|
"name": "onSearchChange",
|
|
8743
8853
|
"type": "(query: string) => void"
|
|
8854
|
+
},
|
|
8855
|
+
{
|
|
8856
|
+
"description": "Accessible name for the COMBOBOX TRIGGER — the element a keyboard user lands on, not the panel. Inside a FormField it is injected for you and you do not pass it; on a bare Cascader (a toolbar scope filter, a compact drilldown with no label row) it is the only name the control has, and cardinal rule 227 requires one. Route it through t(). The rest of the field-a11y contract — aria-labelledby / describedby / errormessage / invalid / required — is accepted on every form-capable component here and is FormField's to wire.",
|
|
8857
|
+
"name": "aria-label",
|
|
8858
|
+
"type": "string"
|
|
8744
8859
|
}
|
|
8745
8860
|
],
|
|
8746
8861
|
"related": [
|
|
@@ -8774,7 +8889,7 @@
|
|
|
8774
8889
|
]
|
|
8775
8890
|
},
|
|
8776
8891
|
{
|
|
8777
|
-
"example": "import { useState } from \"react\";\nimport { FormField, TreeSelect } from \"@godxjp/ui/data-entry\";\n\nconst accountTree = [\n {\n value: \"assets\",\n label: \"Assets\",\n
|
|
8892
|
+
"example": "import { useState } from \"react\";\nimport { FormField, TreeSelect } from \"@godxjp/ui/data-entry\";\n\nconst accountTree = [\n {\n value: \"assets\",\n label: \"Assets\",\n children: [\n { value: \"current-assets\", label: \"Current Assets\", children: [\n { value: \"cash\", label: \"Cash\" },\n { value: \"ar\", label: \"Accounts Receivable\" },\n ],\n },\n { value: \"fixed-assets\", label: \"Fixed Assets\", children: [\n { value: \"equipment\", label: \"Equipment\" },\n ],\n },\n ],\n },\n {\n value: \"liabilities\",\n label: \"Liabilities\",\n children: [\n { value: \"ap\", label: \"Accounts Payable\" },\n ],\n },\n];\n\n// Single-select (returns string | undefined)\nexport function AccountPicker() {\n const [account, setAccount] = useState<string | undefined>();\n return (\n <FormField id=\"account-picker\" label=\"GL Account\">\n <TreeSelect\n id=\"account-picker\"\n treeData={accountTree}\n value={account}\n onValueChange={(v) => setAccount(v as string | undefined)}\n showSearch\n treeDefaultExpandAll\n placeholder=\"Select account…\"\n allowClear\n />\n </FormField>\n );\n}\n\n// Multi-select with checkboxes + cascade + SHOW_PARENT display\nexport function DepartmentFilter() {\n const [selected, setSelected] = useState<string[]>([]);\n return (\n <TreeSelect\n id=\"dept-filter\"\n treeData={accountTree}\n value={selected}\n onValueChange={(v) => setSelected(v as string[])}\n treeCheckable\n showCheckedStrategy={TreeSelect.SHOW_PARENT}\n showSearch\n placeholder=\"Filter by department…\"\n />\n );\n}",
|
|
8778
8893
|
"group": "data-entry",
|
|
8779
8894
|
"importPath": "@godxjp/ui/data-entry",
|
|
8780
8895
|
"name": "TreeSelect",
|
|
@@ -9006,7 +9121,7 @@
|
|
|
9006
9121
|
]
|
|
9007
9122
|
},
|
|
9008
9123
|
{
|
|
9009
|
-
"example": "import { useState } from \"react\";\nimport { Transfer } from \"@godxjp/ui/data-entry\";\n\nconst ALL_ACCOUNTS = [\n {
|
|
9124
|
+
"example": "import { useState } from \"react\";\nimport { Transfer } from \"@godxjp/ui/data-entry\";\n\nconst ALL_ACCOUNTS = [\n { key: \"1010\", title: \"Cash\", description: \"Asset\" },\n { key: \"1020\", title: \"Accounts Receivable\", description: \"Asset\" },\n { key: \"2010\", title: \"Accounts Payable\", description: \"Liability\" },\n { key: \"3010\", title: \"Revenue\", description: \"Income\" },\n { key: \"4010\", title: \"Cost of Goods Sold\", description: \"Expense\", disabled: true },\n];\n\nexport function AccountMapping() {\n const [targetKeys, setTargetKeys] = useState<string[]>([\"1010\"]);\n\n return (\n <Transfer\n dataSource={ALL_ACCOUNTS}\n targetKeys={targetKeys}\n onValueChange={(nextKeys) => setTargetKeys(nextKeys)}\n titles={[\"Available Accounts\", \"Mapped Accounts\"]}\n showSearch\n />\n );\n}",
|
|
9010
9125
|
"group": "data-entry",
|
|
9011
9126
|
"importPath": "@godxjp/ui/data-entry",
|
|
9012
9127
|
"name": "Transfer",
|
|
@@ -9101,6 +9216,11 @@
|
|
|
9101
9216
|
"name": "className",
|
|
9102
9217
|
"type": "string"
|
|
9103
9218
|
},
|
|
9219
|
+
{
|
|
9220
|
+
"description": "Lands on the `role=\"group\"` shuttle container, not on any one input — a two-pane shuttle has no single labelable control, so this is what a FormField label points at. FormField injects it; pass it yourself only for a bare Transfer.",
|
|
9221
|
+
"name": "id",
|
|
9222
|
+
"type": "string"
|
|
9223
|
+
},
|
|
9104
9224
|
{
|
|
9105
9225
|
"description": "Controlled selection state as a tuple: index 0 = keys checked in the source panel, index 1 = keys checked in the target panel. Omit to use internal (uncontrolled) selection state. Must be paired with `onSelectChange` when provided.",
|
|
9106
9226
|
"name": "selectedKeys",
|
|
@@ -9215,6 +9335,11 @@
|
|
|
9215
9335
|
"name": "className",
|
|
9216
9336
|
"type": "string"
|
|
9217
9337
|
},
|
|
9338
|
+
{
|
|
9339
|
+
"description": "Lands on the native `<input type=\"file\">`, NOT on the wrapper — the hidden input is the semantic focus target, so this is what makes a `<label htmlFor>` (or FormField, which injects it) actually focus the picker. Putting it on the visible dropzone instead is the usual reason a label click does nothing.",
|
|
9340
|
+
"name": "id",
|
|
9341
|
+
"type": "string"
|
|
9342
|
+
},
|
|
9218
9343
|
{
|
|
9219
9344
|
"description": "Custom button label for variant='button'. Falls back to the i18n 'Upload file' string.",
|
|
9220
9345
|
"name": "children",
|
|
@@ -9368,7 +9493,7 @@
|
|
|
9368
9493
|
]
|
|
9369
9494
|
},
|
|
9370
9495
|
{
|
|
9371
|
-
"example": "{`import { useState } from \"react\";\nimport { UploadCropDialog } from \"@godxjp/ui/
|
|
9496
|
+
"example": "{`import { useState } from \"react\";\nimport { UploadCropDialog } from \"@godxjp/ui/data-entry\"; // internal — prefer Upload variant=\"avatar-crop\" instead\n\nexport function AvatarField() {\n const [cropFile, setCropFile] = useState<File | null>(null);\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0] ?? null;\n setCropFile(file);\n e.target.value = \"\"; // reset so re-selecting same file fires onChange\n };\n\n const handleConfirm = (cropped: File) => {\n // cropped is always image/jpeg 256×256\n const form = new FormData();\n form.append(\"avatar\", cropped);\n fetch(\"/api/avatar\", { method: \"POST\", body: form });\n };\n\n return (\n <>\n <input type=\"file\" accept=\"image/*\" onChange={handleFileChange} />\n <UploadCropDialog\n open={cropFile !== null}\n onOpenChange={(open) => { if (!open) setCropFile(null); }}\n file={cropFile}\n onConfirm={handleConfirm}\n />\n </>\n );\n}`}",
|
|
9372
9497
|
"group": "data-entry",
|
|
9373
9498
|
"importPath": "@godxjp/ui/data-entry",
|
|
9374
9499
|
"name": "UploadCropDialog",
|
|
@@ -11960,7 +12085,7 @@
|
|
|
11960
12085
|
]
|
|
11961
12086
|
},
|
|
11962
12087
|
{
|
|
11963
|
-
"example": "import { PasswordInput, PasswordStrength } from \"@godxjp/ui/data-entry\";\n\
|
|
12088
|
+
"example": "import { PasswordInput, PasswordStrength } from \"@godxjp/ui/data-entry\";\n\nexport default function PasswordBlock() {\n const [value, setValue] = useState(\"\");\n return (\n <div className=\"ui-stack\">\n <PasswordInput value={value} onChange={(event) => setValue(event.target.value)} />\n <PasswordStrength value={value} rules={[\"length\", \"upper\", \"lower\", \"number\", \"symbol\"]} />\n </div>\n );\n}",
|
|
11964
12089
|
"group": "data-entry",
|
|
11965
12090
|
"importPath": "@godxjp/ui/data-entry",
|
|
11966
12091
|
"name": "PasswordStrength",
|
|
@@ -12082,6 +12207,41 @@
|
|
|
12082
12207
|
"description": "Main-axis alignment of the whole code row (groups + separators) inside the field. `center` is the canonical auth challenge. Before this existed, every consumer wrapped the OTP in their own flex-centring div — do not. A service that wants all code fields centred sets `--otp-container-align` once instead.",
|
|
12083
12208
|
"name": "align",
|
|
12084
12209
|
"type": "\"start\" | \"center\" | \"end\""
|
|
12210
|
+
},
|
|
12211
|
+
{
|
|
12212
|
+
"description": "Fires ONCE the last slot fills, whether the user typed it or pasted the whole code. This is the auto-submit hook: a 2FA challenge with a visible submit button is a step nobody wants, and the alternative — watching `value.length === maxLength` in an effect — re-fires on every re-render. Keep the submit button anyway for the paste-then-correct case.",
|
|
12213
|
+
"name": "onComplete",
|
|
12214
|
+
"type": "(value: string) => void"
|
|
12215
|
+
},
|
|
12216
|
+
{
|
|
12217
|
+
"description": "Rewrites CLIPBOARD text before it reaches the field. Distinct from `formatter`, which normalises every value: this one only sees a paste, which is where the junk arrives — `\"123 456\"`, `\"code: 123456\"`, a copied SMS line. Note the order the field applies them: `pattern` is matched against the RAW keystroke first, so a pattern must accept what a user actually types, not only what these two produce.",
|
|
12218
|
+
"name": "pasteTransformer",
|
|
12219
|
+
"type": "(pasted: string) => string"
|
|
12220
|
+
},
|
|
12221
|
+
{
|
|
12222
|
+
"description": "Class on the ROW container `input-otp` renders (the slots' flex parent), which `className` cannot reach — `className` lands on the hidden input, because that is the real field. Prefer `align` and the `--otp-*` tokens; this is the vendored escape hatch underneath them.",
|
|
12223
|
+
"name": "containerClassName",
|
|
12224
|
+
"type": "string"
|
|
12225
|
+
},
|
|
12226
|
+
{
|
|
12227
|
+
"description": "`input-otp`'s answer to the 1Password / LastPass badge that browsers float over a code field and that covers the last slot. `increase-width` (its default) reserves room so the badge sits beside the row; `none` turns the accommodation off, which is what a row already centred by `align` usually wants. Pure layout — it changes no value and no keyboard behaviour.",
|
|
12228
|
+
"name": "pushPasswordManagerStrategy",
|
|
12229
|
+
"type": "\"increase-width\" | \"none\""
|
|
12230
|
+
},
|
|
12231
|
+
{
|
|
12232
|
+
"description": "The `<noscript>` stylesheet `input-otp` injects so the slots are still visible with JS disabled. Pass `null` to suppress it — the one real reason being a CSP that forbids inline styles and that `nonce` cannot satisfy. Leave it alone otherwise.",
|
|
12233
|
+
"name": "noScriptCSSFallback",
|
|
12234
|
+
"type": "string | null"
|
|
12235
|
+
},
|
|
12236
|
+
{
|
|
12237
|
+
"description": "CSP nonce stamped on the stylesheet `input-otp` injects. Required only under a `style-src 'nonce-…'` policy, where the field otherwise renders unstyled and the console reports a blocked inline style. Pass the same nonce the document was served with.",
|
|
12238
|
+
"name": "nonce",
|
|
12239
|
+
"type": "string"
|
|
12240
|
+
},
|
|
12241
|
+
{
|
|
12242
|
+
"description": "`input-otp`'s headless mode: you draw the entire row from the slot state instead of composing InputOTPGroup / InputOTPSlot. It is mutually exclusive with `children` — the vendor types the two as a union and this component keeps that union. Reaching for it means giving up the slot styling, the group outline and the separator this package owns, so it is the last resort, not a customisation point.",
|
|
12243
|
+
"name": "render",
|
|
12244
|
+
"type": "(props: InputOTPRenderProps) => React.ReactNode"
|
|
12085
12245
|
}
|
|
12086
12246
|
],
|
|
12087
12247
|
"related": [
|
|
@@ -12763,7 +12923,7 @@
|
|
|
12763
12923
|
"type": "ChartDatum[]"
|
|
12764
12924
|
},
|
|
12765
12925
|
{
|
|
12766
|
-
"description": "Plotted series: { dataKey, label?, color? }. Colour defaults to the --chart-1..6 palette.",
|
|
12926
|
+
"description": "Plotted series: { dataKey, label?, color?, fillColor? }. Colour defaults to the --chart-1..6 palette; fillColor is the AreaChart band only.",
|
|
12767
12927
|
"name": "series",
|
|
12768
12928
|
"required": true,
|
|
12769
12929
|
"type": "ChartSeriesProp[]"
|
|
@@ -12819,12 +12979,28 @@
|
|
|
12819
12979
|
"name": "numberFormat",
|
|
12820
12980
|
"type": "Intl.NumberFormatOptions"
|
|
12821
12981
|
},
|
|
12982
|
+
{
|
|
12983
|
+
"description": "Explicit [min, max] for the VALUE axis (y vertical, x on a horizontal bar). Omit to let the data set it. A non-zero baseline is a charting-ETHICS call: legitimate where the SHAPE is the message and zero is not a reference (price, temperature, an index, a latency percentile), misleading wherever the reader compares magnitudes — which is every bar chart.",
|
|
12984
|
+
"name": "valueDomain",
|
|
12985
|
+
"type": "[number, number]"
|
|
12986
|
+
},
|
|
12987
|
+
{
|
|
12988
|
+
"description": "Explicit tick positions on the value axis, in data units. Values outside valueDomain are not drawn.",
|
|
12989
|
+
"name": "valueTicks",
|
|
12990
|
+
"type": "number[]"
|
|
12991
|
+
},
|
|
12822
12992
|
{
|
|
12823
12993
|
"defaultValue": "false",
|
|
12824
12994
|
"description": "Render smooth (monotone) lines instead of straight segments.",
|
|
12825
12995
|
"name": "curved",
|
|
12826
12996
|
"type": "boolean"
|
|
12827
12997
|
},
|
|
12998
|
+
{
|
|
12999
|
+
"defaultValue": "false",
|
|
13000
|
+
"description": "Draw a marker at every data point. Off by default — markers crowd a dense series.",
|
|
13001
|
+
"name": "showDots",
|
|
13002
|
+
"type": "boolean"
|
|
13003
|
+
},
|
|
12828
13004
|
{
|
|
12829
13005
|
"description": "Message shown when `data` is empty (defaults to a localized 'no data').",
|
|
12830
13006
|
"name": "emptyMessage",
|
|
@@ -12845,7 +13021,9 @@
|
|
|
12845
13021
|
"DO install the `recharts` optional peer dependency in the consuming app — charts are the only part of @godxjp/ui that needs it, so apps without charts never pay for it.",
|
|
12846
13022
|
"DO pass an i18n'd `label` — it is both the visible caption and the accessible name; the component also emits a screen-reader list of the plotted values (WCAG 1.1.1).",
|
|
12847
13023
|
"DO pre-translate each series' `label`; pass `numberFormat` (e.g. { style: 'currency', currency: 'JPY' }) and the axis/tooltip numbers localize automatically via Intl.",
|
|
12848
|
-
"DON'T hand-roll an SVG/canvas chart or drop raw recharts into a page — LineChart owns the colour tokens, locale formatting, empty state, and accessibility wiring."
|
|
13024
|
+
"DON'T hand-roll an SVG/canvas chart or drop raw recharts into a page — LineChart owns the colour tokens, locale formatting, empty state, and accessibility wiring.",
|
|
13025
|
+
"DO retune the line weight and the grid dash with the `--chart-series-stroke-width` / `--chart-grid-line-dash` theme tokens, globally or on a scoped [data-tenant] region. They are house style; there is no prop for them.",
|
|
13026
|
+
"DON'T reach for `valueDomain` to make a flat trend look dramatic — cropping the axis magnifies every wobble and the reader cannot tell a 2% drift from a collapse. Crop only where zero is not a meaningful reference, and say the range."
|
|
12849
13027
|
],
|
|
12850
13028
|
"useCases": [
|
|
12851
13029
|
"Revenue / KPI trend over months in a dashboard.",
|
|
@@ -12866,7 +13044,7 @@
|
|
|
12866
13044
|
"type": "ChartDatum[]"
|
|
12867
13045
|
},
|
|
12868
13046
|
{
|
|
12869
|
-
"description": "Plotted series: { dataKey, label?, color? }.",
|
|
13047
|
+
"description": "Plotted series: { dataKey, label?, color?, fillColor? }. `fillColor` paints the filled band independently of the line's `color`; it defaults to `color`.",
|
|
12870
13048
|
"name": "series",
|
|
12871
13049
|
"required": true,
|
|
12872
13050
|
"type": "ChartSeriesProp[]"
|
|
@@ -12922,6 +13100,16 @@
|
|
|
12922
13100
|
"name": "numberFormat",
|
|
12923
13101
|
"type": "Intl.NumberFormatOptions"
|
|
12924
13102
|
},
|
|
13103
|
+
{
|
|
13104
|
+
"description": "Explicit [min, max] for the VALUE axis (y vertical, x on a horizontal bar). Omit to let the data set it. A non-zero baseline is a charting-ETHICS call: legitimate where the SHAPE is the message and zero is not a reference (price, temperature, an index, a latency percentile), misleading wherever the reader compares magnitudes — which is every bar chart.",
|
|
13105
|
+
"name": "valueDomain",
|
|
13106
|
+
"type": "[number, number]"
|
|
13107
|
+
},
|
|
13108
|
+
{
|
|
13109
|
+
"description": "Explicit tick positions on the value axis, in data units. Values outside valueDomain are not drawn.",
|
|
13110
|
+
"name": "valueTicks",
|
|
13111
|
+
"type": "number[]"
|
|
13112
|
+
},
|
|
12925
13113
|
{
|
|
12926
13114
|
"defaultValue": "false",
|
|
12927
13115
|
"description": "Stack series into one bar instead of grouping side by side.",
|
|
@@ -12962,7 +13150,7 @@
|
|
|
12962
13150
|
]
|
|
12963
13151
|
},
|
|
12964
13152
|
{
|
|
12965
|
-
"example": "import { CompactBarTrend } from \"@godxjp/ui/charts/compact-bar-trend\";\n\n<CompactBarTrend\n label={t(\"dashboard.newOrganizations7d\")}\n description={t(\"dashboard.newOrganizationsHint\")}\n data={trend}\n categoryKey=\"date\"\n valueKey=\"count\"\n emphasizedIndex={-1}\n size=\"xs\"\n footer={<Text size=\"xs\" tone=\"muted\">{t(\"dashboard.lastUpdated\", { at })}</Text>}\n/>",
|
|
13153
|
+
"example": "import { CompactBarTrend } from \"@godxjp/ui/charts/compact-bar-trend\";\nimport { Text } from \"@godxjp/ui/general\";\n\n<CompactBarTrend\n label={t(\"dashboard.newOrganizations7d\")}\n description={t(\"dashboard.newOrganizationsHint\")}\n data={trend}\n categoryKey=\"date\"\n valueKey=\"count\"\n emphasizedIndex={-1}\n size=\"xs\"\n footer={<Text size=\"xs\" tone=\"muted\">{t(\"dashboard.lastUpdated\", { at: new Date().toISOString() })}</Text>}\n/>",
|
|
12966
13154
|
"group": "data-display",
|
|
12967
13155
|
"importPath": "@godxjp/ui/charts/compact-bar-trend",
|
|
12968
13156
|
"name": "CompactBarTrend",
|
|
@@ -13137,12 +13325,28 @@
|
|
|
13137
13325
|
"name": "stacked",
|
|
13138
13326
|
"type": "boolean"
|
|
13139
13327
|
},
|
|
13328
|
+
{
|
|
13329
|
+
"description": "Explicit [min, max] for the VALUE axis (y vertical, x on a horizontal bar). Omit to let the data set it. A non-zero baseline is a charting-ETHICS call: legitimate where the SHAPE is the message and zero is not a reference (price, temperature, an index, a latency percentile), misleading wherever the reader compares magnitudes — which is every bar chart.",
|
|
13330
|
+
"name": "valueDomain",
|
|
13331
|
+
"type": "[number, number]"
|
|
13332
|
+
},
|
|
13333
|
+
{
|
|
13334
|
+
"description": "Explicit tick positions on the value axis, in data units. Values outside valueDomain are not drawn.",
|
|
13335
|
+
"name": "valueTicks",
|
|
13336
|
+
"type": "number[]"
|
|
13337
|
+
},
|
|
13140
13338
|
{
|
|
13141
13339
|
"defaultValue": "false",
|
|
13142
13340
|
"description": "Render smooth (monotone) areas instead of straight segments.",
|
|
13143
13341
|
"name": "curved",
|
|
13144
13342
|
"type": "boolean"
|
|
13145
13343
|
},
|
|
13344
|
+
{
|
|
13345
|
+
"defaultValue": "false",
|
|
13346
|
+
"description": "Draw a marker at every data point. Off by default — markers crowd a dense series.",
|
|
13347
|
+
"name": "showDots",
|
|
13348
|
+
"type": "boolean"
|
|
13349
|
+
},
|
|
13146
13350
|
{
|
|
13147
13351
|
"description": "Message shown when `data` is empty.",
|
|
13148
13352
|
"name": "emptyMessage",
|
|
@@ -13160,7 +13364,9 @@
|
|
|
13160
13364
|
"DO import from the charts entry: `import { AreaChart } from \"@godxjp/ui/charts\";` (recharts optional peer required).",
|
|
13161
13365
|
"DO import only the chart a screen uses — `import { AreaChart } from \"@godxjp/ui/charts/area-chart\";` — when the `./charts` barrel should not link the whole chart family. Without the `recharts` peer the build then fails ONCE, naming the package and the fix.",
|
|
13162
13366
|
"DO use `stacked` to show how parts accumulate into a total over time.",
|
|
13163
|
-
"DON'T overlay more than 2-3 unstacked areas — fill opacity makes dense overlays unreadable; switch to LineChart."
|
|
13367
|
+
"DON'T overlay more than 2-3 unstacked areas — fill opacity makes dense overlays unreadable; switch to LineChart.",
|
|
13368
|
+
"DO split the band's hue from the line's with `series[].fillColor` when a brand specifies both (a dark line over a lighter wash). The band's DENSITY is the `--chart-area-fill-alpha` theme token, not a prop.",
|
|
13369
|
+
"DON'T tune the band by dropping a page-local CSS rule on `.recharts-area-area` — `--chart-area-fill-alpha` is the supported knob and it follows a scoped [data-tenant] region."
|
|
13164
13370
|
],
|
|
13165
13371
|
"useCases": [
|
|
13166
13372
|
"Cumulative volume over time (e.g. total transactions per day).",
|
|
@@ -13849,6 +14055,16 @@
|
|
|
13849
14055
|
"name": "appearance",
|
|
13850
14056
|
"type": "\"bar\" | \"icon\""
|
|
13851
14057
|
},
|
|
14058
|
+
{
|
|
14059
|
+
"description": "Which way the panel opens. DERIVED from `appearance` when unset — `bottom` in a bar (the panel drops below the trigger, the only direction that does not cover the bar itself), `right` otherwise, because a rail is vertical and its panel goes beside it. State it when the chrome can be RE-DOCKED: `appearance` says the trigger is NOT in a bar, and it cannot say which way is out — a rail pinned to the top edge is not a bar and still opens downward. Measured without it, an embedded bar trigger at (2,50) put its panel at (12,90), lying over the host application's sidebar. Not used by `responsive=\"fullscreen\"`, which has no side.",
|
|
14060
|
+
"name": "side",
|
|
14061
|
+
"type": "\"top\" | \"right\" | \"bottom\" | \"left\""
|
|
14062
|
+
},
|
|
14063
|
+
{
|
|
14064
|
+
"description": "Where the panel sits along the `side` edge — the cross-axis half of the same decision, and derived the same way: `end` in a bar (the Workspace shape, flush with the bar's end), `start` otherwise (aligned to the rail trigger's own start). State it alongside `side` when you state either.",
|
|
14065
|
+
"name": "align",
|
|
14066
|
+
"type": "\"start\" | \"center\" | \"end\""
|
|
14067
|
+
},
|
|
13852
14068
|
{
|
|
13853
14069
|
"description": "Controlled open state.",
|
|
13854
14070
|
"name": "open",
|
|
@@ -13983,7 +14199,7 @@
|
|
|
13983
14199
|
},
|
|
13984
14200
|
{
|
|
13985
14201
|
"docPath": "data-display/permission-matrix.tsx",
|
|
13986
|
-
"example": "import { Card, CardContent, PermissionMatrix } from \"@godxjp/ui/data-display\";\nimport { grantKey } from \"@godxjp/ui/lib/permission-grid\";\n\nconst grants = new Set(rolePermissions.map((rp) => grantKey(rp.roleId, rp.permissionId)));\n\n<Card>\n <CardContent flush>\n <PermissionMatrix\n roles={roles}\n permissions={permissions}\n grants={grants}\n onGrantChange={(roleId, permissionId, granted) => mutate({ roleId, permissionId, granted })}\n />\n </CardContent>\n</Card>",
|
|
14202
|
+
"example": "import { Card, CardContent, PermissionMatrix } from \"@godxjp/ui/data-display\";\nimport { grantKey } from \"@godxjp/ui/lib/permission-grid\";\n\nconst grants = new Set<string>(rolePermissions.map((rp) => grantKey(rp.roleId, rp.permissionId)));\n\n<Card>\n <CardContent flush>\n <PermissionMatrix\n roles={roles}\n permissions={permissions}\n grants={grants}\n onGrantChange={(roleId, permissionId, granted) => mutate({ roleId, permissionId, granted })}\n />\n </CardContent>\n</Card>",
|
|
13987
14203
|
"group": "data-display",
|
|
13988
14204
|
"importPath": "@godxjp/ui/data-display",
|
|
13989
14205
|
"name": "PermissionMatrix",
|
|
@@ -14037,6 +14253,11 @@
|
|
|
14037
14253
|
"description": "Accessible table name (localized default).",
|
|
14038
14254
|
"name": "label",
|
|
14039
14255
|
"type": "string"
|
|
14256
|
+
},
|
|
14257
|
+
{
|
|
14258
|
+
"description": "DOM id on the grid root. Worth setting on a permissions page that also renders a summary or a legend elsewhere: it is the anchor those can point at, and the stable handle for an E2E selector that must not depend on the localized `label`.",
|
|
14259
|
+
"name": "id",
|
|
14260
|
+
"type": "string"
|
|
14040
14261
|
}
|
|
14041
14262
|
],
|
|
14042
14263
|
"related": [
|
|
@@ -14127,6 +14348,16 @@
|
|
|
14127
14348
|
"description": "Override the localized radio labels (e.g. domain wording like 全店舗).",
|
|
14128
14349
|
"name": "allLabel / selectedLabel",
|
|
14129
14350
|
"type": "ReactNode"
|
|
14351
|
+
},
|
|
14352
|
+
{
|
|
14353
|
+
"description": "Native form name, forwarded to the MODE radio group — the all / selected choice is what submits under it. The checked branch ids are not native fields; they live in the single `{ mode, branchIds }` value and are yours to serialise.",
|
|
14354
|
+
"name": "name",
|
|
14355
|
+
"type": "string"
|
|
14356
|
+
},
|
|
14357
|
+
{
|
|
14358
|
+
"description": "DOM id on the picker root, and the SEED for the ids beneath it — the validation message is `${id}-error`, which is what `aria-errormessage` points at. Left out, a `useId`-based id is generated, so the association still holds; set it when a server-rendered page needs those ids to be stable.",
|
|
14359
|
+
"name": "id",
|
|
14360
|
+
"type": "string"
|
|
14130
14361
|
}
|
|
14131
14362
|
],
|
|
14132
14363
|
"related": [
|
|
@@ -14209,6 +14440,11 @@
|
|
|
14209
14440
|
"description": "Forwarded to MasterDetail (localized region labels by default). Never re-derive tracks or breakpoints in the app.",
|
|
14210
14441
|
"name": "railWidth / masterViewport / collapseBelow / masterLabel / detailLabel",
|
|
14211
14442
|
"type": "MasterDetail geometry + region labels"
|
|
14443
|
+
},
|
|
14444
|
+
{
|
|
14445
|
+
"description": "DOM id on the panel root — the two-region MasterDetail wrapper, not the rail or the detail. It is the handle for a deep link onto the roles panel of a settings page, and for an E2E selector that must survive `masterLabel` being localized.",
|
|
14446
|
+
"name": "id",
|
|
14447
|
+
"type": "string"
|
|
14212
14448
|
}
|
|
14213
14449
|
],
|
|
14214
14450
|
"related": [
|
|
@@ -15117,6 +15353,31 @@
|
|
|
15117
15353
|
"description": "Child mode: visible trigger; upload runs through a hidden input beside it.",
|
|
15118
15354
|
"name": "children",
|
|
15119
15355
|
"type": "ReactElement"
|
|
15356
|
+
},
|
|
15357
|
+
{
|
|
15358
|
+
"description": "antd Upload `onRemove`, narrowed to the attachment row. RETURNING `false` (or a promise of it) VETOES the removal and the card stays — anything else, including `undefined`, lets it go. That is how you gate a removal behind a confirm dialog without owning `items` yourself. It is awaited, so an async guard works.",
|
|
15359
|
+
"name": "onRemove",
|
|
15360
|
+
"type": "(item: AttachmentsItemProp) => boolean | void | Promise<boolean | void>"
|
|
15361
|
+
},
|
|
15362
|
+
{
|
|
15363
|
+
"description": "Ant Design X `classNames` — per-part classes (root, list, card, file, upload, placeholder). DECLARED AND FORWARDED, and the only semantic part map in this package: docs/DESIGN-AUTHORITY.md rules that antd's `classNames`/`styles` maps are NOT adopted because this library answers that layer with tokens (cardinal rule #45), and Attachments is the one component that carries them anyway. Recorded there as a contradiction, not a pattern — do not copy it onto another component, and retune through `--attachments-*` instead.",
|
|
15364
|
+
"name": "classNames",
|
|
15365
|
+
"type": "Partial<Record<AttachmentsSemanticProp, string>>"
|
|
15366
|
+
},
|
|
15367
|
+
{
|
|
15368
|
+
"description": "Ant Design X `styles` — the same part map as `classNames`, as inline styles, and under the same standing ruling against it. Inline styles beat every stylesheet rule, so this is the one handle in the package that can take a part off the design system entirely. The `--attachments-*` tokens are the supported route.",
|
|
15369
|
+
"name": "styles",
|
|
15370
|
+
"type": "Partial<Record<AttachmentsSemanticProp, React.CSSProperties>>"
|
|
15371
|
+
},
|
|
15372
|
+
{
|
|
15373
|
+
"description": "Ant Design X `rootClassName` — the outermost node. It is not a duplicate of `className`: in the full-screen-drop mode (`getDropContainer`) the outermost node is the OVERLAY rather than the inline tray, which is why antd separates the two. Both are applied.",
|
|
15374
|
+
"name": "rootClassName",
|
|
15375
|
+
"type": "string"
|
|
15376
|
+
},
|
|
15377
|
+
{
|
|
15378
|
+
"description": "ACCEPTED AND INERT. Ant Design X forwards it to its own Image preview; this package has no Image primitive yet, so the prop exists only so an Ant X call site type-checks, and passing it changes nothing on screen. Do not reach for it expecting a preview knob.",
|
|
15379
|
+
"name": "imageProps",
|
|
15380
|
+
"type": "Record<string, unknown>"
|
|
15120
15381
|
}
|
|
15121
15382
|
],
|
|
15122
15383
|
"related": [
|
|
@@ -15134,7 +15395,7 @@
|
|
|
15134
15395
|
"usage": [
|
|
15135
15396
|
"DO keep antd field names on each item (`thumbUrl`, `originFileObj`, `uid`) — an Ant X call site should compile unchanged.",
|
|
15136
15397
|
"DO use `ref.select({ accept, multiple })` to open the picker programmatically (Ant X 2.0).",
|
|
15137
|
-
"
|
|
15398
|
+
"Ant X's `classNames` / `styles` / `rootClassName` ARE declared and forwarded, although docs/DESIGN-AUTHORITY.md rules that antd's semantic part maps are not adopted here. The older note in this slot said not to expect them at all; the type has never agreed with it, so an agent reading only the catalog was told the opposite of what autocomplete offered. Retune through the `--attachments-*` tokens, and treat the maps as a recorded contradiction on this one component rather than a pattern to reuse.",
|
|
15138
15399
|
"The card is a FIXED box — 268x68 (Ant X's own), from `--attachments-card-size` (inline) and `--attachments-card-block-size`. The block size is also the `+` tile's square and the `overflow=\"scrollY\"` one-row viewport, so retune it once and all three follow. The file input is `sr-only`: never style it visible."
|
|
15139
15400
|
],
|
|
15140
15401
|
"useCases": [
|
|
@@ -15235,7 +15496,7 @@
|
|
|
15235
15496
|
},
|
|
15236
15497
|
{
|
|
15237
15498
|
"docPath": "layout/masonry.tsx",
|
|
15238
|
-
"example": "import { Masonry } from \"@godxjp/ui/layout\";\nimport { Card, CardContent } from \"@godxjp/ui/data-display\";\nimport { Text } from \"@godxjp/ui/general\";\n\n<Masonry\n columns={{ base: 1, sm: 2, lg: 3 }}\n gap=\"md\"\n items={notes.map((note) => ({ key: note.id, data: note }))}\n itemRender={({ data }) => (\n <Card>\n <CardContent>\n <Text>{data
|
|
15499
|
+
"example": "import { Masonry } from \"@godxjp/ui/layout\";\nimport { Card, CardContent } from \"@godxjp/ui/data-display\";\nimport { Text } from \"@godxjp/ui/general\";\n\n// T flows from `items`, so `data` in itemRender is whatever you put there — give the\n// collection a shape and the render callback needs no annotation.\ntype Note = { id: string; body: string };\nconst notes: Note[] = useNotes();\n\n<Masonry\n columns={{ base: 1, sm: 2, lg: 3 }}\n gap=\"md\"\n items={notes.map((note) => ({ key: note.id, data: note }))}\n itemRender={({ data }) => (\n <Card>\n <CardContent>\n <Text>{data?.body}</Text>\n </CardContent>\n </Card>\n )}\n onLayoutChange={(layout) => console.log(layout.length, \"tiles placed\")}\n/>",
|
|
15239
15500
|
"group": "layout",
|
|
15240
15501
|
"importPath": "@godxjp/ui/layout",
|
|
15241
15502
|
"name": "Masonry",
|
|
@@ -15441,7 +15702,12 @@
|
|
|
15441
15702
|
"type": "number"
|
|
15442
15703
|
},
|
|
15443
15704
|
{
|
|
15444
|
-
"description": "
|
|
15705
|
+
"description": "gh#890. The scroll box the sections are measured in AND `Affix` pins the nav against — one function, both halves. `Affix`'s own name and shape (`AffixTargetProp`), the same lazy getter `FloatButton.BackTop.target` already spells here. `null`, or an absent `target`, means the viewport. Wins over `getContainer` when both are given.",
|
|
15706
|
+
"name": "target",
|
|
15707
|
+
"type": "() => Window | HTMLElement | null"
|
|
15708
|
+
},
|
|
15709
|
+
{
|
|
15710
|
+
"description": "antd `getContainer`, default `() => window` — the scroll box holding the sections. Superseded by `target` (gh#890), which mirrors `Affix`'s own spelling for the identical idea; kept live for a call site written before `target` existed.",
|
|
15445
15711
|
"name": "getContainer",
|
|
15446
15712
|
"type": "() => HTMLElement | Window"
|
|
15447
15713
|
},
|