@godxjp/ui 28.13.0 → 30.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/AppShell.json +1 -1
- package/agent/components/AreaChart.json +19 -1
- package/agent/components/BarChart.json +11 -1
- package/agent/components/Cascader.json +1 -1
- package/agent/components/CompactBarTrend.json +1 -1
- 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/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 +1 -1
- package/agent/components/Select.json +1 -0
- package/agent/components/Sidebar.json +1 -1
- package/agent/components/Table.json +8 -3
- package/agent/components/ThemeScope.json +49 -0
- package/agent/components/Topbar.json +1 -0
- package/agent/components/TopbarItem.json +2 -1
- package/agent/components/Transfer.json +1 -1
- package/agent/components/TreeSelect.json +1 -1
- 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 +138 -30
- 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 +5055 -1060
- 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 +21 -3
- 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 +19 -3
- 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/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 +39 -19
- package/dist/styles/badge-layout.css +11 -7
- package/dist/styles/base.css +14 -5
- package/dist/styles/card-layout.css +29 -16
- package/dist/styles/chart-layout.css +27 -5
- package/dist/styles/control.css +225 -103
- package/dist/styles/data-display-layout.css +178 -76
- package/dist/styles/data-entry-layout.css +35 -99
- package/dist/styles/dialog-layout.css +58 -28
- package/dist/styles/float-button-layout.css +5 -5
- package/dist/styles/focus-ring.css +11 -7
- package/dist/styles/layout.css +53 -25
- package/dist/styles/logo-layout.css +3 -3
- package/dist/styles/motion.css +2 -2
- package/dist/styles/navigation-layout.css +117 -44
- package/dist/styles/shell-layout.css +90 -124
- package/dist/styles/table-layout.css +66 -24
- package/dist/styles/text-layout.css +13 -4
- package/dist/styles/toggle.css +9 -3
- 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/banner.css +1 -1
- package/dist/tokens/components/callout.css +1 -1
- package/dist/tokens/components/card.css +13 -8
- package/dist/tokens/components/chart.css +11 -2
- package/dist/tokens/components/chat-bubble.css +2 -2
- package/dist/tokens/components/control.css +41 -22
- package/dist/tokens/components/conversations.css +2 -1
- package/dist/tokens/components/data-display.css +23 -18
- package/dist/tokens/components/data-entry.css +1 -1
- package/dist/tokens/components/descriptions.css +2 -2
- package/dist/tokens/components/draggable-panel.css +2 -2
- package/dist/tokens/components/feedback.css +29 -9
- package/dist/tokens/components/float-button.css +1 -1
- package/dist/tokens/components/legal-document.css +2 -2
- package/dist/tokens/components/logo.css +2 -2
- package/dist/tokens/components/mega-menu.css +6 -4
- package/dist/tokens/components/navigation.css +27 -13
- package/dist/tokens/components/segmented.css +11 -6
- package/dist/tokens/components/separator.css +1 -1
- package/dist/tokens/components/shell.css +24 -10
- package/dist/tokens/components/table.css +13 -5
- package/dist/tokens/components/thought-chain.css +2 -2
- package/dist/tokens/components/toggle.css +3 -1
- package/dist/tokens/components/tree.css +3 -1
- package/dist/tokens/components/upload.css +8 -8
- package/dist/tokens/components/welcome.css +1 -1
- package/dist/tokens/foundation.css +30 -3
- package/docs/COMPOSITION-VS-COMPONENT.md +31 -0
- package/docs/CUSTOMER-THEMING.md +637 -1
- 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 +506 -0
- package/docs/i18n/messages/ja.json +506 -0
- package/docs/i18n/messages/vi.json +506 -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/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-lab.tsx +2125 -0
- package/docs/themes/flat.css +462 -0
- package/docs/themes/glassmorphism.css +958 -0
- package/docs/themes/index.ts +219 -0
- package/docs/themes/neubrutalism.css +475 -0
- package/package.json +4 -3
- package/scripts/explain-token.mjs +382 -0
|
@@ -482,6 +482,11 @@
|
|
|
482
482
|
"name": "OverlayPortalProvider",
|
|
483
483
|
"tagline": "Moves EVERY overlay in this library (Popover, Dialog, Sheet, Tooltip, DropdownMenu, HoverCard, Select, Cascader) into a container you name — the one thing an app mounted inside a SHADOW ROOT cannot do with props."
|
|
484
484
|
},
|
|
485
|
+
{
|
|
486
|
+
"group": "providers",
|
|
487
|
+
"name": "ThemeScope",
|
|
488
|
+
"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."
|
|
489
|
+
},
|
|
485
490
|
{
|
|
486
491
|
"group": "providers",
|
|
487
492
|
"name": "formatDate",
|
package/agent/components.json
CHANGED
|
@@ -1016,7 +1016,7 @@
|
|
|
1016
1016
|
]
|
|
1017
1017
|
},
|
|
1018
1018
|
{
|
|
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 }: {
|
|
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}",
|
|
1020
1020
|
"group": "layout",
|
|
1021
1021
|
"importPath": "@godxjp/ui/layout",
|
|
1022
1022
|
"name": "AppShell",
|
|
@@ -1422,7 +1422,7 @@
|
|
|
1422
1422
|
]
|
|
1423
1423
|
},
|
|
1424
1424
|
{
|
|
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
|
|
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",
|
|
1426
1426
|
"group": "layout",
|
|
1427
1427
|
"importPath": "@godxjp/ui/layout",
|
|
1428
1428
|
"name": "Sidebar",
|
|
@@ -1609,6 +1609,7 @@
|
|
|
1609
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.",
|
|
1610
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.",
|
|
1611
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.",
|
|
1612
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.",
|
|
1613
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.",
|
|
1614
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.",
|
|
@@ -1693,7 +1694,8 @@
|
|
|
1693
1694
|
"DO wrap it in a DropdownMenuTrigger asChild for a user menu; the open state lights the cell via [data-state=open].",
|
|
1694
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.",
|
|
1695
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).",
|
|
1696
|
-
"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)."
|
|
1697
1699
|
],
|
|
1698
1700
|
"useCases": [
|
|
1699
1701
|
"Account / user-menu trigger in the topbar end slot",
|
|
@@ -1765,7 +1767,7 @@
|
|
|
1765
1767
|
]
|
|
1766
1768
|
},
|
|
1767
1769
|
{
|
|
1768
|
-
"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>",
|
|
1769
1771
|
"group": "layout",
|
|
1770
1772
|
"importPath": "@godxjp/ui/layout",
|
|
1771
1773
|
"name": "MasterDetail",
|
|
@@ -3290,7 +3292,7 @@
|
|
|
3290
3292
|
"absorbed": [
|
|
3291
3293
|
"DataGrid"
|
|
3292
3294
|
],
|
|
3293
|
-
"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}",
|
|
3294
3296
|
"group": "data-display",
|
|
3295
3297
|
"importPath": "@godxjp/ui/data-display",
|
|
3296
3298
|
"name": "DataTable",
|
|
@@ -3374,13 +3376,13 @@
|
|
|
3374
3376
|
},
|
|
3375
3377
|
{
|
|
3376
3378
|
"defaultValue": "'default'",
|
|
3377
|
-
"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.",
|
|
3378
3380
|
"name": "preset",
|
|
3379
|
-
"type": "'default' | 'action-collection'"
|
|
3381
|
+
"type": "'default' | 'action-collection' | 'stacked-record-collection'"
|
|
3380
3382
|
},
|
|
3381
3383
|
{
|
|
3382
3384
|
"defaultValue": "'sm'",
|
|
3383
|
-
"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'.",
|
|
3384
3386
|
"name": "collapseBelow",
|
|
3385
3387
|
"type": "'sm' | 'md' | 'lg' | 'xl'"
|
|
3386
3388
|
},
|
|
@@ -4232,7 +4234,7 @@
|
|
|
4232
4234
|
]
|
|
4233
4235
|
},
|
|
4234
4236
|
{
|
|
4235
|
-
"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>",
|
|
4236
4238
|
"group": "data-display",
|
|
4237
4239
|
"importPath": "@godxjp/ui/data-display",
|
|
4238
4240
|
"name": "ListRow",
|
|
@@ -5046,15 +5048,20 @@
|
|
|
5046
5048
|
"name": "striped",
|
|
5047
5049
|
"type": "boolean"
|
|
5048
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
|
+
},
|
|
5049
5056
|
{
|
|
5050
5057
|
"defaultValue": "\"default\"",
|
|
5051
|
-
"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.",
|
|
5052
5059
|
"name": "preset",
|
|
5053
|
-
"type": "\"default\" | \"action-collection\""
|
|
5060
|
+
"type": "\"default\" | \"action-collection\" | \"stacked-record-collection\""
|
|
5054
5061
|
},
|
|
5055
5062
|
{
|
|
5056
5063
|
"defaultValue": "\"sm\"",
|
|
5057
|
-
"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\".",
|
|
5058
5065
|
"name": "collapseBelow",
|
|
5059
5066
|
"type": "\"sm\" | \"md\" | \"lg\" | \"xl\""
|
|
5060
5067
|
},
|
|
@@ -5101,7 +5108,7 @@
|
|
|
5101
5108
|
]
|
|
5102
5109
|
},
|
|
5103
5110
|
{
|
|
5104
|
-
"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>",
|
|
5105
5112
|
"group": "data-display",
|
|
5106
5113
|
"importPath": "@godxjp/ui/query",
|
|
5107
5114
|
"name": "DataState",
|
|
@@ -5180,7 +5187,7 @@
|
|
|
5180
5187
|
]
|
|
5181
5188
|
},
|
|
5182
5189
|
{
|
|
5183
|
-
"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>",
|
|
5184
5191
|
"group": "data-display",
|
|
5185
5192
|
"importPath": "@godxjp/ui/query",
|
|
5186
5193
|
"name": "InfiniteQueryState",
|
|
@@ -5325,7 +5332,7 @@
|
|
|
5325
5332
|
]
|
|
5326
5333
|
},
|
|
5327
5334
|
{
|
|
5328
|
-
"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>",
|
|
5329
5336
|
"group": "data-entry",
|
|
5330
5337
|
"importPath": "@godxjp/ui/data-entry",
|
|
5331
5338
|
"name": "FormField",
|
|
@@ -5513,7 +5520,7 @@
|
|
|
5513
5520
|
]
|
|
5514
5521
|
},
|
|
5515
5522
|
{
|
|
5516
|
-
"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)} />",
|
|
5517
5524
|
"group": "data-entry",
|
|
5518
5525
|
"importPath": "@godxjp/ui/data-entry",
|
|
5519
5526
|
"name": "Input",
|
|
@@ -6268,6 +6275,7 @@
|
|
|
6268
6275
|
"usage": [
|
|
6269
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.",
|
|
6270
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.",
|
|
6271
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.",
|
|
6272
6280
|
"DO use loadOptions + selectedLabel together for async selects: selectedLabel prevents a flash of the raw id string while the first page loads.",
|
|
6273
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.",
|
|
@@ -8372,9 +8380,58 @@
|
|
|
8372
8380
|
]
|
|
8373
8381
|
},
|
|
8374
8382
|
{
|
|
8375
|
-
"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>",
|
|
8376
8384
|
"group": "providers",
|
|
8377
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",
|
|
8378
8435
|
"name": "formatDate",
|
|
8379
8436
|
"props": [
|
|
8380
8437
|
{
|
|
@@ -8618,7 +8675,7 @@
|
|
|
8618
8675
|
]
|
|
8619
8676
|
},
|
|
8620
8677
|
{
|
|
8621
|
-
"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`}",
|
|
8622
8679
|
"group": "data-entry",
|
|
8623
8680
|
"importPath": "@godxjp/ui/data-entry",
|
|
8624
8681
|
"name": "Cascader",
|
|
@@ -8832,7 +8889,7 @@
|
|
|
8832
8889
|
]
|
|
8833
8890
|
},
|
|
8834
8891
|
{
|
|
8835
|
-
"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}",
|
|
8836
8893
|
"group": "data-entry",
|
|
8837
8894
|
"importPath": "@godxjp/ui/data-entry",
|
|
8838
8895
|
"name": "TreeSelect",
|
|
@@ -9064,7 +9121,7 @@
|
|
|
9064
9121
|
]
|
|
9065
9122
|
},
|
|
9066
9123
|
{
|
|
9067
|
-
"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}",
|
|
9068
9125
|
"group": "data-entry",
|
|
9069
9126
|
"importPath": "@godxjp/ui/data-entry",
|
|
9070
9127
|
"name": "Transfer",
|
|
@@ -9436,7 +9493,7 @@
|
|
|
9436
9493
|
]
|
|
9437
9494
|
},
|
|
9438
9495
|
{
|
|
9439
|
-
"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}`}",
|
|
9440
9497
|
"group": "data-entry",
|
|
9441
9498
|
"importPath": "@godxjp/ui/data-entry",
|
|
9442
9499
|
"name": "UploadCropDialog",
|
|
@@ -12028,7 +12085,7 @@
|
|
|
12028
12085
|
]
|
|
12029
12086
|
},
|
|
12030
12087
|
{
|
|
12031
|
-
"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}",
|
|
12032
12089
|
"group": "data-entry",
|
|
12033
12090
|
"importPath": "@godxjp/ui/data-entry",
|
|
12034
12091
|
"name": "PasswordStrength",
|
|
@@ -12866,7 +12923,7 @@
|
|
|
12866
12923
|
"type": "ChartDatum[]"
|
|
12867
12924
|
},
|
|
12868
12925
|
{
|
|
12869
|
-
"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.",
|
|
12870
12927
|
"name": "series",
|
|
12871
12928
|
"required": true,
|
|
12872
12929
|
"type": "ChartSeriesProp[]"
|
|
@@ -12922,12 +12979,28 @@
|
|
|
12922
12979
|
"name": "numberFormat",
|
|
12923
12980
|
"type": "Intl.NumberFormatOptions"
|
|
12924
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
|
+
},
|
|
12925
12992
|
{
|
|
12926
12993
|
"defaultValue": "false",
|
|
12927
12994
|
"description": "Render smooth (monotone) lines instead of straight segments.",
|
|
12928
12995
|
"name": "curved",
|
|
12929
12996
|
"type": "boolean"
|
|
12930
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
|
+
},
|
|
12931
13004
|
{
|
|
12932
13005
|
"description": "Message shown when `data` is empty (defaults to a localized 'no data').",
|
|
12933
13006
|
"name": "emptyMessage",
|
|
@@ -12948,7 +13021,9 @@
|
|
|
12948
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.",
|
|
12949
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).",
|
|
12950
13023
|
"DO pre-translate each series' `label`; pass `numberFormat` (e.g. { style: 'currency', currency: 'JPY' }) and the axis/tooltip numbers localize automatically via Intl.",
|
|
12951
|
-
"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."
|
|
12952
13027
|
],
|
|
12953
13028
|
"useCases": [
|
|
12954
13029
|
"Revenue / KPI trend over months in a dashboard.",
|
|
@@ -12969,7 +13044,7 @@
|
|
|
12969
13044
|
"type": "ChartDatum[]"
|
|
12970
13045
|
},
|
|
12971
13046
|
{
|
|
12972
|
-
"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`.",
|
|
12973
13048
|
"name": "series",
|
|
12974
13049
|
"required": true,
|
|
12975
13050
|
"type": "ChartSeriesProp[]"
|
|
@@ -13025,6 +13100,16 @@
|
|
|
13025
13100
|
"name": "numberFormat",
|
|
13026
13101
|
"type": "Intl.NumberFormatOptions"
|
|
13027
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
|
+
},
|
|
13028
13113
|
{
|
|
13029
13114
|
"defaultValue": "false",
|
|
13030
13115
|
"description": "Stack series into one bar instead of grouping side by side.",
|
|
@@ -13065,7 +13150,7 @@
|
|
|
13065
13150
|
]
|
|
13066
13151
|
},
|
|
13067
13152
|
{
|
|
13068
|
-
"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/>",
|
|
13069
13154
|
"group": "data-display",
|
|
13070
13155
|
"importPath": "@godxjp/ui/charts/compact-bar-trend",
|
|
13071
13156
|
"name": "CompactBarTrend",
|
|
@@ -13240,12 +13325,28 @@
|
|
|
13240
13325
|
"name": "stacked",
|
|
13241
13326
|
"type": "boolean"
|
|
13242
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
|
+
},
|
|
13243
13338
|
{
|
|
13244
13339
|
"defaultValue": "false",
|
|
13245
13340
|
"description": "Render smooth (monotone) areas instead of straight segments.",
|
|
13246
13341
|
"name": "curved",
|
|
13247
13342
|
"type": "boolean"
|
|
13248
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
|
+
},
|
|
13249
13350
|
{
|
|
13250
13351
|
"description": "Message shown when `data` is empty.",
|
|
13251
13352
|
"name": "emptyMessage",
|
|
@@ -13263,7 +13364,9 @@
|
|
|
13263
13364
|
"DO import from the charts entry: `import { AreaChart } from \"@godxjp/ui/charts\";` (recharts optional peer required).",
|
|
13264
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.",
|
|
13265
13366
|
"DO use `stacked` to show how parts accumulate into a total over time.",
|
|
13266
|
-
"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."
|
|
13267
13370
|
],
|
|
13268
13371
|
"useCases": [
|
|
13269
13372
|
"Cumulative volume over time (e.g. total transactions per day).",
|
|
@@ -14096,7 +14199,7 @@
|
|
|
14096
14199
|
},
|
|
14097
14200
|
{
|
|
14098
14201
|
"docPath": "data-display/permission-matrix.tsx",
|
|
14099
|
-
"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>",
|
|
14100
14203
|
"group": "data-display",
|
|
14101
14204
|
"importPath": "@godxjp/ui/data-display",
|
|
14102
14205
|
"name": "PermissionMatrix",
|
|
@@ -15393,7 +15496,7 @@
|
|
|
15393
15496
|
},
|
|
15394
15497
|
{
|
|
15395
15498
|
"docPath": "layout/masonry.tsx",
|
|
15396
|
-
"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/>",
|
|
15397
15500
|
"group": "layout",
|
|
15398
15501
|
"importPath": "@godxjp/ui/layout",
|
|
15399
15502
|
"name": "Masonry",
|
|
@@ -15599,7 +15702,12 @@
|
|
|
15599
15702
|
"type": "number"
|
|
15600
15703
|
},
|
|
15601
15704
|
{
|
|
15602
|
-
"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.",
|
|
15603
15711
|
"name": "getContainer",
|
|
15604
15712
|
"type": "() => HTMLElement | Window"
|
|
15605
15713
|
},
|
package/agent/index.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"counts": {
|
|
3
3
|
"anti-ai-tells": 26,
|
|
4
|
-
"components":
|
|
5
|
-
"patterns":
|
|
6
|
-
"rules":
|
|
7
|
-
"tokens":
|
|
4
|
+
"components": 171,
|
|
5
|
+
"patterns": 20,
|
|
6
|
+
"rules": 50,
|
|
7
|
+
"tokens": 2070,
|
|
8
8
|
"vocabulary": 14
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"file": "components-index.json",
|
|
13
|
-
"note": "45 KB — name + group + tagline for all
|
|
13
|
+
"note": "45 KB — name + group + tagline for all 171. FETCH THIS FIRST, then fetch only the components you chose.",
|
|
14
14
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"file": "components/<Name>.json",
|
|
18
|
-
"note": "One file per component (1 KB–
|
|
18
|
+
"note": "One file per component (1 KB–34 KB, median 6 KB), each carrying its importPath. This is the selective route: read the index, then fetch only what you need instead of the 1.2 MB blob.",
|
|
19
19
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/<Name>.json"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
@@ -48,9 +48,19 @@
|
|
|
48
48
|
"note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
|
|
49
49
|
"read": {
|
|
50
50
|
"live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
|
|
51
|
-
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/
|
|
51
|
+
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v30.0.0/agent/index.json"
|
|
52
52
|
},
|
|
53
|
-
"source": "mcp/src/data — the same data @godxjp/ui-mcp serves",
|
|
53
|
+
"source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
|
|
54
54
|
"start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
|
|
55
|
-
"
|
|
55
|
+
"tokenTiers": {
|
|
56
|
+
"component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
|
|
57
|
+
"counts": {
|
|
58
|
+
"component": 1756,
|
|
59
|
+
"foundation": 211,
|
|
60
|
+
"semantic": 103
|
|
61
|
+
},
|
|
62
|
+
"foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
|
|
63
|
+
"semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
|
|
64
|
+
},
|
|
65
|
+
"version": "30.0.0"
|
|
56
66
|
}
|