@godxjp/ui 28.9.0 → 28.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/START-HERE.md +193 -0
- package/agent/anti-ai-tells.json +158 -0
- package/agent/components/Accordion.json +60 -0
- package/agent/components/AccountChip.json +59 -0
- package/agent/components/Actions.json +78 -0
- package/agent/components/Activity.json +80 -0
- package/agent/components/Affix.json +78 -0
- package/agent/components/Alert.json +65 -0
- package/agent/components/AlertDialog.json +109 -0
- package/agent/components/AlertDialogRoot.json +52 -0
- package/agent/components/Anchor.json +119 -0
- package/agent/components/AppLauncher.json +95 -0
- package/agent/components/AppProvider.json +105 -0
- package/agent/components/AppSettingPicker.json +88 -0
- package/agent/components/AppSettingToggle.json +70 -0
- package/agent/components/AppShell.json +159 -0
- package/agent/components/AreaChart.json +105 -0
- package/agent/components/AspectRatio.json +38 -0
- package/agent/components/Attachments.json +76 -0
- package/agent/components/AuthAccountSummary.json +64 -0
- package/agent/components/AuthDivider.json +35 -0
- package/agent/components/AuthFooter.json +48 -0
- package/agent/components/AuthIdentity.json +42 -0
- package/agent/components/AuthShell.json +108 -0
- package/agent/components/AuthStack.json +21 -0
- package/agent/components/Avatar.json +89 -0
- package/agent/components/Badge.json +96 -0
- package/agent/components/Banner.json +48 -0
- package/agent/components/BarChart.json +108 -0
- package/agent/components/BranchScopePicker.json +89 -0
- package/agent/components/Breadcrumb.json +54 -0
- package/agent/components/Button.json +133 -0
- package/agent/components/Calendar.json +259 -0
- package/agent/components/Callout.json +46 -0
- package/agent/components/Card.json +112 -0
- package/agent/components/CardBar.json +49 -0
- package/agent/components/CardContent.json +51 -0
- package/agent/components/Carousel.json +50 -0
- package/agent/components/Cascader.json +209 -0
- package/agent/components/CenteredShell.json +66 -0
- package/agent/components/ChatBubble.json +100 -0
- package/agent/components/ChatBubbleList.json +64 -0
- package/agent/components/ChatComposer.json +160 -0
- package/agent/components/ChatSuggestion.json +86 -0
- package/agent/components/Checkbox.json +68 -0
- package/agent/components/CheckboxGroup.json +96 -0
- package/agent/components/CodeBlock.json +64 -0
- package/agent/components/Collapsible.json +74 -0
- package/agent/components/ColorPicker.json +87 -0
- package/agent/components/Command.json +168 -0
- package/agent/components/CommandPalette.json +84 -0
- package/agent/components/CompactBarTrend.json +101 -0
- package/agent/components/Conversations.json +82 -0
- package/agent/components/CredentialReveal.json +93 -0
- package/agent/components/DataState.json +79 -0
- package/agent/components/DataTable.json +268 -0
- package/agent/components/DatePicker.json +275 -0
- package/agent/components/Descriptions.json +67 -0
- package/agent/components/Dialog.json +78 -0
- package/agent/components/DraggablePanel.json +106 -0
- package/agent/components/DropdownMenu.json +102 -0
- package/agent/components/EmptyState.json +83 -0
- package/agent/components/ErrorSurface.json +128 -0
- package/agent/components/FeatureList.json +43 -0
- package/agent/components/Field.json +64 -0
- package/agent/components/FilterBar.json +99 -0
- package/agent/components/Flex.json +153 -0
- package/agent/components/FloatButton.json +91 -0
- package/agent/components/Form.json +87 -0
- package/agent/components/FormErrors.json +51 -0
- package/agent/components/FormField.json +137 -0
- package/agent/components/FormFieldArray.json +39 -0
- package/agent/components/FormFieldControl.json +129 -0
- package/agent/components/FormRoot.json +122 -0
- package/agent/components/Heading.json +61 -0
- package/agent/components/HoverCard.json +55 -0
- package/agent/components/Icon.json +60 -0
- package/agent/components/InfiniteQueryState.json +58 -0
- package/agent/components/Input.json +122 -0
- package/agent/components/InputOTP.json +106 -0
- package/agent/components/Label.json +43 -0
- package/agent/components/LegalDocumentShell.json +102 -0
- package/agent/components/Legend.json +42 -0
- package/agent/components/LineChart.json +103 -0
- package/agent/components/Link.json +41 -0
- package/agent/components/ListRow.json +92 -0
- package/agent/components/Logo.json +85 -0
- package/agent/components/Marquee.json +91 -0
- package/agent/components/Masonry.json +82 -0
- package/agent/components/MasterDetail.json +95 -0
- package/agent/components/MegaMenu.json +120 -0
- package/agent/components/MobileShell.json +73 -0
- package/agent/components/NavList.json +63 -0
- package/agent/components/NumberInput.json +158 -0
- package/agent/components/OrgSwitcher.json +89 -0
- package/agent/components/OverlayPortalProvider.json +42 -0
- package/agent/components/PageContainer.json +181 -0
- package/agent/components/Pagination.json +132 -0
- package/agent/components/Paragraph.json +40 -0
- package/agent/components/PasswordInput.json +79 -0
- package/agent/components/PasswordStrength.json +51 -0
- package/agent/components/PermissionMatrix.json +81 -0
- package/agent/components/PieChart.json +99 -0
- package/agent/components/Popover.json +110 -0
- package/agent/components/PrefetchLink.json +65 -0
- package/agent/components/Progress.json +79 -0
- package/agent/components/Prose.json +57 -0
- package/agent/components/QrCode.json +62 -0
- package/agent/components/Radio.json +98 -0
- package/agent/components/RadioGroup.json +91 -0
- package/agent/components/RangeTimeline.json +80 -0
- package/agent/components/Rating.json +92 -0
- package/agent/components/ResizablePanel.json +69 -0
- package/agent/components/ResponsiveGrid.json +77 -0
- package/agent/components/Reveal.json +70 -0
- package/agent/components/ScrollArea.json +104 -0
- package/agent/components/SearchInput.json +98 -0
- package/agent/components/Segmented.json +96 -0
- package/agent/components/Select.json +397 -0
- package/agent/components/Separator.json +86 -0
- package/agent/components/ServiceCatalogCta.json +46 -0
- package/agent/components/ServiceLauncherCard.json +86 -0
- package/agent/components/ServiceRolePanel.json +83 -0
- package/agent/components/Sheet.json +85 -0
- package/agent/components/Sidebar.json +118 -0
- package/agent/components/Skeleton.json +57 -0
- package/agent/components/SkeletonArticle.json +71 -0
- package/agent/components/SkeletonAvatar.json +50 -0
- package/agent/components/SkeletonButton.json +57 -0
- package/agent/components/SkeletonForm.json +52 -0
- package/agent/components/SkeletonImage.json +37 -0
- package/agent/components/SkeletonInput.json +51 -0
- package/agent/components/SkeletonNode.json +42 -0
- package/agent/components/SkeletonRows.json +49 -0
- package/agent/components/SkeletonTable.json +45 -0
- package/agent/components/Slider.json +160 -0
- package/agent/components/SplitPane.json +66 -0
- package/agent/components/StatCard.json +83 -0
- package/agent/components/Steps.json +95 -0
- package/agent/components/Swatch.json +41 -0
- package/agent/components/Switch.json +81 -0
- package/agent/components/Table.json +112 -0
- package/agent/components/Tabs.json +158 -0
- package/agent/components/TagInput.json +105 -0
- package/agent/components/Text.json +201 -0
- package/agent/components/Textarea.json +126 -0
- package/agent/components/ThoughtChain.json +76 -0
- package/agent/components/Thumbnail.json +70 -0
- package/agent/components/TimePicker.json +200 -0
- package/agent/components/TimeRangePicker.json +90 -0
- package/agent/components/Timeline.json +47 -0
- package/agent/components/TimelineGrid.json +92 -0
- package/agent/components/Title.json +67 -0
- package/agent/components/Toaster.json +42 -0
- package/agent/components/Toggle.json +90 -0
- package/agent/components/ToggleGroup.json +102 -0
- package/agent/components/Toolbar.json +120 -0
- package/agent/components/Tooltip.json +110 -0
- package/agent/components/Topbar.json +83 -0
- package/agent/components/TopbarItem.json +79 -0
- package/agent/components/Transfer.json +141 -0
- package/agent/components/Tree.json +185 -0
- package/agent/components/TreeSelect.json +232 -0
- package/agent/components/TwoFactorSetup.json +79 -0
- package/agent/components/Typography.json +42 -0
- package/agent/components/Upload.json +221 -0
- package/agent/components/UploadCropDialog.json +60 -0
- package/agent/components/VisuallyHidden.json +20 -0
- package/agent/components/Welcome.json +65 -0
- package/agent/components/formatDate.json +46 -0
- package/agent/components/inertiaUpload.json +32 -0
- package/agent/components/useZodForm.json +39 -0
- package/agent/components-index.json +884 -0
- package/agent/components.json +15507 -0
- package/agent/index.json +56 -0
- package/agent/llms.txt +32 -0
- package/agent/patterns/account-recovery-settings.json +19 -0
- package/agent/patterns/async-data-state.json +20 -0
- package/agent/patterns/auth-recovery-panels.json +29 -0
- package/agent/patterns/badge-coloring.json +14 -0
- package/agent/patterns/common-fixes.json +16 -0
- package/agent/patterns/confirm-destructive.json +11 -0
- package/agent/patterns/data-table-page.json +18 -0
- package/agent/patterns/deferred-loading.json +12 -0
- package/agent/patterns/error-pages.json +28 -0
- package/agent/patterns/inertia-detail-page.json +13 -0
- package/agent/patterns/inertia-list-page.json +15 -0
- package/agent/patterns/inertia-persistent-layout.json +14 -0
- package/agent/patterns/organization-memberships.json +19 -0
- package/agent/patterns/page-sections.json +18 -0
- package/agent/patterns/settings-page-responsive.json +18 -0
- package/agent/patterns/settings-section-rows.json +23 -0
- package/agent/patterns/signup-form.json +13 -0
- package/agent/patterns/topbar-account-chip.json +18 -0
- package/agent/patterns/transactional-email.json +22 -0
- package/agent/patterns-index.json +323 -0
- package/agent/patterns.json +342 -0
- package/agent/rules.json +237 -0
- package/agent/tokens.json +8422 -0
- package/agent/vocabulary.json +198 -0
- package/dist/components/data-entry/input.js +8 -1
- package/dist/components/layout/flex.d.ts +2 -2
- package/dist/components/layout/flex.js +2 -0
- package/dist/components/ui/tag-input.d.ts +10 -0
- package/dist/components/ui/tag-input.js +35 -2
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +23 -1
- package/dist/i18n/messages/ja.json +21 -1
- package/dist/i18n/messages/vi.json +21 -1
- package/dist/props/components/data-entry.prop.d.ts +21 -2
- package/dist/props/components/layout.prop.d.ts +42 -0
- package/dist/props/registry.d.ts +9 -0
- package/dist/props/registry.js +6 -0
- package/dist/styles/card-layout.css +4 -4
- package/dist/styles/control.css +31 -2
- package/dist/styles/data-display-layout.css +1 -1
- package/dist/styles/layout.css +17 -0
- package/dist/styles/navigation-layout.css +3 -1
- package/dist/styles/shell-layout.css +2 -0
- package/dist/styles/table-layout.css +50 -9
- package/dist/tokens/components/segmented.css +5 -1
- package/dist/tokens/components/table.css +2 -1
- package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
- package/docs/DESIGN-AUTHORITY.md +14 -0
- package/docs/DEVELOPMENT.md +81 -6
- package/docs/data-entry/tag-input.tsx +37 -0
- package/docs/layout/flex.tsx +40 -0
- package/docs/roadmap/website-components.md +34 -0
- package/docs/showcase/marketing-page.tsx +54 -45
- package/docs/showcase/table-pagination.tsx +88 -18
- package/docs/showcase/theme-customization.tsx +25 -2
- package/package.json +6 -4
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Flex, MobileShell } from \"@godxjp/ui/layout\";\nimport { Button, Heading, Text } from \"@godxjp/ui/general\";\nimport { Inbox, Package, ScanLine, Truck } from \"lucide-react\";\n\nexport function HandyInbound() {\n return (\n <MobileShell\n statusBar={<Text size=\"sm\" tabular>9:41</Text>}\n header={\n <Flex align=\"center\" justify=\"between\" gap=\"xs\" className=\"w-full\">\n <Heading level={3} as=\"h1\">入庫</Heading>\n <Button variant=\"ghost\" size=\"sm\">選択</Button>\n </Flex>\n }\n actions={\n <Button className=\"flex-[2]\">\n <ScanLine aria-hidden=\"true\" />\n スキャン\n </Button>\n }\n tabBar={\n <>\n <Button variant=\"ghost\" aria-current=\"page\" className=\"h-full flex-col rounded-none\">\n <Inbox aria-hidden=\"true\" />\n <Text size=\"2xs\">入庫</Text>\n </Button>\n <Button variant=\"ghost\" className=\"h-full flex-col rounded-none\">\n <Package aria-hidden=\"true\" />\n <Text size=\"2xs\">梱包</Text>\n </Button>\n <Button variant=\"ghost\" className=\"h-full flex-col rounded-none\">\n <Truck aria-hidden=\"true\" />\n <Text size=\"2xs\">出庫</Text>\n </Button>\n </>\n }\n >\n <Flex direction=\"col\" gap=\"sm\">\n <Text>洗顔フォーム</Text>\n <Text>日焼け止め</Text>\n </Flex>\n </MobileShell>\n );\n}",
|
|
3
|
+
"group": "layout",
|
|
4
|
+
"importPath": "@godxjp/ui/layout",
|
|
5
|
+
"name": "MobileShell",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "The scrolling screen body — the ONLY scroll container in the shell and its only elastic band. Everything else is fixed chrome, so a long list scrolls under a stationary app bar and tab bar instead of taking them off screen with it.",
|
|
9
|
+
"name": "children",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "ReactNode"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "App bar (banner) pinned to the top: the screen title plus its inline actions. Absorbs the top safe-area inset when there is no statusBar. Omit → no banner. For a screen MODE (multi-select, search, edit) swap the whole node rather than stacking a second strip under it — replacing the bar's contents is the platform pattern on iOS and Android alike.",
|
|
15
|
+
"name": "header",
|
|
16
|
+
"type": "ReactNode"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "The band that sits IN the OS status-bar strip above the app bar — the carrier/clock row of a display-mode:standalone PWA, or the simulated one in a device-frame preview. It owns the top safe-area inset when present. Omit it in an ordinary browser tab, where the OS already paints that strip and the header takes the inset instead.",
|
|
20
|
+
"name": "statusBar",
|
|
21
|
+
"type": "ReactNode"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "The sticky action bar pinned above the tab bar — the screen's primary verb (Scan, Save, Hand over) and at most one secondary. It sits OUTSIDE the scroll region, so it is always reachable with no `position: sticky` and no scroll-padding hack, and it takes the home-indicator inset whenever no tabBar follows it.",
|
|
25
|
+
"name": "actions",
|
|
26
|
+
"type": "ReactNode"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"description": "Bottom tab bar (navigation) — the app's top-level destinations. Children TILE: equal width, no seam, no page gutter, so you never hand-roll a grid with a column count. Always the last band, so it owns the home-indicator inset.",
|
|
30
|
+
"name": "tabBar",
|
|
31
|
+
"type": "ReactNode"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"defaultValue": "\"viewport\"",
|
|
35
|
+
"description": "Where the shell's one-screen height comes from. \"viewport\" (default) is the real app: exactly 100dvh, so the DOCUMENT never scrolls and the tab bar cannot slide away under a collapsing URL bar. \"fill\" fills a BOUNDED parent instead — a device-frame preview, or a phone view embedded in a wider page — where a viewport-tall root would overflow its frame. Nothing else differs between the two.",
|
|
36
|
+
"name": "height",
|
|
37
|
+
"type": "\"viewport\" | \"fill\""
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"defaultValue": "\"fill\"",
|
|
41
|
+
"description": "How wide the shell is allowed to get. \"fill\" (default) takes the whole inline size it is given — a real handheld, where that IS the phone. \"phone\" caps it at --mobile-shell-max-inline-size (430px, the widest current handheld logical width) and centres the column: the SAME situation height=\"fill\" already names on the other axis, a handheld screen rendered on a viewport wider than a handheld. Measured at 1280px before this axis existed: max-inline-size none, shell 1232px wide, a four-destination tab bar spread across the whole screen.",
|
|
42
|
+
"name": "width",
|
|
43
|
+
"type": "\"fill\" | \"phone\""
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"related": [
|
|
47
|
+
"AppShell — the authenticated shell WITH a sidebar rail and a mobile drawer at the 900px step. Use it for an admin console that happens to be viewed on a phone; use MobileShell when the phone IS the product.",
|
|
48
|
+
"CenteredShell — the authenticated no-sidebar shell whose main scrolls the PAGE. MobileShell is its handheld counterpart: same 'no rail' shape, opposite scroll contract.",
|
|
49
|
+
"AuthShell — the unauthenticated root. A login screen inside a handheld app still belongs to AuthShell, not MobileShell.",
|
|
50
|
+
"Sheet — `side=\"bottom\"` is the handheld modal: scanners, pickers and forms open from the bottom over MobileShell rather than navigating away."
|
|
51
|
+
],
|
|
52
|
+
"rules": [
|
|
53
|
+
23,
|
|
54
|
+
24,
|
|
55
|
+
45
|
|
56
|
+
],
|
|
57
|
+
"storyPath": "layout/MobileShell.stories.tsx",
|
|
58
|
+
"tagline": "Handheld app shell (gh#354) — status band · app bar · the ONE scroll region · sticky action bar · bottom tab bar, with device safe-area insets and a document that never scrolls.",
|
|
59
|
+
"usage": [
|
|
60
|
+
"DO use MobileShell for a HANDHELD app screen — a warehouse/handy terminal, a driver app, a field-work PWA. It is the fourth root shell: AppShell (needs a sidebar) · AuthShell (unauthenticated card) · CenteredShell (authenticated scrolling document) · MobileShell (a phone app that does NOT scroll its document).",
|
|
61
|
+
"DO let `children` be the only thing that scrolls. Put the primary verb in `actions` and navigation in `tabBar` — both sit outside the scroll region, so neither needs `position: sticky`, a z-index, or bottom padding to clear the other.",
|
|
62
|
+
"DO NOT compose one out of <Card> + `ui-card-inset*` + `overflow-y-auto` (what docs/showcase/case6 did before gh#354). That reproduces the look and neither behaviour that matters on a device: the document still scrolls, and nothing pads out of env(safe-area-inset-*), so the notch covers the app bar and the home indicator covers the primary button.",
|
|
63
|
+
"DO swap the `header` node for a screen MODE (select mode, search mode) instead of stacking a second contextual strip below it — one bar to read, and the platform pattern on both iOS and Android.",
|
|
64
|
+
"DO use `height=\"fill\"` ONLY when the shell is inside a bounded parent (a device-frame preview). In a real app leave it at \"viewport\": that is what keeps the document from scrolling.",
|
|
65
|
+
"DO NOT nest MobileShell inside AppShell / AuthShell / CenteredShell (or the reverse) — it is a ROOT shell. Retune the page gutter and the three band heights from the theme (--mobile-shell-inset-inline, --mobile-shell-header-bar-height, --mobile-shell-tab-bar-height, --mobile-shell-status-bar-height); never fork .ui-mobile-shell-* CSS."
|
|
66
|
+
],
|
|
67
|
+
"useCases": [
|
|
68
|
+
"Warehouse handheld (代理店ハンディ): a status band, an app bar with a select-mode text action, a scrolling item list, a dominant Scan button in `actions`, and a three-destination `tabBar` (inbound · packing · outbound). See the case6-agency-handy showcase.",
|
|
69
|
+
"Driver / delivery app: route list in the scroll region, 'Arrived' as the single `actions` verb, tabs for today · history · profile.",
|
|
70
|
+
"Field-inspection PWA installed to the home screen: `statusBar` paints the standalone strip, `header` carries the site name, and the form scrolls under both.",
|
|
71
|
+
"A phone view embedded in a desktop device-frame preview: the same composition with `height=\"fill\"` inside a fixed-size frame."
|
|
72
|
+
]
|
|
73
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "<NavList\n label=\"Settings\"\n activeId={route}\n linkComponent={Link}\n items={[\n // A GROUP: one NavList, one <nav>, however many groups (gh#815).\n {\n id: \"account\",\n label: \"Account\",\n icon: User,\n children: [\n { id: \"profile\", label: \"Profile\", href: \"/settings/profile\" },\n { id: \"organization\", label: \"Organization\", href: \"/settings/organization\" },\n ],\n },\n // A LEAF, at the same level. `icon` takes the COMPONENT, never an element, and is optional.\n { id: \"appearance\", label: \"Appearance\", icon: Palette, href: \"/settings/appearance\" },\n ]}\n/>",
|
|
3
|
+
"group": "layout",
|
|
4
|
+
"importPath": "@godxjp/ui/layout",
|
|
5
|
+
"name": "NavList",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Rows in reading order. Same shape as the rail's items, deliberately: one item vocabulary for both navigations. An item carrying `children` renders as a collapsible GROUP — the rail's own `.sb-nav-group`, opened by the route whenever a descendant is active (gh#815). `icon` is optional; a row without one keeps an empty 16px slot so the label column still aligns.",
|
|
9
|
+
"name": "items",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "SidebarItemProp[]"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "`id` of the current route. That row gets the active tokens and aria-current=\"page\".",
|
|
15
|
+
"name": "activeId",
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "Accessible name for the <nav> landmark. Required because a page routinely holds more than one navigation (breadcrumb, rail, this one).",
|
|
20
|
+
"name": "label",
|
|
21
|
+
"required": true,
|
|
22
|
+
"type": "string"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "Router link component — identical contract to Sidebar.linkComponent. The library composes the row and passes it as children; the component only renders the <a>.",
|
|
26
|
+
"name": "linkComponent",
|
|
27
|
+
"type": "SidebarLinkComponentProp"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"description": "Reports the activated row's id, for consumers driving navigation themselves.",
|
|
31
|
+
"name": "onSelect",
|
|
32
|
+
"type": "(id: string) => void"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"related": [
|
|
36
|
+
"Sidebar",
|
|
37
|
+
"MasterDetail",
|
|
38
|
+
"PageContainer"
|
|
39
|
+
],
|
|
40
|
+
"rules": [
|
|
41
|
+
2,
|
|
42
|
+
3,
|
|
43
|
+
5,
|
|
44
|
+
6
|
|
45
|
+
],
|
|
46
|
+
"storyPath": "layout/NavList.stories.tsx",
|
|
47
|
+
"tagline": "Vertical route navigation for INSIDE a page — the shape a settings nav takes. Renders the same `.sb-nav-item` rows as the Sidebar rail (icon column, label, badge, active tokens, `aria-current=\"page\"`) in a `<nav>` landmark, without the AppShell grid the rail depends on.",
|
|
48
|
+
"usage": [
|
|
49
|
+
"DO use NavList for a settings / account / preferences sub-navigation beside the pane it drives — typically as MasterDetail's `master`.",
|
|
50
|
+
"DO group with `item.children` — one NavList, one `<nav>` landmark, however many groups. Before gh#815 NavList dropped `children` silently (it type-checked, rendered one flat row, and the subtree vanished), so the workaround was one NavList per group wrapped in SidebarSection, i.e. N landmarks for one navigation. Don't write that any more.",
|
|
51
|
+
"DON'T repeat a group's icons on its children: a nested row draws the rail's dot marker instead of the 16px icon column, exactly like the rail's submenu. Icons belong on the rows that sit at the top level.",
|
|
52
|
+
"DON'T reach for Sidebar here: it is the app's primary rail and lays into AppShell's grid area, so nested in a page it has no grid to lay into.",
|
|
53
|
+
"DON'T hand-roll the rows out of Buttons with the current one encoded as a variant swap. That loses aria-current, loses the icon column the labels align to, and invents a different nav in every app.",
|
|
54
|
+
"DON'T use Tabs: each entry here is a separate ROUTE the router renders, not a panel this component owns. Tabs would mean faking tab state from the URL and never rendering a TabsContent.",
|
|
55
|
+
"DO pass `label` — the <nav> landmark needs a name to be distinguishable from the breadcrumb and the rail.",
|
|
56
|
+
"There is no collapsed state by design: a page-level navigation has no rail to collapse into. Only the shell rail collapses. (A GROUP still opens and closes — that is `item.children`, not the rail's collapsed mode.)"
|
|
57
|
+
],
|
|
58
|
+
"useCases": [
|
|
59
|
+
"Settings page / settings nav / preferences nav: Account / Security / Notifications / Billing beside the selected settings form — grouped, in ONE <nav> landmark.",
|
|
60
|
+
"Settings screen: Profile / Appearance / Security beside the selected settings form.",
|
|
61
|
+
"Account area: a vertical route nav inside a PageContainer, driving the detail pane."
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { NumberInput } from \"@godxjp/ui/data-entry\";\n\n<NumberInput\n value={qty}\n onValueChange={setQty}\n min={1}\n max={99}\n step={1}\n prefix=\"¥\"\n aria-label=\"数量\"\n/>",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "NumberInput",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Validation state the field paints — Ant Design `status`. `error` also reports `aria-invalid`, so the red boundary and what a screen reader hears are one fact; `warning` paints only, because a warning is not a validity failure. antd's `success`/`validating` are not implemented: antd only draws them together with its `hasFeedback` icon slot, which FormField owns here.",
|
|
9
|
+
"name": "status",
|
|
10
|
+
"type": "\"error\" | \"warning\""
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"defaultValue": "\"outlined\"",
|
|
14
|
+
"description": "Chrome level — Ant Design `variant`. `outlined` is the historical field; `filled` swaps the boundary for a tinted surface (dense forms); `borderless` drops both, for a field inside a box that already draws one. antd's fourth member `underlined` is deliberately absent — a single bottom rule is a Material convention and SmartHR, the JP authority here, draws every field as a full box.",
|
|
15
|
+
"name": "variant",
|
|
16
|
+
"type": "\"outlined\" | \"filled\" | \"borderless\""
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "antd `formatter` — how the committed number is DISPLAYED at rest (a unit, a separator, 円). Replaces the built-in `Intl.NumberFormat`; pair it with `parser` or the text cannot be read back.",
|
|
20
|
+
"name": "formatter",
|
|
21
|
+
"type": "(value: number | null) => string"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "antd `parser` — the inverse of `formatter`. Without one, the built-in reader NFKC-folds 全角 digits, periods and minus signs to ASCII first, so a value typed on a Japanese keyboard survives blur.",
|
|
25
|
+
"name": "parser",
|
|
26
|
+
"type": "(display: string) => number | null"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"defaultValue": "true",
|
|
30
|
+
"description": "antd `keyboard` — ArrowUp/ArrowDown step the value (Shift = ×10).",
|
|
31
|
+
"name": "keyboard",
|
|
32
|
+
"type": "boolean"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"defaultValue": "false",
|
|
36
|
+
"description": "antd `changeOnWheel` — a mouse wheel steps the value. Off by default and gated on FOCUS even when on, so scrolling past a long form cannot silently edit it.",
|
|
37
|
+
"name": "changeOnWheel",
|
|
38
|
+
"type": "boolean"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"defaultValue": "true",
|
|
42
|
+
"description": "antd `controls` — draw the increment/decrement buttons. `false` keeps the spinbutton role and the arrow keys; it only stops drawing the two buttons.",
|
|
43
|
+
"name": "controls",
|
|
44
|
+
"type": "boolean"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"description": "Controlled value. `null` = empty field. Pair with `onValueChange`.",
|
|
48
|
+
"name": "value",
|
|
49
|
+
"type": "number | null"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"defaultValue": "null",
|
|
53
|
+
"description": "Uncontrolled initial value.",
|
|
54
|
+
"name": "defaultValue",
|
|
55
|
+
"type": "number | null"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"description": "Value change callback (vocabulary triad — NOT onChange). Receives `null` when the field is empty.",
|
|
59
|
+
"name": "onValueChange",
|
|
60
|
+
"type": "(value: number | null) => void"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"description": "Lower bound — clamps on commit and disables the decrement stepper at the floor.",
|
|
64
|
+
"name": "min",
|
|
65
|
+
"type": "number"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"description": "Upper bound — clamps on commit and disables the increment stepper at the ceiling.",
|
|
69
|
+
"name": "max",
|
|
70
|
+
"type": "number"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"defaultValue": "1",
|
|
74
|
+
"description": "Increment for the steppers + ArrowUp/ArrowDown (Shift = ×10).",
|
|
75
|
+
"name": "step",
|
|
76
|
+
"type": "number"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"description": "Committed decimal places. Inferred from `step` when omitted.",
|
|
80
|
+
"name": "precision",
|
|
81
|
+
"type": "number"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"description": "Disables typing and stepping.",
|
|
85
|
+
"name": "disabled",
|
|
86
|
+
"type": "boolean"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"description": "Value is shown and selectable but not typeable or steppable.",
|
|
90
|
+
"name": "readOnly",
|
|
91
|
+
"type": "boolean"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"defaultValue": "\"md\"",
|
|
95
|
+
"description": "Control height tier (--control-height). Aligns with sibling controls on a row.",
|
|
96
|
+
"name": "size",
|
|
97
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"description": "Placeholder shown when empty.",
|
|
101
|
+
"name": "placeholder",
|
|
102
|
+
"type": "string"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"description": "Leading decorative affix inside the field (e.g. `¥`). aria-hidden.",
|
|
106
|
+
"name": "prefix",
|
|
107
|
+
"type": "React.ReactNode"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"description": "Trailing decorative affix inside the field (e.g. `%`). aria-hidden.",
|
|
111
|
+
"name": "suffix",
|
|
112
|
+
"type": "React.ReactNode"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"description": "Form field name — submits its value natively.",
|
|
116
|
+
"name": "name",
|
|
117
|
+
"type": "string"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"description": "Associates with a <label htmlFor> / FormField.",
|
|
121
|
+
"name": "id",
|
|
122
|
+
"type": "string"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"description": "Accessible name for the spinbutton when no visible FormField label is present.",
|
|
126
|
+
"name": "aria-label",
|
|
127
|
+
"type": "string"
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
"related": [
|
|
131
|
+
"Input — the plain single-line field NumberInput composes; use Input directly only for free numeric text with no stepper/clamp need.",
|
|
132
|
+
"Slider — use instead when the user picks an approximate value within a range by dragging; NumberInput is for exact keyed entry.",
|
|
133
|
+
"FormField — compose NumberInput inside FormField (matching id) for label/helper/error a11y wiring.",
|
|
134
|
+
"TimePicker — the HH:mm time sibling; NumberInput is for plain numbers, TimePicker for clock times."
|
|
135
|
+
],
|
|
136
|
+
"rules": [
|
|
137
|
+
3,
|
|
138
|
+
6
|
|
139
|
+
],
|
|
140
|
+
"storyPath": "data-entry/NumberInput.stories.tsx",
|
|
141
|
+
"tagline": "WAI-ARIA spinbutton for localized numeric entry — composes the real Input (role=spinbutton, inputMode=decimal) with stacked increment/decrement step Buttons. Type freely, Arrow/Shift-Arrow step, value commits clamped to min/max + rounded to precision.",
|
|
142
|
+
"usage": [
|
|
143
|
+
"DO use NumberInput (not `<Input type='number'>`) whenever numeric entry wants steppers, min/max clamping, precision rounding, or a ¥/% affix — it is the canonical numeric primitive. Plain Input has no stepper and no clamp.",
|
|
144
|
+
"DO drive it controlled with `value` + `onValueChange` carrying `number | null` (the vocabulary triad — NOT `onChange`). `null` means the field is empty; never substitute 0 for empty.",
|
|
145
|
+
"DON'T pass `value` without `onValueChange` — like every controlled @godxjp/ui input it would freeze. Omit both for uncontrolled (use `defaultValue`).",
|
|
146
|
+
"DO set `step` to your increment and let `precision` (or the decimals of `step`) round the committed value: `step={0.25} precision={2}` gives quarter-step entry rounded to 2 places on blur/Enter.",
|
|
147
|
+
"DO set `min`/`max` for bounded quantities — the value clamps on commit and the matching stepper Button auto-disables at the bound. The steppers are tabIndex=-1 so they never pollute the keyboard tab order (Arrow keys cover keyboard stepping).",
|
|
148
|
+
"DON'T wrap it in a hand-rolled label/error markup — compose it inside FormField (matching `id`) for the aria wiring, exactly like Input.",
|
|
149
|
+
"DON'T format the value yourself for display — NumberInput formats at rest via Intl.NumberFormat in the active locale while keeping the raw value typeable on focus."
|
|
150
|
+
],
|
|
151
|
+
"useCases": [
|
|
152
|
+
"Quantity / line-item steppers in order, invoice, or cart forms (min={1}, step={1}) where ± buttons and a floor are expected.",
|
|
153
|
+
"Price / amount fields with a currency affix (prefix='¥', step={10}) — the affix is decorative and the committed value stays a plain number.",
|
|
154
|
+
"Percentage / rate inputs bounded 0–100 (suffix='%', min={0} max={100}).",
|
|
155
|
+
"Decimal measurements — weight, dimensions, exchange rates (step={0.25}, precision={2}) needing rounded commit.",
|
|
156
|
+
"Any bounded numeric setting (timeouts, retry counts, page sizes) where a slider is too coarse and a free Input lacks clamping."
|
|
157
|
+
]
|
|
158
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { OrgSwitcher } from \"@godxjp/ui/layout\";\nimport { Badge } from \"@godxjp/ui/data-display\";\n\n<OrgSwitcher\n organizations={[\n {\n id: \"dxs\",\n name: \"DXS Holdings\",\n meta: t(\"org.role.owner\"),\n badge: <Badge variant=\"secondary\">{t(\"org.plan.trial\")}</Badge>,\n badgeLabel: t(\"org.plan.trial.sr\"),\n },\n ]}\n value={organizationId}\n onValueChange={setOrganizationId}\n labels={labels}\n/>",
|
|
3
|
+
"group": "layout",
|
|
4
|
+
"importPath": "@godxjp/ui/layout",
|
|
5
|
+
"name": "OrgSwitcher",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Consumer-provided organization choices.",
|
|
9
|
+
"name": "organizations",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "readonly OrgSwitcherOrganization[]"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Selected organization id.",
|
|
15
|
+
"name": "value",
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "Selection callback; persistence remains consumer-owned.",
|
|
20
|
+
"name": "onValueChange",
|
|
21
|
+
"type": "(value: string) => void"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "Localized trigger, search, state, and retry copy.",
|
|
25
|
+
"name": "labels",
|
|
26
|
+
"required": true,
|
|
27
|
+
"type": "OrgSwitcherLabels"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"defaultValue": "false",
|
|
31
|
+
"description": "Compact trigger presentation.",
|
|
32
|
+
"name": "collapsed",
|
|
33
|
+
"type": "boolean"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"defaultValue": "false",
|
|
37
|
+
"description": "Disables the trigger.",
|
|
38
|
+
"name": "disabled",
|
|
39
|
+
"type": "boolean"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"defaultValue": "false",
|
|
43
|
+
"description": "Shows the loading state.",
|
|
44
|
+
"name": "loading",
|
|
45
|
+
"type": "boolean"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"description": "Consumer-supplied error state.",
|
|
49
|
+
"name": "error",
|
|
50
|
+
"type": "ReactNode"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"description": "Consumer-owned retry callback.",
|
|
54
|
+
"name": "onRetry",
|
|
55
|
+
"type": "() => void"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"defaultValue": "\"auto\"",
|
|
59
|
+
"description": "Which SURFACE the panel opens on. \"auto\" (default) is a desktop popover above --sheet-responsive-breakpoint-width (48rem/768px) and a focus-trapped bottom Sheet at/below it. \"dialog\" is a centred modal above that breakpoint and the SAME Sheet below it — reach for it once a row carries more than a name (a role, a plan, a member count, a create-organization action): a popover is anchored to its trigger, clipped by the viewport and sized by --org-switcher-menu-width, while a dialog has a real title, a scrolling body and room for a footer, and takes full attention, which is the right trade when switching organization re-scopes everything on screen. \"popover\" and \"sheet\" pin one surface at every width, for deterministic embedded surfaces and component tests. \"auto\" and \"dialog\" are the two RESPONSIVE pairs and share their mobile half, because a centred modal on a phone is a Sheet with worse ergonomics. All four resolve the breakpoint through the shared useSheetResponsiveMode() hook, so moving that one token moves the line for every overlay. Width of the dialog surface is --org-switcher-dialog-width (26rem), separate from --dialog-width-default so re-tuning the picker does not re-tune every dialog.",
|
|
60
|
+
"name": "responsive",
|
|
61
|
+
"type": "\"auto\" | \"popover\" | \"sheet\" | \"dialog\""
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"description": "Controlled open state.",
|
|
65
|
+
"name": "open",
|
|
66
|
+
"type": "boolean"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"description": "Open-state callback.",
|
|
70
|
+
"name": "onOpenChange",
|
|
71
|
+
"type": "(open: boolean) => void"
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"related": [
|
|
75
|
+
"AppShell (navRail) — WHERE this control goes when switching organization is constant: the rail is the docked platform-scope column, and its own prop doc is the authority on which of the three columns owns which scope. Pass the collapsed trigger; the rail is 3.5rem.",
|
|
76
|
+
"AppLauncher — the OTHER platform-scope control: which APP, not which ORGANIZATION. They compose (a launcher in the bar and a switcher in the rail is one coherent platform surface); neither replaces the other.",
|
|
77
|
+
"Sidebar — APP scope, and therefore NOT where this goes. Its `brand` slot is the app's own lockup; putting the organization switcher there mixes two scopes in one header."
|
|
78
|
+
],
|
|
79
|
+
"rules": [],
|
|
80
|
+
"storyPath": "layout/OrgSwitcher.stories.tsx",
|
|
81
|
+
"tagline": "Searchable organization switcher with canonical trigger geometry, desktop popover, and mobile sheet.",
|
|
82
|
+
"usage": [
|
|
83
|
+
"Provide only organizations the current user may select; the component does not authorize or fetch tenants.",
|
|
84
|
+
"Keep persistence and navigation in `onValueChange`; use loading/error props for the real query state.",
|
|
85
|
+
"DO put a plan/status affordance in `organization.badge` (a <Badge>) instead of stuffing it into `meta` — the badge is end-aligned in the expanded trigger and in the menu row, and hidden in the collapsed rail. ALWAYS pair it with a localized `organization.badgeLabel`: the trigger's accessible name comes from `labels.trigger`, so the badge is announced as an aria-describedby DESCRIPTION and the raw node is marked presentational (WCAG 1.1.1). `badgeLabel` also becomes a search keyword.",
|
|
86
|
+
"DON'T wrap OrgSwitcher in your own media query to pick popover vs sheet — `responsive=\"auto\"` already reads the shared --sheet-responsive-breakpoint-width token.",
|
|
87
|
+
"DON'T put it in the SIDEBAR — not as a row, not in `Sidebar`'s `brand` slot, not stacked under the product lockup. Which organization you are in is PLATFORM scope: it survives changing app, while everything else in the sidebar is this app's own sections. Its two legal homes are `AppShell`'s `navRail` (a permanent 3.5rem column — the Slack shape, when switching is frequent enough to deserve the width) and the topbar (a `TopbarItem`-height control, with `topbarSpan=\"full\"` so the bar outranks the section beneath it). Stacking it under the sidebar's own logo also puts two lockups of different heights in one header, which is what visibly skews the sidebar's top edge against the topbar's."
|
|
88
|
+
]
|
|
89
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { OverlayPortalProvider } from \"@godxjp/ui/app\";\n\n// The React root lives inside a shadow root, so every overlay must portal there too.\nconst shadowRoot = host.attachShadow({ mode: \"open\" });\n\n<OverlayPortalProvider container={shadowRoot}>\n <AppProvider defaultLocale=\"ja\">{children}</AppProvider>\n</OverlayPortalProvider>",
|
|
3
|
+
"group": "providers",
|
|
4
|
+
"importPath": "@godxjp/ui/app",
|
|
5
|
+
"name": "OverlayPortalProvider",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "The element overlays render into — pass the shadow root, or any element inside it. `undefined` restores the document.body default, so a subtree can opt back out.",
|
|
9
|
+
"name": "container",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "Element | undefined"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "The subtree whose overlays are relocated.",
|
|
15
|
+
"name": "children",
|
|
16
|
+
"required": true,
|
|
17
|
+
"type": "ReactNode"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"related": [
|
|
21
|
+
"AppProvider — the other root-level provider (locale, timezone, theme axes). Both are mounted once at the root; neither replaces the other.",
|
|
22
|
+
"Popover — one of the overlays this provider relocates; its own `container` has no prop equivalent.",
|
|
23
|
+
"Dialog — likewise portals to document.body unless this provider names another container."
|
|
24
|
+
],
|
|
25
|
+
"rules": [
|
|
26
|
+
5
|
|
27
|
+
],
|
|
28
|
+
"storyPath": "app/OverlayPortalProvider.stories.tsx",
|
|
29
|
+
"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.",
|
|
30
|
+
"usage": [
|
|
31
|
+
"DO mount it when this library is rendered inside a shadow root (an embedded bar, a widget injected into a host page, a Web Component wrapper). Overlays default to document.body, which is outside the tree carrying this library's stylesheet — measured on an embedded bar as border 0, radius 0, a transparent background and the anchor maths 40px off.",
|
|
32
|
+
"DO wrap the whole embedded subtree ONCE, the same way AppProvider is mounted once. Where overlays render is a fact about the tree, not about any one control, and a component that owns its own overlay (AppLauncher) has no prop a consumer could reach it through.",
|
|
33
|
+
"DO expect it to fix DISMISSAL and FOCUS too, not just paint: mounting it switches react-aria onto its shadow-DOM code paths, without which the click that opened an overlay reads as a click outside it (an embedded launchpad that opened and shut on one press) and a focus scope believes focus never entered the dialog.",
|
|
34
|
+
"DON'T mount it on an ordinary page. document.body is the right destination there, and the provider is not a styling knob — it changes nothing you would want changed outside a shadow root.",
|
|
35
|
+
"DON'T look for a per-overlay container prop instead. There is none by design; a consumer asked to remember six of them gets five right."
|
|
36
|
+
],
|
|
37
|
+
"useCases": [
|
|
38
|
+
"A GoDX bar or launchpad injected into a third-party page inside a shadow root, where its Popover and DropdownMenu must stay styled and dismissable.",
|
|
39
|
+
"A Web Component wrapper around an app built with this library, mounting the React tree in its own shadow DOM.",
|
|
40
|
+
"A widget embedded in a host that happens to load this library too — the case where the default is not obviously broken, only subtly wrong, until the first host that does not load it."
|
|
41
|
+
]
|
|
42
|
+
}
|