@adres/mgda-ui 0.0.0-stage → 0.0.1

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.
Files changed (325) hide show
  1. package/brand-knowledge.json +451 -0
  2. package/knowledge/accordion.md +135 -0
  3. package/knowledge/activity-rings.md +123 -0
  4. package/knowledge/alert.md +184 -0
  5. package/knowledge/answer-actions.md +217 -0
  6. package/knowledge/aspect-ratio.md +82 -0
  7. package/knowledge/avatar.md +158 -0
  8. package/knowledge/badge.md +172 -0
  9. package/knowledge/bar-graph.md +167 -0
  10. package/knowledge/bento-grid.md +115 -0
  11. package/knowledge/blockquote.md +153 -0
  12. package/knowledge/box-list.md +177 -0
  13. package/knowledge/bullet-list.md +128 -0
  14. package/knowledge/button-group.md +179 -0
  15. package/knowledge/button.md +231 -0
  16. package/knowledge/calendar.md +168 -0
  17. package/knowledge/card.md +321 -0
  18. package/knowledge/cards-nav.md +170 -0
  19. package/knowledge/chain-of-thought.md +164 -0
  20. package/knowledge/chat-container.md +135 -0
  21. package/knowledge/chat-message.md +224 -0
  22. package/knowledge/checkbox-card.md +172 -0
  23. package/knowledge/checkbox.md +111 -0
  24. package/knowledge/citations.md +241 -0
  25. package/knowledge/combobox.md +265 -0
  26. package/knowledge/countdown-pill.md +153 -0
  27. package/knowledge/cta.md +118 -0
  28. package/knowledge/data-list.md +173 -0
  29. package/knowledge/data-table.md +220 -0
  30. package/knowledge/dialog.md +249 -0
  31. package/knowledge/drawer.md +236 -0
  32. package/knowledge/dropdown-menu.md +238 -0
  33. package/knowledge/dropzone.md +153 -0
  34. package/knowledge/email-chips-input.md +161 -0
  35. package/knowledge/empty-state.md +106 -0
  36. package/knowledge/entity-header.md +166 -0
  37. package/knowledge/feature-grid.md +120 -0
  38. package/knowledge/feedback-bar.md +127 -0
  39. package/knowledge/footer.md +116 -0
  40. package/knowledge/heading.md +189 -0
  41. package/knowledge/hero.md +361 -0
  42. package/knowledge/input-button.md +187 -0
  43. package/knowledge/input-group.md +185 -0
  44. package/knowledge/input.md +256 -0
  45. package/knowledge/inset.md +177 -0
  46. package/knowledge/kpi-card.md +206 -0
  47. package/knowledge/list-nav.md +181 -0
  48. package/knowledge/logo-cloud.md +102 -0
  49. package/knowledge/media-gallery.md +158 -0
  50. package/knowledge/message-actions.md +175 -0
  51. package/knowledge/nav-sidebar.md +225 -0
  52. package/knowledge/number-stepper.md +159 -0
  53. package/knowledge/page-header.md +109 -0
  54. package/knowledge/pagination.md +225 -0
  55. package/knowledge/popover.md +159 -0
  56. package/knowledge/pricing.md +132 -0
  57. package/knowledge/progress.md +156 -0
  58. package/knowledge/prompt-input.md +337 -0
  59. package/knowledge/prompt-suggestions.md +168 -0
  60. package/knowledge/quota-bar.md +160 -0
  61. package/knowledge/quote.md +116 -0
  62. package/knowledge/radio-card.md +172 -0
  63. package/knowledge/radio-group.md +135 -0
  64. package/knowledge/reasoning.md +142 -0
  65. package/knowledge/search.md +131 -0
  66. package/knowledge/section-nav.md +162 -0
  67. package/knowledge/segmented-control.md +181 -0
  68. package/knowledge/select-button.md +205 -0
  69. package/knowledge/select.md +210 -0
  70. package/knowledge/separator.md +75 -0
  71. package/knowledge/shimmer-text.md +140 -0
  72. package/knowledge/sidebar.md +227 -0
  73. package/knowledge/skeleton.md +142 -0
  74. package/knowledge/slider.md +133 -0
  75. package/knowledge/slideshow.md +159 -0
  76. package/knowledge/source.md +156 -0
  77. package/knowledge/spinner.md +120 -0
  78. package/knowledge/stat-strip.md +128 -0
  79. package/knowledge/stats.md +117 -0
  80. package/knowledge/step-indicator.md +157 -0
  81. package/knowledge/switch.md +88 -0
  82. package/knowledge/table.md +190 -0
  83. package/knowledge/tabs.md +169 -0
  84. package/knowledge/testimonials.md +115 -0
  85. package/knowledge/text-link.md +192 -0
  86. package/knowledge/text.md +194 -0
  87. package/knowledge/thinking-bar.md +115 -0
  88. package/knowledge/thinking-steps.md +140 -0
  89. package/knowledge/toaster.md +127 -0
  90. package/knowledge/toggle.md +113 -0
  91. package/knowledge/tool-call-card.md +241 -0
  92. package/knowledge/tooltip.md +120 -0
  93. package/knowledge/top-nav.md +138 -0
  94. package/knowledge.json +19854 -0
  95. package/package.json +94 -4
  96. package/src/components/accordion.stories.tsx +46 -0
  97. package/src/components/accordion.tsx +74 -0
  98. package/src/components/activity-rings.stories.tsx +73 -0
  99. package/src/components/activity-rings.tsx +121 -0
  100. package/src/components/alert.stories.tsx +43 -0
  101. package/src/components/alert.tsx +132 -0
  102. package/src/components/answer-actions.stories.tsx +54 -0
  103. package/src/components/answer-actions.test.tsx +37 -0
  104. package/src/components/answer-actions.tsx +313 -0
  105. package/src/components/aspect-ratio.stories.tsx +32 -0
  106. package/src/components/aspect-ratio.tsx +5 -0
  107. package/src/components/avatar.stories.tsx +40 -0
  108. package/src/components/avatar.tsx +154 -0
  109. package/src/components/badge.stories.tsx +27 -0
  110. package/src/components/badge.tsx +63 -0
  111. package/src/components/bar-graph.stories.tsx +28 -0
  112. package/src/components/bar-graph.tsx +204 -0
  113. package/src/components/bento-grid.stories.tsx +23 -0
  114. package/src/components/bento-grid.tsx +67 -0
  115. package/src/components/blockquote.stories.tsx +28 -0
  116. package/src/components/blockquote.tsx +88 -0
  117. package/src/components/box-list.stories.tsx +25 -0
  118. package/src/components/box-list.tsx +108 -0
  119. package/src/components/bullet-list.stories.tsx +22 -0
  120. package/src/components/bullet-list.tsx +101 -0
  121. package/src/components/button-group.stories.tsx +31 -0
  122. package/src/components/button-group.tsx +156 -0
  123. package/src/components/button.stories.tsx +66 -0
  124. package/src/components/button.test.ts +17 -0
  125. package/src/components/button.tsx +139 -0
  126. package/src/components/calendar.stories.tsx +25 -0
  127. package/src/components/calendar.tsx +367 -0
  128. package/src/components/card.stories.tsx +44 -0
  129. package/src/components/card.tsx +392 -0
  130. package/src/components/cards-nav.stories.tsx +35 -0
  131. package/src/components/cards-nav.tsx +115 -0
  132. package/src/components/chain-of-thought.stories.tsx +67 -0
  133. package/src/components/chain-of-thought.test.tsx +50 -0
  134. package/src/components/chain-of-thought.tsx +170 -0
  135. package/src/components/chat-container.stories.tsx +47 -0
  136. package/src/components/chat-container.test.tsx +49 -0
  137. package/src/components/chat-container.tsx +222 -0
  138. package/src/components/chat-message.stories.tsx +52 -0
  139. package/src/components/chat-message.test.tsx +36 -0
  140. package/src/components/chat-message.tsx +163 -0
  141. package/src/components/checkbox-card.stories.tsx +27 -0
  142. package/src/components/checkbox-card.tsx +200 -0
  143. package/src/components/checkbox.stories.tsx +26 -0
  144. package/src/components/checkbox.tsx +61 -0
  145. package/src/components/citations.stories.tsx +64 -0
  146. package/src/components/citations.test.tsx +61 -0
  147. package/src/components/citations.tsx +220 -0
  148. package/src/components/combobox.stories.tsx +184 -0
  149. package/src/components/combobox.test.tsx +82 -0
  150. package/src/components/combobox.tsx +407 -0
  151. package/src/components/countdown-pill.stories.tsx +24 -0
  152. package/src/components/countdown-pill.tsx +129 -0
  153. package/src/components/cta.stories.tsx +29 -0
  154. package/src/components/cta.tsx +52 -0
  155. package/src/components/data-list.stories.tsx +50 -0
  156. package/src/components/data-list.test.tsx +35 -0
  157. package/src/components/data-list.tsx +181 -0
  158. package/src/components/data-table.stories.tsx +29 -0
  159. package/src/components/data-table.tsx +200 -0
  160. package/src/components/dialog.stories.tsx +42 -0
  161. package/src/components/dialog.tsx +200 -0
  162. package/src/components/drawer.stories.tsx +48 -0
  163. package/src/components/drawer.tsx +385 -0
  164. package/src/components/dropdown-menu.stories.tsx +41 -0
  165. package/src/components/dropdown-menu.tsx +252 -0
  166. package/src/components/dropzone.stories.tsx +19 -0
  167. package/src/components/dropzone.tsx +250 -0
  168. package/src/components/email-chips-input.stories.tsx +135 -0
  169. package/src/components/email-chips-input.tsx +210 -0
  170. package/src/components/empty-state.stories.tsx +22 -0
  171. package/src/components/empty-state.tsx +35 -0
  172. package/src/components/entity-header.stories.tsx +43 -0
  173. package/src/components/entity-header.test.tsx +35 -0
  174. package/src/components/entity-header.tsx +92 -0
  175. package/src/components/feature-grid.stories.tsx +25 -0
  176. package/src/components/feature-grid.tsx +87 -0
  177. package/src/components/feedback-bar.stories.tsx +25 -0
  178. package/src/components/feedback-bar.test.tsx +24 -0
  179. package/src/components/feedback-bar.tsx +62 -0
  180. package/src/components/footer.stories.tsx +23 -0
  181. package/src/components/footer.tsx +78 -0
  182. package/src/components/heading.stories.tsx +45 -0
  183. package/src/components/heading.test.tsx +22 -0
  184. package/src/components/heading.tsx +77 -0
  185. package/src/components/hero.stories.tsx +38 -0
  186. package/src/components/hero.tsx +498 -0
  187. package/src/components/input-button.stories.tsx +19 -0
  188. package/src/components/input-button.tsx +112 -0
  189. package/src/components/input-group.stories.tsx +38 -0
  190. package/src/components/input-group.tsx +157 -0
  191. package/src/components/input.stories.tsx +31 -0
  192. package/src/components/input.tsx +284 -0
  193. package/src/components/inset.stories.tsx +29 -0
  194. package/src/components/inset.tsx +70 -0
  195. package/src/components/kpi-card.stories.tsx +29 -0
  196. package/src/components/kpi-card.tsx +284 -0
  197. package/src/components/list-nav.stories.tsx +22 -0
  198. package/src/components/list-nav.tsx +145 -0
  199. package/src/components/logo-cloud.stories.tsx +23 -0
  200. package/src/components/logo-cloud.tsx +42 -0
  201. package/src/components/media-gallery.stories.tsx +25 -0
  202. package/src/components/media-gallery.tsx +347 -0
  203. package/src/components/message-actions.stories.tsx +45 -0
  204. package/src/components/message-actions.test.tsx +32 -0
  205. package/src/components/message-actions.tsx +119 -0
  206. package/src/components/nav-sidebar.stories.tsx +58 -0
  207. package/src/components/nav-sidebar.test.tsx +54 -0
  208. package/src/components/nav-sidebar.tsx +462 -0
  209. package/src/components/number-stepper.stories.tsx +19 -0
  210. package/src/components/number-stepper.tsx +158 -0
  211. package/src/components/page-header.stories.tsx +25 -0
  212. package/src/components/page-header.tsx +64 -0
  213. package/src/components/pagination.stories.tsx +110 -0
  214. package/src/components/pagination.test.tsx +46 -0
  215. package/src/components/pagination.tsx +144 -0
  216. package/src/components/popover.stories.tsx +26 -0
  217. package/src/components/popover.tsx +62 -0
  218. package/src/components/pricing.stories.tsx +42 -0
  219. package/src/components/pricing.tsx +130 -0
  220. package/src/components/progress.stories.tsx +22 -0
  221. package/src/components/progress.tsx +189 -0
  222. package/src/components/prompt-input.stories.tsx +62 -0
  223. package/src/components/prompt-input.test.tsx +106 -0
  224. package/src/components/prompt-input.tsx +343 -0
  225. package/src/components/prompt-suggestions.stories.tsx +53 -0
  226. package/src/components/prompt-suggestions.test.tsx +37 -0
  227. package/src/components/prompt-suggestions.tsx +122 -0
  228. package/src/components/quota-bar.stories.tsx +19 -0
  229. package/src/components/quota-bar.tsx +191 -0
  230. package/src/components/quote.stories.tsx +18 -0
  231. package/src/components/quote.tsx +43 -0
  232. package/src/components/radio-card.stories.tsx +27 -0
  233. package/src/components/radio-card.tsx +225 -0
  234. package/src/components/radio-group.stories.tsx +32 -0
  235. package/src/components/radio-group.tsx +68 -0
  236. package/src/components/reasoning.stories.tsx +50 -0
  237. package/src/components/reasoning.test.tsx +48 -0
  238. package/src/components/reasoning.tsx +143 -0
  239. package/src/components/search.stories.tsx +18 -0
  240. package/src/components/search.tsx +133 -0
  241. package/src/components/section-nav.stories.tsx +59 -0
  242. package/src/components/section-nav.test.tsx +36 -0
  243. package/src/components/section-nav.tsx +146 -0
  244. package/src/components/segmented-control.stories.tsx +22 -0
  245. package/src/components/segmented-control.tsx +301 -0
  246. package/src/components/select-button.stories.tsx +27 -0
  247. package/src/components/select-button.tsx +174 -0
  248. package/src/components/select.stories.tsx +27 -0
  249. package/src/components/select.tsx +198 -0
  250. package/src/components/separator.stories.tsx +22 -0
  251. package/src/components/separator.tsx +23 -0
  252. package/src/components/shimmer-text.stories.tsx +23 -0
  253. package/src/components/shimmer-text.test.tsx +35 -0
  254. package/src/components/shimmer-text.tsx +43 -0
  255. package/src/components/sidebar.stories.tsx +25 -0
  256. package/src/components/sidebar.tsx +234 -0
  257. package/src/components/skeleton.stories.tsx +22 -0
  258. package/src/components/skeleton.tsx +96 -0
  259. package/src/components/slider.stories.tsx +20 -0
  260. package/src/components/slider.tsx +176 -0
  261. package/src/components/slideshow.stories.tsx +30 -0
  262. package/src/components/slideshow.tsx +291 -0
  263. package/src/components/source.stories.tsx +32 -0
  264. package/src/components/source.test.tsx +40 -0
  265. package/src/components/source.tsx +161 -0
  266. package/src/components/spinner.stories.tsx +19 -0
  267. package/src/components/spinner.tsx +76 -0
  268. package/src/components/stat-strip.stories.tsx +36 -0
  269. package/src/components/stat-strip.test.tsx +25 -0
  270. package/src/components/stat-strip.tsx +85 -0
  271. package/src/components/stats.stories.tsx +24 -0
  272. package/src/components/stats.tsx +83 -0
  273. package/src/components/step-indicator.stories.tsx +18 -0
  274. package/src/components/step-indicator.test.tsx +18 -0
  275. package/src/components/step-indicator.tsx +286 -0
  276. package/src/components/switch.stories.tsx +26 -0
  277. package/src/components/switch.tsx +32 -0
  278. package/src/components/table.stories.tsx +38 -0
  279. package/src/components/table.tsx +220 -0
  280. package/src/components/tabs.stories.tsx +25 -0
  281. package/src/components/tabs.tsx +276 -0
  282. package/src/components/testimonials.stories.tsx +22 -0
  283. package/src/components/testimonials.tsx +109 -0
  284. package/src/components/text-link.stories.tsx +47 -0
  285. package/src/components/text-link.test.tsx +18 -0
  286. package/src/components/text-link.tsx +88 -0
  287. package/src/components/text.stories.tsx +49 -0
  288. package/src/components/text.tsx +105 -0
  289. package/src/components/thinking-bar.stories.tsx +19 -0
  290. package/src/components/thinking-bar.test.tsx +27 -0
  291. package/src/components/thinking-bar.tsx +64 -0
  292. package/src/components/thinking-steps.stories.tsx +44 -0
  293. package/src/components/thinking-steps.test.tsx +42 -0
  294. package/src/components/thinking-steps.tsx +92 -0
  295. package/src/components/toaster.stories.tsx +80 -0
  296. package/src/components/toaster.tsx +36 -0
  297. package/src/components/toggle.stories.tsx +21 -0
  298. package/src/components/toggle.tsx +103 -0
  299. package/src/components/tool-call-card.stories.tsx +97 -0
  300. package/src/components/tool-call-card.test.tsx +88 -0
  301. package/src/components/tool-call-card.tsx +231 -0
  302. package/src/components/tooltip.stories.tsx +25 -0
  303. package/src/components/tooltip.tsx +43 -0
  304. package/src/components/top-nav.stories.tsx +20 -0
  305. package/src/components/top-nav.tsx +76 -0
  306. package/src/docs/Colors.stories.tsx +192 -0
  307. package/src/docs/GettingStarted.mdx +72 -0
  308. package/src/docs/Mcp.mdx +52 -0
  309. package/src/docs/Typography.stories.tsx +114 -0
  310. package/src/hooks/use-in-view.ts +32 -0
  311. package/src/hooks/use-reduced-motion.ts +26 -0
  312. package/src/index.ts +343 -0
  313. package/src/lib/cn.test.ts +67 -0
  314. package/src/lib/cn.ts +81 -0
  315. package/src/lib/motion.ts +109 -0
  316. package/src/lib/prefix-utilities.test.ts +56 -0
  317. package/src/lib/prefix-utilities.ts +50 -0
  318. package/src/styles/base.css +489 -0
  319. package/src/styles/motion.css +69 -0
  320. package/src/styles/overrides.css +44 -0
  321. package/src/styles/typography.css +173 -0
  322. package/src/theme/theme.css +391 -0
  323. package/src/theme/tokens.ts +29 -0
  324. package/src/theme/types.ts +26 -0
  325. 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
+ }