@godxjp/ui 28.9.0 → 28.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +77 -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 +93 -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 +15515 -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 +8427 -0
- package/agent/vocabulary.json +198 -0
- package/dist/components/data-display/service-launcher-card.d.ts +19 -0
- package/dist/components/data-display/service-launcher-card.js +14 -1
- package/dist/components/data-entry/attachments.js +77 -33
- 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/avatar.d.ts +1 -18
- package/dist/components/ui/avatar.js +1 -36
- 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 +190 -1
- package/dist/i18n/messages/ja.json +188 -1
- package/dist/i18n/messages/vi.json +188 -1
- package/dist/lib/image-loading-status.d.ts +25 -0
- package/dist/lib/image-loading-status.js +41 -0
- 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 +14 -1
- package/dist/props/registry.js +18 -1
- package/dist/styles/card-layout.css +10 -4
- package/dist/styles/control.css +33 -4
- package/dist/styles/data-display-layout.css +1 -1
- package/dist/styles/data-entry-layout.css +245 -2
- 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/attachments.css +18 -9
- package/dist/tokens/components/segmented.css +7 -3
- package/dist/tokens/components/table.css +2 -1
- package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
- package/docs/DESIGN-AUTHORITY.md +52 -0
- package/docs/DEVELOPMENT.md +81 -6
- package/docs/assets/service-mark-rose.svg +6 -0
- package/docs/assets/service-mark-teal.svg +5 -0
- package/docs/data-display/service-launcher-card.tsx +232 -92
- 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 +99 -18
- package/docs/showcase/theme-customization.tsx +25 -2
- package/package.json +8 -5
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "{`import {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@godxjp/ui/data-entry\";\n\nfunction AccountQuickPick({ onSelect }: { onSelect: (id: string) => void }) {\n return (\n <Command label=\"Quick pick account\" loop>\n <CommandInput placeholder=\"Search accounts…\" />\n <CommandList>\n <CommandEmpty>No accounts found.</CommandEmpty>\n <CommandGroup heading=\"Revenue\">\n <CommandItem value=\"4001\" onSelect={onSelect}>\n Sales Revenue\n </CommandItem>\n <CommandItem value=\"4002\" onSelect={onSelect}>\n Service Revenue\n </CommandItem>\n </CommandGroup>\n <CommandGroup heading=\"Expenses\">\n <CommandItem value=\"6001\" onSelect={onSelect}>\n Rent Expense\n </CommandItem>\n <CommandItem value=\"6002\" disabled onSelect={onSelect}>\n Deprecated Account\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n );\n}`}",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "Command",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Accessible label for the command menu. Not shown visually — used by screen readers.",
|
|
9
|
+
"name": "label",
|
|
10
|
+
"type": "string"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"defaultValue": "true",
|
|
14
|
+
"description": "Set to false to disable automatic filtering and sorting. When false, you must conditionally render matching items yourself based on the search query.",
|
|
15
|
+
"name": "shouldFilter",
|
|
16
|
+
"type": "boolean"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "Custom filter function. Returns a score between 0 (hidden) and 1 (best match). Defaults to command-score library scoring.",
|
|
20
|
+
"name": "filter",
|
|
21
|
+
"type": "(value: string, search: string, keywords?: string[]) => number"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "Default selected item value on initial render (uncontrolled).",
|
|
25
|
+
"name": "defaultValue",
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"description": "Controlled state of the currently selected item value.",
|
|
30
|
+
"name": "value",
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"description": "Called when the selected item changes.",
|
|
35
|
+
"name": "onValueChange",
|
|
36
|
+
"type": "(value: string) => void"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"defaultValue": "false",
|
|
40
|
+
"description": "When true, keyboard arrow-key navigation wraps from last item back to first and vice versa.",
|
|
41
|
+
"name": "loop",
|
|
42
|
+
"type": "boolean"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"defaultValue": "false",
|
|
46
|
+
"description": "When true, pointer events cannot select items — keyboard only.",
|
|
47
|
+
"name": "disablePointerSelection",
|
|
48
|
+
"type": "boolean"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"defaultValue": "true",
|
|
52
|
+
"description": "Set to false to disable ctrl+n/j/p/k vim-style navigation shortcuts.",
|
|
53
|
+
"name": "vimBindings",
|
|
54
|
+
"type": "boolean"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"defaultValue": "false",
|
|
58
|
+
"description": "antd List `split`. Draws the list as ONE ruled box: group/list padding 0, rows full-bleed to the panel edge with a square highlight, a hairline border-block-end between rows and none after the last visible row, row content aligned with CommandInput's leading glyph. Sets `data-split` on the root; the parts need no prop. Tokens: --command-list-split-padding, --command-list-split-inset, --command-item-divider-width, --command-item-divider-color. Select mode=multiple / mode=tags renders its popup split by default.",
|
|
59
|
+
"name": "split",
|
|
60
|
+
"type": "boolean"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"description": "Additional CSS classes merged onto the root div via cn().",
|
|
64
|
+
"name": "className",
|
|
65
|
+
"type": "string"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"description": "Controlled search string for the input.",
|
|
69
|
+
"name": "CommandInput.value",
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"description": "Called when the search input text changes.",
|
|
74
|
+
"name": "CommandInput.onValueChange",
|
|
75
|
+
"type": "(search: string) => void"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"description": "Accessible label for the list of suggestions. Not shown visually.",
|
|
79
|
+
"name": "CommandList.label",
|
|
80
|
+
"type": "string"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"description": "Unique value for this item. If omitted, inferred from children textContent — must be stable; provide explicitly when text changes between renders.",
|
|
84
|
+
"name": "CommandItem.value",
|
|
85
|
+
"type": "string"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"description": "Called when this item is selected via click or keyboard.",
|
|
89
|
+
"name": "CommandItem.onSelect",
|
|
90
|
+
"type": "(value: string) => void"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"defaultValue": "false",
|
|
94
|
+
"description": "Prevents this item from being selected.",
|
|
95
|
+
"name": "CommandItem.disabled",
|
|
96
|
+
"type": "boolean"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"description": "Additional keywords matched during filtering that are not part of the visible label.",
|
|
100
|
+
"name": "CommandItem.keywords",
|
|
101
|
+
"type": "string[]"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"defaultValue": "false",
|
|
105
|
+
"description": "When true, renders this item regardless of filtering results.",
|
|
106
|
+
"name": "CommandItem.forceMount",
|
|
107
|
+
"type": "boolean"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"description": "Visible heading rendered above items in this group.",
|
|
111
|
+
"name": "CommandGroup.heading",
|
|
112
|
+
"type": "React.ReactNode"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"description": "Required unique identifier for the group when no heading is provided.",
|
|
116
|
+
"name": "CommandGroup.value",
|
|
117
|
+
"type": "string"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"defaultValue": "false",
|
|
121
|
+
"description": "When true, renders this group regardless of filtering.",
|
|
122
|
+
"name": "CommandGroup.forceMount",
|
|
123
|
+
"type": "boolean"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"description": "Renders automatically only when there are no matching results. Place inside CommandList.",
|
|
127
|
+
"name": "CommandEmpty",
|
|
128
|
+
"type": "React.ReactNode (children)"
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
"related": [
|
|
132
|
+
"SearchSelect — higher-level compound component that composes Command + Popover + server search; use SearchSelect for a fully managed async combobox instead of building your own with Command.",
|
|
133
|
+
"Select — simple dropdown for static option lists without type-to-filter; use Select when there are fewer than ~10 options and no search is needed.",
|
|
134
|
+
"SearchInput — standalone text input with a search icon; use SearchInput for filtering visible page content (tables, lists) not for selecting from a command menu.",
|
|
135
|
+
"CommandInput (sub-part) — the styled search input that only works inside a Command root; never use it alone as a general search field."
|
|
136
|
+
],
|
|
137
|
+
"rules": [
|
|
138
|
+
2,
|
|
139
|
+
3,
|
|
140
|
+
6,
|
|
141
|
+
23
|
|
142
|
+
],
|
|
143
|
+
"storyPath": "data-entry/Command.stories.tsx",
|
|
144
|
+
"subParts": [
|
|
145
|
+
"CommandEmpty",
|
|
146
|
+
"CommandGroup",
|
|
147
|
+
"CommandInput",
|
|
148
|
+
"CommandItem",
|
|
149
|
+
"CommandList"
|
|
150
|
+
],
|
|
151
|
+
"tagline": "Accessible, keyboard-navigable command palette / combobox list built on cmdk — always pair CommandInput inside its own wrapper div, never render items outside CommandList.",
|
|
152
|
+
"usage": [
|
|
153
|
+
"DO compose the full tree: Command > CommandInput + CommandList > (CommandEmpty | CommandGroup > CommandItem | CommandItem). Every interactive element must live inside CommandList; items outside it are invisible to the keyboard engine.",
|
|
154
|
+
"DO set shouldFilter={false} and manage filtering yourself when the options list comes from a server/async source (e.g. SearchSelect pattern). With shouldFilter=true the default client-side scoring runs over all rendered items automatically.",
|
|
155
|
+
"DO always provide a stable explicit value prop on CommandItem when the item's text content can change between renders — relying on inferred textContent with dynamic labels causes selection bugs.",
|
|
156
|
+
"DO include CommandEmpty inside CommandList to show a no-results message. It renders automatically only when the filtered count is zero; do not conditionally render it yourself.",
|
|
157
|
+
"DO use split for checkbox/option lists (a filter facet, a picker: Popover + PopoverContent flush + Command split) — rows get a divider between them and 0 list padding, so they read as one box. Keep the default (unsplit) for a command palette. Never re-create the look with page CSS (border on items, padding 0 on the group): retune --command-item-divider-color / --command-item-divider-width instead.",
|
|
158
|
+
"DON'T use CommandInput as a standalone search input — it is only meaningful inside a Command root (the root manages shared filter state). For a standalone search field use SearchInput instead.",
|
|
159
|
+
"DON'T hand-roll keyboard navigation on a list of items; Command handles arrow keys, Enter, Escape, Home/End, and vim bindings. Adding your own keyDown handlers on top creates conflicts — use onSelect on CommandItem for selection logic."
|
|
160
|
+
],
|
|
161
|
+
"useCases": [
|
|
162
|
+
"Command palette / global action launcher (Cmd+K menu): wraps Command + CommandInput + grouped CommandItems for quick navigation across pages or actions.",
|
|
163
|
+
"Popover-based combobox with server-side search: Command with shouldFilter={false} inside a Popover, managing the query state externally and filtering options before rendering — this is exactly how SearchSelect is built internally.",
|
|
164
|
+
"Tree/cascader search panel: inject Command + CommandInput as a search header above a custom scroll area (no CommandList needed for the tree body) to get a styled, accessible search input — as used by CascaderSelect and TreeSelect.",
|
|
165
|
+
"Multi-group option picker: use CommandGroup with heading to visually separate option categories (e.g. 'Accounts', 'Contacts', 'Documents') inside one dropdown with a single search box.",
|
|
166
|
+
"Keyboard-first admin shortcut bar: embed Command with loop={true} and vimBindings={true} in a persistent sidebar for power-user keyboard navigation without page reloads."
|
|
167
|
+
]
|
|
168
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { CommandPalette } from \"@godxjp/ui/data-entry\";\n\n<CommandPalette\n groups={[{ id: \"pages\", label: \"Pages\", items: [{ id: \"home\", label: \"Home\" }] }]}\n labels={{ open: \"Open commands\", title: \"Commands\", description: \"Choose a command\", placeholder: \"Search\", empty: \"No results\" }}\n onSelect={(item) => navigate(item.id)}\n/>",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "CommandPalette",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Grouped command items.",
|
|
9
|
+
"name": "groups",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "CommandPaletteGroup[]"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Localized dialog and search copy.",
|
|
15
|
+
"name": "labels",
|
|
16
|
+
"required": true,
|
|
17
|
+
"type": "CommandPaletteLabels"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Consumer-owned selection handler.",
|
|
21
|
+
"name": "onSelect",
|
|
22
|
+
"required": true,
|
|
23
|
+
"type": "(item: CommandPaletteItem) => void"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"description": "Controlled open state.",
|
|
27
|
+
"name": "open",
|
|
28
|
+
"type": "boolean"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"defaultValue": "false",
|
|
32
|
+
"description": "Initial uncontrolled open state.",
|
|
33
|
+
"name": "defaultOpen",
|
|
34
|
+
"type": "boolean"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"description": "Open-state callback.",
|
|
38
|
+
"name": "onOpenChange",
|
|
39
|
+
"type": "(open: boolean) => void"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"description": "Controlled search-box query. Pairs with `onSearchChange`.",
|
|
43
|
+
"name": "search",
|
|
44
|
+
"type": "string"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"defaultValue": "\"\"",
|
|
48
|
+
"description": "Initial uncontrolled query, and the value the palette resets to when it closes.",
|
|
49
|
+
"name": "defaultSearch",
|
|
50
|
+
"type": "string"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"description": "Fires on every keystroke with the current query — the seam for search-as-you-type. Also fires with `defaultSearch` when the palette closes.",
|
|
54
|
+
"name": "onSearchChange",
|
|
55
|
+
"type": "(query: string) => void"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"defaultValue": "true",
|
|
59
|
+
"description": "Whether the palette filters `groups` itself. Set false for server-side search, which also hands the empty node to the palette (derived from `groups`).",
|
|
60
|
+
"name": "shouldFilter",
|
|
61
|
+
"type": "boolean"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"defaultValue": "false",
|
|
65
|
+
"description": "Shows the supplied loading content.",
|
|
66
|
+
"name": "loading",
|
|
67
|
+
"type": "boolean"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"description": "Consumer-supplied error content.",
|
|
71
|
+
"name": "error",
|
|
72
|
+
"type": "ReactNode"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"rules": [],
|
|
76
|
+
"storyPath": "data-entry/CommandPalette.stories.tsx",
|
|
77
|
+
"tagline": "Searchable command dialog with controlled or uncontrolled open state and consumer-owned selection.",
|
|
78
|
+
"usage": [
|
|
79
|
+
"Provide localized labels and real command groups; the component does not fetch commands.",
|
|
80
|
+
"Use either `open` plus `onOpenChange` or `defaultOpen`; do not mirror both state models.",
|
|
81
|
+
"Search-as-you-type against an API: read the query from `onSearchChange`, pass the results back as `groups`, and set `shouldFilter={false}` so the rows are not scored a second time against the same string.",
|
|
82
|
+
"The empty-state contract: with `shouldFilter` (default) cmdk decides — items exist, the query matches none. With `shouldFilter={false}` the PALETTE decides from props — `labels.empty` renders when `groups` carries no items, and never while `loading` or `error` is set. Hold `loading` for the whole in-flight window: a request that has not answered yet is not an empty result, and asserting over cmdk's own empty node races an async group that populates a frame late."
|
|
83
|
+
]
|
|
84
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { CompactBarTrend } from \"@godxjp/ui/charts/compact-bar-trend\";\n\n<CompactBarTrend\n label={t(\"dashboard.newOrganizations7d\")}\n description={t(\"dashboard.newOrganizationsHint\")}\n data={trend}\n categoryKey=\"date\"\n valueKey=\"count\"\n emphasizedIndex={-1}\n size=\"xs\"\n footer={<Text size=\"xs\" tone=\"muted\">{t(\"dashboard.lastUpdated\", { at })}</Text>}\n/>",
|
|
3
|
+
"group": "data-display",
|
|
4
|
+
"importPath": "@godxjp/ui/charts/compact-bar-trend",
|
|
5
|
+
"name": "CompactBarTrend",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Row data — one bar per row. Any point count (7 is not hard-coded).",
|
|
9
|
+
"name": "data",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "ChartDatum[]"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Key into each datum holding the category (tick) label.",
|
|
15
|
+
"name": "categoryKey",
|
|
16
|
+
"required": true,
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Key into each datum holding the plotted numeric value.",
|
|
21
|
+
"name": "valueKey",
|
|
22
|
+
"required": true,
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"description": "Accessible name + visible caption.",
|
|
27
|
+
"name": "label",
|
|
28
|
+
"required": true,
|
|
29
|
+
"type": "string"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"defaultValue": "true",
|
|
33
|
+
"description": "Paint `label` as a visible caption. Set false when a CardTitle or section heading already says it — the caption stays in the DOM as sr-only, so role=img keeps its accessible name.",
|
|
34
|
+
"name": "showCaption",
|
|
35
|
+
"type": "boolean"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"description": "Extra context appended to the screen-reader description.",
|
|
39
|
+
"name": "description",
|
|
40
|
+
"type": "string"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"defaultValue": "\"xs\"",
|
|
44
|
+
"description": "Plot-height tier (--chart-trend-plot-height-*). xs is the dashboard summary-card density.",
|
|
45
|
+
"name": "size",
|
|
46
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"description": "Index of the emphasized 'current' bar. Negative counts from the end (-1 = latest); out of range = no emphasis. Also annotated in the text alternative, so it is never colour-only.",
|
|
50
|
+
"name": "emphasizedIndex",
|
|
51
|
+
"type": "number"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"defaultValue": "true",
|
|
55
|
+
"description": "Render the category tick labels under the plot.",
|
|
56
|
+
"name": "showCategoryLabels",
|
|
57
|
+
"type": "boolean"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"description": "Locale-aware formatting for the values in the text alternative.",
|
|
61
|
+
"name": "numberFormat",
|
|
62
|
+
"type": "Intl.NumberFormatOptions"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"description": "Activity footer slot below the plot, rendered OUTSIDE the role=img graphic so links/buttons in it stay reachable.",
|
|
66
|
+
"name": "footer",
|
|
67
|
+
"type": "ReactNode"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"description": "Message shown when `data` is empty.",
|
|
71
|
+
"name": "emptyMessage",
|
|
72
|
+
"type": "string"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"description": "Forwarded to the <figure> element.",
|
|
76
|
+
"name": "ref",
|
|
77
|
+
"type": "React.Ref<HTMLElement>"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"related": [
|
|
81
|
+
"BarChart — full cartesian bar chart with axes/grid/tooltip/legend (requires the recharts optional peer).",
|
|
82
|
+
"StatCard — the KPI headline this trend usually sits under.",
|
|
83
|
+
"Progress — one ratio against a target, not a series over time."
|
|
84
|
+
],
|
|
85
|
+
"rules": [],
|
|
86
|
+
"storyPath": "charts/CompactBarTrend.stories.tsx",
|
|
87
|
+
"tagline": "DEPENDENCY-FREE compact vertical bar trend for dashboard summary cards — N category/value pairs, muted marks plus ONE emphasized 'current' bar, all geometry from --chart-trend-* tokens. Needs NO recharts. Sparkline / micro-chart / activity pulse / KPI trend strip.",
|
|
88
|
+
"usage": [
|
|
89
|
+
"DO import from the isolated entry: `import { CompactBarTrend } from \"@godxjp/ui/charts/compact-bar-trend\";` — unlike the charts barrel, this path never links modules that require the optional `recharts` peer.",
|
|
90
|
+
"DO reach for it INSIDE a dashboard summary card (a 7-day signup/organization/activity strip under a StatCard headline) — that is the density `size=\"xs\"` is tuned for.",
|
|
91
|
+
"DO mark the current period with `emphasizedIndex={-1}` (or an explicit index); the highlight is duplicated in the screen-reader text alternative, so it never depends on colour alone.",
|
|
92
|
+
"DO retheme through the `--chart-trend-*` tokens (bar gap/radius/width, plot heights, muted + emphasis fills, opt-in baseline). NEVER add page-local CSS, an inline height calculation, or a hardcoded colour.",
|
|
93
|
+
"DON'T use it when you need axes, a grid, tooltips, multiple series, or a continuous trend — that is BarChart / LineChart / AreaChart (recharts peer). DON'T fake bars with styled divs in the app.",
|
|
94
|
+
"DON'T put interactive content in the plot: anything clickable goes in `footer`, which renders outside the `role=\"img\"` graphic."
|
|
95
|
+
],
|
|
96
|
+
"useCases": [
|
|
97
|
+
"Seven-day new-organizations / new-users trend inside an admin dashboard summary card (SCR-201).",
|
|
98
|
+
"Weekly activity pulse beside a KPI headline, where the SHAPE matters more than exact figures.",
|
|
99
|
+
"A dependency-constrained app (no recharts) that still needs a token-driven chart."
|
|
100
|
+
]
|
|
101
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"docPath": "navigation/conversations.tsx",
|
|
3
|
+
"example": "import { Conversations } from \"@godxjp/ui/navigation\";\n\nconst [active, setActive] = useState(\"c1\");\n\n<Conversations\n activeKey={active}\n onActiveChange={setActive}\n groupable={{ collapsible: true }}\n creation={{ onClick: () => startNewChat() }}\n items={[\n { key: \"c1\", label: \"請求書の下書き\", group: \"today\" },\n { key: \"c2\", label: \"経費精算の規則\", group: \"today\" },\n { key: \"c3\", label: \"出張手当の確認\", group: \"earlier\" },\n ]}\n menu={{\n items: [\n { key: \"rename\", label: \"名前を変更\" },\n { key: \"delete\", label: \"削除\", danger: true },\n ],\n onClick: ({ key, conversation }) => run(key, conversation.key),\n }}\n/>",
|
|
4
|
+
"group": "navigation",
|
|
5
|
+
"importPath": "@godxjp/ui/navigation",
|
|
6
|
+
"name": "Conversations",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"description": "The rows. A conversation is { key, label?, group?, icon?, disabled? }; a rule between runs is { type: \"divider\", key?, dashed? }. Ant Design X `items`.",
|
|
10
|
+
"name": "items",
|
|
11
|
+
"type": "(ConversationsItemProp | ConversationsDividerProp)[]"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Controlled selection — the key of the conversation on screen. Ant Design X `activeKey`.",
|
|
15
|
+
"name": "activeKey",
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "Uncontrolled initial selection. Ant Design X `defaultActiveKey`.",
|
|
20
|
+
"name": "defaultActiveKey",
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "Fires with the picked key and the entry behind it. Ant Design X `onActiveChange`.",
|
|
25
|
+
"name": "onActiveChange",
|
|
26
|
+
"type": "(key: string, item?: ConversationsItemProp | ConversationsDividerProp) => void"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"description": "The per-row overflow menu: { items: [{ key, label, icon?, danger?, disabled? }], onClick?, triggerLabel? }. Pass a function to vary it per row, or return undefined for a row that has no menu. Ant Design X `menu` (antd MenuProps there).",
|
|
30
|
+
"name": "menu",
|
|
31
|
+
"type": "ConversationsMenuProp | ((conversation: ConversationsItemProp) => ConversationsMenuProp | undefined)"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"description": "Bucket rows by their `group` field. The object form takes label (node or (group) => node), collapsible (boolean or (group) => boolean), defaultExpandedKeys, expandedKeys and onExpand. Ant Design X `groupable`.",
|
|
35
|
+
"name": "groupable",
|
|
36
|
+
"type": "boolean | ConversationsGroupableProp"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"description": "The \"new conversation\" button pinned above the rail: { label?, icon?, disabled?, onClick? }. Ant Design X `creation`.",
|
|
40
|
+
"name": "creation",
|
|
41
|
+
"type": "ConversationsCreationProp"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"description": "Accessible name of the rail (a plain string — it lands on aria-label). Localized default otherwise.",
|
|
45
|
+
"name": "label",
|
|
46
|
+
"type": "string"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"description": "DOM id of the rail root.",
|
|
50
|
+
"name": "id",
|
|
51
|
+
"type": "string"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"related": [
|
|
55
|
+
"ChatBubbleList — the feed beside this rail; Conversations picks WHICH feed is shown.",
|
|
56
|
+
"ListRow — a single-line entity row with a trailing action, for short lists inside a Card. It has no selection, no roving focus and no grouping.",
|
|
57
|
+
"Sidebar / NavList — route navigation. Use those when a row changes the URL; use Conversations when a row changes which conversation the surface is on.",
|
|
58
|
+
"DropdownMenu — what the per-row `menu` renders; compose it directly when the menu is not attached to a conversation row."
|
|
59
|
+
],
|
|
60
|
+
"rules": [
|
|
61
|
+
2,
|
|
62
|
+
6,
|
|
63
|
+
23,
|
|
64
|
+
44,
|
|
65
|
+
45
|
|
66
|
+
],
|
|
67
|
+
"storyPath": "navigation/Conversations.stories.tsx",
|
|
68
|
+
"tagline": "The session rail of a chat surface (Ant Design X Conversations): past conversations, the current one marked with aria-current, a per-row overflow menu, and recency buckets — the whole rail one roving-tabindex tab stop, not one tab stop per conversation.",
|
|
69
|
+
"usage": [
|
|
70
|
+
"DO give every conversation a stable `key` — it is what activeKey, onActiveChange and the menu callback all address. A key that changes on re-render moves the selection.",
|
|
71
|
+
"DO reach for `menu` for rename/delete instead of adding a second Button to each row. The trigger is keyboard-reachable with the forward arrow (→ in LTR, ← in RTL), so a row's second action costs no extra tab stop.",
|
|
72
|
+
"DO pass `menu.triggerLabel` when the rows are user content: the default names the row, and twelve identical \"More actions\" buttons are indistinguishable in a screen reader's element list.",
|
|
73
|
+
"DON'T hand-roll the rail out of full-width Buttons plus aria-current. That is one tab stop PER conversation; this is one for the whole rail, with ↑/↓/Home/End inside it.",
|
|
74
|
+
"DON'T expect `styles`/`classNames` from Ant Design X — they are deliberately not ported. Retune the rail through the --conversations-* tokens (rules #44/#45).",
|
|
75
|
+
"DO use `groupable={{ collapsible: true }}` for \"Today / Previous 7 days\": the bucket headings join the same roving order, so collapsing a bucket is reachable without leaving the rail."
|
|
76
|
+
],
|
|
77
|
+
"useCases": [
|
|
78
|
+
"The assistant rail of a chat product — past sessions, the current one marked, rename and delete per row.",
|
|
79
|
+
"Recency buckets over a long history (Today / Yesterday / Previous 7 days) with the older buckets collapsed.",
|
|
80
|
+
"A rail beside ChatBubbleList and ChatComposer: the three are one surface, and Conversations is the half that used to be missing."
|
|
81
|
+
]
|
|
82
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { CredentialReveal } from \"@godxjp/ui/data-display\";\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from \"@godxjp/ui/feedback\";\n\n<Dialog open={open} onOpenChange={setOpen}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>APIキーを発行しました</DialogTitle>\n </DialogHeader>\n <CredentialReveal\n label=\"APIキー\"\n secret=\"gxp_live_8Fh2kQ9wR7nZ1xV4bT6mL0cD\"\n downloadable\n onAcknowledge={() => setOpen(false)}\n />\n </DialogContent>\n</Dialog>",
|
|
3
|
+
"group": "data-display",
|
|
4
|
+
"importPath": "@godxjp/ui/data-display",
|
|
5
|
+
"name": "CredentialReveal",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "The one-time secret value.",
|
|
9
|
+
"name": "secret",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Accessible name / caption for the secret (e.g. 'API key').",
|
|
15
|
+
"name": "label",
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "Caution banner copy; defaults to a localized 'shown only once' warning. Pass null to suppress the banner.",
|
|
20
|
+
"name": "warning",
|
|
21
|
+
"type": "React.ReactNode | null"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "Controlled reveal state (with defaultRevealed / onRevealedChange).",
|
|
25
|
+
"name": "revealed",
|
|
26
|
+
"type": "boolean"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"defaultValue": "false",
|
|
30
|
+
"description": "Uncontrolled initial reveal state.",
|
|
31
|
+
"name": "defaultRevealed",
|
|
32
|
+
"type": "boolean"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"description": "Reveal toggle handler.",
|
|
36
|
+
"name": "onRevealedChange",
|
|
37
|
+
"type": "(revealed: boolean) => void"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"description": "Called after the secret is written to the clipboard.",
|
|
41
|
+
"name": "onCopy",
|
|
42
|
+
"type": "(secret: string) => void"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"description": "Renders a confirm button; wire it to the Dialog's onOpenChange(false).",
|
|
46
|
+
"name": "onAcknowledge",
|
|
47
|
+
"type": "() => void"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"defaultValue": "false",
|
|
51
|
+
"description": "Offer a download-as-file button.",
|
|
52
|
+
"name": "downloadable",
|
|
53
|
+
"type": "boolean"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"defaultValue": "\"md\"",
|
|
57
|
+
"description": "Action button size tier.",
|
|
58
|
+
"name": "size",
|
|
59
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"defaultValue": "\"warning\"",
|
|
63
|
+
"description": "Caution banner severity.",
|
|
64
|
+
"name": "tone",
|
|
65
|
+
"type": "\"warning\" | \"destructive\" | \"info\""
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"related": [
|
|
69
|
+
"PasswordInput — editable password/secret ENTRY with a show/hide toggle (data-entry); CredentialReveal is read-only DISPLAY of an issued secret.",
|
|
70
|
+
"Dialog — the modal CredentialReveal is designed to live inside.",
|
|
71
|
+
"Alert — the caution banner CredentialReveal composes internally."
|
|
72
|
+
],
|
|
73
|
+
"rules": [
|
|
74
|
+
3,
|
|
75
|
+
6,
|
|
76
|
+
23
|
|
77
|
+
],
|
|
78
|
+
"storyPath": "data-display/CredentialReveal.stories.tsx",
|
|
79
|
+
"tagline": "One-time secret surface — masked-by-default value with a show/hide toggle, a copy button that confirms the copy, optional download, and an optional acknowledge action to pair with Dialog. The GitHub/Stripe token-reveal pattern as a real primitive so consumers stop hand-rolling it.",
|
|
80
|
+
"usage": [
|
|
81
|
+
"DO use for a secret shown exactly once after creation (device credential, API key, service-account secret) — it masks by default and confirms the copy.",
|
|
82
|
+
"DO pair it inside a Dialog and reset via controlled `revealed`/`onRevealedChange` (or let it re-blur automatically when the `secret` prop changes) so a reopened dialog starts masked.",
|
|
83
|
+
"DO pass `onAcknowledge` to gate the dialog close behind an explicit 'I've saved it' confirmation.",
|
|
84
|
+
"DON'T use it for an editable password field — that's PasswordInput. CredentialReveal is read-only display of an issued secret.",
|
|
85
|
+
"DON'T hand-roll the copy button + copied-state + aria-live announcement; it's built in."
|
|
86
|
+
],
|
|
87
|
+
"useCases": [
|
|
88
|
+
"Device credential issued after enrollment",
|
|
89
|
+
"API key / personal access token shown once on creation",
|
|
90
|
+
"Service-account secret / client secret reveal",
|
|
91
|
+
"Recovery code or one-time bootstrap password"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { DataState } from \"@godxjp/ui/query\";\n\n<DataState query={membersQuery} skeleton={<SkeletonTable />} isEmpty={(d) => d.items.length === 0} empty={<EmptyState title=\"会員なし\" />}>\n {(d) => <MemberTable items={d.items} />}\n</DataState>",
|
|
3
|
+
"group": "data-display",
|
|
4
|
+
"importPath": "@godxjp/ui/query",
|
|
5
|
+
"name": "DataState",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "The useQuery result.",
|
|
9
|
+
"name": "query",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "UseQueryResult<T>"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Shown while loading.",
|
|
15
|
+
"name": "skeleton",
|
|
16
|
+
"required": true,
|
|
17
|
+
"type": "ReactNode"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Shown when the query is disabled/unstarted (pending + fetchStatus idle).",
|
|
21
|
+
"name": "prerequisite",
|
|
22
|
+
"type": "ReactNode"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"defaultValue": "false",
|
|
26
|
+
"description": "Force Retry even for non-transient causes. Retry is offered automatically for transient/network/5xx errors regardless of this flag.",
|
|
27
|
+
"name": "showRetry",
|
|
28
|
+
"type": "boolean"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"description": "Recovery for 401 / expired-token errors: renew the session or sign in again. A 401 renders this action instead of Retry.",
|
|
32
|
+
"name": "onAuthError",
|
|
33
|
+
"type": "() => void"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"description": "Render function with resolved data.",
|
|
37
|
+
"name": "children",
|
|
38
|
+
"required": true,
|
|
39
|
+
"type": "(data) => ReactNode"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"description": "Shown when isEmpty(data) is true.",
|
|
43
|
+
"name": "empty",
|
|
44
|
+
"type": "ReactNode"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"description": "Custom empty check.",
|
|
48
|
+
"name": "isEmpty",
|
|
49
|
+
"type": "(data) => boolean"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"related": [
|
|
53
|
+
"InfiniteQueryState — use instead of DataState when the query is `useInfiniteQuery`; it accepts a `flatten` function to reduce pages and adds a load-more footer. DataState cannot accept `UseInfiniteQueryResult`.",
|
|
54
|
+
"SkeletonTable / SkeletonStat — pass as the `skeleton` prop of DataState; they are not standalone replacements for DataState, only the loading slot inside it.",
|
|
55
|
+
"EmptyState — pass as the `empty` prop of DataState alongside a matching `isEmpty` predicate; do not hand-roll an empty-check outside DataState by inspecting `query.data` yourself.",
|
|
56
|
+
"AlertMutationFeedback — sibling widget for mutation (not query) lifecycle; use it below a form submit button to surface `useMutation` errors, not DataState which only handles `useQuery`."
|
|
57
|
+
],
|
|
58
|
+
"rules": [],
|
|
59
|
+
"storyPath": "query/DataState.stories.tsx",
|
|
60
|
+
"tagline": "TanStack Query lifecycle widget — skeleton / error / empty / success for one useQuery block. Import from @godxjp/ui/query.",
|
|
61
|
+
"usage": [
|
|
62
|
+
"DO: pass a `UseQueryResult<T>` directly from `useQuery` — DataState reads `isPending`, `isError`, `isFetching`, `data`, and `error` off it; never destructure those fields manually and branch yourself.",
|
|
63
|
+
"DO: always provide a `skeleton` — it renders during both the initial pending phase and during a re-fetch after an error; pass `<SkeletonTable />` for tabular data or `<SkeletonStat />` for stat card lists — never `null` or a spinner div.",
|
|
64
|
+
"DO: provide `empty` + `isEmpty` together when the data can legitimately return 0 items — e.g. `isEmpty={(d) => d.items.length === 0}` paired with `empty={<EmptyState title=\"…\" />}`. Omitting `empty` means an empty array still falls through to `children`, silently rendering a blank table.",
|
|
65
|
+
"DON'T: wrap DataState in your own conditional — e.g. `{query.isSuccess && <DataState …>}`. DataState IS the conditional; the outer guard is redundant and breaks the retry/refetch skeleton.",
|
|
66
|
+
"DON'T: use DataState for `useInfiniteQuery` results. The `query` prop type is `UseQueryResult<T>`, not `UseInfiniteQueryResult`. Use `InfiniteQueryState` (from `@godxjp/ui/query`) instead, which accepts `flatten` and renders a load-more footer.",
|
|
67
|
+
"DO: classify errors by cause. Use session renewal/sign-in for 401, access guidance for 403, contextual correction for domain errors, and opt into showRetry only for transient network/5xx errors.",
|
|
68
|
+
"DO: pass prerequisite for enabled:false queries. Pending + fetchStatus idle is unstarted, not loading, and never renders a skeleton.",
|
|
69
|
+
"DO: rely on the localized, cause-specific error message — the raw backend/token/stack text is never shown. For a domain-specific message (e.g. a 422 field error) pass a custom errorRenderer.",
|
|
70
|
+
"DO: expect a background refetch over existing data to keep the content on screen with a polite sr-only busy status — it does not flash the skeleton. Only the initial fetch (isPending) shows the skeleton."
|
|
71
|
+
],
|
|
72
|
+
"useCases": [
|
|
73
|
+
"A detail page that loads a single invoice/journal entry via `useQuery` — DataState renders the skeleton row while fetching, an error alert with retry if the API fails, and the `<InvoiceCard>` only when data is confirmed non-null.",
|
|
74
|
+
"A list page that shows a `DataTable` of members/partners — wrap the table in DataState so the skeleton matches the column count while loading and `EmptyState` appears when the filtered result set is empty.",
|
|
75
|
+
"A sidebar panel that lazily loads related transactions for the selected entity — DataState keeps the panel in skeleton state during the background fetch without any manual `isPending` branching in the parent.",
|
|
76
|
+
"A dashboard stat card that calls a summary API — DataState handles the loading/error/empty lifecycle so `<StatCard>` is only rendered with fully resolved numbers, preventing NaN or undefined rendering.",
|
|
77
|
+
"Any page using `useQuery` where the empty state and loading state are visually different — DataState enforces the correct visual for each phase without scattered `if` statements across the component tree."
|
|
78
|
+
]
|
|
79
|
+
}
|