@godxjp/ui 28.8.0 → 28.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/lib/variants.js +4 -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/props/vocabulary/layout.prop.d.ts +1 -1
- package/dist/styles/base.css +47 -14
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/chart-layout.css +6 -6
- package/dist/styles/control.css +41 -6
- package/dist/styles/data-display-layout.css +21 -6
- package/dist/styles/density.css +2 -0
- package/dist/styles/dialog-layout.css +4 -1
- package/dist/styles/focus-ring.css +4 -1
- package/dist/styles/layout.css +30 -3
- package/dist/styles/navigation-layout.css +3 -1
- package/dist/styles/shell-layout.css +27 -21
- package/dist/styles/table-layout.css +50 -9
- package/dist/styles/text-layout.css +94 -23
- package/dist/tokens/components/activity.css +13 -4
- package/dist/tokens/components/attachments.css +1 -1
- package/dist/tokens/components/badge.css +1 -1
- package/dist/tokens/components/card.css +28 -7
- package/dist/tokens/components/chart.css +4 -1
- package/dist/tokens/components/chat-composer.css +4 -1
- package/dist/tokens/components/control.css +69 -30
- package/dist/tokens/components/conversations.css +4 -1
- package/dist/tokens/components/data-display.css +42 -15
- package/dist/tokens/components/data-entry.css +8 -2
- package/dist/tokens/components/descriptions.css +1 -1
- package/dist/tokens/components/feedback.css +8 -5
- package/dist/tokens/components/float-button.css +8 -2
- package/dist/tokens/components/legal-document.css +12 -3
- package/dist/tokens/components/logo.css +15 -6
- package/dist/tokens/components/mega-menu.css +14 -5
- package/dist/tokens/components/navigation.css +37 -13
- package/dist/tokens/components/segmented.css +9 -2
- package/dist/tokens/components/separator.css +4 -1
- package/dist/tokens/components/shell.css +96 -31
- package/dist/tokens/components/table.css +13 -6
- package/dist/tokens/components/thought-chain.css +4 -1
- package/dist/tokens/components/toggle.css +4 -1
- package/dist/tokens/components/tree.css +1 -1
- package/dist/tokens/components/upload.css +21 -9
- package/dist/tokens/foundation.css +24 -30
- package/dist/tokens/semantic/layout.css +19 -5
- package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
- package/docs/DESIGN-AUTHORITY.md +14 -0
- package/docs/DEVELOPMENT.md +81 -6
- package/docs/TOKENS.md +16 -1
- 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/case4-login.tsx +10 -2
- package/docs/showcase/case5-shift-calendar.tsx +1 -1
- package/docs/showcase/case6-agency-handy.tsx +6 -6
- package/docs/showcase/futurelastic-web.tsx +7 -9
- package/docs/showcase/marketing-page.tsx +61 -52
- package/docs/showcase/table-expandable-rows.tsx +4 -1
- package/docs/showcase/table-pagination.tsx +88 -18
- package/docs/showcase/theme-customization.tsx +25 -2
- package/package.json +8 -5
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Topbar, TopbarItem, AppShell } from \"@godxjp/ui/layout\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Avatar, AvatarFallback } from \"@godxjp/ui/data-display\";\nimport { AppSettingPicker } from \"@godxjp/ui/navigation\";\nimport { PanelLeftClose, Search } from \"lucide-react\";\n\n// The shell gives you slots; YOU decide what goes in them.\n<AppShell\n sidebar={<MySidebar />}\n topbar={\n <Topbar\n start={\n <>\n <TopbarItem aria-label={t(\"toggleSidebar\")} onClick={toggle}>\n <PanelLeftClose />\n </TopbarItem>\n <Avatar className=\"rounded-md\">\n <AvatarFallback className=\"bg-primary text-primary-foreground font-bold\">C</AvatarFallback>\n </Avatar>\n </>\n }\n center={\n <TopbarItem onClick={openSearch}>\n <Search />\n {t(\"search\")}\n </TopbarItem>\n }\n end={\n <>\n <AppSettingPicker kind=\"locale\" />\n <UserMenu />\n </>\n }\n />\n }\n>\n {children}\n</AppShell>",
|
|
3
|
+
"group": "layout",
|
|
4
|
+
"importPath": "@godxjp/ui/layout",
|
|
5
|
+
"name": "Topbar",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Instance inset using logical token steps.",
|
|
9
|
+
"name": "pad",
|
|
10
|
+
"type": "PadProp"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "bar uses shell bar height/inset. Renders a div, suitable for nested panel chrome.",
|
|
14
|
+
"name": "height",
|
|
15
|
+
"type": "\"bar\" | \"auto\""
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"defaultValue": "undefined",
|
|
19
|
+
"description": "Inline-start cluster — typically the sidebar toggle (a `Button` with a `PanelLeftClose` icon), the brand mark (an `Avatar`), and primary nav.",
|
|
20
|
+
"name": "start",
|
|
21
|
+
"type": "ReactNode"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"defaultValue": "undefined",
|
|
25
|
+
"description": "Center cluster (grows + centers) — optional. e.g. a search trigger (`Button` + `Search` icon opening your command palette) or a page/entity switcher (`DropdownMenu`). ⚠ HIDDEN AT 1100px AND BELOW by default: the slot follows `--topbar-center-compact-display` (default `none`) so it cannot cover the start breadcrumb/title or the end utilities when a 16rem sidebar is docked. A global search placed here is therefore invisible on tablets and every phone until you set `--topbar-center-compact-display: flex`.",
|
|
26
|
+
"name": "center",
|
|
27
|
+
"type": "ReactNode"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"defaultValue": "undefined",
|
|
31
|
+
"description": "Inline-end cluster — settings pickers (`AppSettingPicker kind=\"locale\"|\"theme\"`), a notifications `Button`, the user-menu `DropdownMenu`.",
|
|
32
|
+
"name": "end",
|
|
33
|
+
"type": "ReactNode"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"defaultValue": "\"scroll\"",
|
|
37
|
+
"description": "What a bar that does not fit does with the cells that do not fit (gh#728). scroll (default) lets the BAR scroll on the inline axis while the start and end clusters keep their cells whole — every cell stays in the DOM and in the accessibility tree, Tab reaches each one in order, and the browser scrolls a focused cell into view; the scrollbar is suppressed (a horizontal bar inside a 48px chrome row would eat the height that caused the deficit), so the pointer affordance is drag/wheel. clip is the pre-gh#728 behaviour: a cell past the cluster's edge is simply not painted — still focusable, still announced, invisible and unreachable by pointer. Independent of TopbarItem hideBelow, which REMOVES a cell at a step; this is only about what happens once there is no budget left.",
|
|
38
|
+
"name": "overflow",
|
|
39
|
+
"type": "\"scroll\" | \"clip\""
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"defaultValue": "undefined",
|
|
43
|
+
"description": "Escape hatch — render fully custom bar content instead of the three slots. When set, `start`/`center`/`end` are ignored.",
|
|
44
|
+
"name": "children",
|
|
45
|
+
"type": "ReactNode"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"description": "Merged onto the bar element; arbitrary props (aria-*, etc.) are forwarded.",
|
|
49
|
+
"name": "className",
|
|
50
|
+
"type": "string"
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"related": [
|
|
54
|
+
"AppShell — place Topbar in its `topbar` slot. AppShell also exposes its own `logo`/`topbarLeft`/`topbarRight` slots if you don't want a separate Topbar at all.",
|
|
55
|
+
"Avatar — the brand mark for the `start` slot (use `shape=\"square\"` for a product glyph; never a `rounded-*` utility, which freezes the radius where no theme can reach it).",
|
|
56
|
+
"AppSettingPicker — locale/theme/timezone/currency picker; the consumer drops it into `end`. Its appearance (icon-only, labelled, bordered) is configured on IT, not on Topbar.",
|
|
57
|
+
"DropdownMenu — wrap a `Button` to build an entity switcher or user menu yourself, then place it in a slot."
|
|
58
|
+
],
|
|
59
|
+
"rules": [
|
|
60
|
+
2,
|
|
61
|
+
3,
|
|
62
|
+
5,
|
|
63
|
+
6
|
|
64
|
+
],
|
|
65
|
+
"storyPath": "layout/Topbar.stories.tsx",
|
|
66
|
+
"tagline": "A PURE SLOT bar for the app shell — positions three clusters (start / center / end) and owns ONLY the bar layout. It bakes NO chrome: no product switcher, no search box, no notification bell, no language picker. The CONSUMER composes those from real primitives and drops them into a slot; whether a control is icon-only / labelled / bordered is that control's own config, never the shell's.",
|
|
67
|
+
"usage": [
|
|
68
|
+
"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.",
|
|
69
|
+
"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.",
|
|
70
|
+
"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.",
|
|
71
|
+
"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.",
|
|
72
|
+
"DO render Topbar inside `AppShell`'s `topbar` slot (or any `<header>`). For a non-three-cluster layout, pass `children` and lay it out yourself.",
|
|
73
|
+
"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.",
|
|
74
|
+
"DO rely on the built-in shrink contract instead of hand-tuning widths: the bar never exceeds its shell allocation, `start` shrinks first and `center` yields its whole box, a long tenant/brand string truncates rather than spilling over a sibling or leaking a horizontal document scroll, and `end` keeps its natural width anchored inline-end — the locale picker and user menu stay visible at 1024px with a 16rem sidebar. If a label must degrade gracefully rather than be cut, give THAT element `truncate`/`text-overflow` yourself; don't add `overflow`/`flex` overrides to the slots.",
|
|
75
|
+
"KNOW what happens when the shrink contract runs out: the BAR scrolls (`overflow=\"scroll\"`, the default), it does not slice. The start cluster keeps a floor of one bar cell and the end cluster keeps its cells whole, so what does not fit is off-port rather than half-painted — Tab still reaches every cell and the browser scrolls the focused one fully into view. Measured at 320x568 on a bar 44px over budget: start cluster 0 -> 32px, end cluster clientWidth 150.3/scrollWidth 285 -> 204/204, last end cell x=384.8 (outside a 390px viewport) -> fully inside it, 7/7 cells reachable by keyboard. `overflow=\"clip\"` restores the pre-gh#728 slicing for a bar you have measured as never overflowing (gh#728).",
|
|
76
|
+
"KNOW the shrink contract reaches only the LAST child of `start` — and `Button` ships `shrink-0`, so any Button you put mid-slot (the classic entity switcher, with a brand mark before it and a screen title after) keeps its full width while the cluster clips it. Clipped, but still focusable: a keyboard user tabs to a control they cannot see (SC 2.4.7). Give such a control `fill` so it takes the leftover room and lets its label ellipse, and wrap that label in `<Text truncate>` — NOT `className=\"min-w-0 flex-1\"` plus a `truncate` span, which is the utility pair ui-audit blocks and which this guidance used to recommend. Budget the `end` cluster too — it is `flex: 0 1 auto` and shrink-weighted, but a cell in it is still a cell you chose to put on a phone: an ambient status chip there is space the start cluster does not get (a 93px environment Badge left `start` 25px of a 198px bar at 320). Hide ambient chips below `sm`."
|
|
77
|
+
],
|
|
78
|
+
"useCases": [
|
|
79
|
+
"Admin shell: `start` = sidebar toggle + brand mark (`Avatar`) + an entity switcher (`DropdownMenu` around a `Button`); `center` = a `Button` search trigger; `end` = `AppSettingPicker` (locale) + a notifications `Button` + a user `DropdownMenu`.",
|
|
80
|
+
"Minimal shell (no search, no notifications): pass only `start` (brand mark) and `end` (user menu). Nothing else renders — no empty chrome.",
|
|
81
|
+
"Marketing / docs header: pass `children` with a fully custom flex layout when the three-cluster model doesn't fit."
|
|
82
|
+
]
|
|
83
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Topbar, TopbarItem } from \"@godxjp/ui/layout\";\nimport { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent } from \"@godxjp/ui/navigation\";\n\n<Topbar\n end={\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <TopbarItem aria-label=\"Account\">\n <Avatar><AvatarFallback>SD</AvatarFallback></Avatar>\n </TopbarItem>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">…</DropdownMenuContent>\n </DropdownMenu>\n }\n/>",
|
|
3
|
+
"group": "layout",
|
|
4
|
+
"importPath": "@godxjp/ui/layout",
|
|
5
|
+
"name": "TopbarItem",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "The cell's leading glyph, placed in a slot the CELL sizes (--topbar-icon-size). Prefer it over passing the glyph as a child whenever the glyph is wrapped: `.ui-topbar-item > svg` only reaches a DIRECT child, so a glyph inside a `<Flex hideBelow>` renders at lucide's intrinsic 24px — measured at 390px on an org-switcher cell, 1.5x the cell's step (gh#712). Decorative by convention (the cell's name comes from its own aria-label); pass <Icon as={…} size=… label=… /> here when the glyph needs a name or a different step. Rendered with asChild too (gh#726): the sized slot is inserted INSIDE the child element, ahead of that element's own children, and the child element (e.g. an <a>) stays the rendered root with every merged prop.",
|
|
9
|
+
"name": "icon",
|
|
10
|
+
"type": "ReactNode"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Drop the icon slot FROM a breakpoint step upwards (Flex hideFrom's contract, scoped to the glyph), so the cell reads label-only on a wide bar. Decorative: the accessible name is unaffected (gh#726).",
|
|
14
|
+
"name": "iconHideFrom",
|
|
15
|
+
"type": "BreakpointProp"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "VISUALLY hide the label BELOW a breakpoint step (Flex hideBelow's contract, scoped to the label), so the cell collapses to icon-only on a narrow bar. The label stays in the accessibility tree (clipped, not display:none), so the icon-only cell keeps its accessible name. Under asChild the child element's own children are the label (gh#726).",
|
|
19
|
+
"name": "labelHideBelow",
|
|
20
|
+
"type": "BreakpointProp"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"description": "Overlaid count; does not change the bar cell width. Not rendered with asChild.",
|
|
24
|
+
"name": "badge",
|
|
25
|
+
"type": "ReactNode"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"description": "Count tone, shared with Sidebar badges.",
|
|
29
|
+
"name": "badgeTone",
|
|
30
|
+
"type": "\"neutral\" | \"destructive\""
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"description": "Hide the bar cell below a design-system breakpoint without interrupting its stretch chain.",
|
|
34
|
+
"name": "hideBelow",
|
|
35
|
+
"type": "BreakpointProp"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"defaultValue": "false",
|
|
39
|
+
"description": "Render the bar-cell shape onto the child instead of emitting a <button> — for a router link, or a menu/popover trigger that must own the element.",
|
|
40
|
+
"name": "asChild",
|
|
41
|
+
"type": "boolean"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"description": "Cell content — a glyph, a label, an Avatar, or any pair of them.",
|
|
45
|
+
"name": "children",
|
|
46
|
+
"type": "ReactNode"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"description": "Root class override.",
|
|
50
|
+
"name": "className",
|
|
51
|
+
"type": "string"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"related": [
|
|
55
|
+
"Topbar (the slot bar it belongs to)",
|
|
56
|
+
"Button (the right control everywhere that is not a bar)",
|
|
57
|
+
"AppShell (owns the bar height it stretches to)"
|
|
58
|
+
],
|
|
59
|
+
"rules": [
|
|
60
|
+
2,
|
|
61
|
+
3,
|
|
62
|
+
5,
|
|
63
|
+
6
|
|
64
|
+
],
|
|
65
|
+
"storyPath": "layout/TopbarItem.stories.tsx",
|
|
66
|
+
"tagline": "ONE interactive cell of a Topbar slot — the account button, a settings or notifications trigger. Full bar height, the bar's own hover surface, and the focus mark hosted INSIDE the cell. Use it INSTEAD OF a Button in a Topbar slot: a Button there is a --control-height pill floating in a taller bar, with its own hover fill and a ring drawn around the pill.",
|
|
67
|
+
"usage": [
|
|
68
|
+
"DO put bar triggers in a Topbar slot as TopbarItem, not as Button — that is the difference between chrome and a control that landed in the chrome.",
|
|
69
|
+
"DO wrap it in a DropdownMenuTrigger asChild for a user menu; the open state lights the cell via [data-state=open].",
|
|
70
|
+
"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.",
|
|
71
|
+
"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).",
|
|
72
|
+
"DON'T reach for it outside a Topbar — a full-bleed cell needs a bar to bleed to. Use Button anywhere else."
|
|
73
|
+
],
|
|
74
|
+
"useCases": [
|
|
75
|
+
"Account / user-menu trigger in the topbar end slot",
|
|
76
|
+
"Notifications bell, settings or theme trigger",
|
|
77
|
+
"Sidebar collapse toggle in the topbar start slot"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { useState } from \"react\";\nimport { Transfer } from \"@godxjp/ui/data-entry\";\n\nconst ALL_ACCOUNTS = [\n { value: \"1010\", title: \"Cash\", description: \"Asset\" },\n { value: \"1020\", title: \"Accounts Receivable\", description: \"Asset\" },\n { value: \"2010\", title: \"Accounts Payable\", description: \"Liability\" },\n { value: \"3010\", title: \"Revenue\", description: \"Income\" },\n { value: \"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}",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "Transfer",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Canonical controlled target keys; takes precedence over targetKeys.",
|
|
9
|
+
"name": "value",
|
|
10
|
+
"type": "string[]"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Initial uncontrolled target keys.",
|
|
14
|
+
"name": "defaultValue",
|
|
15
|
+
"type": "string[]"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "Compatibility name for uncontrolled initial target keys.",
|
|
19
|
+
"name": "defaultTargetKeys",
|
|
20
|
+
"type": "string[]"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"description": "Native form name; repeats once per target key.",
|
|
24
|
+
"name": "name",
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"description": "Preserve assignment while preventing edits.",
|
|
29
|
+
"name": "readOnly",
|
|
30
|
+
"type": "boolean"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"description": "Independent pane pages; select-all affects visible enabled rows.",
|
|
34
|
+
"name": "pagination",
|
|
35
|
+
"type": "boolean | { pageSize?: number }"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"description": "Show each pane select-all control; defaults true.",
|
|
39
|
+
"name": "showSelectAll",
|
|
40
|
+
"type": "boolean"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"description": "Custom search predicate.",
|
|
44
|
+
"name": "filterOption",
|
|
45
|
+
"type": "(query: string, item: TransferItemProp) => boolean"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"description": "Custom non-interactive row content; checkbox labels remain associated.",
|
|
49
|
+
"name": "render",
|
|
50
|
+
"type": "(item: TransferItemProp) => ReactNode"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"description": "Full flat list of all items (both source and target). Each item needs a unique `key` string, a `title` (ReactNode rendered in the list row), an optional `description` (shown as a secondary line), and an optional `disabled` boolean to lock individual items.",
|
|
54
|
+
"name": "dataSource",
|
|
55
|
+
"required": true,
|
|
56
|
+
"type": "TransferItemProp[]"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"description": "Keys of items currently in the right (target) panel. Items whose key is NOT in this array appear in the left (source) panel. This is the primary controlled state — you must update it inside `onChange`.",
|
|
60
|
+
"name": "targetKeys",
|
|
61
|
+
"required": true,
|
|
62
|
+
"type": "string[]"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"description": "Called after the user clicks a move button. Receives the new full targetKeys array, the direction of movement ('right' = source→target, 'left' = target→source), and the keys that were actually moved. Update your targetKeys state here.",
|
|
66
|
+
"name": "onChange",
|
|
67
|
+
"type": "(targetKeys: string[], direction: 'left' | 'right', moveKeys: string[]) => void"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"description": "Panel header labels. Index 0 = left/source panel, index 1 = right/target panel. Defaults to i18n strings (dataEntry.transfer.source / dataEntry.transfer.target).",
|
|
71
|
+
"name": "titles",
|
|
72
|
+
"type": "[React.ReactNode, React.ReactNode]"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"defaultValue": "false",
|
|
76
|
+
"description": "When true, renders a SearchInput inside each panel that filters items by title and description text (debounce=0). Does not affect the underlying data; purely a client-side filter.",
|
|
77
|
+
"name": "showSearch",
|
|
78
|
+
"type": "boolean"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"defaultValue": "false",
|
|
82
|
+
"description": "When true, hides the left-pointing move button so items can only flow source → target. Useful for append-only assignment flows.",
|
|
83
|
+
"name": "oneWay",
|
|
84
|
+
"type": "boolean"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"defaultValue": "false",
|
|
88
|
+
"description": "Disables the entire component: all checkboxes, the search input (pointer-events-none), and both move buttons.",
|
|
89
|
+
"name": "disabled",
|
|
90
|
+
"type": "DisabledProp (boolean)"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"description": "Extra Tailwind classes applied to the outer flex wrapper. Use to constrain width or add margin.",
|
|
94
|
+
"name": "className",
|
|
95
|
+
"type": "string"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"description": "Controlled selection state as a tuple: index 0 = keys checked in the source panel, index 1 = keys checked in the target panel. Omit to use internal (uncontrolled) selection state. Must be paired with `onSelectChange` when provided.",
|
|
99
|
+
"name": "selectedKeys",
|
|
100
|
+
"type": "[string[], string[]]"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"description": "Called whenever the checked selection in either panel changes. Provides updated arrays for source and target selections. Required when `selectedKeys` is controlled.",
|
|
104
|
+
"name": "onSelectChange",
|
|
105
|
+
"type": "(sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"description": "Fires when items move between panels; you own `targetKeys` state.",
|
|
109
|
+
"name": "onValueChange",
|
|
110
|
+
"type": "(targetKeys: string[], direction: \"left\" | \"right\", moveKeys: string[]) => void"
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
"related": [
|
|
114
|
+
"MultiSelect — picks multiple values from a dropdown; prefer when the option set is large and a panel layout is not needed.",
|
|
115
|
+
"Checkbox (list) — use for a simple flat multi-select without a shuttle/move metaphor.",
|
|
116
|
+
"Select (compound) — single or multi-value dropdown; not a dual-panel component.",
|
|
117
|
+
"Tree — hierarchical item display; combine with Transfer's dataSource if items have a tree structure but the shuttle UX is still needed."
|
|
118
|
+
],
|
|
119
|
+
"rules": [
|
|
120
|
+
23,
|
|
121
|
+
31
|
|
122
|
+
],
|
|
123
|
+
"storyPath": "data-entry/Transfer.stories.tsx",
|
|
124
|
+
"tagline": "Dual-list shuttle that moves items between source and target via Checkbox selection — you own targetKeys state; never hand-roll a two-panel picker.",
|
|
125
|
+
"usage": [
|
|
126
|
+
"DO own `targetKeys` in state and update it inside `onChange`: `const [targetKeys, setTargetKeys] = useState<string[]>([]); onValueChange={(next) => setTargetKeys(next)}`.",
|
|
127
|
+
"DO NOT hand-roll a two-panel checkbox picker — Transfer ships the full shuttle UX (select-all header, indeterminate state, search, move buttons, empty state) out of the box.",
|
|
128
|
+
"DO enable `showSearch` for lists longer than ~10 items; the built-in SearchInput filters by both `title` and `description` text content, including ReactNode content via `reactNodeText`.",
|
|
129
|
+
"DO use `oneWay={true}` for append-only flows (e.g. adding permissions to a role) where items must never be moved back.",
|
|
130
|
+
"DO control `selectedKeys` / `onSelectChange` only when you need to read which items are currently checked (e.g. for a bulk-action toolbar outside the component). For most cases, leave both props out and let Transfer manage selection internally.",
|
|
131
|
+
"AVOID using Transfer for simple single-select or toggle scenarios — use a Checkbox list or Select multiple instead. Transfer is specifically for shuttle/dual-panel assignment flows."
|
|
132
|
+
],
|
|
133
|
+
"useCases": [
|
|
134
|
+
"Assigning roles or permissions to a user: source panel shows available roles, target panel shows assigned roles; `oneWay={false}` allows removal.",
|
|
135
|
+
"Building a report column picker: source = all available columns, target = columns included in the report, user orders and moves them across.",
|
|
136
|
+
"Account mapping in an accounting app: map external chart-of-accounts entries (source) to canonical internal accounts (target) in a bulk import wizard.",
|
|
137
|
+
"Tag / label assignment in a CMS: move content tags from an available pool into a 'selected' set for a document.",
|
|
138
|
+
"Feature-flag targeting: move user segments from an 'all segments' list into the 'targeted segments' panel for a flag.",
|
|
139
|
+
"Permission set builder in an admin UI: shuttle individual API scopes from 'available' to 'granted' for an API key or OAuth client."
|
|
140
|
+
]
|
|
141
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
{
|
|
2
|
+
"docPath": "docs/data-display/tree.tsx",
|
|
3
|
+
"example": "import * as React from \"react\";\nimport { Tree } from \"@godxjp/ui/data-display\";\n\nconst permissions = [\n {\n value: \"billing\",\n label: \"Billing\",\n children: [\n { value: \"billing.invoice.read\", label: \"Read invoices\" },\n { value: \"billing.invoice.write\", label: \"Issue invoices\" },\n ],\n },\n {\n value: \"people\",\n label: \"People\",\n children: [\n { value: \"people.read\", label: \"Read profiles\" },\n { value: \"people.write\", label: \"Edit profiles\", disabled: true },\n ],\n },\n];\n\nexport function PermissionTree() {\n // The name is YOUR copy — run it through your app's own i18n, not the library's.\n const [granted, setGranted] = React.useState<string[]>([\"billing.invoice.read\"]);\n\n return (\n <Tree\n aria-label=\"Permissions\"\n treeData={permissions}\n checkable\n defaultExpandAll\n checkedValues={granted}\n onCheckedValuesChange={setGranted}\n />\n );\n}",
|
|
4
|
+
"group": "data-display",
|
|
5
|
+
"importPath": "@godxjp/ui/data-display",
|
|
6
|
+
"name": "Tree",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"description": "The hierarchy. Each node: `{ value: string; label: ReactNode; disabled?: boolean; disableCheckbox?: boolean; isLeaf?: boolean; icon?: ReactNode; children?: TreeNodeProp[] }`. Same node shape as TreeSelect/Cascader; use `fieldNames` to remap an API response instead of transforming it.",
|
|
10
|
+
"name": "treeData",
|
|
11
|
+
"required": true,
|
|
12
|
+
"type": "TreeNodeProp[]"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"description": "Remap the data's own key names, e.g. `{ label: 'name', value: 'id', children: 'items' }`.",
|
|
16
|
+
"name": "fieldNames",
|
|
17
|
+
"type": "{ label?: string; value?: string; children?: string }"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Controlled SELECTION (antd `selectedKeys`). `string` while single, `string[]` once `multiple` is on. Selection and checks are separate axes — do not use this for checkboxes.",
|
|
21
|
+
"name": "value",
|
|
22
|
+
"type": "string | string[]"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "Initial selection for an uncontrolled tree. Ignored once `value` is passed.",
|
|
26
|
+
"name": "defaultValue",
|
|
27
|
+
"type": "string | string[]"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"description": "Selection change (antd `onSelect`). Emits `string | undefined` while single, `string[]` once `multiple` is on.",
|
|
31
|
+
"name": "onValueChange",
|
|
32
|
+
"type": "(value: string | string[] | undefined) => void"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"defaultValue": "false",
|
|
36
|
+
"description": "Allow more than one selected node. Also sets `aria-multiselectable` on the tree.",
|
|
37
|
+
"name": "multiple",
|
|
38
|
+
"type": "boolean"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"defaultValue": "false",
|
|
42
|
+
"description": "Draw a checkbox on every node (antd `checkable`). Checking cascades to descendants and a parent shows `indeterminate` while only some are checked. With `checkable`, Enter/Space toggles the checkbox rather than selecting.",
|
|
43
|
+
"name": "checkable",
|
|
44
|
+
"type": "boolean"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"defaultValue": "false",
|
|
48
|
+
"description": "Parent and child checks are independent (antd `checkStrictly`) — no cascade and no indeterminate state.",
|
|
49
|
+
"name": "checkStrictly",
|
|
50
|
+
"type": "boolean"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"description": "Controlled checked nodes (antd `checkedKeys`).",
|
|
54
|
+
"name": "checkedValues",
|
|
55
|
+
"type": "string[]"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"description": "Initial checked nodes for an uncontrolled tree (antd `defaultCheckedKeys`).",
|
|
59
|
+
"name": "defaultCheckedValues",
|
|
60
|
+
"type": "string[]"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"description": "Checked-set change (antd `onCheck`). The array is normalised: a branch appears only when every tickable child is checked, so it can never claim a partial selection is complete.",
|
|
64
|
+
"name": "onCheckedValuesChange",
|
|
65
|
+
"type": "(values: string[]) => void"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"description": "Controlled expanded branches (antd `expandedKeys`).",
|
|
69
|
+
"name": "expandedValues",
|
|
70
|
+
"type": "string[]"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"description": "Initial expanded branches (antd `defaultExpandedKeys`).",
|
|
74
|
+
"name": "defaultExpandedValues",
|
|
75
|
+
"type": "string[]"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"description": "Expansion change (antd `onExpand`). Fires for controlled and uncontrolled trees.",
|
|
79
|
+
"name": "onExpandedValuesChange",
|
|
80
|
+
"type": "(values: string[]) => void"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"defaultValue": "false",
|
|
84
|
+
"description": "Start with every branch open. Seeded once on mount, never re-applied.",
|
|
85
|
+
"name": "defaultExpandAll",
|
|
86
|
+
"type": "boolean"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"description": "Lazy children (antd `loadData`). Called ONCE per node the first time a branch with no `children` and `isLeaf !== true` is expanded; a Skeleton row and `aria-busy` cover the wait. Push the fetched children into `treeData`.",
|
|
90
|
+
"name": "loadData",
|
|
91
|
+
"type": "(node: TreeNodeProp) => void | Promise<void>"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"description": "Render a node's title yourself (antd `titleRender`).",
|
|
95
|
+
"name": "titleRender",
|
|
96
|
+
"type": "(node: TreeNodeProp) => ReactNode"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"defaultValue": "false",
|
|
100
|
+
"description": "Draw the connector rails between a parent and its children (antd `showLine`). Off by default — a rail is chrome, and chrome defaults quiet.",
|
|
101
|
+
"name": "showLine",
|
|
102
|
+
"type": "boolean"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"defaultValue": "false",
|
|
106
|
+
"description": "Rule between the rows: a hairline on every node's block-start except the outline's first, so a tree used as navigation inside a Card reads as a list instead of one block (gh#732). The rule runs the FULL width of the row at every depth — never indented per level, which would make the children read as a nested table. Colour is `--tree-divider-color` (default `hsl(var(--border))`). Off by default, like `showLine` — chrome defaults quiet. Not antd's name: antd's Tree has no such capability, and `bordered` means a grid's frame plus column rules elsewhere in this library.",
|
|
107
|
+
"name": "divided",
|
|
108
|
+
"type": "boolean"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"defaultValue": "false",
|
|
112
|
+
"description": "Draw each node's `icon` (antd `showIcon`). `variant=\"directory\"` supplies folder/file glyphs when a node has no `icon` of its own.",
|
|
113
|
+
"name": "showIcon",
|
|
114
|
+
"type": "boolean"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"defaultValue": "\"default\"",
|
|
118
|
+
"description": "`directory` is antd's `<DirectoryTree>`: folder/file glyphs and a full-row selected band.",
|
|
119
|
+
"name": "variant",
|
|
120
|
+
"type": "\"default\" | \"directory\""
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"defaultValue": "\"md\"",
|
|
124
|
+
"description": "Row height tier — the shared `--control-height` ladder, so rows stay in step with the controls beside them under every density.",
|
|
125
|
+
"name": "size",
|
|
126
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"defaultValue": "false",
|
|
130
|
+
"description": "Disable the whole tree: nothing selects, checks or expands, and the nodes stay readable.",
|
|
131
|
+
"name": "disabled",
|
|
132
|
+
"type": "boolean"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"description": "Classes applied to the `role=\"tree\"` container.",
|
|
136
|
+
"name": "className",
|
|
137
|
+
"type": "string"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"description": "HTML id on the `role=\"tree\"` container.",
|
|
141
|
+
"name": "id",
|
|
142
|
+
"type": "string"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"description": "Accessible name of the tree. Required unless `aria-labelledby` names it from a visible heading.",
|
|
146
|
+
"name": "aria-label",
|
|
147
|
+
"type": "string"
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"related": [
|
|
151
|
+
"TreeList — REMOVED in 21.0.0, replaced by Tree. It was a flat `<ul>` whose `depth` only drove `margin-inline-start`: it LOOKED like a tree and had no expand/collapse, no `role=\"tree\"`, no keyboard and no selection contract. Migration: nest the flat `items` into `treeData` (`id`→`value`, `title`→`label`, `depth`→nesting) and pass `aria-label`. A genuinely static indented outline that never opens is a `Descriptions` or a `ListRow` stack, not a tree.",
|
|
152
|
+
"TreeSelect — the same hierarchy INSIDE a Popover, as a form field. Use TreeSelect when the answer is a value in a form; use Tree when the hierarchy itself is the page.",
|
|
153
|
+
"Cascader — a path picker across columns. Use it when the user walks one path to a leaf; use Tree when several branches are open at once.",
|
|
154
|
+
"Accordion — single-level disclosure with rich panel content. It is not a hierarchy and has no tree keyboard model.",
|
|
155
|
+
"ScrollArea — wrap Tree in one to cap a long outline; Tree does not virtualise in v1."
|
|
156
|
+
],
|
|
157
|
+
"rules": [
|
|
158
|
+
2,
|
|
159
|
+
6,
|
|
160
|
+
23,
|
|
161
|
+
31,
|
|
162
|
+
44,
|
|
163
|
+
45
|
|
164
|
+
],
|
|
165
|
+
"storyPath": "data-display/Tree.stories.tsx",
|
|
166
|
+
"tagline": "The standalone WAI-ARIA tree view on a page (antd `Tree` / `DirectoryTree`) — nested `treeData`, a disclosure triangle per branch, roving-tabindex arrow navigation, optional tri-state checkboxes and async `loadData`. Reach for it whenever nodes expand, collapse or are navigated by keyboard; TreeSelect is the same hierarchy inside a Popover.",
|
|
167
|
+
"usage": [
|
|
168
|
+
"DO name the tree — pass `aria-label` (through your own `t()`) or `aria-labelledby`. A tree with no name is an unlabelled landmark for every screen reader user.",
|
|
169
|
+
"DO keep SELECTION and CHECKS apart: `value`/`onValueChange` is which node is open in the detail pane; `checkedValues`/`onCheckedValuesChange` is which nodes are ticked. They are two axes, exactly as in antd — never drive one from the other.",
|
|
170
|
+
"DO let the keyboard work: the tree ships the full APG contract (Up/Down through visible nodes, Right expands then descends, Left collapses then climbs, Home/End, Enter/Space, `*` to expand the current level, type-ahead). Do not add your own key handling on top.",
|
|
171
|
+
"DON'T nest a Button, Checkbox, Link or any focusable control inside a node label. A tree item owns exactly ONE tab stop; the disclosure triangle and the tick box are decorative glyphs for that reason. Put row actions in a sibling column outside the tree, or open a detail pane on selection.",
|
|
172
|
+
"DON'T hand-roll an indented `<ul>` (or a NavList / ListRow stack with a per-depth margin) for a hierarchy. A flat indented list only LOOKS like a tree: no expand/collapse, no `role=\"tree\"`, no keyboard model, no selection contract. `TreeList` was exactly that list and was REMOVED in 21.0.0 — Tree is what replaced it, and it is the one to reach for whenever nodes expand, collapse or are keyboard-navigated.",
|
|
173
|
+
"DO pass `divided` when the tree IS the navigation of a page — a wiki/document outline or a section index sitting in a `Card` (`Card` > `CardContent flush` > `Tree divided`). Without a rule the rows run together and the outline reads as one block; with it, it reads as the ruled list ListRow and Table already give a flat list. Retint it per theme with `--tree-divider-color`, never with a per-page utility class.",
|
|
174
|
+
"DO cap a long tree with `ScrollArea` — virtualisation is not in v1, so a 5,000-node tree renders 5,000 rows.",
|
|
175
|
+
"DO push fetched children into `treeData` from `loadData`; the tree calls it once per node and shows a Skeleton row until the data lands."
|
|
176
|
+
],
|
|
177
|
+
"useCases": [
|
|
178
|
+
"A permission tree: modules → resources → actions with tri-state checkboxes, where ticking a module ticks everything under it and a partly-granted module shows the dash.",
|
|
179
|
+
"A category browser beside a detail pane — selecting a category loads its products, and the whole hierarchy stays navigable by keyboard.",
|
|
180
|
+
"An organisation chart / department picker on a settings page, where a branch's children are fetched on demand with `loadData`.",
|
|
181
|
+
"A file explorer (`variant=\"directory\"`, `showIcon`, `showLine`) where folders and files read differently and the selected row spans the width.",
|
|
182
|
+
"A chart-of-accounts outline that must expand and collapse — the case a flat indented list only ever looked like it handled.",
|
|
183
|
+
"A wiki / document page index used as the navigation of a screen, inside a Card: `divided` rules the rows so the outline reads as a list, and selection drives the reading pane."
|
|
184
|
+
]
|
|
185
|
+
}
|