@ponti-studios/ui 2.0.1 → 3.0.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.
Files changed (319) hide show
  1. package/README.md +36 -3
  2. package/dist/auth/index.d.ts +3 -0
  3. package/dist/auth/index.js +3 -0
  4. package/dist/components/data-display/accordion.d.ts +10 -0
  5. package/dist/components/data-display/accordion.js +53 -0
  6. package/{src/components/data-display/index.ts → dist/components/data-display/index.d.ts} +1 -10
  7. package/dist/components/data-display/index.js +8 -0
  8. package/dist/components/data-display/metric-card.d.ts +9 -0
  9. package/dist/components/data-display/metric-card.js +25 -0
  10. package/dist/components/data-display/sort-controls.d.ts +13 -0
  11. package/dist/components/data-display/sort-controls.js +57 -0
  12. package/dist/components/data-display/sort-row.d.ts +10 -0
  13. package/dist/components/data-display/sort-row.js +52 -0
  14. package/dist/components/data-display/table.d.ts +10 -0
  15. package/dist/components/data-display/table.js +57 -0
  16. package/dist/components/feedback/alert.d.ts +9 -0
  17. package/dist/components/feedback/alert.js +38 -0
  18. package/dist/components/feedback/empty-state.d.ts +18 -0
  19. package/dist/components/feedback/empty-state.js +36 -0
  20. package/{src/components/feedback/index.ts → dist/components/feedback/index.d.ts} +2 -0
  21. package/dist/components/feedback/index.js +7 -0
  22. package/dist/components/feedback/progress.d.ts +6 -0
  23. package/dist/components/feedback/progress.js +25 -0
  24. package/dist/components/feedback/skeleton.d.ts +2 -0
  25. package/dist/components/feedback/skeleton.js +11 -0
  26. package/dist/components/feedback/spinner.d.ts +14 -0
  27. package/dist/components/feedback/spinner.js +27 -0
  28. package/dist/components/feedback/update-guard.d.ts +13 -0
  29. package/dist/components/feedback/update-guard.js +38 -0
  30. package/dist/components/filters/active-filters-bar.d.ts +14 -0
  31. package/dist/components/filters/active-filters-bar.js +44 -0
  32. package/dist/components/filters/filter-chip.d.ts +11 -0
  33. package/dist/components/filters/filter-chip.js +33 -0
  34. package/dist/components/filters/filter-select.d.ts +17 -0
  35. package/dist/components/filters/filter-select.js +46 -0
  36. package/dist/components/filters/index.js +4 -0
  37. package/dist/components/forms/checkbox.d.ts +4 -0
  38. package/dist/components/forms/checkbox.js +24 -0
  39. package/dist/components/forms/date-month-select.d.ts +11 -0
  40. package/dist/components/forms/date-month-select.js +47 -0
  41. package/dist/components/forms/date-picker.d.ts +60 -0
  42. package/dist/components/forms/date-picker.js +193 -0
  43. package/dist/components/forms/date-utils.d.ts +14 -0
  44. package/dist/components/forms/date-utils.js +18 -0
  45. package/dist/components/forms/drop-zone.d.ts +42 -0
  46. package/dist/components/forms/drop-zone.js +184 -0
  47. package/{src/components/forms/index.ts → dist/components/forms/index.d.ts} +3 -9
  48. package/dist/components/forms/index.js +17 -0
  49. package/dist/components/forms/input.d.ts +3 -0
  50. package/dist/components/forms/input.js +15 -0
  51. package/dist/components/forms/label.d.ts +3 -0
  52. package/dist/components/forms/label.js +14 -0
  53. package/dist/components/forms/passkey-management.d.ts +14 -0
  54. package/dist/components/forms/passkey-management.js +132 -0
  55. package/dist/components/forms/radio-group.d.ts +6 -0
  56. package/dist/components/forms/radio-group.js +28 -0
  57. package/dist/components/forms/select.d.ts +18 -0
  58. package/dist/components/forms/select.js +103 -0
  59. package/dist/components/forms/slider.d.ts +9 -0
  60. package/dist/components/forms/slider.js +25 -0
  61. package/dist/components/forms/stepper.d.ts +15 -0
  62. package/dist/components/forms/stepper.js +41 -0
  63. package/dist/components/forms/switch.d.ts +6 -0
  64. package/dist/components/forms/switch.js +21 -0
  65. package/dist/components/forms/text-field.d.ts +15 -0
  66. package/dist/components/forms/text-field.js +46 -0
  67. package/dist/components/forms/text-field.types.d.ts +8 -0
  68. package/dist/components/forms/textarea.d.ts +3 -0
  69. package/dist/components/forms/textarea.js +14 -0
  70. package/dist/components/layout/index.js +5 -0
  71. package/dist/components/layout/particle-background.d.ts +19 -0
  72. package/dist/components/layout/particle-background.js +171 -0
  73. package/dist/components/layout/scroll-area.d.ts +9 -0
  74. package/dist/components/layout/scroll-area.js +35 -0
  75. package/dist/components/layout/section-intro.d.ts +12 -0
  76. package/dist/components/layout/section-intro.js +28 -0
  77. package/dist/components/layout/surface-panel.d.ts +7 -0
  78. package/dist/components/layout/surface-panel.js +11 -0
  79. package/dist/components/navigation/app-navigation.d.ts +67 -0
  80. package/dist/components/navigation/app-navigation.js +106 -0
  81. package/{src/components/navigation/index.ts → dist/components/navigation/index.d.ts} +1 -7
  82. package/dist/components/navigation/index.js +4 -0
  83. package/dist/components/navigation/pagination-controls.d.ts +7 -0
  84. package/dist/components/navigation/pagination-controls.js +50 -0
  85. package/dist/components/navigation/tabs.d.ts +7 -0
  86. package/dist/components/navigation/tabs.js +35 -0
  87. package/dist/components/overlays/bottom-sheet.d.ts +20 -0
  88. package/dist/components/overlays/bottom-sheet.js +101 -0
  89. package/dist/components/overlays/command.d.ts +29 -0
  90. package/dist/components/overlays/command.js +123 -0
  91. package/dist/components/overlays/dialog.d.ts +30 -0
  92. package/dist/components/overlays/dialog.js +105 -0
  93. package/dist/components/overlays/dropdown-menu.d.ts +29 -0
  94. package/dist/components/overlays/dropdown-menu.js +128 -0
  95. package/dist/components/overlays/index.d.ts +7 -0
  96. package/dist/components/overlays/index.js +8 -0
  97. package/dist/components/overlays/popover.d.ts +14 -0
  98. package/dist/components/overlays/popover.js +56 -0
  99. package/dist/components/overlays/sheet.d.ts +27 -0
  100. package/dist/components/overlays/sheet.js +81 -0
  101. package/dist/components/overlays/tooltip.d.ts +10 -0
  102. package/dist/components/overlays/tooltip.js +23 -0
  103. package/dist/components/primitives/avatar.d.ts +14 -0
  104. package/dist/components/primitives/avatar.js +72 -0
  105. package/dist/components/primitives/badge.d.ts +7 -0
  106. package/dist/components/primitives/badge.js +27 -0
  107. package/dist/components/primitives/button.d.ts +13 -0
  108. package/dist/components/primitives/button.js +113 -0
  109. package/{src/components/primitives/button.types.ts → dist/components/primitives/button.types.d.ts} +4 -6
  110. package/dist/components/primitives/card.d.ts +9 -0
  111. package/dist/components/primitives/card.js +56 -0
  112. package/{src/components/primitives/index.ts → dist/components/primitives/index.d.ts} +1 -9
  113. package/dist/components/primitives/index.js +7 -0
  114. package/dist/components/primitives/segmented-control.d.ts +20 -0
  115. package/dist/components/primitives/segmented-control.js +27 -0
  116. package/dist/components/primitives/separator.d.ts +4 -0
  117. package/dist/components/primitives/separator.js +16 -0
  118. package/dist/constants/chart-colors.d.ts +33 -0
  119. package/dist/constants/chart-colors.js +31 -0
  120. package/dist/hooks/index.js +9 -0
  121. package/{src/hooks/sort.types.ts → dist/hooks/sort.types.d.ts} +2 -4
  122. package/dist/hooks/use-api-client.d.ts +30 -0
  123. package/dist/hooks/use-api-client.js +106 -0
  124. package/dist/hooks/use-debounced-value.d.ts +1 -0
  125. package/dist/hooks/use-debounced-value.js +12 -0
  126. package/dist/hooks/use-derived-filter-state.d.ts +20 -0
  127. package/dist/hooks/use-derived-filter-state.js +39 -0
  128. package/dist/hooks/use-filter-state.d.ts +12 -0
  129. package/dist/hooks/use-filter-state.js +37 -0
  130. package/dist/hooks/use-media-query.d.ts +6 -0
  131. package/dist/hooks/use-media-query.js +27 -0
  132. package/dist/hooks/use-mobile.d.ts +1 -0
  133. package/dist/hooks/use-mobile.js +14 -0
  134. package/dist/hooks/use-sort.d.ts +11 -0
  135. package/dist/hooks/use-sort.js +14 -0
  136. package/{src/index.ts → dist/index.d.ts} +4 -32
  137. package/dist/index.js +15 -0
  138. package/dist/integrations/service-worker.d.ts +2 -0
  139. package/dist/integrations/service-worker.js +2 -0
  140. package/dist/lib/auth.d.ts +2 -0
  141. package/dist/lib/auth.js +7 -0
  142. package/dist/lib/clipboard.d.ts +1 -0
  143. package/dist/lib/clipboard.js +10 -0
  144. package/dist/lib/index.js +4 -0
  145. package/{src/lib/slot.tsx → dist/lib/slot.d.ts} +2 -15
  146. package/dist/lib/slot.js +14 -0
  147. package/dist/lib/utils.d.ts +2 -0
  148. package/dist/lib/utils.js +8 -0
  149. package/dist/native/components.d.ts +41 -0
  150. package/dist/native/components.js +135 -0
  151. package/dist/native/index.js +3 -0
  152. package/dist/native/theme.d.ts +19 -0
  153. package/dist/native/theme.js +58 -0
  154. package/dist/styles/tokens/css-vars.d.ts +7 -0
  155. package/dist/styles/tokens/css-vars.js +5 -0
  156. package/dist/styles/tokens/generated/dark.colors.generated.d.ts +40 -0
  157. package/dist/styles/tokens/generated/dark.colors.generated.js +39 -0
  158. package/dist/styles/tokens/generated/foundations.generated.d.ts +323 -0
  159. package/dist/styles/tokens/generated/foundations.generated.js +453 -0
  160. package/dist/styles/tokens/generated/light.colors.generated.d.ts +40 -0
  161. package/dist/styles/tokens/generated/light.colors.generated.js +39 -0
  162. package/dist/styles/tokens/index.d.ts +317 -0
  163. package/dist/styles/tokens/index.js +14 -0
  164. package/dist/styles.css +1 -1
  165. package/dist/translations/en.d.ts +34 -0
  166. package/dist/translations/index.d.ts +39 -0
  167. package/dist/translations/ui-provider.d.ts +9 -0
  168. package/dist/translations/ui-provider.js +16 -0
  169. package/dist/types/upload.d.ts +36 -0
  170. package/package.json +50 -33
  171. package/src/components/data-display/accordion.stories.tsx +0 -57
  172. package/src/components/data-display/accordion.tsx +0 -86
  173. package/src/components/data-display/metric-card.stories.tsx +0 -48
  174. package/src/components/data-display/metric-card.tsx +0 -20
  175. package/src/components/data-display/sort-controls.stories.tsx +0 -45
  176. package/src/components/data-display/sort-controls.tsx +0 -96
  177. package/src/components/data-display/sort-row.tsx +0 -88
  178. package/src/components/data-display/table.stories.tsx +0 -75
  179. package/src/components/data-display/table.tsx +0 -80
  180. package/src/components/feedback/alert.stories.tsx +0 -51
  181. package/src/components/feedback/alert.tsx +0 -64
  182. package/src/components/feedback/empty-state.tsx +0 -76
  183. package/src/components/feedback/progress.stories.tsx +0 -54
  184. package/src/components/feedback/progress.tsx +0 -41
  185. package/src/components/feedback/skeleton.stories.tsx +0 -36
  186. package/src/components/feedback/skeleton.tsx +0 -7
  187. package/src/components/feedback/spinner.stories.tsx +0 -52
  188. package/src/components/feedback/spinner.tsx +0 -47
  189. package/src/components/feedback/update-guard.stories.tsx +0 -28
  190. package/src/components/feedback/update-guard.tsx +0 -57
  191. package/src/components/filters/active-filters-bar.stories.tsx +0 -114
  192. package/src/components/filters/active-filters-bar.tsx +0 -61
  193. package/src/components/filters/filter-chip.tsx +0 -57
  194. package/src/components/filters/filter-select.stories.tsx +0 -71
  195. package/src/components/filters/filter-select.tsx +0 -68
  196. package/src/components/forms/checkbox.stories.tsx +0 -35
  197. package/src/components/forms/checkbox.tsx +0 -38
  198. package/src/components/forms/date-month-select.stories.tsx +0 -26
  199. package/src/components/forms/date-month-select.tsx +0 -79
  200. package/src/components/forms/date-picker.stories.tsx +0 -188
  201. package/src/components/forms/date-picker.tsx +0 -303
  202. package/src/components/forms/date-utils.ts +0 -37
  203. package/src/components/forms/drop-zone.stories.tsx +0 -140
  204. package/src/components/forms/drop-zone.tsx +0 -298
  205. package/src/components/forms/input.stories.tsx +0 -47
  206. package/src/components/forms/input.tsx +0 -24
  207. package/src/components/forms/label.stories.tsx +0 -16
  208. package/src/components/forms/label.tsx +0 -25
  209. package/src/components/forms/passkey-management.stories.tsx +0 -85
  210. package/src/components/forms/passkey-management.tsx +0 -168
  211. package/src/components/forms/radio-group.stories.tsx +0 -46
  212. package/src/components/forms/radio-group.tsx +0 -44
  213. package/src/components/forms/select.stories.tsx +0 -207
  214. package/src/components/forms/select.tsx +0 -182
  215. package/src/components/forms/slider.stories.tsx +0 -70
  216. package/src/components/forms/slider.tsx +0 -50
  217. package/src/components/forms/stepper.stories.tsx +0 -111
  218. package/src/components/forms/stepper.tsx +0 -66
  219. package/src/components/forms/switch.stories.tsx +0 -144
  220. package/src/components/forms/switch.tsx +0 -39
  221. package/src/components/forms/text-field.stories.tsx +0 -87
  222. package/src/components/forms/text-field.tsx +0 -75
  223. package/src/components/forms/text-field.types.ts +0 -9
  224. package/src/components/forms/textarea.stories.tsx +0 -150
  225. package/src/components/forms/textarea.tsx +0 -22
  226. package/src/components/layout/particle-background.stories.tsx +0 -38
  227. package/src/components/layout/particle-background.tsx +0 -378
  228. package/src/components/layout/scroll-area.stories.tsx +0 -91
  229. package/src/components/layout/scroll-area.tsx +0 -79
  230. package/src/components/layout/section-intro.stories.tsx +0 -29
  231. package/src/components/layout/section-intro.tsx +0 -31
  232. package/src/components/layout/surface-panel.stories.tsx +0 -32
  233. package/src/components/layout/surface-panel.tsx +0 -23
  234. package/src/components/navigation/app-navigation.stories.tsx +0 -217
  235. package/src/components/navigation/app-navigation.tsx +0 -234
  236. package/src/components/navigation/pagination-controls.stories.tsx +0 -41
  237. package/src/components/navigation/pagination-controls.tsx +0 -46
  238. package/src/components/navigation/tabs.stories.tsx +0 -48
  239. package/src/components/navigation/tabs.tsx +0 -52
  240. package/src/components/overlays/bottom-sheet.stories.tsx +0 -52
  241. package/src/components/overlays/bottom-sheet.tsx +0 -179
  242. package/src/components/overlays/command.stories.tsx +0 -65
  243. package/src/components/overlays/command.tsx +0 -227
  244. package/src/components/overlays/dialog.stories.tsx +0 -148
  245. package/src/components/overlays/dialog.tsx +0 -203
  246. package/src/components/overlays/dropdown-menu.stories.tsx +0 -74
  247. package/src/components/overlays/dropdown-menu.tsx +0 -236
  248. package/src/components/overlays/index.ts +0 -56
  249. package/src/components/overlays/popover.stories.tsx +0 -51
  250. package/src/components/overlays/popover.tsx +0 -78
  251. package/src/components/overlays/sheet.stories.tsx +0 -68
  252. package/src/components/overlays/sheet.tsx +0 -155
  253. package/src/components/overlays/tooltip.stories.tsx +0 -29
  254. package/src/components/overlays/tooltip.tsx +0 -41
  255. package/src/components/primitives/avatar-placeholder.svg +0 -13
  256. package/src/components/primitives/avatar.stories.tsx +0 -72
  257. package/src/components/primitives/avatar.tsx +0 -136
  258. package/src/components/primitives/badge.stories.tsx +0 -43
  259. package/src/components/primitives/badge.tsx +0 -43
  260. package/src/components/primitives/button.stories.tsx +0 -113
  261. package/src/components/primitives/button.tsx +0 -131
  262. package/src/components/primitives/card.stories.tsx +0 -59
  263. package/src/components/primitives/card.tsx +0 -66
  264. package/src/components/primitives/segmented-control.stories.tsx +0 -54
  265. package/src/components/primitives/segmented-control.tsx +0 -73
  266. package/src/components/primitives/separator.stories.tsx +0 -34
  267. package/src/components/primitives/separator.tsx +0 -27
  268. package/src/constants/chart-colors.ts +0 -39
  269. package/src/env.d.ts +0 -9
  270. package/src/hooks/use-api-client.ts +0 -122
  271. package/src/hooks/use-debounced-value.ts +0 -12
  272. package/src/hooks/use-derived-filter-state.ts +0 -70
  273. package/src/hooks/use-filter-state.ts +0 -66
  274. package/src/hooks/use-media-query.ts +0 -69
  275. package/src/hooks/use-mobile.tsx +0 -19
  276. package/src/hooks/use-sort.ts +0 -20
  277. package/src/lib/auth.ts +0 -5
  278. package/src/lib/clipboard.ts +0 -8
  279. package/src/lib/utils.ts +0 -6
  280. package/src/native/components.tsx +0 -198
  281. package/src/native/theme.tsx +0 -69
  282. package/src/storybook/controls.ts +0 -117
  283. package/src/storybook/options.ts +0 -52
  284. package/src/styles/build.ts +0 -1
  285. package/src/styles/index.css +0 -423
  286. package/src/styles/nokia.css +0 -36
  287. package/src/styles/package.css +0 -3
  288. package/src/styles/tokens/chart-colors.stories.tsx +0 -189
  289. package/src/styles/tokens/color-systems.stories.tsx +0 -190
  290. package/src/styles/tokens/css-vars.ts +0 -15
  291. package/src/styles/tokens/generated/dark-class.css +0 -39
  292. package/src/styles/tokens/generated/dark.colors.generated.ts +0 -39
  293. package/src/styles/tokens/generated/dark.css +0 -39
  294. package/src/styles/tokens/generated/foundations.css +0 -149
  295. package/src/styles/tokens/generated/foundations.generated.ts +0 -454
  296. package/src/styles/tokens/generated/light.colors.generated.ts +0 -39
  297. package/src/styles/tokens/generated/light.css +0 -39
  298. package/src/styles/tokens/index.ts +0 -70
  299. package/src/styles/tokens/layout.stories.tsx +0 -50
  300. package/src/styles/tokens/scripts/check-foundations.mjs +0 -126
  301. package/src/styles/tokens/scripts/check-source.mjs +0 -53
  302. package/src/styles/tokens/source/dark.tokens.json +0 -138
  303. package/src/styles/tokens/source/foundations.tokens.json +0 -344
  304. package/src/styles/tokens/source/light.tokens.json +0 -138
  305. package/src/styles/tokens/style-dictionary.dark-class.json +0 -16
  306. package/src/styles/tokens/style-dictionary.dark-storybook.json +0 -16
  307. package/src/styles/tokens/style-dictionary.dark.json +0 -21
  308. package/src/styles/tokens/style-dictionary.foundations.json +0 -21
  309. package/src/styles/tokens/style-dictionary.light-storybook.json +0 -16
  310. package/src/styles/tokens/style-dictionary.light.json +0 -21
  311. package/src/styles/tokens/typography.stories.tsx +0 -157
  312. package/src/translations/en.ts +0 -34
  313. package/src/translations/index.ts +0 -32
  314. package/src/types/upload.ts +0 -42
  315. /package/{src/components/filters/index.ts → dist/components/filters/index.d.ts} +0 -0
  316. /package/{src/components/layout/index.ts → dist/components/layout/index.d.ts} +0 -0
  317. /package/{src/hooks/index.ts → dist/hooks/index.d.ts} +0 -0
  318. /package/{src/lib/index.ts → dist/lib/index.d.ts} +0 -0
  319. /package/{src/native/index.ts → dist/native/index.d.ts} +0 -0
