@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,111 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { useState } from "react";
3
-
4
- import { booleanControl, hiddenControl, numberControl } from "../../storybook/controls";
5
- import { Stepper } from "./stepper";
6
-
7
- const meta = {
8
- title: "Forms/Stepper",
9
- component: Stepper,
10
- tags: ["autodocs"],
11
- argTypes: {
12
- value: numberControl("Current value", { defaultValue: 1 }),
13
- min: numberControl("Minimum allowed value", { defaultValue: 0 }),
14
- max: numberControl("Maximum allowed value", { defaultValue: 100 }),
15
- step: numberControl("Amount to change per click", { defaultValue: 1 }),
16
- disabled: booleanControl("Prevents user interaction", false),
17
- onChange: hiddenControl,
18
- },
19
- } satisfies Meta<typeof Stepper>;
20
-
21
- export default meta;
22
- type Story = StoryObj<typeof meta>;
23
-
24
- function StepperPreview({
25
- value,
26
- min,
27
- max,
28
- step,
29
- disabled,
30
- format,
31
- }: {
32
- value: number;
33
- min?: number;
34
- max?: number;
35
- step?: number;
36
- disabled?: boolean;
37
- format?: (value: number) => string;
38
- }) {
39
- const [currentValue, setCurrentValue] = useState(value);
40
-
41
- return (
42
- <Stepper
43
- value={currentValue}
44
- min={min}
45
- max={max}
46
- step={step}
47
- disabled={disabled}
48
- format={format}
49
- onChange={setCurrentValue}
50
- />
51
- );
52
- }
53
-
54
- export const Default: Story = {
55
- args: {
56
- value: 1,
57
- min: 0,
58
- max: 10,
59
- step: 1,
60
- onChange: () => {},
61
- },
62
- render: (args) => (
63
- <StepperPreview value={args.value} min={args.min} max={args.max} step={args.step} />
64
- ),
65
- };
66
-
67
- export const AtMin: Story = {
68
- args: {
69
- value: 0,
70
- min: 0,
71
- max: 10,
72
- onChange: () => {},
73
- },
74
- render: (args) => <StepperPreview value={args.value} min={args.min} max={args.max} />,
75
- };
76
-
77
- export const AtMax: Story = {
78
- args: {
79
- value: 10,
80
- min: 0,
81
- max: 10,
82
- onChange: () => {},
83
- },
84
- render: (args) => <StepperPreview value={args.value} min={args.min} max={args.max} />,
85
- };
86
-
87
- export const Disabled: Story = {
88
- args: {
89
- value: 3,
90
- disabled: true,
91
- onChange: () => {},
92
- },
93
- render: (args) => <StepperPreview value={args.value} disabled={args.disabled} />,
94
- };
95
-
96
- export const CustomFormat: Story = {
97
- args: {
98
- value: 5,
99
- min: 0,
100
- max: 20,
101
- onChange: () => {},
102
- },
103
- render: (args) => (
104
- <StepperPreview
105
- value={args.value}
106
- min={args.min}
107
- max={args.max}
108
- format={(value) => `${value} guests`}
109
- />
110
- ),
111
- };
@@ -1,66 +0,0 @@
1
- import { Minus, Plus } from "lucide-react";
2
-
3
- import { cn } from "../../lib/utils";
4
- import { Button } from "../primitives/button";
5
-
6
- interface StepperProps {
7
- value: number;
8
- min?: number;
9
- max?: number;
10
- step?: number;
11
- format?: (value: number) => string;
12
- onChange: (value: number) => void;
13
- className?: string;
14
- disabled?: boolean;
15
- increaseLabel?: string;
16
- decreaseLabel?: string;
17
- }
18
-
19
- function Stepper({
20
- value,
21
- min = 0,
22
- max = 100,
23
- step = 1,
24
- format,
25
- onChange,
26
- className,
27
- disabled,
28
- increaseLabel = "Increase",
29
- decreaseLabel = "Decrease",
30
- }: StepperProps) {
31
- const atMin = value <= min;
32
- const atMax = value >= max;
33
-
34
- return (
35
- <div className={cn("border-input bg-muted flex items-center rounded-lg border", className)}>
36
- <Button
37
- type="button"
38
- variant="ghost"
39
- onClick={() => onChange(Math.max(min, value - step))}
40
- disabled={disabled || atMin}
41
- className="text-muted-foreground rounded-none rounded-l-lg border-r"
42
- aria-label={decreaseLabel}
43
- >
44
- <Minus className="size-3.5" />
45
- </Button>
46
- <div className="flex min-w-[3ch] flex-1 items-center justify-center px-3">
47
- <span className="text-foreground text-sm font-semibold tabular-nums">
48
- {format ? format(value) : value}
49
- </span>
50
- </div>
51
- <Button
52
- type="button"
53
- variant="ghost"
54
- onClick={() => onChange(Math.min(max, value + step))}
55
- disabled={disabled || atMax}
56
- className="text-muted-foreground rounded-none rounded-r-lg border-l"
57
- aria-label={increaseLabel}
58
- >
59
- <Plus className="size-3.5" />
60
- </Button>
61
- </div>
62
- );
63
- }
64
-
65
- export { Stepper };
66
- export type { StepperProps };
@@ -1,144 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { useEffect, useState } from "react";
3
-
4
- import { booleanControl, selectControl } from "../../storybook/controls";
5
- import { switchSizeOptions } from "../../storybook/options";
6
- import { Switch } from "./switch";
7
-
8
- const meta = {
9
- title: "Forms/Switch",
10
- component: Switch,
11
- tags: ["autodocs"],
12
- argTypes: {
13
- checked: booleanControl("Controlled on/off state of the switch", false),
14
- disabled: booleanControl("Prevents user interaction and applies disabled styling", false),
15
- size: selectControl(switchSizeOptions, "Size variant of the switch", {
16
- defaultValue: "default",
17
- }),
18
- },
19
- } satisfies Meta<typeof Switch>;
20
-
21
- export default meta;
22
- type Story = StoryObj<typeof meta>;
23
-
24
- function SwitchPreview({
25
- checked,
26
- disabled = false,
27
- id,
28
- label,
29
- size = "default",
30
- }: {
31
- checked: boolean;
32
- disabled: boolean;
33
- id: string;
34
- label: string;
35
- size: "default" | "sm";
36
- }) {
37
- const [currentChecked, setCurrentChecked] = useState(checked);
38
-
39
- useEffect(() => {
40
- setCurrentChecked(checked);
41
- }, [checked]);
42
-
43
- return (
44
- <div className="flex items-center space-x-2">
45
- <Switch
46
- id={id}
47
- checked={currentChecked}
48
- disabled={disabled}
49
- onCheckedChange={setCurrentChecked}
50
- size={size}
51
- />
52
- <label htmlFor={id} className="text-foreground text-sm leading-none font-medium">
53
- {label}
54
- </label>
55
- </div>
56
- );
57
- }
58
-
59
- export const Default: Story = {
60
- args: {
61
- checked: false,
62
- },
63
- render: (args) => (
64
- <SwitchPreview
65
- checked={args.checked ?? false}
66
- disabled={args.disabled ?? false}
67
- id="switch-default"
68
- label="Airplane Mode"
69
- size={args.size ?? "default"}
70
- />
71
- ),
72
- };
73
-
74
- export const WithLabel: Story = {
75
- args: {
76
- checked: false,
77
- size: "default",
78
- },
79
- render: (args) => (
80
- <SwitchPreview
81
- checked={args.checked ?? false}
82
- disabled={args.disabled ?? false}
83
- id="airplane-mode"
84
- label="Airplane Mode"
85
- size={args.size ?? "default"}
86
- />
87
- ),
88
- };
89
-
90
- export const Checked: Story = {
91
- args: {
92
- checked: true,
93
- },
94
- render: (args) => (
95
- <SwitchPreview
96
- checked={args.checked ?? true}
97
- disabled={args.disabled ?? false}
98
- id="notifications"
99
- label="Enable Notifications"
100
- size={args.size ?? "default"}
101
- />
102
- ),
103
- };
104
-
105
- export const Disabled: Story = {
106
- args: {
107
- checked: false,
108
- disabled: true,
109
- },
110
- render: (args) => (
111
- <div className="flex flex-col gap-3">
112
- <SwitchPreview
113
- checked={args.checked ?? false}
114
- disabled={args.disabled ?? false}
115
- id="disabled-off"
116
- label="Disabled off"
117
- size={args.size ?? "default"}
118
- />
119
- <SwitchPreview
120
- checked
121
- disabled={args.disabled ?? false}
122
- id="disabled-on"
123
- label="Disabled on"
124
- size={args.size ?? "default"}
125
- />
126
- </div>
127
- ),
128
- };
129
-
130
- export const Small: Story = {
131
- args: {
132
- checked: false,
133
- size: "sm",
134
- },
135
- render: (args) => (
136
- <SwitchPreview
137
- checked={args.checked ?? false}
138
- disabled={args.disabled ?? false}
139
- id="small"
140
- label="Small switch"
141
- size={args.size ?? "default"}
142
- />
143
- ),
144
- };
@@ -1,39 +0,0 @@
1
- import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
2
- import * as React from "react";
3
-
4
- import { cn } from "../../lib/utils";
5
-
6
- function Switch({
7
- className,
8
- size = "default",
9
- ...props
10
- }: React.ComponentProps<typeof SwitchPrimitive.Root> & {
11
- size?: "sm" | "default";
12
- }) {
13
- return (
14
- <SwitchPrimitive.Root
15
- data-slot="switch"
16
- data-size={size}
17
- className={cn(
18
- "peer inline-flex shrink-0 items-center rounded-full border border-transparent p-px",
19
- "data-checked:bg-primary data-unchecked:bg-input data-checked:justify-end data-unchecked:justify-start",
20
- "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none",
21
- "disabled:cursor-not-allowed disabled:opacity-50",
22
- size === "default" ? "h-[1.15rem] w-8" : "h-3.5 w-6",
23
- className,
24
- )}
25
- {...props}
26
- >
27
- <SwitchPrimitive.Thumb
28
- data-slot="switch-thumb"
29
- data-size={size}
30
- className={cn(
31
- "pointer-events-none block rounded-full bg-background shadow-sm ring-1 ring-border/5 transition-all",
32
- size === "default" ? "size-4" : "size-3",
33
- )}
34
- />
35
- </SwitchPrimitive.Root>
36
- );
37
- }
38
-
39
- export { Switch };
@@ -1,87 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import { expect, userEvent, within } from "storybook/test";
3
-
4
- import { booleanControl, selectControl, textControl } from "../../storybook/controls";
5
- import { textFieldTypeOptions } from "../../storybook/options";
6
- import { TextField } from "./text-field";
7
-
8
- const meta = {
9
- title: "Forms/TextField",
10
- component: TextField,
11
- tags: ["autodocs"],
12
- argTypes: {
13
- label: textControl("Label displayed above the field"),
14
- type: selectControl(textFieldTypeOptions, "Input type used by the field", {
15
- defaultValue: "text",
16
- }),
17
- placeholder: textControl("Placeholder text shown when the field is empty"),
18
- helpText: textControl("Supporting text shown below the field"),
19
- error: textControl("Validation error text shown below the field"),
20
- disabled: booleanControl("Prevents user interaction and applies disabled styling", false),
21
- required: booleanControl("Marks the field as required for form submission", false),
22
- },
23
- } satisfies Meta<typeof TextField>;
24
-
25
- export default meta;
26
- type Story = StoryObj<typeof meta>;
27
-
28
- export const Default: Story = {
29
- args: {
30
- label: "Email",
31
- placeholder: "you@example.com",
32
- type: "email",
33
- },
34
- play: async ({ canvasElement }) => {
35
- const canvas = within(canvasElement);
36
- const input = canvas.getByLabelText("Email");
37
-
38
- await userEvent.click(input);
39
- await userEvent.type(input, "you@example.com");
40
-
41
- await expect(input).toHaveFocus();
42
- await expect(input).toHaveValue("you@example.com");
43
- },
44
- };
45
-
46
- export const WithHelpText: Story = {
47
- args: {
48
- label: "Search",
49
- helpText: "Press Enter to submit",
50
- placeholder: "Search notes",
51
- type: "search",
52
- },
53
- play: async ({ canvasElement }) => {
54
- const canvas = within(canvasElement);
55
-
56
- await expect(canvas.getByText("Press Enter to submit")).toBeInTheDocument();
57
- await expect(canvas.getByLabelText("Search")).toBeEnabled();
58
- },
59
- };
60
-
61
- export const Error: Story = {
62
- args: {
63
- label: "Password",
64
- error: "Password is required",
65
- type: "password",
66
- },
67
- play: async ({ canvasElement }) => {
68
- const canvas = within(canvasElement);
69
- const input = canvas.getByLabelText("Password");
70
-
71
- await expect(input).toHaveAttribute("aria-invalid", "true");
72
- await expect(canvas.getByText("Password is required")).toBeInTheDocument();
73
- },
74
- };
75
-
76
- export const Disabled: Story = {
77
- args: {
78
- disabled: true,
79
- label: "Handle",
80
- placeholder: "@hominem",
81
- },
82
- play: async ({ canvasElement }) => {
83
- const canvas = within(canvasElement);
84
-
85
- await expect(canvas.getByLabelText("Handle")).toBeDisabled();
86
- },
87
- };
@@ -1,75 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
- import { Input } from "./input";
5
- import type { TextFieldBaseProps, TextFieldType } from "./text-field.types";
6
-
7
- interface TextFieldProps extends Omit<React.ComponentProps<"input">, "type">, TextFieldBaseProps {
8
- type?: TextFieldType | undefined;
9
- }
10
-
11
- /**
12
- * TextField — composed input with label, helper text, and error state.
13
- * Replaces raw `<input>` + `<label>` + error span patterns in feature code.
14
- *
15
- * @example
16
- * <TextField label="Email" type="email" error={errors.email?.message} />
17
- * <TextField label="Search" type="search" helpText="Press Enter to search" />
18
- */
19
- function TextField({
20
- id,
21
- label,
22
- helpText,
23
- error,
24
- className,
25
- disabled,
26
- type = "text",
27
- ...inputProps
28
- }: TextFieldProps) {
29
- const generatedId = React.useId();
30
- const inputId = id ?? generatedId;
31
- const descriptionId = `${inputId}-description`;
32
- const errorId = `${inputId}-error`;
33
-
34
- if (!label && !helpText && !error) {
35
- return (
36
- <Input id={inputId} className={className} disabled={disabled} type={type} {...inputProps} />
37
- );
38
- }
39
-
40
- return (
41
- <div className="flex flex-col gap-1.5">
42
- {label && (
43
- <label
44
- htmlFor={inputId}
45
- className={cn(
46
- "text-foreground text-sm font-medium",
47
- inputProps.required && "after:text-destructive-text after:ml-0.5 after:content-['*']",
48
- )}
49
- >
50
- {label}
51
- </label>
52
- )}
53
- <Input
54
- id={inputId}
55
- className={className}
56
- disabled={disabled}
57
- type={type}
58
- aria-describedby={error ? errorId : helpText ? descriptionId : undefined}
59
- aria-invalid={error ? true : undefined}
60
- {...inputProps}
61
- />
62
- {error ? (
63
- <p id={errorId} role="alert" className="text-destructive-text text-sm">
64
- {error}
65
- </p>
66
- ) : helpText ? (
67
- <p id={descriptionId} className="text-muted-foreground text-sm">
68
- {helpText}
69
- </p>
70
- ) : null}
71
- </div>
72
- );
73
- }
74
-
75
- export { TextField, type TextFieldProps };
@@ -1,9 +0,0 @@
1
- export type TextFieldType = "text" | "email" | "password" | "search";
2
-
3
- export interface TextFieldBaseProps {
4
- disabled?: boolean | undefined;
5
- error?: string | undefined;
6
- helpText?: string | undefined;
7
- label?: string | undefined;
8
- id?: string | undefined;
9
- }
@@ -1,150 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- import type { ComponentProps } from "react";
3
- import { expect, userEvent, within } from "storybook/test";
4
-
5
- import { booleanControl, numberControl, textControl } from "../../storybook/controls";
6
- import { Textarea } from "./textarea";
7
-
8
- function TextareaField({
9
- error,
10
- helpText,
11
- id,
12
- label,
13
- ...props
14
- }: ComponentProps<typeof Textarea> & {
15
- error?: string;
16
- helpText?: string;
17
- id: string;
18
- label: string;
19
- }) {
20
- const helpId = helpText ? `${id}-help` : undefined;
21
- const errorId = error ? `${id}-error` : undefined;
22
-
23
- return (
24
- <div className="grid gap-2">
25
- <label htmlFor={id} className="text-foreground text-sm leading-none font-medium">
26
- {label}
27
- </label>
28
- <Textarea
29
- id={id}
30
- aria-describedby={[helpId, errorId].filter(Boolean).join(" ") || undefined}
31
- aria-invalid={error ? true : undefined}
32
- {...props}
33
- />
34
- {helpText ? (
35
- <p id={helpId} className="text-muted-foreground text-sm">
36
- {helpText}
37
- </p>
38
- ) : null}
39
- {error ? (
40
- <p id={errorId} className="text-destructive-text text-sm" role="alert">
41
- {error}
42
- </p>
43
- ) : null}
44
- </div>
45
- );
46
- }
47
-
48
- const meta = {
49
- title: "Forms/Textarea",
50
- component: Textarea,
51
- tags: ["autodocs"],
52
- argTypes: {
53
- placeholder: textControl("Placeholder text shown when the textarea is empty"),
54
- rows: numberControl("Number of visible text lines", { min: 1, max: 20, defaultValue: 4 }),
55
- disabled: booleanControl("Prevents user interaction and applies disabled styling", false),
56
- },
57
- } satisfies Meta<typeof Textarea>;
58
-
59
- export default meta;
60
- type Story = StoryObj<typeof meta>;
61
-
62
- export const Default: Story = {
63
- args: {
64
- placeholder: "Enter your notes here",
65
- rows: 4,
66
- },
67
- render: (args) => <TextareaField id="notes" label="Notes" {...args} />,
68
- play: async ({ canvasElement }) => {
69
- const canvas = within(canvasElement);
70
- const textarea = canvas.getByLabelText("Notes") as HTMLTextAreaElement;
71
-
72
- await userEvent.click(textarea);
73
- await userEvent.type(textarea, "Sample note content");
74
-
75
- await expect(textarea).toHaveValue("Sample note content");
76
- await expect(textarea).toHaveFocus();
77
- },
78
- };
79
-
80
- export const WithHelpText: Story = {
81
- args: {
82
- placeholder: "Write a detailed description...",
83
- rows: 5,
84
- },
85
- render: (args) => (
86
- <TextareaField
87
- id="description"
88
- label="Description"
89
- helpText="Markdown formatting is supported"
90
- {...args}
91
- />
92
- ),
93
- play: async ({ canvasElement }) => {
94
- const canvas = within(canvasElement);
95
-
96
- await expect(canvas.getByText("Markdown formatting is supported")).toBeInTheDocument();
97
- },
98
- };
99
-
100
- export const Error: Story = {
101
- args: {
102
- placeholder: "Write something...",
103
- rows: 3,
104
- },
105
- render: (args) => (
106
- <TextareaField
107
- id="message"
108
- label="Message"
109
- error="Message is required and cannot be empty"
110
- {...args}
111
- />
112
- ),
113
- play: async ({ canvasElement }) => {
114
- const canvas = within(canvasElement);
115
-
116
- await expect(canvas.getByRole("alert")).toHaveTextContent(
117
- "Message is required and cannot be empty",
118
- );
119
- },
120
- };
121
-
122
- export const Disabled: Story = {
123
- args: {
124
- disabled: true,
125
- placeholder: "This field is disabled",
126
- rows: 4,
127
- },
128
- render: (args) => <TextareaField id="archived-notes" label="Archived Notes" {...args} />,
129
- play: async ({ canvasElement }) => {
130
- const canvas = within(canvasElement);
131
- const textarea = canvas.getByLabelText("Archived Notes");
132
-
133
- await expect(textarea).toBeDisabled();
134
- },
135
- };
136
-
137
- export const LargeField: Story = {
138
- args: {
139
- placeholder: "Write as much as you need...",
140
- rows: 8,
141
- },
142
- render: (args) => (
143
- <TextareaField
144
- id="long-form-content"
145
- label="Long Form Content"
146
- helpText="Use this field for extended writing"
147
- {...args}
148
- />
149
- ),
150
- };
@@ -1,22 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
- return (
7
- <textarea
8
- data-slot="textarea"
9
- className={cn(
10
- "border-input flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base transition-colors md:text-sm",
11
- "placeholder:text-muted-foreground",
12
- "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none",
13
- "disabled:cursor-not-allowed disabled:opacity-50",
14
- "aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
15
- className,
16
- )}
17
- {...props}
18
- />
19
- );
20
- }
21
-
22
- export { Textarea };