@adres/mgda-ui 0.0.0-stage → 0.0.2
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/brand-knowledge.json +451 -0
- package/knowledge/accordion.md +135 -0
- package/knowledge/activity-rings.md +123 -0
- package/knowledge/alert.md +184 -0
- package/knowledge/answer-actions.md +217 -0
- package/knowledge/aspect-ratio.md +82 -0
- package/knowledge/avatar.md +158 -0
- package/knowledge/badge.md +172 -0
- package/knowledge/bar-graph.md +167 -0
- package/knowledge/bento-grid.md +115 -0
- package/knowledge/blockquote.md +153 -0
- package/knowledge/box-list.md +177 -0
- package/knowledge/bullet-list.md +128 -0
- package/knowledge/button-group.md +179 -0
- package/knowledge/button.md +231 -0
- package/knowledge/calendar.md +168 -0
- package/knowledge/card.md +321 -0
- package/knowledge/cards-nav.md +170 -0
- package/knowledge/chain-of-thought.md +164 -0
- package/knowledge/chat-container.md +135 -0
- package/knowledge/chat-message.md +224 -0
- package/knowledge/checkbox-card.md +172 -0
- package/knowledge/checkbox.md +111 -0
- package/knowledge/citations.md +241 -0
- package/knowledge/combobox.md +265 -0
- package/knowledge/countdown-pill.md +153 -0
- package/knowledge/cta.md +118 -0
- package/knowledge/data-list.md +173 -0
- package/knowledge/data-table.md +220 -0
- package/knowledge/dialog.md +249 -0
- package/knowledge/drawer.md +236 -0
- package/knowledge/dropdown-menu.md +238 -0
- package/knowledge/dropzone.md +153 -0
- package/knowledge/email-chips-input.md +161 -0
- package/knowledge/empty-state.md +106 -0
- package/knowledge/entity-header.md +166 -0
- package/knowledge/feature-grid.md +120 -0
- package/knowledge/feedback-bar.md +127 -0
- package/knowledge/footer.md +116 -0
- package/knowledge/heading.md +189 -0
- package/knowledge/hero.md +361 -0
- package/knowledge/input-button.md +187 -0
- package/knowledge/input-group.md +185 -0
- package/knowledge/input.md +256 -0
- package/knowledge/inset.md +177 -0
- package/knowledge/kpi-card.md +206 -0
- package/knowledge/list-nav.md +181 -0
- package/knowledge/logo-cloud.md +102 -0
- package/knowledge/media-gallery.md +158 -0
- package/knowledge/message-actions.md +175 -0
- package/knowledge/nav-sidebar.md +225 -0
- package/knowledge/number-stepper.md +159 -0
- package/knowledge/page-header.md +109 -0
- package/knowledge/pagination.md +225 -0
- package/knowledge/popover.md +159 -0
- package/knowledge/pricing.md +132 -0
- package/knowledge/progress.md +156 -0
- package/knowledge/prompt-input.md +337 -0
- package/knowledge/prompt-suggestions.md +168 -0
- package/knowledge/quota-bar.md +160 -0
- package/knowledge/quote.md +116 -0
- package/knowledge/radio-card.md +172 -0
- package/knowledge/radio-group.md +135 -0
- package/knowledge/reasoning.md +142 -0
- package/knowledge/search.md +131 -0
- package/knowledge/section-nav.md +162 -0
- package/knowledge/segmented-control.md +181 -0
- package/knowledge/select-button.md +205 -0
- package/knowledge/select.md +210 -0
- package/knowledge/separator.md +75 -0
- package/knowledge/shimmer-text.md +140 -0
- package/knowledge/sidebar.md +227 -0
- package/knowledge/skeleton.md +142 -0
- package/knowledge/slider.md +133 -0
- package/knowledge/slideshow.md +159 -0
- package/knowledge/source.md +156 -0
- package/knowledge/spinner.md +120 -0
- package/knowledge/stat-strip.md +128 -0
- package/knowledge/stats.md +117 -0
- package/knowledge/step-indicator.md +157 -0
- package/knowledge/switch.md +88 -0
- package/knowledge/table.md +190 -0
- package/knowledge/tabs.md +169 -0
- package/knowledge/testimonials.md +115 -0
- package/knowledge/text-link.md +192 -0
- package/knowledge/text.md +194 -0
- package/knowledge/thinking-bar.md +115 -0
- package/knowledge/thinking-steps.md +140 -0
- package/knowledge/toaster.md +127 -0
- package/knowledge/toggle.md +113 -0
- package/knowledge/tool-call-card.md +241 -0
- package/knowledge/tooltip.md +120 -0
- package/knowledge/top-nav.md +138 -0
- package/knowledge.json +19854 -0
- package/package.json +94 -4
- package/src/components/accordion.stories.tsx +46 -0
- package/src/components/accordion.tsx +74 -0
- package/src/components/activity-rings.stories.tsx +73 -0
- package/src/components/activity-rings.tsx +121 -0
- package/src/components/alert.stories.tsx +43 -0
- package/src/components/alert.tsx +132 -0
- package/src/components/answer-actions.stories.tsx +54 -0
- package/src/components/answer-actions.test.tsx +37 -0
- package/src/components/answer-actions.tsx +313 -0
- package/src/components/aspect-ratio.stories.tsx +32 -0
- package/src/components/aspect-ratio.tsx +5 -0
- package/src/components/avatar.stories.tsx +40 -0
- package/src/components/avatar.tsx +154 -0
- package/src/components/badge.stories.tsx +27 -0
- package/src/components/badge.tsx +63 -0
- package/src/components/bar-graph.stories.tsx +28 -0
- package/src/components/bar-graph.tsx +204 -0
- package/src/components/bento-grid.stories.tsx +23 -0
- package/src/components/bento-grid.tsx +67 -0
- package/src/components/blockquote.stories.tsx +28 -0
- package/src/components/blockquote.tsx +88 -0
- package/src/components/box-list.stories.tsx +25 -0
- package/src/components/box-list.tsx +108 -0
- package/src/components/bullet-list.stories.tsx +22 -0
- package/src/components/bullet-list.tsx +101 -0
- package/src/components/button-group.stories.tsx +31 -0
- package/src/components/button-group.tsx +156 -0
- package/src/components/button.stories.tsx +66 -0
- package/src/components/button.test.ts +17 -0
- package/src/components/button.tsx +139 -0
- package/src/components/calendar.stories.tsx +25 -0
- package/src/components/calendar.tsx +367 -0
- package/src/components/card.stories.tsx +44 -0
- package/src/components/card.tsx +392 -0
- package/src/components/cards-nav.stories.tsx +35 -0
- package/src/components/cards-nav.tsx +115 -0
- package/src/components/chain-of-thought.stories.tsx +67 -0
- package/src/components/chain-of-thought.test.tsx +50 -0
- package/src/components/chain-of-thought.tsx +170 -0
- package/src/components/chat-container.stories.tsx +47 -0
- package/src/components/chat-container.test.tsx +49 -0
- package/src/components/chat-container.tsx +222 -0
- package/src/components/chat-message.stories.tsx +52 -0
- package/src/components/chat-message.test.tsx +36 -0
- package/src/components/chat-message.tsx +163 -0
- package/src/components/checkbox-card.stories.tsx +27 -0
- package/src/components/checkbox-card.tsx +200 -0
- package/src/components/checkbox.stories.tsx +26 -0
- package/src/components/checkbox.tsx +61 -0
- package/src/components/citations.stories.tsx +64 -0
- package/src/components/citations.test.tsx +61 -0
- package/src/components/citations.tsx +220 -0
- package/src/components/combobox.stories.tsx +184 -0
- package/src/components/combobox.test.tsx +82 -0
- package/src/components/combobox.tsx +407 -0
- package/src/components/countdown-pill.stories.tsx +24 -0
- package/src/components/countdown-pill.tsx +129 -0
- package/src/components/cta.stories.tsx +29 -0
- package/src/components/cta.tsx +52 -0
- package/src/components/data-list.stories.tsx +50 -0
- package/src/components/data-list.test.tsx +35 -0
- package/src/components/data-list.tsx +181 -0
- package/src/components/data-table.stories.tsx +29 -0
- package/src/components/data-table.tsx +200 -0
- package/src/components/dialog.stories.tsx +42 -0
- package/src/components/dialog.tsx +200 -0
- package/src/components/drawer.stories.tsx +48 -0
- package/src/components/drawer.tsx +385 -0
- package/src/components/dropdown-menu.stories.tsx +41 -0
- package/src/components/dropdown-menu.tsx +252 -0
- package/src/components/dropzone.stories.tsx +19 -0
- package/src/components/dropzone.tsx +250 -0
- package/src/components/email-chips-input.stories.tsx +135 -0
- package/src/components/email-chips-input.tsx +210 -0
- package/src/components/empty-state.stories.tsx +22 -0
- package/src/components/empty-state.tsx +35 -0
- package/src/components/entity-header.stories.tsx +43 -0
- package/src/components/entity-header.test.tsx +35 -0
- package/src/components/entity-header.tsx +92 -0
- package/src/components/feature-grid.stories.tsx +25 -0
- package/src/components/feature-grid.tsx +87 -0
- package/src/components/feedback-bar.stories.tsx +25 -0
- package/src/components/feedback-bar.test.tsx +24 -0
- package/src/components/feedback-bar.tsx +62 -0
- package/src/components/footer.stories.tsx +23 -0
- package/src/components/footer.tsx +78 -0
- package/src/components/heading.stories.tsx +45 -0
- package/src/components/heading.test.tsx +22 -0
- package/src/components/heading.tsx +77 -0
- package/src/components/hero.stories.tsx +38 -0
- package/src/components/hero.tsx +498 -0
- package/src/components/input-button.stories.tsx +19 -0
- package/src/components/input-button.tsx +112 -0
- package/src/components/input-group.stories.tsx +38 -0
- package/src/components/input-group.tsx +157 -0
- package/src/components/input.stories.tsx +31 -0
- package/src/components/input.tsx +284 -0
- package/src/components/inset.stories.tsx +29 -0
- package/src/components/inset.tsx +70 -0
- package/src/components/kpi-card.stories.tsx +29 -0
- package/src/components/kpi-card.tsx +284 -0
- package/src/components/list-nav.stories.tsx +22 -0
- package/src/components/list-nav.tsx +145 -0
- package/src/components/logo-cloud.stories.tsx +23 -0
- package/src/components/logo-cloud.tsx +42 -0
- package/src/components/media-gallery.stories.tsx +25 -0
- package/src/components/media-gallery.tsx +347 -0
- package/src/components/message-actions.stories.tsx +45 -0
- package/src/components/message-actions.test.tsx +32 -0
- package/src/components/message-actions.tsx +119 -0
- package/src/components/nav-sidebar.stories.tsx +58 -0
- package/src/components/nav-sidebar.test.tsx +54 -0
- package/src/components/nav-sidebar.tsx +462 -0
- package/src/components/number-stepper.stories.tsx +19 -0
- package/src/components/number-stepper.tsx +158 -0
- package/src/components/page-header.stories.tsx +25 -0
- package/src/components/page-header.tsx +64 -0
- package/src/components/pagination.stories.tsx +110 -0
- package/src/components/pagination.test.tsx +46 -0
- package/src/components/pagination.tsx +144 -0
- package/src/components/popover.stories.tsx +26 -0
- package/src/components/popover.tsx +62 -0
- package/src/components/pricing.stories.tsx +42 -0
- package/src/components/pricing.tsx +130 -0
- package/src/components/progress.stories.tsx +22 -0
- package/src/components/progress.tsx +189 -0
- package/src/components/prompt-input.stories.tsx +62 -0
- package/src/components/prompt-input.test.tsx +106 -0
- package/src/components/prompt-input.tsx +343 -0
- package/src/components/prompt-suggestions.stories.tsx +53 -0
- package/src/components/prompt-suggestions.test.tsx +37 -0
- package/src/components/prompt-suggestions.tsx +122 -0
- package/src/components/quota-bar.stories.tsx +19 -0
- package/src/components/quota-bar.tsx +191 -0
- package/src/components/quote.stories.tsx +18 -0
- package/src/components/quote.tsx +43 -0
- package/src/components/radio-card.stories.tsx +27 -0
- package/src/components/radio-card.tsx +225 -0
- package/src/components/radio-group.stories.tsx +32 -0
- package/src/components/radio-group.tsx +68 -0
- package/src/components/reasoning.stories.tsx +50 -0
- package/src/components/reasoning.test.tsx +48 -0
- package/src/components/reasoning.tsx +143 -0
- package/src/components/search.stories.tsx +18 -0
- package/src/components/search.tsx +133 -0
- package/src/components/section-nav.stories.tsx +59 -0
- package/src/components/section-nav.test.tsx +36 -0
- package/src/components/section-nav.tsx +146 -0
- package/src/components/segmented-control.stories.tsx +22 -0
- package/src/components/segmented-control.tsx +301 -0
- package/src/components/select-button.stories.tsx +27 -0
- package/src/components/select-button.tsx +174 -0
- package/src/components/select.stories.tsx +27 -0
- package/src/components/select.tsx +198 -0
- package/src/components/separator.stories.tsx +22 -0
- package/src/components/separator.tsx +23 -0
- package/src/components/shimmer-text.stories.tsx +23 -0
- package/src/components/shimmer-text.test.tsx +35 -0
- package/src/components/shimmer-text.tsx +43 -0
- package/src/components/sidebar.stories.tsx +25 -0
- package/src/components/sidebar.tsx +234 -0
- package/src/components/skeleton.stories.tsx +22 -0
- package/src/components/skeleton.tsx +96 -0
- package/src/components/slider.stories.tsx +20 -0
- package/src/components/slider.tsx +176 -0
- package/src/components/slideshow.stories.tsx +30 -0
- package/src/components/slideshow.tsx +291 -0
- package/src/components/source.stories.tsx +32 -0
- package/src/components/source.test.tsx +40 -0
- package/src/components/source.tsx +161 -0
- package/src/components/spinner.stories.tsx +19 -0
- package/src/components/spinner.tsx +76 -0
- package/src/components/stat-strip.stories.tsx +36 -0
- package/src/components/stat-strip.test.tsx +25 -0
- package/src/components/stat-strip.tsx +85 -0
- package/src/components/stats.stories.tsx +24 -0
- package/src/components/stats.tsx +83 -0
- package/src/components/step-indicator.stories.tsx +18 -0
- package/src/components/step-indicator.test.tsx +18 -0
- package/src/components/step-indicator.tsx +286 -0
- package/src/components/switch.stories.tsx +26 -0
- package/src/components/switch.tsx +32 -0
- package/src/components/table.stories.tsx +38 -0
- package/src/components/table.tsx +220 -0
- package/src/components/tabs.stories.tsx +25 -0
- package/src/components/tabs.tsx +276 -0
- package/src/components/testimonials.stories.tsx +22 -0
- package/src/components/testimonials.tsx +109 -0
- package/src/components/text-link.stories.tsx +47 -0
- package/src/components/text-link.test.tsx +18 -0
- package/src/components/text-link.tsx +88 -0
- package/src/components/text.stories.tsx +49 -0
- package/src/components/text.tsx +105 -0
- package/src/components/thinking-bar.stories.tsx +19 -0
- package/src/components/thinking-bar.test.tsx +27 -0
- package/src/components/thinking-bar.tsx +64 -0
- package/src/components/thinking-steps.stories.tsx +44 -0
- package/src/components/thinking-steps.test.tsx +42 -0
- package/src/components/thinking-steps.tsx +92 -0
- package/src/components/toaster.stories.tsx +80 -0
- package/src/components/toaster.tsx +36 -0
- package/src/components/toggle.stories.tsx +21 -0
- package/src/components/toggle.tsx +103 -0
- package/src/components/tool-call-card.stories.tsx +97 -0
- package/src/components/tool-call-card.test.tsx +88 -0
- package/src/components/tool-call-card.tsx +231 -0
- package/src/components/tooltip.stories.tsx +25 -0
- package/src/components/tooltip.tsx +43 -0
- package/src/components/top-nav.stories.tsx +20 -0
- package/src/components/top-nav.tsx +76 -0
- package/src/docs/Colors.stories.tsx +192 -0
- package/src/docs/GettingStarted.mdx +72 -0
- package/src/docs/Mcp.mdx +52 -0
- package/src/docs/Typography.stories.tsx +114 -0
- package/src/hooks/use-in-view.ts +32 -0
- package/src/hooks/use-reduced-motion.ts +26 -0
- package/src/index.ts +343 -0
- package/src/lib/cn.test.ts +67 -0
- package/src/lib/cn.ts +81 -0
- package/src/lib/motion.ts +109 -0
- package/src/lib/prefix-utilities.test.ts +56 -0
- package/src/lib/prefix-utilities.ts +50 -0
- package/src/styles/base.css +489 -0
- package/src/styles/motion.css +69 -0
- package/src/styles/overrides.css +44 -0
- package/src/styles/typography.css +173 -0
- package/src/theme/theme.css +391 -0
- package/src/theme/tokens.ts +29 -0
- package/src/theme/types.ts +26 -0
- package/README.md +0 -3
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Check, AlertTriangle } from "lucide-react"
|
|
5
|
+
import { cn } from "../lib/cn"
|
|
6
|
+
|
|
7
|
+
export type StepIndicatorSize = "sm" | "md" | "lg"
|
|
8
|
+
export type StepIndicatorOrientation = "horizontal" | "vertical"
|
|
9
|
+
|
|
10
|
+
export interface StepIndicatorStep {
|
|
11
|
+
/** Step label. A string in the common case; rich content (ReactNode) is
|
|
12
|
+
* allowed for the vertical timeline layout, e.g. a details block per step. */
|
|
13
|
+
label: React.ReactNode
|
|
14
|
+
/** Optional glyph rendered inside the circle in place of the step number. */
|
|
15
|
+
icon?: React.ReactNode
|
|
16
|
+
/** Marks the step as failed - tints the circle and connector destructive
|
|
17
|
+
* and renders a warning glyph regardless of the step's done/active state. */
|
|
18
|
+
status?: "error"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface StepIndicatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
22
|
+
steps: StepIndicatorStep[]
|
|
23
|
+
/** Zero-based index of the active step. Steps before it render as done; the
|
|
24
|
+
* step at this index renders as active; later steps stay muted. */
|
|
25
|
+
currentStep: number
|
|
26
|
+
/** Visual density. */
|
|
27
|
+
size?: StepIndicatorSize
|
|
28
|
+
/** Layout direction. `horizontal` (default) is best for desktop wizards;
|
|
29
|
+
* `vertical` is better on mobile or in side panels. */
|
|
30
|
+
orientation?: StepIndicatorOrientation
|
|
31
|
+
/** When provided, the indicator becomes interactive: done and active steps
|
|
32
|
+
* render as buttons that call back with the step index. Upcoming steps
|
|
33
|
+
* remain inert (users can't jump ahead). */
|
|
34
|
+
onStepClick?: (index: number) => void
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ─── Size tokens ──────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
const CIRCLE_SIZE: Record<StepIndicatorSize, string> = {
|
|
40
|
+
sm: "ax:size-6", // 24px
|
|
41
|
+
md: "ax:size-8", // 32px
|
|
42
|
+
lg: "ax:size-10", // 40px
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Height of the circle row - used to pin the connector track to the circle's
|
|
46
|
+
// vertical centerline without hard-coded pixel offsets.
|
|
47
|
+
const CIRCLE_H: Record<StepIndicatorSize, string> = {
|
|
48
|
+
sm: "ax:h-6",
|
|
49
|
+
md: "ax:h-8",
|
|
50
|
+
lg: "ax:h-10",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const NUMBER_CLASS: Record<StepIndicatorSize, string> = {
|
|
54
|
+
sm: "ax:t-label-sm",
|
|
55
|
+
md: "ax:t-label-sm",
|
|
56
|
+
lg: "ax:t-label-lg",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const LABEL_CLASS: Record<StepIndicatorSize, string> = {
|
|
60
|
+
sm: "ax:t-caption",
|
|
61
|
+
md: "ax:t-label-sm",
|
|
62
|
+
lg: "ax:t-label-sm",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const ICON_CLASS: Record<StepIndicatorSize, string> = {
|
|
66
|
+
sm: "ax:size-2.5",
|
|
67
|
+
md: "ax:size-3.5",
|
|
68
|
+
lg: "ax:size-4",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Shared with the connector track so a "done"/"active" line is the exact same
|
|
72
|
+
// fill as the circle it connects, not just the same hue.
|
|
73
|
+
const PRIMARY_FILL = "ax:bg-primary-weaker ax:dark:bg-primary"
|
|
74
|
+
|
|
75
|
+
// ─── Circle ───────────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
type CircleState = "done" | "active" | "upcoming" | "error"
|
|
78
|
+
|
|
79
|
+
interface StepCircleProps {
|
|
80
|
+
state: CircleState
|
|
81
|
+
size: StepIndicatorSize
|
|
82
|
+
index: number
|
|
83
|
+
icon?: React.ReactNode
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function StepCircle({ state, size, index, icon }: StepCircleProps) {
|
|
87
|
+
const glyph =
|
|
88
|
+
state === "error" ? <AlertTriangle className={ICON_CLASS[size]} /> :
|
|
89
|
+
state === "done" ? <Check className={ICON_CLASS[size]} strokeWidth={4} /> :
|
|
90
|
+
icon ?? <span>{index + 1}</span>
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
className={cn(
|
|
95
|
+
CIRCLE_SIZE[size],
|
|
96
|
+
"ax:rounded-full ax:flex ax:items-center ax:justify-center ax:font-semibold ax:transition-colors ax:shrink-0",
|
|
97
|
+
NUMBER_CLASS[size],
|
|
98
|
+
state === "done" && cn(PRIMARY_FILL, "ax:text-primary-strong ax:dark:text-primary-weakest"),
|
|
99
|
+
state === "active" && cn(PRIMARY_FILL, "ax:text-primary-strong ax:dark:text-primary-weakest"),
|
|
100
|
+
state === "upcoming" && "ax:bg-muted ax:text-muted-foreground",
|
|
101
|
+
state === "error" && "ax:bg-destructive ax:text-destructive-muted",
|
|
102
|
+
)}
|
|
103
|
+
>
|
|
104
|
+
{glyph}
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── StepIndicator ────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
export const StepIndicator = React.forwardRef<HTMLDivElement, StepIndicatorProps>(
|
|
112
|
+
(
|
|
113
|
+
{ steps, currentStep, size = "md", orientation = "horizontal", onStepClick, className, ...props },
|
|
114
|
+
ref,
|
|
115
|
+
) => {
|
|
116
|
+
const interactive = !!onStepClick
|
|
117
|
+
|
|
118
|
+
const stateFor = (i: number, status?: "error"): CircleState => {
|
|
119
|
+
if (status === "error") return "error"
|
|
120
|
+
if (i < currentStep) return "done"
|
|
121
|
+
if (i === currentStep) return "active"
|
|
122
|
+
return "upcoming"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const isClickable = (i: number) =>
|
|
126
|
+
interactive && (i < currentStep || i === currentStep)
|
|
127
|
+
|
|
128
|
+
// ── Vertical layout ────────────────────────────────────────────────────────
|
|
129
|
+
if (orientation === "vertical") {
|
|
130
|
+
return (
|
|
131
|
+
<div ref={ref} className={cn("ax:flex ax:flex-col", className)} {...props}>
|
|
132
|
+
{steps.map((step, i) => {
|
|
133
|
+
const state = stateFor(i, step.status)
|
|
134
|
+
const isLast = i === steps.length - 1
|
|
135
|
+
const clickable = isClickable(i)
|
|
136
|
+
const isError = step.status === "error"
|
|
137
|
+
|
|
138
|
+
const row = (
|
|
139
|
+
<>
|
|
140
|
+
<div className="ax:flex ax:flex-col ax:items-center ax:self-stretch ax:shrink-0">
|
|
141
|
+
<StepCircle state={state} size={size} index={i} icon={step.icon} />
|
|
142
|
+
{!isLast && (
|
|
143
|
+
<div
|
|
144
|
+
className={cn(
|
|
145
|
+
"ax:w-px ax:flex-1 ax:my-xs",
|
|
146
|
+
isError ? "ax:bg-destructive" : state === "done" ? PRIMARY_FILL : "ax:bg-border",
|
|
147
|
+
)}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
<span
|
|
152
|
+
className={cn(
|
|
153
|
+
LABEL_CLASS[size],
|
|
154
|
+
// leading-snug (not the label's leading-none): vertical labels
|
|
155
|
+
// wrap, and a 1.0 line-height crams the lines and clips descenders.
|
|
156
|
+
"ax:ms-sm ax:pb-md ax:text-start ax:leading-snug",
|
|
157
|
+
state === "active" ? "ax:text-foreground ax:font-medium" :
|
|
158
|
+
state === "error" ? "ax:text-destructive ax:font-medium" :
|
|
159
|
+
"ax:text-foreground",
|
|
160
|
+
)}
|
|
161
|
+
>
|
|
162
|
+
{step.label}
|
|
163
|
+
</span>
|
|
164
|
+
</>
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return clickable ? (
|
|
168
|
+
<button
|
|
169
|
+
key={i}
|
|
170
|
+
type="button"
|
|
171
|
+
onClick={() => onStepClick?.(i)}
|
|
172
|
+
aria-current={state === "active" ? "step" : undefined}
|
|
173
|
+
className={cn(
|
|
174
|
+
"ax:flex ax:items-start ax:text-start",
|
|
175
|
+
"ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring ax:rounded-md",
|
|
176
|
+
"ax:transition-opacity ax:hover:opacity-80",
|
|
177
|
+
)}
|
|
178
|
+
>
|
|
179
|
+
{row}
|
|
180
|
+
</button>
|
|
181
|
+
) : (
|
|
182
|
+
<div
|
|
183
|
+
key={i}
|
|
184
|
+
aria-current={state === "active" ? "step" : undefined}
|
|
185
|
+
className="ax:flex ax:items-start"
|
|
186
|
+
>
|
|
187
|
+
{row}
|
|
188
|
+
</div>
|
|
189
|
+
)
|
|
190
|
+
})}
|
|
191
|
+
</div>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ── Horizontal layout ──────────────────────────────────────────────────────
|
|
196
|
+
// Colour of the connector segment whose left-hand ("source") step is `src`.
|
|
197
|
+
const connectorColor = (src: number) =>
|
|
198
|
+
steps[src]?.status === "error" ? "ax:bg-destructive" :
|
|
199
|
+
src < currentStep ? PRIMARY_FILL :
|
|
200
|
+
"ax:bg-border"
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div ref={ref} className={cn("ax:flex ax:items-start", className)} {...props}>
|
|
204
|
+
{steps.map((step, i) => {
|
|
205
|
+
const state = stateFor(i, step.status)
|
|
206
|
+
const isFirst = i === 0
|
|
207
|
+
const isLast = i === steps.length - 1
|
|
208
|
+
const clickable = isClickable(i)
|
|
209
|
+
|
|
210
|
+
// `text-align` can't anchor overflowing nowrap text to a box edge - browsers
|
|
211
|
+
// overflow nowrap content toward its natural start regardless of alignment.
|
|
212
|
+
// So the visible label is absolutely positioned against the circle's own box
|
|
213
|
+
// (not centered-by-content), anchored by its inset edge instead: start-0 for
|
|
214
|
+
// the first step, end-0 for the last, centered for everything in between. An
|
|
215
|
+
// invisible clone stays in normal flow purely to reserve the row's height.
|
|
216
|
+
const labelPosition = cn(
|
|
217
|
+
"ax:absolute ax:top-full ax:mt-xs ax:whitespace-nowrap",
|
|
218
|
+
isFirst ? "ax:start-0 ax:text-start" :
|
|
219
|
+
isLast ? "ax:end-0 ax:text-end" :
|
|
220
|
+
"ax:start-1/2 ax:-translate-x-1/2 ax:rtl:translate-x-1/2 ax:text-center",
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
const stepNode = (
|
|
224
|
+
<div className="ax:flex ax:flex-col ax:items-center ax:gap-xs">
|
|
225
|
+
<div className="ax:relative">
|
|
226
|
+
<StepCircle state={state} size={size} index={i} icon={step.icon} />
|
|
227
|
+
<span
|
|
228
|
+
className={cn(
|
|
229
|
+
LABEL_CLASS[size],
|
|
230
|
+
labelPosition,
|
|
231
|
+
"ax:leading-snug",
|
|
232
|
+
state === "active" ? "ax:text-foreground ax:font-medium" :
|
|
233
|
+
state === "error" ? "ax:text-destructive-foreground ax:font-medium" :
|
|
234
|
+
"ax:text-muted-foreground",
|
|
235
|
+
)}
|
|
236
|
+
>
|
|
237
|
+
{step.label}
|
|
238
|
+
</span>
|
|
239
|
+
</div>
|
|
240
|
+
<span aria-hidden className={cn(LABEL_CLASS[size], "ax:invisible ax:whitespace-nowrap ax:leading-snug")}>
|
|
241
|
+
{step.label}
|
|
242
|
+
</span>
|
|
243
|
+
</div>
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<div key={i} className="ax:relative ax:flex ax:flex-1 ax:flex-col ax:items-center">
|
|
248
|
+
{/* Connector track - pinned to the circle's centerline and spanning
|
|
249
|
+
the full (equal-width) column, so it always meets the circle
|
|
250
|
+
edges and never shifts with the label width. Each column draws
|
|
251
|
+
the half-segment on either side of its circle; the circle's
|
|
252
|
+
opaque fill masks the seam where the halves meet at its centre. */}
|
|
253
|
+
<div
|
|
254
|
+
aria-hidden
|
|
255
|
+
className={cn("ax:pointer-events-none ax:absolute ax:inset-x-0 ax:top-0 ax:flex ax:items-center", CIRCLE_H[size])}
|
|
256
|
+
>
|
|
257
|
+
<div className={cn("ax:h-px ax:flex-1", isFirst ? "ax:opacity-0" : connectorColor(i - 1))} />
|
|
258
|
+
<div className={cn("ax:h-px ax:flex-1", isLast ? "ax:opacity-0" : connectorColor(i))} />
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
{clickable ? (
|
|
262
|
+
<button
|
|
263
|
+
type="button"
|
|
264
|
+
onClick={() => onStepClick?.(i)}
|
|
265
|
+
aria-current={state === "active" ? "step" : undefined}
|
|
266
|
+
className={cn(
|
|
267
|
+
"ax:relative ax:rounded-md",
|
|
268
|
+
"ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring",
|
|
269
|
+
"ax:transition-opacity ax:hover:opacity-80",
|
|
270
|
+
)}
|
|
271
|
+
>
|
|
272
|
+
{stepNode}
|
|
273
|
+
</button>
|
|
274
|
+
) : (
|
|
275
|
+
<div aria-current={state === "active" ? "step" : undefined} className="ax:relative">
|
|
276
|
+
{stepNode}
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
</div>
|
|
280
|
+
)
|
|
281
|
+
})}
|
|
282
|
+
</div>
|
|
283
|
+
)
|
|
284
|
+
},
|
|
285
|
+
)
|
|
286
|
+
StepIndicator.displayName = "StepIndicator"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Label } from "./input"
|
|
4
|
+
import { Switch } from "./switch"
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Switch> = {
|
|
7
|
+
title: "Components/Switch",
|
|
8
|
+
component: Switch,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
type Story = StoryObj<typeof Switch>
|
|
14
|
+
|
|
15
|
+
export const Default: Story = {
|
|
16
|
+
render: () => (
|
|
17
|
+
<div className="ax:flex ax:items-center ax:gap-sm">
|
|
18
|
+
<Switch id="airplane" />
|
|
19
|
+
<Label htmlFor="airplane">Airplane mode</Label>
|
|
20
|
+
</div>
|
|
21
|
+
),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const Checked: Story = {
|
|
25
|
+
render: () => <Switch defaultChecked />,
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch"
|
|
5
|
+
import { cn } from "../lib/cn"
|
|
6
|
+
|
|
7
|
+
export const Switch = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof SwitchPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => (
|
|
11
|
+
<SwitchPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn(
|
|
14
|
+
"ax:peer ax:inline-flex ax:h-5 ax:w-9 ax:shrink-0 ax:cursor-pointer ax:items-center ax:rounded-full ax:border-2 ax:border-transparent",
|
|
15
|
+
"ax:transition-colors ax:transition-snappy",
|
|
16
|
+
"ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring ax:focus-visible:ring-offset-2 ax:focus-visible:ring-offset-background",
|
|
17
|
+
"ax:disabled:cursor-not-allowed ax:disabled:opacity-50",
|
|
18
|
+
"ax:data-[state=checked]:bg-primary ax:data-[state=unchecked]:bg-input",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
<SwitchPrimitive.Thumb
|
|
24
|
+
className={cn(
|
|
25
|
+
"ax:pointer-events-none ax:block ax:size-4 ax:rounded-full ax:bg-background ax:shadow-sm",
|
|
26
|
+
"ax:transition-transform ax:transition-snappy",
|
|
27
|
+
"ax:data-[state=checked]:translate-x-4 ax:data-[state=unchecked]:translate-x-0"
|
|
28
|
+
)}
|
|
29
|
+
/>
|
|
30
|
+
</SwitchPrimitive.Root>
|
|
31
|
+
))
|
|
32
|
+
Switch.displayName = SwitchPrimitive.Root.displayName
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table"
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Table> = {
|
|
6
|
+
title: "Components/Table",
|
|
7
|
+
component: Table,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
type Story = StoryObj<typeof Table>
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<Table>
|
|
17
|
+
<TableHeader>
|
|
18
|
+
<TableRow>
|
|
19
|
+
<TableHead>Name</TableHead>
|
|
20
|
+
<TableHead>Status</TableHead>
|
|
21
|
+
<TableHead>Role</TableHead>
|
|
22
|
+
</TableRow>
|
|
23
|
+
</TableHeader>
|
|
24
|
+
<TableBody>
|
|
25
|
+
<TableRow>
|
|
26
|
+
<TableCell>Aisha Khan</TableCell>
|
|
27
|
+
<TableCell>Active</TableCell>
|
|
28
|
+
<TableCell>Admin</TableCell>
|
|
29
|
+
</TableRow>
|
|
30
|
+
<TableRow>
|
|
31
|
+
<TableCell>Marco Silva</TableCell>
|
|
32
|
+
<TableCell>Active</TableCell>
|
|
33
|
+
<TableCell>Editor</TableCell>
|
|
34
|
+
</TableRow>
|
|
35
|
+
</TableBody>
|
|
36
|
+
</Table>
|
|
37
|
+
),
|
|
38
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../lib/cn"
|
|
5
|
+
|
|
6
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export type TableSize = "sm" | "md" | "lg"
|
|
9
|
+
export type TableVariant = "surface" | "ghost"
|
|
10
|
+
|
|
11
|
+
// ─── Context ──────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
interface TableContextValue {
|
|
14
|
+
size: TableSize
|
|
15
|
+
variant: TableVariant
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const TableContext = React.createContext<TableContextValue>({
|
|
19
|
+
size: "md",
|
|
20
|
+
variant: "surface",
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// `py-1.5` (6px) and `py-2.5` (10px) are intentional half-steps: they fall
|
|
24
|
+
// between the token scale (xs=8, sm=12) and tune vertical rhythm to the
|
|
25
|
+
// row's font size. `px-*` values map cleanly to spacing tokens.
|
|
26
|
+
const CELL_PADDING: Record<TableSize, string> = {
|
|
27
|
+
sm: "ax:px-sm ax:py-1.5",
|
|
28
|
+
md: "ax:px-md ax:py-2.5",
|
|
29
|
+
lg: "ax:px-lg ax:py-md",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const TEXT_SIZE: Record<TableSize, string> = {
|
|
33
|
+
sm: "ax:text-xs",
|
|
34
|
+
md: "ax:text-sm",
|
|
35
|
+
lg: "ax:text-base",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Root ─────────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
export interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
|
|
41
|
+
size?: TableSize
|
|
42
|
+
variant?: TableVariant
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
|
46
|
+
({ className, size = "md", variant = "surface", ...props }, ref) => (
|
|
47
|
+
<TableContext.Provider value={{ size, variant }}>
|
|
48
|
+
<div
|
|
49
|
+
className={cn(
|
|
50
|
+
"ax:w-full ax:overflow-auto",
|
|
51
|
+
variant === "surface" && "ax:rounded-(--input-radius) ax:border ax:border-border"
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
<table
|
|
55
|
+
ref={ref}
|
|
56
|
+
className={cn(
|
|
57
|
+
"ax:w-full ax:caption-bottom ax:border-collapse",
|
|
58
|
+
TEXT_SIZE[size],
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
</TableContext.Provider>
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
Table.displayName = "Table"
|
|
68
|
+
|
|
69
|
+
// ─── Header ───────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
const TableHeader = React.forwardRef<
|
|
72
|
+
HTMLTableSectionElement,
|
|
73
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
74
|
+
>(({ className, ...props }, ref) => {
|
|
75
|
+
const { variant } = React.useContext(TableContext)
|
|
76
|
+
return (
|
|
77
|
+
<thead
|
|
78
|
+
ref={ref}
|
|
79
|
+
className={cn(
|
|
80
|
+
variant === "surface" && "ax:bg-muted/50",
|
|
81
|
+
"ax:[&_tr]:border-b ax:[&_tr]:border-border",
|
|
82
|
+
className
|
|
83
|
+
)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
TableHeader.displayName = "TableHeader"
|
|
89
|
+
|
|
90
|
+
// ─── Body ─────────────────────────────────────────────────────────────────────
|
|
91
|
+
//
|
|
92
|
+
// `bg-card` and the hover state live HERE (not on `TableRow`) so they only
|
|
93
|
+
// apply to data rows. Putting them on `TableRow` would paint over the
|
|
94
|
+
// header's `bg-muted/50` and add a misleading hover effect to header rows.
|
|
95
|
+
|
|
96
|
+
const TableBody = React.forwardRef<
|
|
97
|
+
HTMLTableSectionElement,
|
|
98
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
99
|
+
>(({ className, ...props }, ref) => (
|
|
100
|
+
<tbody
|
|
101
|
+
ref={ref}
|
|
102
|
+
className={cn(
|
|
103
|
+
"ax:[&_tr]:border-b ax:[&_tr]:border-border ax:[&_tr:last-child]:border-0",
|
|
104
|
+
"ax:[&_tr]:bg-card ax:[&_tr]:transition-colors",
|
|
105
|
+
"ax:[&_tr:hover]:bg-background",
|
|
106
|
+
// Selected row: `weakest` auto-inverts to a deep tint in dark; keep it
|
|
107
|
+
// translucent there (/25) so the row reads as a subtle highlight, not a slab.
|
|
108
|
+
"ax:[&_tr[data-state=selected]]:bg-primary-muted",
|
|
109
|
+
"ax:dark:[&_tr[data-state=selected]]:bg-primary-muted",
|
|
110
|
+
className
|
|
111
|
+
)}
|
|
112
|
+
{...props}
|
|
113
|
+
/>
|
|
114
|
+
))
|
|
115
|
+
TableBody.displayName = "TableBody"
|
|
116
|
+
|
|
117
|
+
// ─── Footer ───────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
const TableFooter = React.forwardRef<
|
|
120
|
+
HTMLTableSectionElement,
|
|
121
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
122
|
+
>(({ className, ...props }, ref) => {
|
|
123
|
+
const { variant } = React.useContext(TableContext)
|
|
124
|
+
return (
|
|
125
|
+
<tfoot
|
|
126
|
+
ref={ref}
|
|
127
|
+
className={cn(
|
|
128
|
+
"ax:border-t ax:border-border ax:font-medium",
|
|
129
|
+
variant === "surface" && "ax:bg-muted/50",
|
|
130
|
+
className
|
|
131
|
+
)}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
})
|
|
136
|
+
TableFooter.displayName = "TableFooter"
|
|
137
|
+
|
|
138
|
+
// ─── Row ──────────────────────────────────────────────────────────────────────
|
|
139
|
+
//
|
|
140
|
+
// Intentionally minimal: base bg / hover / selected styles are scoped on
|
|
141
|
+
// `TableBody` so they apply only to data rows.
|
|
142
|
+
|
|
143
|
+
const TableRow = React.forwardRef<
|
|
144
|
+
HTMLTableRowElement,
|
|
145
|
+
React.HTMLAttributes<HTMLTableRowElement>
|
|
146
|
+
>(({ className, ...props }, ref) => (
|
|
147
|
+
<tr ref={ref} className={cn(className)} {...props} />
|
|
148
|
+
))
|
|
149
|
+
TableRow.displayName = "TableRow"
|
|
150
|
+
|
|
151
|
+
// ─── Head cell ────────────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
const TableHead = React.forwardRef<
|
|
154
|
+
HTMLTableCellElement,
|
|
155
|
+
React.ThHTMLAttributes<HTMLTableCellElement>
|
|
156
|
+
>(({ className, ...props }, ref) => {
|
|
157
|
+
const { size } = React.useContext(TableContext)
|
|
158
|
+
return (
|
|
159
|
+
<th
|
|
160
|
+
ref={ref}
|
|
161
|
+
className={cn(
|
|
162
|
+
CELL_PADDING[size],
|
|
163
|
+
"ax:text-start ax:align-middle ax:font-medium ax:text-foreground-strong",
|
|
164
|
+
"ax:[&:has([role=checkbox])]:pe-0",
|
|
165
|
+
className
|
|
166
|
+
)}
|
|
167
|
+
{...props}
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
})
|
|
171
|
+
TableHead.displayName = "TableHead"
|
|
172
|
+
|
|
173
|
+
// ─── Data cell ────────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
const TableCell = React.forwardRef<
|
|
176
|
+
HTMLTableCellElement,
|
|
177
|
+
React.TdHTMLAttributes<HTMLTableCellElement>
|
|
178
|
+
>(({ className, ...props }, ref) => {
|
|
179
|
+
const { size } = React.useContext(TableContext)
|
|
180
|
+
return (
|
|
181
|
+
<td
|
|
182
|
+
ref={ref}
|
|
183
|
+
className={cn(
|
|
184
|
+
CELL_PADDING[size],
|
|
185
|
+
"ax:align-middle ax:text-foreground",
|
|
186
|
+
"ax:[&:has([role=checkbox])]:pe-0",
|
|
187
|
+
className
|
|
188
|
+
)}
|
|
189
|
+
{...props}
|
|
190
|
+
/>
|
|
191
|
+
)
|
|
192
|
+
})
|
|
193
|
+
TableCell.displayName = "TableCell"
|
|
194
|
+
|
|
195
|
+
// ─── Caption ──────────────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
const TableCaption = React.forwardRef<
|
|
198
|
+
HTMLTableCaptionElement,
|
|
199
|
+
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
200
|
+
>(({ className, ...props }, ref) => (
|
|
201
|
+
<caption
|
|
202
|
+
ref={ref}
|
|
203
|
+
className={cn("ax:mt-3 ax:t-body-sm ax:text-muted-foreground", className)}
|
|
204
|
+
{...props}
|
|
205
|
+
/>
|
|
206
|
+
))
|
|
207
|
+
TableCaption.displayName = "TableCaption"
|
|
208
|
+
|
|
209
|
+
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
export {
|
|
212
|
+
Table,
|
|
213
|
+
TableHeader,
|
|
214
|
+
TableBody,
|
|
215
|
+
TableFooter,
|
|
216
|
+
TableRow,
|
|
217
|
+
TableHead,
|
|
218
|
+
TableCell,
|
|
219
|
+
TableCaption,
|
|
220
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs"
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Tabs> = {
|
|
6
|
+
title: "Components/Tabs",
|
|
7
|
+
component: Tabs,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
type Story = StoryObj<typeof Tabs>
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<Tabs defaultValue="account" className="ax:w-[400px]">
|
|
17
|
+
<TabsList>
|
|
18
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
19
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
20
|
+
</TabsList>
|
|
21
|
+
<TabsContent value="account">Make changes to your account here.</TabsContent>
|
|
22
|
+
<TabsContent value="password">Change your password here.</TabsContent>
|
|
23
|
+
</Tabs>
|
|
24
|
+
),
|
|
25
|
+
}
|