@@ -1,217 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import * as React from "react";
3
- import { userEvent, within } from "storybook/test";
4
-
5
- import { Button } from "../primitives/button";
6
- import { Navigation } from "./app-navigation";
7
-
8
- const meta: Meta<typeof Navigation> = {
9
- title: "Navigation/AppNavigation",
10
- component: Navigation,
11
- tags: ["autodocs"],
12
- parameters: {
13
- layout: "fullscreen",
14
- },
15
- };
16
- export default meta;
17
- type Story = StoryObj<typeof meta>;
18
-
19
- // ---------------------------------------------------------------------------
20
- // Shared helpers
21
- // ---------------------------------------------------------------------------
22
-
23
- const defaultLinks = [
24
- { href: "/features", label: "Features" },
25
- { href: "/pricing", label: "Pricing" },
26
- { href: "/blog", label: "Blog" },
27
- { href: "/about", label: "About" },
28
- ];
29
-
30
- /** Adds a little page content so the navigation can be viewed in context. */
31
- function PageContent({
32
- message = "Page content appears below the sticky nav.",
33
- }: {
34
- message?: string;
35
- }) {
36
- return (
37
- <div className="p-8">
38
- <p className="text-muted-foreground text-sm">{message}</p>
39
- </div>
40
- );
41
- }
42
-
43
- // ---------------------------------------------------------------------------
44
- // Stories
45
- // ---------------------------------------------------------------------------
46
-
47
- export const Default: Story = {
48
- render: () => (
49
- <>
50
- <Navigation>
51
- <Navigation.Brand>
52
- <a href="/">Acme</a>
53
- </Navigation.Brand>
54
-
55
- <Navigation.List>
56
- {defaultLinks.map((link) => (
57
- <Navigation.Item key={link.href} href={link.href}>
58
- {link.label}
59
- </Navigation.Item>
60
- ))}
61
- </Navigation.List>
62
-
63
- <Navigation.Action>
64
- <Button size="md" asChild>
65
- <a href="/get-started">Get started</a>
66
- </Button>
67
- </Navigation.Action>
68
- </Navigation>
69
- <PageContent />
70
- </>
71
- ),
72
- };
73
-
74
- export const NoCta: Story = {
75
- render: () => (
76
- <>
77
- <Navigation>
78
- <Navigation.Brand>
79
- <a href="/">Acme</a>
80
- </Navigation.Brand>
81
- <Navigation.List>
82
- {defaultLinks.map((link) => (
83
- <Navigation.Item key={link.href} href={link.href}>
84
- {link.label}
85
- </Navigation.Item>
86
- ))}
87
- </Navigation.List>
88
- </Navigation>
89
- <PageContent message="Same nav, no Action content." />
90
- </>
91
- ),
92
- };
93
-
94
- /**
95
- * `Navigation.Item` (and any of the other pieces) support the same
96
- * `asChild` composition as `Button`: pass your router's `Link` (or
97
- * anything else) as the single child. This story stands in for a router
98
- * `Link` with a plain anchor that no-ops on click, since Storybook has no
99
- * router.
100
- */
101
- export const WithCustomRouterLink: Story = {
102
- render: () => {
103
- const activeHref = "/pricing";
104
- return (
105
- <>
106
- <Navigation>
107
- <Navigation.Brand>
108
- <a href="/" data-router-link="stand-in" onClick={(e) => e.preventDefault()}>
109
- Acme
110
- </a>
111
- </Navigation.Brand>
112
-
113
- <Navigation.List>
114
- {defaultLinks.map((link) => (
115
- <Navigation.Item key={link.href} asChild active={link.href === activeHref}>
116
- <a href={link.href} data-router-link="stand-in" onClick={(e) => e.preventDefault()}>
117
- {link.label}
118
- </a>
119
- </Navigation.Item>
120
- ))}
121
- </Navigation.List>
122
-
123
- <Navigation.Action>
124
- <Button size="md" asChild>
125
- <a
126
- href="/get-started"
127
- data-router-link="stand-in"
128
- onClick={(e) => e.preventDefault()}
129
- >
130
- Get started
131
- </a>
132
- </Button>
133
- </Navigation.Action>
134
- </Navigation>
135
- <PageContent message="Every link here is a custom element composed via asChild - open the elements panel to see data-router-link on each one, and the /pricing item's data-active/aria-current." />
136
- </>
137
- );
138
- },
139
- };
140
-
141
- /**
142
- * Composition means arbitrary content can sit alongside the link list and
143
- * action area without any prop-shape changes to `Navigation` itself.
144
- */
145
- export const WithExtraContent: Story = {
146
- render: () => (
147
- <>
148
- <Navigation>
149
- <Navigation.Brand>
150
- <a href="/">Acme</a>
151
- </Navigation.Brand>
152
-
153
- <Navigation.List>
154
- {defaultLinks.slice(0, 2).map((link) => (
155
- <Navigation.Item key={link.href} href={link.href}>
156
- {link.label}
157
- </Navigation.Item>
158
- ))}
159
- </Navigation.List>
160
-
161
- <input
162
- type="search"
163
- placeholder="Search..."
164
- className="h-9 w-40 rounded-md border bg-background px-3 text-sm"
165
- />
166
-
167
- <Navigation.Action>
168
- <Button size="md" variant="outline" asChild>
169
- <a href="/get-started">Get started</a>
170
- </Button>
171
- </Navigation.Action>
172
- </Navigation>
173
- <PageContent message="A search input sits directly between the link list and the action area — no endContent prop needed." />
174
- </>
175
- ),
176
- };
177
-
178
- /**
179
- * `@internal` — undocumented Nokia 3310-style LCD skin for the mobile Sheet
180
- * menu, opted into via `variant="nokia"`. Only affects the mobile Sheet, so
181
- * this story forces a mobile viewport and auto-opens the menu trigger.
182
- */
183
- export const Nokia: Story = {
184
- parameters: {
185
- viewport: { defaultViewport: "mobile1" },
186
- chromatic: { viewports: [375] },
187
- },
188
- render: () => (
189
- <>
190
- <Navigation variant="nokia">
191
- <Navigation.Brand>
192
- <a href="/">Acme</a>
193
- </Navigation.Brand>
194
-
195
- <Navigation.List>
196
- {defaultLinks.map((link) => (
197
- <Navigation.Item key={link.href} href={link.href} active={link.href === "/pricing"}>
198
- {link.label}
199
- </Navigation.Item>
200
- ))}
201
- </Navigation.List>
202
-
203
- <Navigation.Action>
204
- <Button size="md" asChild>
205
- <a href="/get-started">Get started</a>
206
- </Button>
207
- </Navigation.Action>
208
- </Navigation>
209
- <PageContent message="Open the menu button to see the Nokia 3310-style LCD skin." />
210
- </>
211
- ),
212
- play: async ({ canvasElement }) => {
213
- const canvas = within(canvasElement);
214
- const trigger = canvas.getByRole("button", { name: /open menu/i });
215
- await userEvent.click(trigger);
216
- },
217
- };
@@ -1,234 +0,0 @@
1
- import { Menu } from "lucide-react";
2
- import type { ComponentPropsWithoutRef, ReactNode } from "react";
3
- import * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
- import { Slot } from "../../lib/slot";
7
- import { Sheet, SheetClose, SheetContent, SheetTitle, SheetTrigger } from "../overlays/sheet";
8
- import { Button } from "../primitives/button";
9
-
10
- type MobileMenuState = {
11
- /** True while rendering inside the mobile Sheet copy of the nav content. */
12
- isMobile: boolean;
13
- variant?: NavigationVariant;
14
- };
15
-
16
- const MobileMenuContext = React.createContext<MobileMenuState>({ isMobile: false });
17
-
18
- /** @internal Undocumented — not part of the public API surface. */
19
- type NavigationVariant = "nokia";
20
-
21
- // Nokia 3310-style monochrome LCD skin for the mobile Sheet. See
22
- // src/styles/nokia.css for the palette/font tokens and scanline texture.
23
- const NOKIA_SHEET_CLASSNAME =
24
- "nokia-screen rounded-none border-[6px] border-nokia-bezel bg-nokia-screen p-4 font-nokia text-nokia-ink shadow-[0_0_0_3px_var(--color-nokia-screen-deep),0_10px_30px_-6px_rgba(0,0,0,0.65)]";
25
- const NOKIA_SHEET_CLOSE_CLASSNAME =
26
- "text-nokia-ink hover:bg-nokia-ink hover:text-nokia-screen rounded-none border-2 border-nokia-ink";
27
- const NOKIA_LIST_CLASSNAME = "!grid grid-cols-3 gap-3";
28
- const NOKIA_ITEM_CLASSNAME =
29
- "!h-auto !max-h-none w-full flex-col justify-center gap-1 rounded-none border-2 border-nokia-ink/40 px-2 py-3 text-center font-nokia text-base tracking-widest uppercase hover:bg-transparent active:bg-nokia-ink active:text-nokia-screen data-[active]:border-nokia-ink data-[active]:bg-nokia-ink data-[active]:text-nokia-screen";
30
-
31
- export interface NavigationRootProps {
32
- children: ReactNode;
33
- ariaLabel?: string;
34
- className?: string;
35
- /**
36
- * @internal Undocumented mobile-Sheet skin, not part of the public API
37
- * (no README/Storybook entry). Opt in at your own risk.
38
- */
39
- variant?: NavigationVariant;
40
- }
41
-
42
- /**
43
- * Sticky nav shell. Compose it with `Navigation.Brand`, `Navigation.List`
44
- * (containing `Navigation.Item`s), and any other content — a CTA `Button`,
45
- * a search input, a notification bell — as plain children. `Navigation`
46
- * only owns the responsive chrome: `Navigation.Brand` stays visible at
47
- * every width, everything else collapses into a Sheet-based mobile menu
48
- * below `sm`, reusing the exact elements you composed (not a parallel data
49
- * shape) so `Navigation.Item`'s active state and `asChild` composition
50
- * behave identically in both places.
51
- */
52
- function NavigationRoot({
53
- children,
54
- ariaLabel = "Primary navigation",
55
- className,
56
- variant,
57
- }: NavigationRootProps) {
58
- const [mobileOpen, setMobileOpen] = React.useState(false);
59
- const isNokia = variant === "nokia";
60
-
61
- const items = React.Children.toArray(children);
62
- const brand = items.find(
63
- (child) => React.isValidElement(child) && child.type === NavigationBrand,
64
- );
65
- const rest = items.filter((child) => child !== brand);
66
-
67
- return (
68
- <header className="bg-background/95 supports-backdrop-filter:bg-background/60 sticky top-0 z-50 flex w-full justify-center border-b px-4 backdrop-blur">
69
- <nav
70
- className={cn(
71
- "flex min-h-14 w-full max-w-7xl items-center justify-between gap-6",
72
- className,
73
- )}
74
- aria-label={ariaLabel}
75
- >
76
- <div className="flex min-w-0 items-center">{brand}</div>
77
-
78
- <div className="ml-auto hidden min-w-0 items-center gap-6 sm:flex">{rest}</div>
79
-
80
- <div className="ml-auto flex items-center sm:hidden">
81
- <Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
82
- <SheetTrigger asChild>
83
- <Button
84
- variant="outline"
85
- size="icon"
86
- aria-label={mobileOpen ? "Close menu" : "Open menu"}
87
- >
88
- <Menu className="size-4" aria-hidden="true" />
89
- </Button>
90
- </SheetTrigger>
91
- <SheetContent
92
- className={isNokia ? NOKIA_SHEET_CLASSNAME : undefined}
93
- closeClassName={isNokia ? NOKIA_SHEET_CLOSE_CLASSNAME : undefined}
94
- >
95
- <SheetTitle className="sr-only">Navigation menu</SheetTitle>
96
- <div className="mt-8 flex flex-1 flex-col gap-4">
97
- <MobileMenuContext.Provider value={{ isMobile: true, variant }}>
98
- {rest}
99
- </MobileMenuContext.Provider>
100
- </div>
101
- </SheetContent>
102
- </Sheet>
103
- </div>
104
- </nav>
105
- </header>
106
- );
107
- }
108
-
109
- export interface NavigationBrandProps {
110
- children: ReactNode;
111
- className?: string;
112
- }
113
-
114
- /** Always visible, at every viewport width — compose your own link/logo markup directly. */
115
- function NavigationBrand({ children, className }: NavigationBrandProps) {
116
- return <div className={cn("flex items-center font-semibold", className)}>{children}</div>;
117
- }
118
-
119
- export interface NavigationListProps {
120
- children: ReactNode;
121
- className?: string;
122
- }
123
-
124
- /** Semantic `<ul>` of `Navigation.Item`s. Lays out horizontally on desktop, stacks inside the mobile Sheet. */
125
- function NavigationList({ children, className }: NavigationListProps) {
126
- const { isMobile, variant } = React.useContext(MobileMenuContext);
127
- const isNokia = isMobile && variant === "nokia";
128
- return (
129
- <ul
130
- className={cn(
131
- "flex items-center gap-1",
132
- isMobile && "flex-col items-stretch gap-1",
133
- isNokia && NOKIA_LIST_CLASSNAME,
134
- className,
135
- )}
136
- >
137
- {children}
138
- </ul>
139
- );
140
- }
141
-
142
- interface NavigationItemOwnProps {
143
- active?: boolean;
144
- /** Apply the item styling to a single child (e.g. your router's `Link`) instead of rendering an `<a>`. */
145
- asChild?: boolean;
146
- }
147
-
148
- export type NavigationItemProps = NavigationItemOwnProps &
149
- Omit<ComponentPropsWithoutRef<"a">, keyof NavigationItemOwnProps>;
150
-
151
- /** A single nav link, wrapped in `<li>`. Auto-closes the mobile Sheet on click when rendered there. */
152
- function NavigationItem({
153
- active = false,
154
- asChild = false,
155
- className,
156
- children,
157
- ...props
158
- }: NavigationItemProps) {
159
- const { isMobile, variant } = React.useContext(MobileMenuContext);
160
- const isNokia = isMobile && variant === "nokia";
161
- const itemClassName = cn(
162
- "inline-flex h-9 max-h-9 shrink-0 items-center px-3 py-2 text-sm font-medium hover:bg-muted hover:text-muted-foreground data-[active]:font-semibold data-[active]:text-foreground",
163
- isNokia && NOKIA_ITEM_CLASSNAME,
164
- className,
165
- );
166
-
167
- const content = asChild ? (
168
- <Slot
169
- {...props}
170
- data-active={active || undefined}
171
- aria-current={active ? "page" : undefined}
172
- className={itemClassName}
173
- >
174
- {children}
175
- </Slot>
176
- ) : (
177
- <a
178
- {...props}
179
- data-active={active || undefined}
180
- aria-current={active ? "page" : undefined}
181
- className={itemClassName}
182
- >
183
- {children}
184
- </a>
185
- );
186
-
187
- return <li>{isMobile ? <SheetClose asChild>{content}</SheetClose> : content}</li>;
188
- }
189
-
190
- export interface NavigationActionProps {
191
- children: ReactNode;
192
- className?: string;
193
- }
194
-
195
- /**
196
- * Layout wrapper for a CTA, avatar menu, theme toggle, etc. — bring your
197
- * own `Button`, no dedicated CTA component needed. Content here does not
198
- * auto-close the mobile Sheet on click (it may hold more than one
199
- * element, or non-navigational controls) — use `Navigation.Item` instead
200
- * for a link that should close the menu when tapped.
201
- */
202
- function NavigationAction({ children, className }: NavigationActionProps) {
203
- const { isMobile, variant } = React.useContext(MobileMenuContext);
204
- const isNokia = isMobile && variant === "nokia";
205
- return (
206
- <div
207
- className={cn(
208
- "flex items-center gap-3",
209
- isMobile && "mt-2 flex-col items-stretch gap-3 border-t pt-4",
210
- isNokia && "border-nokia-ink/40",
211
- className,
212
- )}
213
- >
214
- {children}
215
- </div>
216
- );
217
- }
218
-
219
- type NavigationComponent = typeof NavigationRoot & {
220
- Brand: typeof NavigationBrand;
221
- List: typeof NavigationList;
222
- Item: typeof NavigationItem;
223
- Action: typeof NavigationAction;
224
- };
225
-
226
- export const Navigation: NavigationComponent = Object.assign(NavigationRoot, {
227
- Brand: NavigationBrand,
228
- List: NavigationList,
229
- Item: NavigationItem,
230
- Action: NavigationAction,
231
- });
232
-
233
- /** @deprecated Use `Navigation` instead. */
234
- export const AppNavigation = Navigation;
@@ -1,41 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { useState } from "react";
3
-
4
- import { PaginationControls } from "./pagination-controls";
5
-
6
- const meta = {
7
- title: "Navigation/PaginationControls",
8
- component: PaginationControls,
9
- tags: ["autodocs"],
10
- } satisfies Meta<typeof PaginationControls>;
11
-
12
- export default meta;
13
- type Story = StoryObj<typeof meta>;
14
-
15
- function PaginationPreview({ totalPages }: { totalPages: number }) {
16
- const [currentPage, setCurrentPage] = useState(0);
17
- return (
18
- <PaginationControls
19
- currentPage={currentPage}
20
- totalPages={totalPages}
21
- onPageChange={setCurrentPage}
22
- />
23
- );
24
- }
25
-
26
- const baseArgs = {
27
- currentPage: 0,
28
- totalPages: 5,
29
- onPageChange: () => {},
30
- };
31
-
32
- export const Default: Story = {
33
- args: baseArgs,
34
- render: () => <PaginationPreview totalPages={5} />,
35
- };
36
-
37
- export const SinglePage: Story = {
38
- name: "Single page (disabled controls)",
39
- args: { ...baseArgs, totalPages: 1 },
40
- render: () => <PaginationPreview totalPages={1} />,
41
- };
@@ -1,46 +0,0 @@
1
- import { ChevronLeft, ChevronRight } from "lucide-react";
2
-
3
- import { Button } from "../primitives/button";
4
-
5
- export interface PaginationControlsProps {
6
- currentPage: number;
7
- totalPages: number;
8
- onPageChange: (page: number) => void;
9
- }
10
-
11
- /** Zero-indexed prev/next pagination control. */
12
- export function PaginationControls({
13
- currentPage,
14
- totalPages,
15
- onPageChange,
16
- }: PaginationControlsProps) {
17
- return (
18
- <div className="border-border bg-background text-foreground inline-flex w-fit shrink-0 items-center self-start overflow-hidden rounded-full border text-sm">
19
- <Button
20
- type="button"
21
- variant="outline"
22
- size="icon"
23
- className="rounded-none border-0 border-r bg-transparent shadow-none"
24
- onClick={() => onPageChange(currentPage - 1)}
25
- disabled={currentPage === 0}
26
- aria-label="Previous page"
27
- >
28
- <ChevronLeft className="size-4" aria-hidden />
29
- </Button>
30
- <span className="min-w-20 px-3 text-center text-xs tabular-nums">
31
- {currentPage + 1} of {totalPages}
32
- </span>
33
- <Button
34
- type="button"
35
- variant="outline"
36
- size="icon"
37
- className="rounded-none border-0 border-l bg-transparent shadow-none"
38
- onClick={() => onPageChange(currentPage + 1)}
39
- disabled={currentPage >= totalPages - 1}
40
- aria-label="Next page"
41
- >
42
- <ChevronRight className="size-4" aria-hidden />
43
- </Button>
44
- </div>
45
- );
46
- }
@@ -1,48 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
-
3
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
4
-
5
- const meta = {
6
- title: "Navigation/Tabs",
7
- component: Tabs,
8
- tags: ["autodocs"],
9
- } satisfies Meta<typeof Tabs>;
10
-
11
- export default meta;
12
- type Story = StoryObj<typeof meta>;
13
-
14
- export const Default: Story = {
15
- render: () => (
16
- <Tabs defaultValue="account" className="w-[400px]">
17
- <TabsList>
18
- <TabsTrigger value="account">Account</TabsTrigger>
19
- <TabsTrigger value="password">Password</TabsTrigger>
20
- </TabsList>
21
- <TabsContent value="account">
22
- <p className="text-muted-foreground p-4 text-sm">
23
- Make changes to your account here. Click save when you're done.
24
- </p>
25
- </TabsContent>
26
- <TabsContent value="password">
27
- <p className="text-muted-foreground p-4 text-sm">
28
- Change your password here. After saving, you'll be logged out.
29
- </p>
30
- </TabsContent>
31
- </Tabs>
32
- ),
33
- };
34
-
35
- export const Vertical: Story = {
36
- render: () => (
37
- <Tabs defaultValue="tab1" orientation="vertical" className="w-[400px]">
38
- <TabsList>
39
- <TabsTrigger value="tab1">Account</TabsTrigger>
40
- <TabsTrigger value="tab2">Billing</TabsTrigger>
41
- <TabsTrigger value="tab3">Security</TabsTrigger>
42
- </TabsList>
43
- <TabsContent value="tab1">Account settings</TabsContent>
44
- <TabsContent value="tab2">Billing information</TabsContent>
45
- <TabsContent value="tab3">Security settings</TabsContent>
46
- </Tabs>
47
- ),
48
- };
@@ -1,52 +0,0 @@
1
- import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
2
- import * as React from "react";
3
-
4
- import { cn } from "../../lib/utils";
5
-
6
- function Tabs({ ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {
7
- return <TabsPrimitive.Root data-slot="tabs" {...props} />;
8
- }
9
-
10
- function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {
11
- return (
12
- <TabsPrimitive.List
13
- data-slot="tabs-list"
14
- className={cn(
15
- "bg-muted flex h-auto w-full items-center gap-1 rounded-full border p-1",
16
- className,
17
- )}
18
- {...props}
19
- />
20
- );
21
- }
22
-
23
- function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Tab>) {
24
- return (
25
- <TabsPrimitive.Tab
26
- data-slot="tabs-trigger"
27
- className={cn(
28
- "text-foreground flex flex-1 items-center justify-center gap-2 rounded-full px-3 py-2 text-sm font-medium whitespace-nowrap transition-all",
29
- "disabled:pointer-events-none disabled:opacity-50",
30
- "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
31
- "data-active:ring-1 data-active:ring-inset data-active:bg-background data-active:text-foreground data-active:ring-border",
32
- className,
33
- )}
34
- {...props}
35
- />
36
- );
37
- }
38
-
39
- function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Panel>) {
40
- return (
41
- <TabsPrimitive.Panel
42
- data-slot="tabs-content"
43
- className={cn(
44
- "focus-visible:bg-muted/10 mt-2 rounded-md focus-visible:border focus-visible:outline-none",
45
- className,
46
- )}
47
- {...props}
48
- />
49
- );
50
- }
51
-
52
- export { Tabs, TabsContent, TabsList, TabsTrigger };
@@ -1,52 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
-
3
- import { BottomSheet } from "./bottom-sheet";
4
-
5
- const meta = {
6
- title: "Overlays/BottomSheet",
7
- component: BottomSheet,
8
- tags: ["autodocs"],
9
- } satisfies Meta<typeof BottomSheet>;
10
-
11
- export default meta;
12
- type Story = StoryObj<typeof meta>;
13
-
14
- export const Default: Story = {
15
- args: {
16
- label: "Example panel",
17
- children: "Panel content",
18
- },
19
- render: () => (
20
- <div className="bg-muted relative h-105 w-120 overflow-hidden rounded-xl border">
21
- <BottomSheet
22
- label="Example panel"
23
- defaultExpanded
24
- className="bg-card absolute inset-x-4 bottom-4 max-h-[70%] rounded-2xl border shadow-lg"
25
- scrollClassName="p-4"
26
- >
27
- <p className="text-sm font-semibold">Drag the handle or press it to collapse.</p>
28
- <p className="text-muted-foreground mt-1 text-sm">
29
- Non-modal persistent panel. Positioning and sizing are app-owned via className.
30
- </p>
31
- </BottomSheet>
32
- </div>
33
- ),
34
- };
35
-
36
- export const Collapsed: Story = {
37
- args: {
38
- label: "Example panel",
39
- children: "Panel content",
40
- },
41
- render: () => (
42
- <div className="bg-muted relative h-105 w-120 overflow-hidden rounded-xl border">
43
- <BottomSheet
44
- label="Example panel"
45
- className="bg-card absolute inset-x-4 bottom-4 max-h-[70%] rounded-2xl border shadow-lg"
46
- scrollClassName="p-4"
47
- >
48
- <p className="text-sm font-semibold">Starts collapsed to its 48px peek.</p>
49
- </BottomSheet>
50
- </div>
51
- ),
52
- };