@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,158 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Tabs } from \"@godxjp/ui/navigation\";\n\n<Tabs\n defaultValue=\"overview\"\n items={[\n { value: \"overview\", label: \"概要\", content: \"概要コンテンツ\" },\n { value: \"history\", label: \"履歴\", content: \"履歴コンテンツ\" },\n ]}\n/>",
|
|
3
|
+
"group": "navigation",
|
|
4
|
+
"importPath": "@godxjp/ui/navigation",
|
|
5
|
+
"name": "Tabs",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Optional data-driven tab list. When provided, Tabs renders all triggers and content panels. When Tabs owns the initial selection (no `value`, and no `defaultValue` naming an existing ENABLED item), it falls back to the first item that is NOT `disabled` — never a disabled one — and selects nothing if every item is disabled. `icon` is a leading glyph in the trigger (Ant Design `Tab.icon`). `closable` / `closeIcon` only apply under `variant=\"editable-card\"`; `closable: false` opts one tab out of removal (Ant Design `getRemovable`). `forceRender` (Ant Design `Tab.forceRender`) mounts THAT panel up front and keeps it mounted while another tab is selected, without switching the whole strip over with `destroyOnHidden={false}` — antd's per-item `destroyOnHidden` is deliberately not offered, because on this component \"kept mounted\" is one state and it would be a second spelling of `forceRender`. `count` (+ `overflowCount`, default 99; `showZero`, default true; `countLabel`) draws the counter pill beside the label — 「未対応 12」 — in the SAME vocabulary Button and Toggle publish and through the same helper, formatted with `Intl.NumberFormat` in the active locale. Pass `countLabel` to say what the number is: the pill is `aria-hidden` and an `sr-only` clause carries the digits, so the tab announces \"未対応, 12 件の課題\" and never the concatenated \"未対応12\". Prefer this over antd's answer, which is to put a Badge inside `label` — with the number inside the label the package owns neither its size nor its tone as the tab moves between selected / unselected / disabled, so every consumer aligns it differently.",
|
|
9
|
+
"name": "items",
|
|
10
|
+
"type": "{ value: string; label: React.ReactNode; content: React.ReactNode; disabled?: boolean; icon?: React.ReactNode; closable?: boolean; closeIcon?: React.ReactNode; forceRender?: boolean; count?: number; overflowCount?: number; showZero?: boolean; countLabel?: string }[]"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Controlled active tab key.",
|
|
14
|
+
"name": "value",
|
|
15
|
+
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "Uncontrolled initial tab key. Ignored (falls back to the first enabled item) when it names a disabled item or an unknown key.",
|
|
19
|
+
"name": "defaultValue",
|
|
20
|
+
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"description": "Active-tab change handler.",
|
|
24
|
+
"name": "onValueChange",
|
|
25
|
+
"type": "(value: string) => void"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"defaultValue": "\"default\"",
|
|
29
|
+
"description": "Trigger-strip appearance — this is Ant Design's `type` under the library's own `variant` vocabulary. `default` is the pill strip (antd has no equivalent). `line` is UNDERLINE-ONLY: the selected trigger gets no ring or card border at all — only the token-owned 2px primary bar (--tabs-indicator-{background,size,offset}) — so the `:focus-visible` keyboard ring stays visible and clearly distinct from selection. `card` gives each tab a boxed face on a rail (--tabs-card-*). `editable-card` is `card` plus the add button and per-tab remove shortcut, and needs `onEdit` to do anything. With `items`, the variant is forwarded to the list; when composing manually, pass the same value to `<TabsList variant=\"line\">`.",
|
|
30
|
+
"name": "variant",
|
|
31
|
+
"type": "\"default\" | \"line\" | \"card\" | \"editable-card\""
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"defaultValue": "\"top\"",
|
|
35
|
+
"description": "Which edge the trigger strip parks on — Ant Design 6.6.2's `tabPlacement` (its `tabPosition` is deprecated there), so the inline values are already RTL-logical. `start`/`end` also flip the tablist to vertical roving focus, which is what `orientation=\"vertical\"` did on its own before; either prop still works and the other is derived from it. The strip stays FIRST in the DOM at every placement — `bottom`/`end` are a flex reversal, not a re-ordered tree. NARROW FOLD: `start`/`end` become `top`/`bottom` (arrow keys included) at or below `--tabs-placement-responsive-breakpoint-width` (48rem) — a vertical strip and its panel share one inline axis and a phone has room for one of them; Ant Design folds the same pair the same way. Set that token to `0px` to keep the strip vertical at every width.",
|
|
36
|
+
"name": "tabPlacement",
|
|
37
|
+
"type": "\"top\" | \"bottom\" | \"start\" | \"end\""
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"defaultValue": "\"md\"",
|
|
41
|
+
"description": "Control tier of the triggers (Ant Design `size`). Expressed as the library's own control bands (--tabs-trigger-height-*/--tabs-trigger-font-size-*), so a tab strip and the Buttons beside it stay on one rhythm; `md` reproduces the previous trigger exactly.",
|
|
42
|
+
"name": "size",
|
|
43
|
+
"type": "\"sm\" | \"md\" | \"lg\""
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"description": "Ant Design `centered` — centre the strip on its own inline axis. Keeps the `safe` centring rule the strip depends on, so a strip that overflows still falls back to start alignment instead of stranding the leading tab outside the scrollport.",
|
|
47
|
+
"name": "centered",
|
|
48
|
+
"type": "boolean"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"defaultValue": "false",
|
|
52
|
+
"description": "Draw the PANEL BODY the card strip opens into, so the strip and the panel are ONE object: a surface, a border continuous with the rail, and the radius only on the two corners AWAY from the strip. `variant=\"card\"` already repaints the active tab's joined edge in the surface colour (antd `genCardStyle`), but the package shipped no surface for it to merge into — measured at 1440px, `<Card>` under a card strip left an 8px gap AND a second 1px border at a 9.708px radius (two stacked boxes), `<Card variant=\"borderless\">` and no box at all left the same 8px gap. With `bodied`: gap 0 (the body is pulled back exactly one border width so its edge and the tabs' sit on one device row), one continuous line around the whole object, and no line at all across the active tab. HONOURED ONLY BY `card` / `editable-card` — the pill strip floats by design and the `line` strip's body is the `Card` it lives in (`<Card tabList>`), which is already joined; on any other variant it emits no attribute and changes no pixel. Retune it from `--tabs-panel-{background,border-width,radius,space-inset}`; `--tabs-panel-background` is read by the body AND by the merged tab edge, so the two can never disagree.",
|
|
53
|
+
"name": "bodied",
|
|
54
|
+
"type": "boolean"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"description": "Ant Design `tabBarExtraContent`, renamed to the library's `extra` slot and made logical: antd's `left`/`right` keys are `start`/`end` here. A bare node goes to `end` (antd's own default). Renders a bar row beside the strip; without it — and without an add button — no extra wrapper is emitted at all. Under `bodied` the bar row aligns its contents to the JOINED edge instead of centring them, so the add button and the extra sit on the rail rather than hanging across it.",
|
|
58
|
+
"name": "extra",
|
|
59
|
+
"type": "React.ReactNode | { start?: React.ReactNode; end?: React.ReactNode }"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"defaultValue": "true",
|
|
63
|
+
"description": "Ant Design `destroyOnHidden`. `true` (the default here, and Radix's own behaviour) unmounts a panel the moment it stops being selected. `false` keeps EVERY panel mounted and only hides the inactive ones, so a live chart, a scroll position or an unsent form draft survives a tab switch. The default is deliberately the opposite of antd's, which keeps panels mounted.",
|
|
64
|
+
"name": "destroyOnHidden",
|
|
65
|
+
"type": "boolean"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"description": "Ant Design `onEdit`. Required for `variant=\"editable-card\"` to grow its controls. `remove` passes the item's own `value`; `add` passes the click event. Tabs never mutates `items` itself — the consumer owns the list.",
|
|
69
|
+
"name": "onEdit",
|
|
70
|
+
"type": "(target: string | React.MouseEvent<HTMLButtonElement>, action: \"add\" | \"remove\") => void"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"description": "Ant Design `addIcon` — replaces the default + on the editable-card add button.",
|
|
74
|
+
"name": "addIcon",
|
|
75
|
+
"type": "React.ReactNode"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"description": "Ant Design `hideAdd` — keep editable-card's remove shortcuts but drop the add button.",
|
|
79
|
+
"name": "hideAdd",
|
|
80
|
+
"type": "boolean"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"description": "Ant Design `removeIcon` — the strip-wide default glyph on `editable-card`'s remove shortcut. An item's own `closeIcon` still wins over it, which is antd's precedence. It replaces the glyph only: the × stays an `aria-hidden` pointer shortcut inside the tab and the announced route stays Delete/Backspace, so a custom icon never becomes a second focusable control inside a `role=\"tab\"`.",
|
|
84
|
+
"name": "closeIcon",
|
|
85
|
+
"type": "React.ReactNode"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"defaultValue": "\"scroll\"",
|
|
89
|
+
"description": "What the trigger strip does when there are more tabs than fit — Ant Design's `more`, mapped onto the `overflow` vocabulary Toolbar already uses for the same question rather than re-spelled. `scroll` (default, and what every strip does today) keeps one bounded row that scrolls its own inline overflow, with the active (or, under manual activation, focused) trigger re-pinned into view. `menu` keeps all of that AND puts a real named button beside the strip listing the tabs currently outside the scrollport; choosing one selects it. DIVERGES FROM ANTD DELIBERATELY: antd REMOVES the overflowing tabs from the bar, but the WAI-ARIA APG tab pattern requires the tablist to own every tab and a `display: none` tab cannot take roving focus — so here every tab stays in the strip and the menu is an ADDITIONAL pointer route, not a relocation. The default is not `menu` because switching it would change the rendered bar for every existing consumer at once.",
|
|
90
|
+
"name": "overflow",
|
|
91
|
+
"type": "\"scroll\" | \"menu\""
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"description": "Ant Design `onTabClick`. POINTER activation of a trigger, carrying the DOM event — that is what makes it a different prop from `onValueChange` rather than a second spelling of it. Keyboard activation is NOT routed here: under `activationMode=\"manual\"` the arrow keys move focus without activating, so a key-driven \"click\" would be a fiction. Use `onValueChange` for the selection, whatever moved it. Note that `onValueChange` also fires when the ALREADY SELECTED tab is clicked, so `onTabClick` is not the way to detect a re-click.",
|
|
95
|
+
"name": "onTabClick",
|
|
96
|
+
"type": "(value: string, event: React.MouseEvent<HTMLButtonElement>) => void"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"defaultValue": "{ inkBar: true, tabPane: false }",
|
|
100
|
+
"description": "Ant Design `animated`, ported from antd's own `useAnimateConfig`: `false` turns both switches off, `true` turns both ON, an object merges over `{ inkBar: true }`. `inkBar` is the `line` variant's active bar cross-fading between triggers (on by default, and the default is byte for byte what the strip already painted). `tabPane` fades the panel in when the selection moves — antd's motion really is an opacity fade and nothing else, so this is a port, not an invention; it reads `--tabs-pane-motion-duration`. Both switches are additionally off under `prefers-reduced-motion`, which antd's are not. What is NOT ported is antd's fade-OUT of the leaving pane (it parks the old node `position: absolute; inset: 0`): this component destroys a hidden panel by default, so there is usually no leaving node.",
|
|
101
|
+
"name": "animated",
|
|
102
|
+
"type": "boolean | { inkBar?: boolean; tabPane?: boolean }"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"defaultValue": "{ size: \"full\", align: \"center\" }",
|
|
106
|
+
"description": "Ant Design `indicator`, governing the `line` variant's active bar only (no other variant has one). `align` keeps antd's name AND its values, which are already logical, so it reads the shared TextAlignProp vocabulary. `size` keeps antd's name with this library's values: antd takes `number | (origin) => number` — a px length or a function of the measured tab width — and neither can enter this API (a literal is what `no-arbitrary-spacing` stops; an origin function is the free-form escape hatch docs/DESIGN-AUTHORITY.md refuses). `full` is the whole trigger (antd's default, today's bar) and `label` is the trigger's content box, i.e. minus its own inline padding — which is what `size: (origin) => origin - 2 * padding` is written to produce. `align` only has anything to place once `size` is shorter than the trigger, exactly as upstream.",
|
|
107
|
+
"name": "indicator",
|
|
108
|
+
"type": "{ size?: \"full\" | \"label\"; align?: \"start\" | \"center\" | \"end\" }"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"description": "Ant Design `moreIcon` (its `more.icon` in 6.x; the flat prop is still published there) — the glyph on the `overflow=\"menu\"` button. Flat here for the same reason `addIcon` and `closeIcon` are: `overflow` names the BEHAVIOUR, the icon is a slot. The button keeps its own `aria-label`, so a custom glyph never costs the control its accessible name.",
|
|
112
|
+
"name": "moreIcon",
|
|
113
|
+
"type": "React.ReactNode"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"description": "Ant Design `onTabScroll`, fired whenever the trigger strip's own scrollport moves — a swipe, a wheel, or the component re-pinning the active trigger (antd reports its own re-pins too). LOGICAL VALUES instead of antd's `left | right | top | bottom`: two of those four are just the other axis of the same event, and upstream's pair is read off the sign of an inner transform, so in an RTL strip its `left` means the opposite of what it means in LTR. `start`/`end` say the same thing on whichever axis and in whichever direction the strip is written. Only fires for the `items` API, which is the path that owns the strip element.",
|
|
117
|
+
"name": "onTabScroll",
|
|
118
|
+
"type": "(info: { direction: \"start\" | \"end\" }) => void"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"related": [
|
|
122
|
+
"Steps (@godxjp/ui/navigation) — sequential wizard/progress indicator. Use Steps when order and completion state matter (multi-step forms, onboarding flows); use Tabs when panels are non-sequential and any tab can be visited freely.",
|
|
123
|
+
"Toolbar / ToolbarGroup (@godxjp/ui/navigation) — horizontal filter chip row. Visually resembles `line`-variant tabs but is semantically different: Toolbar filters a dataset, it does not switch content panels. Never use Tabs as a filter control.",
|
|
124
|
+
"DropdownMenu (@godxjp/ui/navigation) — use for space-constrained contexts where showing all tab triggers at once is impractical (e.g. mobile overflow menu). If only 2-3 options exist and screen space is tight, a DropdownSidebar is a lighter alternative to a full tab strip."
|
|
125
|
+
],
|
|
126
|
+
"rules": [],
|
|
127
|
+
"storyPath": "navigation/Tabs.stories.tsx",
|
|
128
|
+
"subParts": [
|
|
129
|
+
"TabsContent",
|
|
130
|
+
"TabsList",
|
|
131
|
+
"TabsTrigger"
|
|
132
|
+
],
|
|
133
|
+
"tagline": "Radix tab container with optional Ant-style `items` API. Pass items for the common full TabsList/TabsContent set, or compose TabsList/TabsTrigger/TabsContent manually when you need per-panel control.",
|
|
134
|
+
"usage": [
|
|
135
|
+
"DO pass `items` when all tab content is known up front — each item needs a unique `value`, trigger `label`, and panel `content`.",
|
|
136
|
+
"When not using `items`, compose the full four-part tree — `<Tabs>` root, `<TabsList>` trigger bar, one `<TabsTrigger value=\"…\">` per tab, one `<TabsContent value=\"…\">` per matching trigger.",
|
|
137
|
+
"DO: use `defaultValue` (uncontrolled) for simple local state; use `value` + `onValueChange` together (controlled) when the active tab is driven by URL query params, router state, or parent state. NEVER set both simultaneously.",
|
|
138
|
+
"DO use `variant` on Tabs when using `items`; when composing manually, set `variant` on `TabsList`.",
|
|
139
|
+
"DO: pass `orientation=\"vertical\"` to `<Tabs>` (not to `TabsList`) for a side-rail layout — the CSS group classes on root and triggers respond automatically, so no extra className gymnastics are needed.",
|
|
140
|
+
"DON'T: hand-roll the active-indicator underline or selected-state ring — `TabsTrigger` already applies `data-[state=active]` styles, including the token-owned indicator bar for the `line` variant. Adding your own `border-b-2 border-primary` (or a page-local `ring-0` override to remove one) breaks the design; retune --tabs-indicator-{background,size,offset} in the service theme instead.",
|
|
141
|
+
"DO trust the horizontal `TabsList` to scroll its own overflow (hidden scrollbar, swipeable) instead of clipping when tab labels — especially long localized ones (Japanese, German) — don't fit a narrow container. Don't wrap it in your own `overflow-x-auto` div or truncate labels to work around clipping; that is now the framework's job.",
|
|
142
|
+
"DON'T assume the first item is ever auto-selected when it is `disabled` — Tabs always resolves the fallback to the first ENABLED item (or none, if all are disabled). A `disabled: true` first item is safe to author without also setting `defaultValue`.",
|
|
143
|
+
"DON'T write your own resize/scroll-into-view effect to keep the selected tab on screen — `TabsList` observes its own size and its triggers' `data-state` and re-pins the active (or focused, under `activationMode=\"manual\"`) trigger with `scrollIntoView({ block: \"nearest\", inline: \"nearest\" })`, honoring `prefers-reduced-motion` and leaving a deliberate manual scroll alone. Before that, a 1440 → 1024 → 390 resize could strand the ACTIVE FIRST tab entirely outside the strip while it still reported `aria-selected=\"true\"`.",
|
|
144
|
+
"DO reach for `variant=\"editable-card\"` + `onEdit` instead of hand-rolling a closable tab bar. The × inside a tab is an `aria-hidden` pointer shortcut and the announced keyboard route is Delete/Backspace on the focused tab (`aria-keyshortcuts`) — a real <button> there is an axe failure twice over (`aria-required-children`, because a tablist may own nothing but tabs, and `nested-interactive`). The ADD button is a real button because it sits outside the tablist.",
|
|
145
|
+
"DO use `destroyOnHidden={false}` when a hidden panel must keep state — a mounted chart, a scroll position, an unsent draft. Note it is the opposite default from Ant Design: here panels are destroyed unless you say otherwise.",
|
|
146
|
+
"DO add `bodied` whenever a `card`/`editable-card` strip stands on its own (a saved-views ribbon over a list). DON'T wrap the panel in your own `<Card>` to give it a surface — that is the measured defect `bodied` exists for: it adds a SECOND border at the Card radius over the strip↔panel gap and the pair reads as two stacked boxes. The one place you do not need it is inside `<Card tabList>`, where the card's own border already wraps strip and body.",
|
|
147
|
+
"DO put a tab's count in `count` (+ `countLabel`), not inside `label`. A number concatenated into the label loses the pill's size, its tone as the tab is selected/deselected, and — measured on Button in gh#734 — the accessible name: the digits run straight onto the label («未対応12»). With the slot the tab announces \"未対応, 12 件の課題\". DON'T drop a `<Badge>` into `label` to get the same look; there is one counting API and Button, Toggle and Tabs all draw it.",
|
|
148
|
+
"DON'T go looking for antd's `tabBarGutter`, `tabBarStyle`, `renderTabBar`, `classNames`/`styles` or `more.popupRender` — each is declined on the record, not missing. The gutter between triggers is a theme knob (`--tabs-list-line-space-gap`, `--tabs-card-list-space-gap`; the pill strip has no gutter by design) because a px number is a constant, not a semantic axis; the other four exist upstream to let a consumer replace the rendered markup, and this library answers that layer with tokens (docs/DESIGN-AUTHORITY.md refuses them by name).",
|
|
149
|
+
"DON'T re-centre the strip with a `justify-center` utility. `TabsList` aligns with `safe center` on purpose: plain centring splits the overflow across BOTH edges while `scrollLeft` only ever covers the trailing one, so the leading tab ends up permanently outside the scrollport and no gesture reaches it. `safe` keeps the centred look while the tabs fit and falls back to start alignment the moment they don't."
|
|
150
|
+
],
|
|
151
|
+
"useCases": [
|
|
152
|
+
"Detail drawers or pages that need full per-panel control — e.g. an accounting journal-entry sheet where one panel has `forceMount` to keep a live chart mounted, requiring custom `TabsContent` props that `Tabs` cannot pass.",
|
|
153
|
+
"Controlled tabs driven by URL search params (e.g. `?tab=history`) where the parent reads/writes the active key and passes it to `value` / `onValueChange`.",
|
|
154
|
+
"Vertical side-rail navigation inside a `SplitPane` or settings layout where `orientation=\"vertical\"` on the root and `variant=\"line\"` on `TabsList` combine to produce a sidebar-style tab strip.",
|
|
155
|
+
"Lightweight widget tabs on a dashboard card — e.g. switching a `DataTable` between 'Pending' and 'Paid' invoice views — where an uncontrolled `defaultValue` is sufficient and no URL state is needed.",
|
|
156
|
+
"Admin entity profile pages (company, partner, employee) where each `TabsContent` wraps an Inertia deferred prop panel, lazy-loading expensive data only when the tab is first activated."
|
|
157
|
+
]
|
|
158
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { TagInput } from \"@godxjp/ui/data-entry\";\n\n<TagInput name=\"labels\" placeholder=\"ラベルを追加…\" onValueChange={(tags) => setTags(tags)} />",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "TagInput",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Prevent adding and removing tags.",
|
|
9
|
+
"name": "readOnly",
|
|
10
|
+
"type": "boolean"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Controlled tag list.",
|
|
14
|
+
"name": "value",
|
|
15
|
+
"type": "string[]"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "Uncontrolled initial tags.",
|
|
19
|
+
"name": "defaultValue",
|
|
20
|
+
"type": "string[]"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"description": "Tag-list callback.",
|
|
24
|
+
"name": "onValueChange",
|
|
25
|
+
"type": "(tags: string[]) => void"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"description": "Shown when empty.",
|
|
29
|
+
"name": "placeholder",
|
|
30
|
+
"type": "string"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"description": "Hidden input (comma-joined) for native form submission.",
|
|
34
|
+
"name": "name",
|
|
35
|
+
"type": "string"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"description": "antd `status`. `error` recolours the control AND sets aria-invalid (a colour-only error fails WCAG 2.2 SC 1.4.1); `warning` recolours only. An aria-invalid injected by FormField always wins.",
|
|
39
|
+
"name": "status",
|
|
40
|
+
"type": "\"error\" | \"warning\""
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"description": "antd `variant` — the control surface. Default `outlined`. Drawn from --control-{surface,filled,borderless}-* tokens, so a theme retunes all three at once.",
|
|
44
|
+
"name": "variant",
|
|
45
|
+
"type": "\"outlined\" | \"filled\" | \"borderless\""
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"description": "antd `size` — height tier on the shared --control-height ladder.",
|
|
49
|
+
"name": "size",
|
|
50
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"description": "antd `maxTagCount` — how many selected values stay visible before the rest collapse into the overflow node. antd's `\"responsive\"` is not supported (see the parity PR).",
|
|
54
|
+
"name": "maxTagCount",
|
|
55
|
+
"type": "number"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"description": "antd `maxTagPlaceholder` — the node standing in for what maxTagCount hid. Defaults to a localized `+N`.",
|
|
59
|
+
"name": "maxTagPlaceholder",
|
|
60
|
+
"type": "React.ReactNode | ((omitted: { value: string; label: React.ReactNode }[]) => React.ReactNode)"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"description": "antd `maxCount` — a hard ceiling on how many tags may be held. A tag past the limit is refused, so the value handed to onValueChange is never over it.",
|
|
64
|
+
"name": "maxCount",
|
|
65
|
+
"type": "number"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"description": "antd `maxTagTextLength` — cut each chip's TEXT to this many characters (an ellipsis marks the cut). The value is untouched and stays in the chip's title and in the remover's accessible name, so a long identifier is never silently swallowed. Without it one unbroken token sets the chip's width and the chip sets the row's (gh#840).",
|
|
69
|
+
"name": "maxTagTextLength",
|
|
70
|
+
"type": "number"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"description": "antd `tagRender` — owns the chip body. The onClose it receives is the same remover the built-in ✕ calls, so a custom chip can never be unremovable.",
|
|
74
|
+
"name": "tagRender",
|
|
75
|
+
"type": "(props: { value: string; label: React.ReactNode; onClose: () => void; index: number; disabled: boolean }) => React.ReactNode"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"description": "antd `tokenSeparators` (default [\",\"]) — characters that commit the draft into a tag. A pasted run containing one is split into several tags. Enter always commits and is not a separator.",
|
|
79
|
+
"name": "tokenSeparators",
|
|
80
|
+
"type": "string[]"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"related": [
|
|
84
|
+
"Select (multiple) — when the values come from a fixed option set",
|
|
85
|
+
"Combobox (multi) — searchable known set"
|
|
86
|
+
],
|
|
87
|
+
"rules": [
|
|
88
|
+
3,
|
|
89
|
+
6,
|
|
90
|
+
23
|
|
91
|
+
],
|
|
92
|
+
"storyPath": "data-entry/TagInput.stories.tsx",
|
|
93
|
+
"tagline": "Chips/tags input — type + Enter (or comma) to add a tag, Backspace to remove the last; controlled via value/onValueChange (string[]).",
|
|
94
|
+
"usage": [
|
|
95
|
+
"DO use for free-form multi-value entry (labels, emails, keywords) where options aren't a fixed list.",
|
|
96
|
+
"DO note dedupe is built in; Enter/comma commits, Backspace on empty removes the last chip.",
|
|
97
|
+
"DON'T use for choosing from a KNOWN set — use Select (multiple) or a multi-Combobox instead."
|
|
98
|
+
],
|
|
99
|
+
"useCases": [
|
|
100
|
+
"Labels / tags on a record",
|
|
101
|
+
"Recipient email entry",
|
|
102
|
+
"Keyword / skill lists",
|
|
103
|
+
"Ad-hoc filter terms"
|
|
104
|
+
]
|
|
105
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Text } from \"@godxjp/ui/general\";\n\n<Text size=\"xs\" tone=\"muted\">補助テキスト</Text>\n<Text weight=\"medium\" tabular>¥1,240,000</Text>\n<Text size=\"xs\" mono tone=\"muted\">RC-204881</Text>\n\n// A link in running content — the affordance, composed onto a router link.\n<Text asChild link><Link href=\"/view/PKG-12\">ログイン画面の余白</Link></Text>",
|
|
3
|
+
"group": "general",
|
|
4
|
+
"importPath": "@godxjp/ui/general",
|
|
5
|
+
"name": "Text",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Text decoration, including deleted Markdown content.",
|
|
9
|
+
"name": "decoration",
|
|
10
|
+
"type": "\"none\" | \"underline\" | \"line-through\""
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Inline code chip: compose Text as=\"code\" chip without Prose wrapper.",
|
|
14
|
+
"name": "chip",
|
|
15
|
+
"type": "boolean"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"defaultValue": "\"sm\"",
|
|
19
|
+
"description": "Type-scale step. NEVER an arbitrary px (`text-[13px]` is banned) — pick the nearest step. Ten steps in two ramps: 2xs…2xl is the golden-ratio UI ramp (≈11…22px, the dense enterprise scale); 3xl…5xl is the DISPLAY ramp (≈28/42/54px, derived from --font-size-display) for a marketing hero, CTA headline or stat figure. The same ladder `Heading size` reads, so a headline and the figure beside it are one step name apart.",
|
|
20
|
+
"name": "size",
|
|
21
|
+
"type": "\"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"4xl\" | \"5xl\""
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"defaultValue": "\"default\"",
|
|
25
|
+
"description": "Semantic foreground colour. Replaces `text-muted-foreground` etc. on a raw span.",
|
|
26
|
+
"name": "tone",
|
|
27
|
+
"type": "\"default\" | \"muted\" | \"primary\" | \"success\" | \"warning\" | \"destructive\" | \"info\" | \"inherit\""
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"defaultValue": "\"regular\"",
|
|
31
|
+
"description": "Font weight — the 3-weight canon: regular 400 (body), medium 500 (label), bold 700 (emphasis). `semibold` is an alias that renders at the canon's 500 (there is no 600 face), so code written in the common Tailwind vocabulary type-checks without a fourth weight.",
|
|
32
|
+
"name": "weight",
|
|
33
|
+
"type": "\"regular\" | \"medium\" | \"semibold\" | \"bold\""
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"description": "Logical text alignment.",
|
|
37
|
+
"name": "align",
|
|
38
|
+
"type": "\"start\" | \"center\" | \"end\""
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"description": "Single-line ellipsis. Mutually exclusive with `clamp` (`clamp` wins).",
|
|
42
|
+
"name": "truncate",
|
|
43
|
+
"type": "boolean"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"description": "Multi-line clamp — max rendered lines (integer ≥ 1); overflow ends in an ellipsis. Visual-only — the full text stays in the DOM / accessible name. Mutually exclusive with `truncate` (`clamp` wins; dev builds warn).",
|
|
47
|
+
"name": "clamp",
|
|
48
|
+
"type": "number"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"defaultValue": "\"normal\"",
|
|
52
|
+
"description": "Whitespace handling. 'normal' is CSS's own behaviour (newlines and space runs collapse) and stays the default. 'pre-wrap' is for text a PERSON typed — a plain-text note, an issue description, a pasted log — where the line breaks and the indentation are CONTENT: it keeps both, still wraps long lines at the container edge, and breaks an over-long unbroken token (a URL, an id) instead of overflowing. Use it INSTEAD of `className=\"whitespace-pre-wrap\"`. Precedence is explicit: `truncate` is a single-line contract and wins (dev builds warn); `clamp` COMPOSES with it, showing the first N preserved lines. Not for rendered Markdown/HTML — that is `Prose`, which styles rendered elements and does nothing to whitespace.",
|
|
53
|
+
"name": "whitespace",
|
|
54
|
+
"type": "\"normal\" | \"pre-wrap\""
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"description": "Tabular figures for aligned numbers.",
|
|
58
|
+
"name": "tabular",
|
|
59
|
+
"type": "boolean"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"description": "Monospace family for codes / ids.",
|
|
63
|
+
"name": "mono",
|
|
64
|
+
"type": "boolean"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"defaultValue": "\"span\"",
|
|
68
|
+
"description": "Rendered element. `code`/`kbd` are monospace by default.",
|
|
69
|
+
"name": "as",
|
|
70
|
+
"type": "\"span\" | \"p\" | \"div\" | \"a\" | \"label\" | \"strong\" | \"em\" | \"small\" | \"code\" | \"kbd\" | \"dt\" | \"dd\" | \"caption\" | \"abbr\""
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"description": "This text IS a link: underline on hover AND on keyboard focus at --text-link-underline-offset, plus the focus mark every other interactive element draws. An AFFORDANCE, not a colour — `tone` still owns the colour and merely defaults to `primary` here, so `link tone=\"destructive\"` reads destructive and still underlines.",
|
|
74
|
+
"name": "link",
|
|
75
|
+
"type": "boolean"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"defaultValue": "false",
|
|
79
|
+
"description": "Render the typography ONTO the child instead of emitting an element — the shape a router link takes (`<Text asChild link><Link href=…>…</Link></Text>`). The child owns the element and its navigation; Text owns the type step, tone, weight and truncation.",
|
|
80
|
+
"name": "asChild",
|
|
81
|
+
"type": "boolean"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"description": "When set, Text renders as a `<label>` bound to this control id (polymorphic label use).",
|
|
85
|
+
"name": "htmlFor",
|
|
86
|
+
"type": "string"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"description": "Anchor target, for `as=\"a\"`. Declared explicitly rather than by widening the base to AnchorHTMLAttributes, for the same reason `htmlFor` is declared explicitly for `as=\"label\"`: the element union IS the contract, so each polymorphic branch names the attributes it actually accepts instead of every span silently offering an href it will never render.",
|
|
90
|
+
"name": "href",
|
|
91
|
+
"type": "string"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"description": "Anchor browsing context, for `as=\"a\"`.",
|
|
95
|
+
"name": "target",
|
|
96
|
+
"type": "React.HTMLAttributeAnchorTarget"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"description": "Anchor relationship, for `as=\"a\"` — pair `rel=\"noopener noreferrer\"` with `target=\"_blank\"`.",
|
|
100
|
+
"name": "rel",
|
|
101
|
+
"type": "string"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"description": "Anchor download hint, for `as=\"a\"`.",
|
|
105
|
+
"name": "download",
|
|
106
|
+
"type": "boolean | string"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"description": "antd's emphasis axis. This library's `tone` is the SAME axis and is wider (it also carries default/primary/info), so `tone` wins when both are passed and dev builds warn. Folds secondary → muted, danger → destructive.",
|
|
110
|
+
"name": "type",
|
|
111
|
+
"type": "\"secondary\" | \"success\" | \"warning\" | \"danger\""
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"description": "Renders the run as unavailable: the disabled ink, `cursor: not-allowed`, no text selection, and `aria-disabled` so a screen reader hears \"unavailable\" rather than merely seeing it de-emphasised.",
|
|
115
|
+
"name": "disabled",
|
|
116
|
+
"type": "boolean"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"description": "Copy affordance beside the text. `true` copies the rendered children. `text` may be a string or an async function, so the value can be fetched on demand. `icon` and `tooltips` each take `[idle, copied]`; `tooltips: false` drops the tooltip but keeps the accessible name. `format: \"text/html\"` writes an HTML flavour alongside the plain one. `onCopy` fires only AFTER the write resolves — a clipboard refusal leaves the button unconfirmed instead of claiming a copy that never happened.",
|
|
120
|
+
"name": "copyable",
|
|
121
|
+
"type": "boolean | { text, onCopy, icon, tooltips, format, tabIndex }"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"description": "In-place editing. The run is REPLACED by an auto-growing textarea that inherits its type step, family and weight, so editing is WYSIWYG. Enter confirms with the trimmed value then fires `onEnd`; blur confirms WITHOUT `onEnd` (antd's split); Escape cancels. `triggerType: [\"text\"]` makes the text itself the affordance and renders no icon. Focus returns to the edit button when the editor closes.",
|
|
125
|
+
"name": "editable",
|
|
126
|
+
"type": "boolean | { text, editing, icon, tooltip, onStart, onChange, onCancel, onEnd, maxLength, autoSize, triggerType, enterIcon, tabIndex }"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"description": "Single-line truncation, antd's spelling. antd drops `rows` / `expandable` / `onExpand` on `Text` — an inline run has no second line to expand into — and that omission is ported; reach for `Paragraph` when you want them. It is the SAME axis as `truncate` / `clamp` and OUTRANKS both (dev builds warn), because it is the only spelling that can also carry a suffix or a tooltip.",
|
|
130
|
+
"name": "ellipsis",
|
|
131
|
+
"type": "boolean | { suffix, symbol, defaultExpanded, expanded, onEllipsis, tooltip }"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"description": "Which side the copy / edit / expand cluster sits on. Logical, so it mirrors in RTL. Default `end`.",
|
|
135
|
+
"name": "actions",
|
|
136
|
+
"type": "{ placement?: \"start\" | \"end\" }"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"description": "Wrap the content in a real `<code>` element. Different from `mono`, which only swaps the font family: this changes what the content IS, so it reaches assistive technology.",
|
|
140
|
+
"name": "code",
|
|
141
|
+
"type": "boolean"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"description": "Wrap in `<mark>` — highlighted.",
|
|
145
|
+
"name": "mark",
|
|
146
|
+
"type": "boolean"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"description": "Wrap in `<u>` — underlined.",
|
|
150
|
+
"name": "underline",
|
|
151
|
+
"type": "boolean"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"description": "Wrap in `<del>` — struck through.",
|
|
155
|
+
"name": "delete",
|
|
156
|
+
"type": "boolean"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"description": "Wrap in `<strong>` — bold AND semantically strong. Composes with `weight`: `weight` paints, `strong` means. The seven decoration flags nest in antd's order (strong → u → del → code → mark → kbd → i).",
|
|
160
|
+
"name": "strong",
|
|
161
|
+
"type": "boolean"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"description": "Wrap in `<kbd>` — a key or key combination.",
|
|
165
|
+
"name": "keyboard",
|
|
166
|
+
"type": "boolean"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"description": "Wrap in `<i>` — italic.",
|
|
170
|
+
"name": "italic",
|
|
171
|
+
"type": "boolean"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"description": "antd's private alias for `as`. Accepted so antd code pastes in unchanged; `as` wins when both are passed.",
|
|
175
|
+
"name": "component",
|
|
176
|
+
"type": "string"
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"rules": [
|
|
180
|
+
2,
|
|
181
|
+
23
|
|
182
|
+
],
|
|
183
|
+
"storyPath": "general/typography.tsx",
|
|
184
|
+
"tagline": "Typographic primitive — use INSTEAD of a hand-rolled `<span className=\"text-[13px] font-medium text-muted-foreground\">`. Size is a type-scale step (never px); tone/weight are tokens.",
|
|
185
|
+
"usage": [
|
|
186
|
+
"DO use `<Text>` for ALL body / inline / caption text instead of a styled `<span>`/`<p>`. Pick `size` from the scale; never write `text-[13px]`/`text-[11px]` or `font-semibold` by hand.",
|
|
187
|
+
"DO use `tone` for colour (`muted`/`primary`/semantic), `tabular` for numbers, `mono` for codes — not `className=\"text-muted-foreground font-mono tabular-nums\"`.",
|
|
188
|
+
"DO use `clamp={n}` for a description limited to n lines (card grids) and `truncate` for a one-line ellipsis — never `className=\"line-clamp-2\"` (banned utility). They are mutually exclusive; `clamp` wins.",
|
|
189
|
+
"For a heading, use `<Heading level>` instead of a large-size `<Text>`.",
|
|
190
|
+
"DO use `link` for a link inside RUNNING CONTENT — an issue subject in a table cell, a page name in a list, a \"see all\" at the end of a row — and compose it onto your router link with `asChild`. Never `className=\"text-primary hover:underline\"`: that is consumer rules 6 and 7 in one string, and it leaves a keyboard user with no underline because `hover:` cannot fire for them.",
|
|
191
|
+
"DON'T reach for `Button variant=\"link\"` in running content. `.ui-button` is a CONTROL box — `white-space: nowrap`, `flex-shrink: 0`, a `--control-height` tier and inline padding — so in a table cell it cannot wrap to a second line and cannot share the cell's line box. If you find yourself writing `whitespace-normal` back on top of it, you wanted `<Text link>`. `Button variant=\"link\"` stays right for a link-LOOKING action that submits or opens something."
|
|
192
|
+
],
|
|
193
|
+
"useCases": [
|
|
194
|
+
"A muted caption under a value: `<Text size=\"xs\" tone=\"muted\">2026年5月度</Text>`.",
|
|
195
|
+
"A monospace id in a list row: `<Text size=\"xs\" mono tone=\"muted\">RC-204881</Text>`.",
|
|
196
|
+
"An emphasized inline figure: `<Text weight=\"medium\" tabular>¥1,240,000</Text>`.",
|
|
197
|
+
"A service-card description clamped to 2 lines on a narrow (390px) index: `<Text as=\"p\" size=\"sm\" tone=\"muted\" clamp={2}>{description}</Text>`.",
|
|
198
|
+
"An issue subject linking out of a table cell, wrapping to two lines: `<Text asChild link truncate={false}><Link href={`/view/${key}`}>{subject}</Link></Text>`.",
|
|
199
|
+
"A destructive link in a settings row: `<Text as=\"a\" href=\"/danger\" link tone=\"destructive\">取り消す</Text>`."
|
|
200
|
+
]
|
|
201
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Textarea } from \"@godxjp/ui/data-entry\";\n\n<Textarea id=\"notes\" rows={4} placeholder=\"自由記述\" value={notes} onChange={(e) => setNotes(e.target.value)} />\n\n// Chat / comment composer — grows with its content, scrolls past 8 rows, collapses on send.\n<Textarea autoGrow minRows={1} maxRows={8} value={draft} onChange={(e) => setDraft(e.target.value)} placeholder=\"メッセージを入力...\" />",
|
|
3
|
+
"group": "data-entry",
|
|
4
|
+
"importPath": "@godxjp/ui/data-entry",
|
|
5
|
+
"name": "Textarea",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Measured padding escape, stamped on the field.",
|
|
9
|
+
"name": "padRaw",
|
|
10
|
+
"type": "PadRawProp"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"description": "Instance padding with logical asymmetric sides.",
|
|
14
|
+
"name": "pad",
|
|
15
|
+
"type": "PadProp"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"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.",
|
|
19
|
+
"name": "status",
|
|
20
|
+
"type": "\"error\" | \"warning\""
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"defaultValue": "\"outlined\"",
|
|
24
|
+
"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. This library's older `default`/`ghost` are still accepted and resolve to `outlined`/`borderless`.",
|
|
25
|
+
"name": "variant",
|
|
26
|
+
"type": "\"outlined\" | \"filled\" | \"borderless\" | \"default\" | \"ghost\""
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"defaultValue": "\"md\"",
|
|
30
|
+
"description": "Control height tier — reads the shared `--control-height` ladder.",
|
|
31
|
+
"name": "size",
|
|
32
|
+
"type": "\"sm\" | \"md\" | \"lg\""
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"description": "antd `autoSize`. `true` is `autoGrow`; the object form carries the row bounds with it, so `autoSize={{ minRows: 2, maxRows: 6 }}` is `autoGrow minRows={2} maxRows={6}`. An explicit `minRows`/`maxRows` still wins.",
|
|
36
|
+
"name": "autoSize",
|
|
37
|
+
"type": "boolean | { minRows?: number; maxRows?: number }"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"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.",
|
|
41
|
+
"name": "count",
|
|
42
|
+
"type": "{ max?: number; show?: boolean; formatter?: (info: { value: string; count: number; max?: number }) => React.ReactNode; strategy?: (value: string) => number }"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"description": "Associates with a <Label htmlFor>.",
|
|
46
|
+
"name": "id",
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"description": "Visible text rows.",
|
|
51
|
+
"name": "rows",
|
|
52
|
+
"type": "number"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"description": "Controlled value.",
|
|
56
|
+
"name": "value",
|
|
57
|
+
"type": "string"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"description": "Change handler.",
|
|
61
|
+
"name": "onChange",
|
|
62
|
+
"type": "React.ChangeEventHandler<HTMLTextAreaElement>"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"defaultValue": "false",
|
|
66
|
+
"description": "Opt-in inline ✕ at the top-end that clears the field while it holds text (controlled + uncontrolled). Off by default.",
|
|
67
|
+
"name": "allowClear",
|
|
68
|
+
"type": "boolean"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"description": "Called after the field is cleared via the inline ✕ (requires `allowClear`).",
|
|
72
|
+
"name": "onClear",
|
|
73
|
+
"type": "() => void"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"defaultValue": "false",
|
|
77
|
+
"description": "The floor is `minRows` (or `rows`, when that is the only one given) and never undercuts the `--control-height` tier, so a resting one-row composer still lines up with the Button beside it; past `maxRows` the control stops growing and scrolls internally. Sizing happens in CSS from a hidden replica of the text, so it follows a paste, an IME composition, a programmatic value change, a font swap and a container resize — not just typing — and the component never writes `style.height`, never reads `scrollHeight` and never moves `scrollTop`. Works controlled and uncontrolled. Off by default: an existing Textarea keeps the exact geometry it has today.",
|
|
78
|
+
"name": "autoGrow",
|
|
79
|
+
"type": "boolean"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"defaultValue": "1 (--textarea-autogrow-min-height-rows)",
|
|
83
|
+
"description": "Floor in text rows while `autoGrow`. Theme-global default is the `--textarea-autogrow-min-height-rows` token; this prop overrides it per instance. Ignored when `autoGrow` is false.",
|
|
84
|
+
"name": "minRows",
|
|
85
|
+
"type": "number"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"defaultValue": "8 (--textarea-autogrow-max-height-rows)",
|
|
89
|
+
"description": "Ceiling in text rows while `autoGrow`; beyond it the box stops growing and scrolls internally rather than pushing the page. Pass `0` for no ceiling — only correct inside an owning scroll container. Theme-global default is the `--textarea-autogrow-max-height-rows` token. Ignored when `autoGrow` is false.",
|
|
90
|
+
"name": "maxRows",
|
|
91
|
+
"type": "number"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"description": "Immediate value callback; native onChange remains supported. Shared form bindings are emitted once.",
|
|
95
|
+
"name": "onValueChange",
|
|
96
|
+
"type": "(value: string) => void"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"related": [
|
|
100
|
+
"Input — use Input for single-line values (names, amounts, codes). Use Textarea only when the expected value spans multiple lines or could be longer than ~80 characters.",
|
|
101
|
+
"FormField — always the parent wrapper for Textarea in forms; provides label, helper text, error message, and injects all required aria attributes onto the Textarea child automatically.",
|
|
102
|
+
"Select — when the user must pick from a finite set of multi-line-looking options (e.g. template choices) use Select, not a Textarea presenting options as free text.",
|
|
103
|
+
"Select with showSearch or SearchSelect — if the multi-line field is actually a tag/token input or a constrained lookup, prefer Select (showSearch) or SearchSelect over a Textarea that the user types into freely."
|
|
104
|
+
],
|
|
105
|
+
"rules": [],
|
|
106
|
+
"storyPath": "data-entry/Textarea.stories.tsx",
|
|
107
|
+
"tagline": "Styled wrapper around native <textarea>. Pair with FormField for labelled fields.",
|
|
108
|
+
"usage": [
|
|
109
|
+
"DO reach for `variant=\"ghost\"` ONLY when a parent surface already draws the box and owns focus — the composer Card, an inline edit cell. A standalone field keeps the default: without its own border it reads as plain text, not something you can type into.",
|
|
110
|
+
"DO always wrap Textarea in FormField when it appears in a form — FormField clones aria-describedby, aria-required, and aria-invalid onto the child, giving error/helper announcements and screen-reader labelling for free. Pass matching id props to both.",
|
|
111
|
+
"DO use the godx-ui Textarea (`import { Textarea } from '@godxjp/ui/data-entry'`) — never a raw `<textarea>`. The component applies the `ui-control-multiline` token class that picks up density, focus-ring, and border tokens from the design system.",
|
|
112
|
+
"DO control the value with `value` + `onChange` in React-managed forms (e.g. Inertia `useForm`). Textarea is a plain `forwardRef` over the native element so it accepts all standard `HTMLTextAreaElement` attributes — `rows`, `maxLength`, `disabled`, `name`, `placeholder`, `readOnly` all pass through directly.",
|
|
113
|
+
"DO pass `name` when the textarea sits inside an HTML `<form>` for native form submission or when Inertia's `useForm` destructures field values by key — the `name` attribute maps the value into the form data bag.",
|
|
114
|
+
"DON'T apply manual height or padding classes directly on Textarea to simulate a taller field — use `rows` for a fixed height, or `autoGrow` for a box that grows with its content.",
|
|
115
|
+
"DON'T hand-roll auto-grow with an `onInput` handler that sets `style.height` from `scrollHeight` — that is a forced synchronous reflow on every keystroke, it re-derives the library's own box model (`--control-height`, `--control-padding-x`, `--control-border-width`), it freezes the height against the density axis and a tenant retheme, and it misses the paths that matter: a paste, an IME composition in ja/vi, a programmatic reset after submit, and a webfont swap. Pass `autoGrow` instead — the library owns the measurement in CSS.",
|
|
116
|
+
"DO reach for `autoGrow` + `minRows`/`maxRows` for a chat or comment composer: `<Textarea autoGrow minRows={1} maxRows={8} />` starts one row tall, grows line by line as the author types or pastes, scrolls internally past the ceiling, and collapses back to `minRows` the moment a controlled value is reset to `\"\"` after send. Bound it in ROWS, never in px — a row count survives a density change and a `--font-size-base` retheme.",
|
|
117
|
+
"DON'T hand-roll label + error markup next to a bare Textarea. Always use FormField: it injects aria-invalid (red ring on the control), renders a `role='alert'` error paragraph, and links them via aria-describedby automatically."
|
|
118
|
+
],
|
|
119
|
+
"useCases": [
|
|
120
|
+
"Free-text memo or note fields on an invoice or transaction detail form — e.g. '備考 / Notes' that can hold multi-line internal comments alongside structured Invoice fields.",
|
|
121
|
+
"Rejection reason or approval comment in an admin workflow dialog — a short-to-medium text block a reviewer types before confirming an action in a Dialog or Sheet.",
|
|
122
|
+
"Address or multi-line description input on a vendor / partner entity form where a single-line Input would be too restrictive.",
|
|
123
|
+
"Email body composer or message template editor in a lightweight CRM or notification settings screen where rich text is not required.",
|
|
124
|
+
"Audit log annotation — allowing an accountant to attach a plain-text explanation to a manual journal entry or adjustment record."
|
|
125
|
+
]
|
|
126
|
+
}
|