@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,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { InfiniteQueryState, flattenItemPages } from \"@godxjp/ui/query\";\n\n<InfiniteQueryState query={q} skeleton={<SkeletonRows />} flatten={flattenItemPages} isEmpty={(it) => it.length === 0}>\n {(items) => items.map((a) => <ActivityRow key={a.id} activity={a} />)}\n</InfiniteQueryState>",
|
|
3
|
+
"group": "data-display",
|
|
4
|
+
"importPath": "@godxjp/ui/query",
|
|
5
|
+
"name": "InfiniteQueryState",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "The useInfiniteQuery result.",
|
|
9
|
+
"name": "query",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "UseInfiniteQueryResult"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Shown while initial load pends.",
|
|
15
|
+
"name": "skeleton",
|
|
16
|
+
"required": true,
|
|
17
|
+
"type": "ReactNode"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Reduce pages to a flat list (use flattenItemPages helper).",
|
|
21
|
+
"name": "flatten",
|
|
22
|
+
"required": true,
|
|
23
|
+
"type": "(data) => TFlat"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"description": "Render with flat data + { fetchNextPage, hasNextPage, isFetchingNextPage }.",
|
|
27
|
+
"name": "children",
|
|
28
|
+
"required": true,
|
|
29
|
+
"type": "(flat, helpers) => ReactNode"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"related": [
|
|
33
|
+
"DataState — use instead when the query is a plain `useQuery` (not infinite). Identical lifecycle surface (skeleton/empty/error/children) but expects a single page of data, not accumulated pages. Pick DataState for any paginated table where only one page is visible at a time.",
|
|
34
|
+
"DataTable — use for tabular data with server-side pagination where pages are swapped, not appended. DataTable manages its own pagination UI (cursor buttons); InfiniteQueryState is for append-only / infinite-scroll patterns.",
|
|
35
|
+
"SkeletonTable / SkeletonStat — pass as the `skeleton` prop to InfiniteQueryState; do not render them manually alongside InfiniteQueryState since the component controls when skeleton is visible.",
|
|
36
|
+
"ButtonRefetch — companion component for the page header refresh action wired to `query.refetch()`. Use alongside InfiniteQueryState when you want an explicit refresh control in addition to the built-in load-more footer."
|
|
37
|
+
],
|
|
38
|
+
"rules": [],
|
|
39
|
+
"storyPath": "query/InfiniteQueryState.stories.tsx",
|
|
40
|
+
"tagline": "useInfiniteQuery widget — flatten pages, skeleton/empty/error, load-more footer. Import from @godxjp/ui/query.",
|
|
41
|
+
"usage": [
|
|
42
|
+
"DO: Import from `@godxjp/ui/query` (not `@godxjp/ui`). Use the bundled `flattenItemPages` helper for any API that returns `{ items: T[] }` pages — it handles `undefined` data safely. Custom page shapes require a custom `flatten` function.",
|
|
43
|
+
"DO: Always pass `skeleton` (e.g. `<SkeletonTable />` or `<SkeletonStat />`). It shows on initial `isPending`, on refetch-after-error, and whenever `data` is absent. Never show a blank area while loading.",
|
|
44
|
+
"DO: Pass `empty` (an `<EmptyState>` node) to handle the zero-results case — without it the children render-prop is called with an empty array and you get a silent blank screen. Provide a custom `isEmpty` only when `TFlat` is not an array.",
|
|
45
|
+
"DO: Let errors remain cause-aware. Retry is automatic only for classified transient/network/5xx failures. Unknown errors do not get a blind retry unless `showRetry` or `onRetry` is explicitly supplied; 401 routes to `onAuthError`, and raw backend/token text is never rendered.",
|
|
46
|
+
"DON'T: Hand-roll a load-more button. The component renders a default centered outline Button when `hasNextPage` is true. Override only via `loadMore` (custom node) or `showLoadMore={false}` (hide entirely). Never call `query.fetchNextPage()` outside the component for pagination.",
|
|
47
|
+
"DON'T: Use `InfiniteQueryState` for a `useQuery` result — it expects `UseInfiniteQueryResult` shape (`pages`, `hasNextPage`, `fetchNextPage`, `isFetchingNextPage`). For regular `useQuery` use `DataState` instead.",
|
|
48
|
+
"DON'T: Confuse the two generics: `TPage` is the raw page shape from the API, `TFlat` is what `flatten` returns (usually `TItem[]`). The `children` render-prop receives `TFlat`, not `TPage`. Pass `isEmpty` if `TFlat` is not a plain array so empty detection works correctly."
|
|
49
|
+
],
|
|
50
|
+
"useCases": [
|
|
51
|
+
"Activity / audit-log feed that accumulates pages as the user scrolls down or clicks 'Load more' — the default footer button handles `fetchNextPage` automatically.",
|
|
52
|
+
"Invoice or transaction list with cursor-based pagination where total count is unknown and pages are appended rather than replaced (replacing pages is DataTable's job).",
|
|
53
|
+
"Notification inbox, comment thread, or journal entry list where new items are appended at the bottom and the user never pages backwards.",
|
|
54
|
+
"Search results with a 'Show more' button rather than numbered pages — pass `showLoadMore={true}` (default) and hide the button once `hasNextPage` is false without any extra state.",
|
|
55
|
+
"Admin dashboard 'recent events' widget backed by `useInfiniteQuery` — use `SkeletonTable` as `skeleton` and `<EmptyState title='No events yet' />` as `empty` so every state is handled.",
|
|
56
|
+
"Infinite-scroll implementation: receive the `helpers` argument in `children` (`{ fetchNextPage, hasNextPage, isFetchingNextPage }`) to wire a scroll sentinel (Intersection Observer) instead of the built-in button, while still benefiting from error/skeleton/empty lifecycle handling."
|
|
57
|
+
]
|
|
58
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Input } from \"@godxjp/ui/data-entry\";\n\n<Input id=\"qty\" type=\"number\" placeholder=\"例: 500\" value={value} onValueChange={(e) => setValue(e.target.value)} />",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "Input",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Validation state the field paints — Ant Design `status`. `error` also reports `aria-invalid`, so the red boundary and what a screen reader hears are one fact; `warning` paints only, because a warning is not a validity failure. antd's `success`/`validating` are not implemented: antd only draws them together with its `hasFeedback` icon slot, which FormField owns here.",
|
|
9
|
+
"name": "status",
|
|
10
|
+
"type": "\"error\" | \"warning\""
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"defaultValue": "\"outlined\"",
|
|
14
|
+
"description": "Chrome level — Ant Design `variant`. `outlined` is the historical field; `filled` swaps the boundary for a tinted surface (dense forms); `borderless` drops both, for a field inside a box that already draws one. antd's fourth member `underlined` is deliberately absent — a single bottom rule is a Material convention and SmartHR, the JP authority here, draws every field as a full box.",
|
|
15
|
+
"name": "variant",
|
|
16
|
+
"type": "\"outlined\" | \"filled\" | \"borderless\""
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"defaultValue": "\"md\"",
|
|
20
|
+
"description": "Control height tier — reads the shared `--control-height` ladder.",
|
|
21
|
+
"name": "size",
|
|
22
|
+
"type": "\"sm\" | \"md\" | \"lg\""
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "antd `prefix` — content pinned INSIDE the start of the field (¥, a unit, a glyph). Unlike `leadingIcon` it is not aria-hidden, because a unit is meaning and not decoration.",
|
|
26
|
+
"name": "prefix",
|
|
27
|
+
"type": "React.ReactNode"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"description": "antd `suffix` — content pinned INSIDE the end of the field (%, 円). The clear ✕ replaces it while `allowClear` has a value, exactly as it replaces `trailingIcon`.",
|
|
31
|
+
"name": "suffix",
|
|
32
|
+
"type": "React.ReactNode"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"description": "antd `addonBefore` — a LABEL segment welded OUTSIDE the field's box (`https://`, a currency). Outside is the whole distinction from `prefix`: an addon has its own surface and closes the field's corners on the joined side. For a GLYPH use `prefix` — one box, one border, no seam. antd deprecates both addons in favour of `Space.Compact` (verified in ant-design@master: a `@deprecated` tag and a dev-time warning, still rendering); this library keeps them, because it has no `Space` and `addonBefore` IS its joined control (gh#841).",
|
|
36
|
+
"name": "addonBefore",
|
|
37
|
+
"type": "React.ReactNode"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"description": "antd `addonAfter` — the same, welded to the end of the box (`.com`, a unit). Deprecated upstream alongside `addonBefore`, and kept here for the same reason.",
|
|
41
|
+
"name": "addonAfter",
|
|
42
|
+
"type": "React.ReactNode"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"description": "Character counter — Ant Design `count`. Counts CODE POINTS by default, so one emoji and one 全角 kanji are each worth one. It REPORTS an overrun (`data-exceeded`) and never edits the value: antd's `exceedFormatter` truncates while the user types, which in Japanese cuts a live IME conversion in half.",
|
|
46
|
+
"name": "count",
|
|
47
|
+
"type": "{ max?: number; show?: boolean; formatter?: (info: { value: string; count: number; max?: number }) => React.ReactNode; strategy?: (value: string) => number }"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"description": "Associates with a <label htmlFor>.",
|
|
51
|
+
"name": "id",
|
|
52
|
+
"type": "string"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"defaultValue": "\"text\"",
|
|
56
|
+
"description": "Native input type.",
|
|
57
|
+
"name": "type",
|
|
58
|
+
"type": "string"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"description": "Placeholder.",
|
|
62
|
+
"name": "placeholder",
|
|
63
|
+
"type": "string"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"description": "Controlled value.",
|
|
67
|
+
"name": "value",
|
|
68
|
+
"type": "string | number"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"defaultValue": "false",
|
|
72
|
+
"description": "Opt-in inline ✕ that clears the field while it holds text (works controlled + uncontrolled). Off by default, so existing inputs are unchanged.",
|
|
73
|
+
"name": "allowClear",
|
|
74
|
+
"type": "boolean"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"description": "Called after the field is cleared via the inline ✕ (requires `allowClear`).",
|
|
78
|
+
"name": "onClear",
|
|
79
|
+
"type": "() => void"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"description": "A leading affordance pinned inside the start of the field (e.g. a Mail / Lock / Search glyph) — purely decorative, rendered before the text. Sized to the control via tokens and offset with `ps-9` automatically; never hand-roll an absolutely-positioned icon over a plain Input.",
|
|
83
|
+
"name": "leadingIcon",
|
|
84
|
+
"type": "React.ReactNode"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"description": "A trailing affordance pinned inside the field (e.g. a calendar / clock popover trigger). ONE trailing icon shows at a time: when `allowClear` and the field holds a value the clear ✕ REPLACES this icon; otherwise this icon shows. Never both — this is how DatePicker/TimePicker render their open trigger.",
|
|
88
|
+
"name": "trailingIcon",
|
|
89
|
+
"type": "React.ReactNode"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"description": "Immediate value callback; native onChange remains supported. Shared form bindings are emitted once.",
|
|
93
|
+
"name": "onValueChange",
|
|
94
|
+
"type": "(value: string) => void"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"related": [
|
|
98
|
+
"PasswordInput — the password field. It owns the reveal toggle, the caps-lock hint and the autocomplete contract; a raw Input with type=password re-implements all three.",
|
|
99
|
+
"SearchInput — use instead of Input when the value drives a live filter or search query; SearchInput debounces internally, fires `onSearch` (not `onChange`), and provides a built-in clear button. Never put debounce logic on top of a plain Input.",
|
|
100
|
+
"Textarea — use instead of Input for multi-line text (notes, descriptions, memo fields). Input is strictly single-line.",
|
|
101
|
+
"FormField — always compose Input inside FormField when the field needs a visible label, helper hint, or validation error message; FormField handles all a11y wiring so Input stays a pure unstyled-but-styled primitive.",
|
|
102
|
+
"Select — use instead of Input when the value must come from a fixed or async option list; never render a plain Input and parse free text when the set of valid values is enumerable."
|
|
103
|
+
],
|
|
104
|
+
"rules": [],
|
|
105
|
+
"storyPath": "data-entry/Input.stories.tsx",
|
|
106
|
+
"tagline": "Styled wrapper around native <input>; accepts all HTML input attributes. Pair with FormField for labelled fields.",
|
|
107
|
+
"usage": [
|
|
108
|
+
"DO always wrap Input in FormField when the field needs a label, helper text, or validation error — FormField injects aria-describedby and aria-invalid onto Input automatically; never wire these attributes by hand.",
|
|
109
|
+
"DO match the `id` prop on Input to the `id` prop on its parent FormField so that `htmlFor` linkage and the generated helper/error ids are consistent.",
|
|
110
|
+
"DO use Input in controlled mode (`value` + `onChange`) for forms driven by Inertia's `useForm` or React state; uncontrolled usage (no `value`) is only acceptable for fire-and-forget inline edits where form state is not needed.",
|
|
111
|
+
"DON'T use a raw `<input>` element — Input adds the full token-based styling (border-input, focus ring, disabled/invalid states, file-slot styling) and the `data-slot='input'` marker that FormField relies on to inject aria attributes.",
|
|
112
|
+
"DON'T hand-roll an error border or red ring with className — Input reads `aria-invalid` (set by FormField) and applies `border-destructive` + `ring-destructive/20` automatically; adding manual destructive classes will conflict.",
|
|
113
|
+
"DON'T use Input for multi-line text — use Textarea; DON'T use it for filtered/debounced search — use SearchInput which fires `onSearch` after a debounce and includes a clear button."
|
|
114
|
+
],
|
|
115
|
+
"useCases": [
|
|
116
|
+
"Single-line text fields in create/edit forms — invoice reference numbers, company names, contact emails, coupon codes, amounts typed as text (pair with `type='number'` for numeric entry).",
|
|
117
|
+
"Inline editable cells or quick-edit dialogs where a single short value needs to be changed (e.g. editing a journal entry memo or an account code) and full Select/DatePicker overhead is unnecessary.",
|
|
118
|
+
"File upload trigger when wrapped with `type='file'` — the file-slot classes style the native file button consistently without any extra wrapper.",
|
|
119
|
+
"Password entry fields (`type='password'`) in auth or settings screens, where the styled focus ring and disabled-state opacity are needed without building a custom control.",
|
|
120
|
+
"Numeric/currency input in accounting forms (`type='number'`, `inputMode='decimal'`) for quantities, exchange rates, or tax amounts where a free-form numeric entry is required rather than a slider or stepper."
|
|
121
|
+
]
|
|
122
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { InputOTP, InputOTPGroup, InputOTPSlot } from \"@godxjp/ui/data-entry\";\n\n<InputOTP maxLength={6}>\n <InputOTPGroup>\n <InputOTPSlot index={0} /><InputOTPSlot index={1} /><InputOTPSlot index={2} />\n <InputOTPSlot index={3} /><InputOTPSlot index={4} /><InputOTPSlot index={5} />\n </InputOTPGroup>\n</InputOTP>",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "InputOTP",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Initial uncontrolled code.",
|
|
9
|
+
"name": "defaultValue",
|
|
10
|
+
"type": "string"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Canonical value callback, compatible with FormFieldControl.",
|
|
14
|
+
"name": "onValueChange",
|
|
15
|
+
"type": "(value: string) => void"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "Mask filled visual slots without changing the submitted code.",
|
|
19
|
+
"name": "mask",
|
|
20
|
+
"type": "boolean | string"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"description": "Normalize typed and pasted codes.",
|
|
24
|
+
"name": "formatter",
|
|
25
|
+
"type": "(value: string) => string"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"description": "Shared control height tier.",
|
|
29
|
+
"name": "size",
|
|
30
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"description": "Validation appearance.",
|
|
34
|
+
"name": "status",
|
|
35
|
+
"type": "\"error\" | \"warning\""
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"description": "Shared field chrome.",
|
|
39
|
+
"name": "variant",
|
|
40
|
+
"type": "\"outlined\" | \"filled\" | \"borderless\" | \"underlined\""
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"description": "Refuse edits including paste.",
|
|
44
|
+
"name": "readOnly",
|
|
45
|
+
"type": "boolean"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"description": "Number of slots (e.g. 6).",
|
|
49
|
+
"name": "maxLength",
|
|
50
|
+
"required": true,
|
|
51
|
+
"type": "number"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"description": "Controlled value.",
|
|
55
|
+
"name": "value",
|
|
56
|
+
"type": "string"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"description": "Value callback (this is a true text input — onChange is the DOM-style value handler here).",
|
|
60
|
+
"name": "onChange",
|
|
61
|
+
"type": "(value: string) => void"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"description": "Allowed-char regex (e.g. digits only).",
|
|
65
|
+
"name": "pattern",
|
|
66
|
+
"type": "string"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"defaultValue": "\"start\"",
|
|
70
|
+
"description": "Main-axis alignment of the whole code row (groups + separators) inside the field. `center` is the canonical auth challenge. Before this existed, every consumer wrapped the OTP in their own flex-centring div — do not. A service that wants all code fields centred sets `--otp-container-align` once instead.",
|
|
71
|
+
"name": "align",
|
|
72
|
+
"type": "\"start\" | \"center\" | \"end\""
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"related": [
|
|
76
|
+
"Input (a normal single text field)",
|
|
77
|
+
"PasswordInput (masked secret field)"
|
|
78
|
+
],
|
|
79
|
+
"rules": [
|
|
80
|
+
3,
|
|
81
|
+
6
|
|
82
|
+
],
|
|
83
|
+
"storyPath": "data-entry/InputOTP.stories.tsx",
|
|
84
|
+
"subParts": [
|
|
85
|
+
"InputOTPGroup",
|
|
86
|
+
"InputOTPSeparator",
|
|
87
|
+
"InputOTPSlot"
|
|
88
|
+
],
|
|
89
|
+
"tagline": "One-time-code / 2FA input (input-otp) — N single-character slots that behave as one field. Compose InputOTP > InputOTPGroup > InputOTPSlot.",
|
|
90
|
+
"usage": [
|
|
91
|
+
"DO set `maxLength` to the code length and render that many InputOTPSlot with sequential `index`.",
|
|
92
|
+
"DO centre a challenge with `align=\"center\"` on InputOTP — never with a wrapper `<div className=\"flex justify-center\">`. The container is owned by input-otp, so a wrapper is the only thing a consumer CAN reach, which is exactly why the prop exists.",
|
|
93
|
+
"DO wrap slots in InputOTPGroup; use InputOTPSeparator between groups (e.g. 3 + 3).",
|
|
94
|
+
"For device codes, set `appearance='grouped'` on each InputOTPGroup to render one outline per group while preserving the single hidden input, paste, caret, keyboard and screen-reader behavior.",
|
|
95
|
+
"DON'T build N separate Inputs — this is ONE field with paste, arrow-key, and caret handling built in.",
|
|
96
|
+
"DO widen the slots with `--otp-slot-size` when a challenge row must fill a wide auth panel — it defaults to the live `--control-height` tier, so re-scoping `--control-height` on the card instead would also resize the submit button and every other input in it. Set a NAMED tier (`var(--control-height-lg)`), never an ad-hoc calc offset.",
|
|
97
|
+
"DO use `--otp-slot-inline-size` / `--otp-slot-block-size` when the code field is NOT square — a device-grant slot is taller than it is wide. They win over the `--otp-slot-size` shorthand and fall back to it, so setting neither keeps the square control tier. You rarely set them by hand inside `AuthShell preset=\"device-authorization\"`: that preset already owns its code-field measure.",
|
|
98
|
+
"DO drive the sign-in MFA challenge from FormField: `error` wires aria-invalid + aria-errormessage + a role=alert message onto the single field, and the slot borders turn destructive. State is never colour-only."
|
|
99
|
+
],
|
|
100
|
+
"useCases": [
|
|
101
|
+
"2FA / OTP verification code",
|
|
102
|
+
"Email / SMS confirmation code",
|
|
103
|
+
"PIN entry",
|
|
104
|
+
"Invite / redemption code"
|
|
105
|
+
]
|
|
106
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Label } from \"@godxjp/ui/data-entry\";\n\n<Label htmlFor=\"stackable\">併用を許可</Label>",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "Label",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Id of the associated control.",
|
|
9
|
+
"name": "htmlFor",
|
|
10
|
+
"type": "string"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Label content.",
|
|
14
|
+
"name": "children",
|
|
15
|
+
"type": "ReactNode"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"related": [
|
|
19
|
+
"FormField — prefer this over a bare Label whenever the field needs helper text, an error message, or a required marker; FormField renders Label internally and wires aria-describedby/aria-invalid automatically.",
|
|
20
|
+
"Field — use for a Checkbox or Radio.Item that needs a visible label and optional description line; it renders Label internally — do NOT add a second Label around it.",
|
|
21
|
+
"Field — use instead of a bare Switch + Label pair when the control must submit a value via an HTML form name; Field owns the Label + hidden input composition.",
|
|
22
|
+
"Checkbox — the most common bare-Label partner; pair with Label via shared useId() id/htmlFor when Field's layout is too heavy."
|
|
23
|
+
],
|
|
24
|
+
"rules": [],
|
|
25
|
+
"storyPath": "data-entry/Label.stories.tsx",
|
|
26
|
+
"tagline": "Styled Radix Label; use htmlFor to associate with a control.",
|
|
27
|
+
"usage": [
|
|
28
|
+
"DO: always pass `htmlFor` matching the `id` of the associated control — this is the entire purpose of the component. Without it, clicking the label text does NOT focus or toggle the control, breaking a11y and UX.",
|
|
29
|
+
"DO: import from `@godxjp/ui/data-entry` (not shadcn or Radix directly). The godx-ui Label extends Radix's LabelPrimitive with `data-slot=\"label\"`, `select-none`, and `group-data-[disabled]` opacity-50 — hand-rolling a `<label>` loses all of these.",
|
|
30
|
+
"DON'T: use Label as a standalone visible heading or section title. It is a form-control association primitive. For page/section headings use semantic HTML (`<h2>`, etc.) or a typography class instead.",
|
|
31
|
+
"DON'T: wrap Label around a control that is already labelled internally. FormField, Field, and CheckboxGroup all render Label internally — adding a second Label creates a duplicate association and redundant screen-reader announcement.",
|
|
32
|
+
"DO: pair Label with Checkbox or Switch when NOT using the compound wrapper (Field). In that case generate the shared id with `React.useId()` and pass it to both `id` on the control and `htmlFor` on Label.",
|
|
33
|
+
"PREFER FormField over a bare Label + control pair whenever you also need helper text, error messages, or `required` asterisk. FormField injects `aria-describedby` and `aria-invalid` automatically; a bare Label does not."
|
|
34
|
+
],
|
|
35
|
+
"useCases": [
|
|
36
|
+
"Pairing with a standalone Checkbox when Field's two-line layout is unnecessary — e.g. a single 'Remember me' option in a login form.",
|
|
37
|
+
"Labelling a bare Switch (not Field) in a settings row where the switch is controlled by parent state and no HTML form name attribute is needed.",
|
|
38
|
+
"Adding a visible label to a custom or third-party control that accepts an `id` prop but isn't wrapped by FormField or Field.",
|
|
39
|
+
"Labelling a Textarea in a free-text form field when FormField's helper/error slots aren't needed, keeping the markup minimal.",
|
|
40
|
+
"Rendering an accessible label inside a table row where a FormField's block layout would break the inline/grid structure.",
|
|
41
|
+
"Adding a label to a DatePicker, TimePicker, or ColorPicker inside a simple layout that doesn't need the full FormField wrapper."
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { LegalDocumentShell, CenteredShell, Flex, Topbar } from \"@godxjp/ui/layout\";\nimport { Button, Text } from \"@godxjp/ui/general\";\nimport { useState } from \"react\";\n\nexport function TermsPage() {\n const [activeSection, setActiveSection] = useState(\"acceptance\");\n\n return (\n <CenteredShell width=\"lg\" topbar={<Topbar start={<Brand />} />}>\n <LegalDocumentShell\n title=\"利用規約\"\n version=\"2.4\"\n effectiveDate=\"2026-04-01\"\n summary=\"本規約は、当社が提供するサービスの利用条件を定めるものです。\"\n contentsLabel=\"目次\"\n activeSection={activeSection}\n onActiveSectionChange={setActiveSection}\n sections={[\n { id: \"acceptance\", title: \"第1条(本規約への同意)\", content: <Text as=\"p\">…</Text> },\n { id: \"accounts\", title: \"第2条(アカウントの管理)\", content: <Text as=\"p\">…</Text> },\n ]}\n documentNavigation={\n <Flex direction=\"col\" gap=\"xs\" role=\"group\" aria-label=\"法的文書\">\n <Button variant=\"secondary\" size=\"sm\" fullWidth aria-current=\"page\">利用規約</Button>\n <Button variant=\"ghost\" size=\"sm\" fullWidth>プライバシーポリシー</Button>\n </Flex>\n }\n footerAction={<Button size=\"sm\">同意する</Button>}\n />\n </CenteredShell>\n );\n}",
|
|
3
|
+
"group": "layout",
|
|
4
|
+
"importPath": "@godxjp/ui/layout",
|
|
5
|
+
"name": "LegalDocumentShell",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Document title — the <h1> that names the <article> (e.g. 'Terms of Service').",
|
|
9
|
+
"name": "title",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "ReactNode"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "The document's sections in reading order. Drives BOTH the contents list and the body: `id` is the REAL anchor target (<section id> + href='#id'), `title` becomes the <h2> AND the contents label, `content` is the consumer-owned legal copy.",
|
|
15
|
+
"name": "sections",
|
|
16
|
+
"required": true,
|
|
17
|
+
"type": "{ id: string; title: string; content: ReactNode }[]"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Bare version identifier (e.g. '2.4'). Rendered as a localized 'Version {version}' — never pass a pre-localized sentence.",
|
|
21
|
+
"name": "version",
|
|
22
|
+
"type": "string"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "ISO 8601 calendar date (yyyy-MM-dd) or a full ISO instant. Formatted with Intl.DateTimeFormat in the active locale and emitted inside <time dateTime={effectiveDate}>, so the machine-readable value is always the ISO input. NEVER pass a pre-formatted date string.",
|
|
26
|
+
"name": "effectiveDate",
|
|
27
|
+
"type": "string"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"description": "Short plain-language summary rendered under the metadata, above the contents.",
|
|
31
|
+
"name": "summary",
|
|
32
|
+
"type": "ReactNode"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"description": "Accessible name + visible caption of the contents <nav>. Defaults to a localized 'Contents'. Override it when two documents share a view, so the two nav landmarks stay distinguishable (axe landmark-unique, WCAG 2.4.1).",
|
|
36
|
+
"name": "contentsLabel",
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"description": "Controlled active section id (the entry marked aria-current='location'). Pair with onActiveSectionChange; omit both for the uncontrolled form.",
|
|
41
|
+
"name": "activeSection",
|
|
42
|
+
"type": "string"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"description": "Uncontrolled initial active section id. Defaults to the first section.",
|
|
46
|
+
"name": "defaultActiveSection",
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"description": "Fires on contents-anchor activation, on an initial hash deep link, and continuously from the scroll spy as the reader moves through the document.",
|
|
51
|
+
"name": "onActiveSectionChange",
|
|
52
|
+
"type": "(sectionId: string) => void"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"description": "Rail slot above the contents list — a switcher across the legal set (Terms · Privacy · Cookies). Rendered as a plain wrapper, so the consumer owns its semantics.",
|
|
56
|
+
"name": "documentNavigation",
|
|
57
|
+
"type": "ReactNode"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"description": "Slot below the last section — accept / download / print / contact actions.",
|
|
61
|
+
"name": "footerAction",
|
|
62
|
+
"type": "ReactNode"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"description": "Root element id; also seeds the internal ids.",
|
|
66
|
+
"name": "id",
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"description": "Root class override (rarely needed).",
|
|
71
|
+
"name": "className",
|
|
72
|
+
"type": "string"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"related": [
|
|
76
|
+
"CenteredShell — the page shell to put a LegalDocumentShell inside (brand bar + centred column + footer). LegalDocumentShell is the document, not the page chrome; never nest two shells of the same kind.",
|
|
77
|
+
"SplitPane — a generic main + fixed aside. It gives similar GEOMETRY but owns no scroll-spy / hash / focus behaviour, so it is the wrong pick for a document with a table of contents.",
|
|
78
|
+
"PageContainer — for a titled section inside an app page; LegalDocumentShell already renders its own document header (h1 + version + effective date + summary).",
|
|
79
|
+
"Text / Heading — compose the section `content` from these; the shell only supplies the section heading (h2) and the body wrapper."
|
|
80
|
+
],
|
|
81
|
+
"rules": [
|
|
82
|
+
23,
|
|
83
|
+
24
|
|
84
|
+
],
|
|
85
|
+
"storyPath": "layout/LegalDocumentShell.stories.tsx",
|
|
86
|
+
"tagline": "Long-form legal/policy document surface (terms, privacy, DPA, cookie policy, SLA) — semantic article/nav/section landmarks, real anchors, a sticky table-of-contents rail, scroll-spy aria-current, hash deep links with a token-driven scroll offset and focus handoff. All legal text stays consumer-owned.",
|
|
87
|
+
"usage": [
|
|
88
|
+
"DO use LegalDocumentShell for ANY long-form legal/policy document — terms of service, privacy policy, DPA, cookie policy, SLA, EULA, security policy. It is the only primitive that owns the document behaviour: scroll-spy active section, hash deep links, scroll offset, focus handoff and reduced-motion scrolling.",
|
|
89
|
+
"DO pass `effectiveDate` as an ISO 8601 string ('2026-04-01'). The shell formats it with Intl.DateTimeFormat in the active locale and keeps the ISO value in <time dateTime>. A pre-formatted string ('April 1, 2026') is a bug — it will not localize.",
|
|
90
|
+
"DO keep ALL legal text in the consumer: the shell only receives `sections` (+ the `summary` / `documentNavigation` / `footerAction` slots). It never ships legal copy.",
|
|
91
|
+
"DO give every section a URL-safe, page-unique `id` — it is simultaneously the <section id>, the contents href, the deep-link target and the aria-current key.",
|
|
92
|
+
"DO NOT hand-roll this from CenteredShell + SplitPane + `.legal-*` CSS. The geometry is the easy half; the scroll spy, hash offset, focus handoff and aria-current wiring are what the shell exists to own, and no token can express them.",
|
|
93
|
+
"DO NOT add a consumer `className` for the measure, the rail width, the section rhythm or the dividers — every one of those is a --legal-document-* token. Dividers default to `none` (rule #44): opt in with `--legal-document-toc-border: 1px solid hsl(var(--border))`.",
|
|
94
|
+
"DO NOT expect a viewport media query: the shell owns its query container, so the one-column ⇄ two-column split (56rem) is decided by the SHELL's own width. Below it the contents are a STATIC compact block (never pinned on a phone); above it they are a sticky rail."
|
|
95
|
+
],
|
|
96
|
+
"useCases": [
|
|
97
|
+
"Hosted legal terms/privacy screen (SCR-005): <CenteredShell width=\"lg\" topbar={<Topbar …/>}><LegalDocumentShell title=\"利用規約\" version=\"2.4\" effectiveDate=\"2026-04-01\" contentsLabel=\"目次\" sections={sections} activeSection={active} onActiveSectionChange={setActive} documentNavigation={<DocumentSwitcher/>} footerAction={<Button>同意する</Button>} /></CenteredShell>",
|
|
98
|
+
"In-app policy viewer inside a Dialog/Sheet during onboarding: the same `sections` with `footerAction` carrying the accept button; the shell stays single-column because its container is narrow.",
|
|
99
|
+
"Deep-linkable DPA / sub-processor document: link to /legal/dpa#data-retention — the shell selects, scrolls to (with the scroll offset) and focuses that section on arrival.",
|
|
100
|
+
"Multi-document legal set (Terms · Privacy · Cookies): render the switcher in `documentNavigation` so it sits above the contents in the sticky rail."
|
|
101
|
+
]
|
|
102
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Legend } from \"@godxjp/ui/data-display\";\n\n<CardAction>\n <Legend\n items={[\n { tone: \"destructive\", label: \"期限超過\" },\n { tone: \"warning\", label: \"期限間近\" },\n { tone: \"success\", label: \"対応済\" },\n ]}\n />\n</CardAction>",
|
|
3
|
+
"group": "data-display",
|
|
4
|
+
"importPath": "@godxjp/ui/data-display",
|
|
5
|
+
"name": "Legend",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "The keys, in the order the marks they explain appear. `label` is required and cannot be omitted: colour alone never carries meaning (WCAG 1.4.1), so a wordless key would be the exact failure the component prevents.",
|
|
9
|
+
"name": "items",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "{ tone: \"default\" | \"success\" | \"warning\" | \"destructive\" | \"info\" | \"muted\" | \"neutral\"; label: ReactNode }[]"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Root class. The swatch geometry lives in the --legend-* tokens, not here.",
|
|
15
|
+
"name": "className",
|
|
16
|
+
"type": "string"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"related": [
|
|
20
|
+
"Progress — `segments` draws the breakdown this key explains; the two take the same tones.",
|
|
21
|
+
"Badge — a Badge labels ONE thing in place; a Legend explains a colour used across many marks.",
|
|
22
|
+
"Swatch — one read-only sample of an ARBITRARY colour (a brand's primary_color). Legend is a key over the closed semantic tones and always shows words; Swatch shows a value and takes its name from aria-label.",
|
|
23
|
+
"CardAction — the header slot a Legend usually sits in, so the key lands opposite the CardTitle."
|
|
24
|
+
],
|
|
25
|
+
"rules": [],
|
|
26
|
+
"storyPath": "data-display/Legend.stories.tsx",
|
|
27
|
+
"tagline": "The KEY for a colour-coded surface: which tone means what, spelled out in words once — a square swatch per tone with its label.",
|
|
28
|
+
"usage": [
|
|
29
|
+
"DO import from `@godxjp/ui/data-display`: `import { Legend } from \"@godxjp/ui/data-display\";`",
|
|
30
|
+
"DO put it in the `CardAction` slot of the card whose bars or chart it explains, so the key sits on the same line as the card title and reads before the data.",
|
|
31
|
+
"DO feed it the same tone order as the marks it explains — a key whose order differs from the bars forces the reader to map three colours by hand.",
|
|
32
|
+
"DON'T build a key out of Badges. A Badge is a chip that reads as clickable and carries a tinted fill + border; a legend swatch is a SAMPLE of the exact colour the mark uses.",
|
|
33
|
+
"DON'T hand-roll a coloured square: `<span className=\"w-[10px] h-[10px] rounded-[2px] bg-[#c0392f]\" />` is blocked by ui-audit three ways at once (no-arbitrary-size, no-arbitrary-radius, no-arbitrary-hex). For a colour the USER chose — a brand colour, a tag tint — reach for `Swatch`, which takes that value as a prop.",
|
|
34
|
+
"DON'T give the swatch its own text or aria — it is aria-hidden on purpose, because it repeats the label beside it."
|
|
35
|
+
],
|
|
36
|
+
"useCases": [
|
|
37
|
+
"The key above a set of `Progress` breakdown bars — 期限超過 / 期限間近 / 対応済 — so the three tones are named once for the whole card instead of on every row.",
|
|
38
|
+
"A chart card where the series colours need naming outside the chart's own runtime legend.",
|
|
39
|
+
"A status column in a dense table: name the tones once in the card header rather than repeating a Badge in every cell.",
|
|
40
|
+
"A calendar or heatmap whose cell tints encode a state (booked / held / free)."
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { LineChart } from \"@godxjp/ui/charts\";\n\n<LineChart\n label={t(\"dashboard.revenueTrend\")}\n data={data}\n categoryKey=\"month\"\n series={[\n { dataKey: \"plan\", label: t(\"metric.plan\") },\n { dataKey: \"actual\", label: t(\"metric.actual\") },\n ]}\n numberFormat={{ style: \"currency\", currency: \"JPY\" }}\n/>",
|
|
3
|
+
"group": "data-display",
|
|
4
|
+
"importPath": "@godxjp/ui/charts",
|
|
5
|
+
"name": "LineChart",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Row data: one category per row with a numeric value per series.",
|
|
9
|
+
"name": "data",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "ChartDatum[]"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Plotted series: { dataKey, label?, color? }. Colour defaults to the --chart-1..6 palette.",
|
|
15
|
+
"name": "series",
|
|
16
|
+
"required": true,
|
|
17
|
+
"type": "ChartSeriesProp[]"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Key into each datum holding the x-axis category label.",
|
|
21
|
+
"name": "categoryKey",
|
|
22
|
+
"required": true,
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"description": "Accessible name + visible caption (role=img needs a name).",
|
|
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": "\"md\"",
|
|
44
|
+
"description": "Canvas height preset. Ignored when `height` is set.",
|
|
45
|
+
"name": "size",
|
|
46
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"description": "Explicit canvas height in px (overrides `size`).",
|
|
50
|
+
"name": "height",
|
|
51
|
+
"type": "number"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"defaultValue": "true",
|
|
55
|
+
"description": "Show the series legend.",
|
|
56
|
+
"name": "showLegend",
|
|
57
|
+
"type": "boolean"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"defaultValue": "true",
|
|
61
|
+
"description": "Show the cartesian background grid.",
|
|
62
|
+
"name": "showGrid",
|
|
63
|
+
"type": "boolean"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"description": "Locale-aware formatting for axis ticks + tooltip values.",
|
|
67
|
+
"name": "numberFormat",
|
|
68
|
+
"type": "Intl.NumberFormatOptions"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"defaultValue": "false",
|
|
72
|
+
"description": "Render smooth (monotone) lines instead of straight segments.",
|
|
73
|
+
"name": "curved",
|
|
74
|
+
"type": "boolean"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"description": "Message shown when `data` is empty (defaults to a localized 'no data').",
|
|
78
|
+
"name": "emptyMessage",
|
|
79
|
+
"type": "string"
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"related": [
|
|
83
|
+
"AreaChart — use when the filled magnitude under the line matters (cumulative/stacked volume).",
|
|
84
|
+
"BarChart — use for discrete category comparison rather than a continuous trend.",
|
|
85
|
+
"DataTable — use when exact per-row figures matter more than the trend shape."
|
|
86
|
+
],
|
|
87
|
+
"rules": [],
|
|
88
|
+
"storyPath": "charts/LineChart.stories.tsx",
|
|
89
|
+
"tagline": "Trends over an ordered category axis — one or more series, locale-formatted ticks/tooltips, screen-reader text alternative built in. Data-visualization graph / plot.",
|
|
90
|
+
"usage": [
|
|
91
|
+
"DO import from the tree-shaken charts entry: `import { LineChart } from \"@godxjp/ui/charts\";`. Importing any other subpath never pulls in recharts.",
|
|
92
|
+
"DO import only the chart a screen uses — `import { LineChart } from \"@godxjp/ui/charts/line-chart\";` — when the `./charts` barrel should not link the whole chart family. Without the `recharts` peer the build then fails ONCE, naming the package and the fix.",
|
|
93
|
+
"DO install the `recharts` optional peer dependency in the consuming app — charts are the only part of @godxjp/ui that needs it, so apps without charts never pay for it.",
|
|
94
|
+
"DO pass an i18n'd `label` — it is both the visible caption and the accessible name; the component also emits a screen-reader list of the plotted values (WCAG 1.1.1).",
|
|
95
|
+
"DO pre-translate each series' `label`; pass `numberFormat` (e.g. { style: 'currency', currency: 'JPY' }) and the axis/tooltip numbers localize automatically via Intl.",
|
|
96
|
+
"DON'T hand-roll an SVG/canvas chart or drop raw recharts into a page — LineChart owns the colour tokens, locale formatting, empty state, and accessibility wiring."
|
|
97
|
+
],
|
|
98
|
+
"useCases": [
|
|
99
|
+
"Revenue / KPI trend over months in a dashboard.",
|
|
100
|
+
"Multi-series comparison (e.g. plan vs actual) across a time axis.",
|
|
101
|
+
"Any continuous metric where the shape of the trend is the message."
|
|
102
|
+
]
|
|
103
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Link } from \"@godxjp/ui/general\";\n\n<Link href=\"/docs/billing\">請求の設定</Link>\n<Link href=\"https://example.com\" target=\"_blank\">外部サイト</Link>",
|
|
3
|
+
"group": "general",
|
|
4
|
+
"importPath": "@godxjp/ui/general",
|
|
5
|
+
"name": "Link",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"defaultValue": "\"a\"",
|
|
9
|
+
"description": "Rendered element.",
|
|
10
|
+
"name": "as",
|
|
11
|
+
"type": "\"a\" | \"span\""
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Single-line truncation. antd allows only a boolean on `Link`, and that restriction is ported.",
|
|
15
|
+
"name": "ellipsis",
|
|
16
|
+
"type": "boolean"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"related": [
|
|
20
|
+
"Text",
|
|
21
|
+
"Typography",
|
|
22
|
+
"Button",
|
|
23
|
+
"PrefetchLink"
|
|
24
|
+
],
|
|
25
|
+
"rules": [
|
|
26
|
+
2,
|
|
27
|
+
6,
|
|
28
|
+
23
|
|
29
|
+
],
|
|
30
|
+
"storyPath": "general/typography.tsx",
|
|
31
|
+
"tagline": "antd Typography.Link — an anchor that already carries this library's inline link affordance (underline on hover AND on keyboard focus), plus antd's rel guard for target=\"_blank\".",
|
|
32
|
+
"usage": [
|
|
33
|
+
"DO use `Link` when you want an anchor with the link affordance already on it. `<Text link>` is the same affordance for a run that is not an anchor, and `<Text asChild link>` is the shape a router link takes.",
|
|
34
|
+
"DON'T reach for `Button variant=\"link\"` inside running content: that is a CONTROL box (nowrap, a control height, inline padding), so in a table cell it neither wraps nor shares the cell's line box.",
|
|
35
|
+
"A `target=\"_blank\"` with no `rel` of its own is given `noopener noreferrer`, because the opened document otherwise keeps a live handle back into this one."
|
|
36
|
+
],
|
|
37
|
+
"useCases": [
|
|
38
|
+
"A documentation link at the end of a helper line: `<Link href=\"/docs/billing\">請求の設定</Link>`.",
|
|
39
|
+
"An external link that must not leak the opener: `<Link href=\"https://example.com\" target=\"_blank\">外部サイト</Link>`."
|
|
40
|
+
]
|
|
41
|
+
}
|