aural-ui 2.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 (308) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +456 -0
  3. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +1327 -0
  4. package/dist/components/aspect-ratio/index.tsx +10 -0
  5. package/dist/components/aspect-ratio/meta.ts +8 -0
  6. package/dist/components/avatar/Avatar.stories.tsx +645 -0
  7. package/dist/components/avatar/index.tsx +50 -0
  8. package/dist/components/avatar/meta.ts +8 -0
  9. package/dist/components/badge/Badge.stories.tsx +169 -0
  10. package/dist/components/badge/index.tsx +28 -0
  11. package/dist/components/badge/meta.ts +6 -0
  12. package/dist/components/banner/Banner.stories.tsx +475 -0
  13. package/dist/components/banner/index.tsx +256 -0
  14. package/dist/components/banner/meta.ts +36 -0
  15. package/dist/components/button/Button.stories.tsx +74 -0
  16. package/dist/components/button/index.tsx +158 -0
  17. package/dist/components/button/meta.ts +33 -0
  18. package/dist/components/card/Card.stories.tsx +377 -0
  19. package/dist/components/card/index.tsx +85 -0
  20. package/dist/components/card/meta.ts +14 -0
  21. package/dist/components/char-count/CharCount.stories.tsx +334 -0
  22. package/dist/components/char-count/index.tsx +51 -0
  23. package/dist/components/char-count/meta.ts +13 -0
  24. package/dist/components/checkbox/Checkbox.stories.tsx +209 -0
  25. package/dist/components/checkbox/index.tsx +34 -0
  26. package/dist/components/checkbox/meta.ts +19 -0
  27. package/dist/components/chip/Chip.stories.tsx +207 -0
  28. package/dist/components/chip/index.tsx +92 -0
  29. package/dist/components/chip/meta.ts +17 -0
  30. package/dist/components/circular-loader/CircularLoader.stories.tsx +741 -0
  31. package/dist/components/circular-loader/index.tsx +138 -0
  32. package/dist/components/circular-loader/meta.ts +11 -0
  33. package/dist/components/collapsible/Collapsible.stories.tsx +319 -0
  34. package/dist/components/collapsible/index.tsx +158 -0
  35. package/dist/components/collapsible/meta.ts +22 -0
  36. package/dist/components/command/Command.stories.tsx +996 -0
  37. package/dist/components/command/index.tsx +324 -0
  38. package/dist/components/command/meta.ts +18 -0
  39. package/dist/components/dialog/Dialog.stories.tsx +963 -0
  40. package/dist/components/dialog/index.tsx +250 -0
  41. package/dist/components/dialog/meta.ts +28 -0
  42. package/dist/components/divider/Divider.stories.tsx +633 -0
  43. package/dist/components/divider/index.tsx +181 -0
  44. package/dist/components/divider/meta.ts +12 -0
  45. package/dist/components/dot-loader/DotLoader.stories.tsx +352 -0
  46. package/dist/components/dot-loader/index.tsx +78 -0
  47. package/dist/components/dot-loader/meta.ts +14 -0
  48. package/dist/components/dropdown/Dropdown.stories.tsx +1210 -0
  49. package/dist/components/dropdown/index.tsx +479 -0
  50. package/dist/components/dropdown/meta.ts +21 -0
  51. package/dist/components/form/Form.stories.tsx +320 -0
  52. package/dist/components/form/index.tsx +183 -0
  53. package/dist/components/form/meta.ts +11 -0
  54. package/dist/components/helper-text/HelperText.stories.tsx +254 -0
  55. package/dist/components/helper-text/index.tsx +102 -0
  56. package/dist/components/helper-text/meta.ts +18 -0
  57. package/dist/components/hover-card/HoverCard.stories.tsx +1328 -0
  58. package/dist/components/hover-card/index.tsx +42 -0
  59. package/dist/components/hover-card/meta.ts +12 -0
  60. package/dist/components/icon-button/IconButton.stories.tsx +252 -0
  61. package/dist/components/icon-button/index.tsx +130 -0
  62. package/dist/components/icon-button/meta.ts +20 -0
  63. package/dist/components/if-else/if-else.stories.tsx +100 -0
  64. package/dist/components/if-else/index.tsx +56 -0
  65. package/dist/components/if-else/meta.ts +6 -0
  66. package/dist/components/index.ts +70 -0
  67. package/dist/components/input/Input.stories.tsx +431 -0
  68. package/dist/components/input/index.tsx +487 -0
  69. package/dist/components/input/meta.ts +28 -0
  70. package/dist/components/label/Label.stories.tsx +200 -0
  71. package/dist/components/label/index.tsx +43 -0
  72. package/dist/components/label/meta.ts +14 -0
  73. package/dist/components/list/List.stories.tsx +963 -0
  74. package/dist/components/list/index.tsx +567 -0
  75. package/dist/components/list/meta.ts +24 -0
  76. package/dist/components/marquee/Marquee.stories.tsx +819 -0
  77. package/dist/components/marquee/index.tsx +107 -0
  78. package/dist/components/marquee/meta.ts +6 -0
  79. package/dist/components/overlay/Overlay.stories.tsx +954 -0
  80. package/dist/components/overlay/index.tsx +58 -0
  81. package/dist/components/overlay/meta.ts +10 -0
  82. package/dist/components/pagination/Pagination.stories.tsx +354 -0
  83. package/dist/components/pagination/index.tsx +455 -0
  84. package/dist/components/pagination/meta.ts +29 -0
  85. package/dist/components/popover/Popover.stories.tsx +1037 -0
  86. package/dist/components/popover/index.tsx +67 -0
  87. package/dist/components/popover/meta.ts +12 -0
  88. package/dist/components/radio/Radio.stories.tsx +146 -0
  89. package/dist/components/radio/index.tsx +41 -0
  90. package/dist/components/radio/meta.ts +19 -0
  91. package/dist/components/resizable/Resizable.stories.tsx +866 -0
  92. package/dist/components/resizable/index.tsx +55 -0
  93. package/dist/components/resizable/meta.ts +12 -0
  94. package/dist/components/scroll-area/ScrollArea.stories.tsx +1104 -0
  95. package/dist/components/scroll-area/index.tsx +55 -0
  96. package/dist/components/scroll-area/meta.ts +8 -0
  97. package/dist/components/search/Search.stories.tsx +678 -0
  98. package/dist/components/search/index.tsx +141 -0
  99. package/dist/components/search/meta.ts +6 -0
  100. package/dist/components/select/Select.stories.tsx +962 -0
  101. package/dist/components/select/index.tsx +512 -0
  102. package/dist/components/select/meta.ts +40 -0
  103. package/dist/components/sheet/Sheet.stories.tsx +1060 -0
  104. package/dist/components/sheet/index.tsx +440 -0
  105. package/dist/components/sheet/meta.ts +38 -0
  106. package/dist/components/skelton/Skelton.stories.tsx +859 -0
  107. package/dist/components/skelton/index.tsx +17 -0
  108. package/dist/components/skelton/meta.ts +6 -0
  109. package/dist/components/slider/Slider.stories.tsx +876 -0
  110. package/dist/components/slider/index.tsx +156 -0
  111. package/dist/components/slider/meta.ts +29 -0
  112. package/dist/components/stepper/Stepper.stories.tsx +639 -0
  113. package/dist/components/stepper/index.tsx +650 -0
  114. package/dist/components/stepper/meta.ts +19 -0
  115. package/dist/components/switch/Switch.stories.tsx +85 -0
  116. package/dist/components/switch/index.tsx +37 -0
  117. package/dist/components/switch/meta.ts +24 -0
  118. package/dist/components/switch-case/SwitchCase.stories.tsx +209 -0
  119. package/dist/components/switch-case/index.tsx +89 -0
  120. package/dist/components/switch-case/meta.ts +6 -0
  121. package/dist/components/table/Table.stories.tsx +1095 -0
  122. package/dist/components/table/index.tsx +113 -0
  123. package/dist/components/table/meta.ts +20 -0
  124. package/dist/components/tabs/Tabs.stories.tsx +1379 -0
  125. package/dist/components/tabs/index.tsx +186 -0
  126. package/dist/components/tabs/meta.ts +25 -0
  127. package/dist/components/tag/Tag.stories.tsx +625 -0
  128. package/dist/components/tag/index.tsx +320 -0
  129. package/dist/components/tag/meta.ts +52 -0
  130. package/dist/components/textarea/TextArea.stories.tsx +723 -0
  131. package/dist/components/textarea/index.tsx +480 -0
  132. package/dist/components/textarea/meta.ts +23 -0
  133. package/dist/components/toast/Toast.stories.tsx +1427 -0
  134. package/dist/components/toast/index.tsx +26 -0
  135. package/dist/components/toast/meta.ts +19 -0
  136. package/dist/components/toggle/Toggle.stories.tsx +1093 -0
  137. package/dist/components/toggle/index.tsx +44 -0
  138. package/dist/components/toggle/meta.ts +19 -0
  139. package/dist/components/tooltip/Tooltip.stories.tsx +1548 -0
  140. package/dist/components/tooltip/index.tsx +304 -0
  141. package/dist/components/tooltip/meta.ts +21 -0
  142. package/dist/components/typography/Typography.stories.tsx +197 -0
  143. package/dist/components/typography/index.tsx +184 -0
  144. package/dist/components/typography/meta.ts +38 -0
  145. package/dist/fonts/LabGrotesque-Regular.ttf +0 -0
  146. package/dist/fonts/LabGrotesqueTRIAL-Bold.otf +0 -0
  147. package/dist/fonts/LabGrotesqueTRIAL-Light.otf +0 -0
  148. package/dist/fonts/LabGrotesqueTRIAL-Medium.otf +0 -0
  149. package/dist/fonts/LabGrotesqueTRIAL-Regular.otf +0 -0
  150. package/dist/fonts/PPSupplySans-Regular (1).otf +0 -0
  151. package/dist/fonts/PPSupplySans-Regular.otf +0 -0
  152. package/dist/fonts/PPSupplySans-Ultralight.otf +0 -0
  153. package/dist/hooks/index.ts +3 -0
  154. package/dist/hooks/use-previous/UsePrevious.stories.tsx +997 -0
  155. package/dist/hooks/use-previous/index.ts +15 -0
  156. package/dist/hooks/use-previous/meta.ts +6 -0
  157. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +983 -0
  158. package/dist/hooks/use-standalone-pagination/index.ts +146 -0
  159. package/dist/hooks/use-standalone-pagination/meta.ts +6 -0
  160. package/dist/icons/Icons.stories.tsx +29 -0
  161. package/dist/icons/alert-icon/AlertIcon.stories.tsx +991 -0
  162. package/dist/icons/alert-icon/index.tsx +48 -0
  163. package/dist/icons/alert-icon/meta.ts +8 -0
  164. package/dist/icons/all-icons.tsx +738 -0
  165. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +1031 -0
  166. package/dist/icons/angle-down-icon/index.tsx +25 -0
  167. package/dist/icons/angle-down-icon/meta.ts +8 -0
  168. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +1080 -0
  169. package/dist/icons/arrow-box-left-icon/index.tsx +24 -0
  170. package/dist/icons/arrow-box-left-icon/meta.ts +8 -0
  171. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +1151 -0
  172. package/dist/icons/arrow-right-icon/index.tsx +26 -0
  173. package/dist/icons/arrow-right-icon/meta.ts +8 -0
  174. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +1273 -0
  175. package/dist/icons/arrow-right-up-icon/index.tsx +24 -0
  176. package/dist/icons/arrow-right-up-icon/meta.ts +8 -0
  177. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +670 -0
  178. package/dist/icons/art-board-icon/index.tsx +24 -0
  179. package/dist/icons/art-board-icon/meta.ts +7 -0
  180. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +1244 -0
  181. package/dist/icons/audio-bar-icon/index.tsx +19 -0
  182. package/dist/icons/audio-bar-icon/meta.ts +8 -0
  183. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +1239 -0
  184. package/dist/icons/bubble-check-icon/index.tsx +24 -0
  185. package/dist/icons/bubble-check-icon/meta.ts +8 -0
  186. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +1228 -0
  187. package/dist/icons/bubble-crossed-icon/index.tsx +24 -0
  188. package/dist/icons/bubble-crossed-icon/meta.ts +8 -0
  189. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +912 -0
  190. package/dist/icons/bubble-sparkle-icon/index.tsx +26 -0
  191. package/dist/icons/bubble-sparkle-icon/meta.ts +8 -0
  192. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +1021 -0
  193. package/dist/icons/chevron-double-left-icon/index.tsx +34 -0
  194. package/dist/icons/chevron-double-left-icon/meta.ts +8 -0
  195. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +1021 -0
  196. package/dist/icons/chevron-double-right-icon/index.tsx +34 -0
  197. package/dist/icons/chevron-double-right-icon/meta.ts +8 -0
  198. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +1001 -0
  199. package/dist/icons/chevron-down-icon/index.tsx +27 -0
  200. package/dist/icons/chevron-down-icon/meta.ts +8 -0
  201. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +1029 -0
  202. package/dist/icons/chevron-left-icon/index.tsx +27 -0
  203. package/dist/icons/chevron-left-icon/meta.ts +8 -0
  204. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +1021 -0
  205. package/dist/icons/chevron-right-icon/index.tsx +27 -0
  206. package/dist/icons/chevron-right-icon/meta.ts +8 -0
  207. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +1036 -0
  208. package/dist/icons/chevron-up-icon/index.tsx +27 -0
  209. package/dist/icons/chevron-up-icon/meta.ts +8 -0
  210. package/dist/icons/command-icon/CommandIcon.stories.tsx +1098 -0
  211. package/dist/icons/command-icon/index.tsx +24 -0
  212. package/dist/icons/command-icon/meta.ts +8 -0
  213. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +1061 -0
  214. package/dist/icons/cross-circle-icon/index.tsx +23 -0
  215. package/dist/icons/cross-circle-icon/meta.ts +8 -0
  216. package/dist/icons/cross-icon/CrossIcon.stories.tsx +1027 -0
  217. package/dist/icons/cross-icon/index.tsx +24 -0
  218. package/dist/icons/cross-icon/meta.ts +8 -0
  219. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +1092 -0
  220. package/dist/icons/edit-big-icon/index.tsx +22 -0
  221. package/dist/icons/edit-big-icon/meta.ts +8 -0
  222. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +1090 -0
  223. package/dist/icons/eye-close-icon/index.tsx +26 -0
  224. package/dist/icons/eye-close-icon/meta.ts +8 -0
  225. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +1098 -0
  226. package/dist/icons/eye-open-icon/index.tsx +24 -0
  227. package/dist/icons/eye-open-icon/meta.ts +8 -0
  228. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +1071 -0
  229. package/dist/icons/feature-shine-icon/index.tsx +29 -0
  230. package/dist/icons/feature-shine-icon/meta.ts +8 -0
  231. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +1115 -0
  232. package/dist/icons/file-chart-icon/index.tsx +24 -0
  233. package/dist/icons/file-chart-icon/meta.ts +8 -0
  234. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +668 -0
  235. package/dist/icons/file-text-icon/index.tsx +24 -0
  236. package/dist/icons/file-text-icon/meta.ts +8 -0
  237. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +1239 -0
  238. package/dist/icons/grip-vertical-icon/index.tsx +28 -0
  239. package/dist/icons/grip-vertical-icon/meta.ts +8 -0
  240. package/dist/icons/image-icon/ImageIcon.stories.tsx +1181 -0
  241. package/dist/icons/image-icon/index.tsx +24 -0
  242. package/dist/icons/image-icon/meta.ts +8 -0
  243. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +1248 -0
  244. package/dist/icons/import-folder-icon/index.tsx +22 -0
  245. package/dist/icons/import-folder-icon/meta.ts +8 -0
  246. package/dist/icons/index.ts +46 -0
  247. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +1272 -0
  248. package/dist/icons/light-bulb-simple-icon/index.tsx +24 -0
  249. package/dist/icons/light-bulb-simple-icon/meta.ts +8 -0
  250. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +1245 -0
  251. package/dist/icons/magic-book-icon/index.tsx +32 -0
  252. package/dist/icons/magic-book-icon/meta.ts +8 -0
  253. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +1251 -0
  254. package/dist/icons/maintenance-icon/index.tsx +23 -0
  255. package/dist/icons/maintenance-icon/meta.ts +8 -0
  256. package/dist/icons/message-icon/MessageIcon.stories.tsx +595 -0
  257. package/dist/icons/message-icon/index.tsx +30 -0
  258. package/dist/icons/message-icon/meta.ts +8 -0
  259. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +1245 -0
  260. package/dist/icons/move-horizontal-icon/index.tsx +23 -0
  261. package/dist/icons/move-horizontal-icon/meta.ts +8 -0
  262. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +1196 -0
  263. package/dist/icons/move-vertical-icon/index.tsx +23 -0
  264. package/dist/icons/move-vertical-icon/meta.ts +8 -0
  265. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +1167 -0
  266. package/dist/icons/page-search-icon/index.tsx +21 -0
  267. package/dist/icons/page-search-icon/meta.ts +8 -0
  268. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +1131 -0
  269. package/dist/icons/pencil-icon/index.tsx +21 -0
  270. package/dist/icons/pencil-icon/meta.ts +8 -0
  271. package/dist/icons/plus-icon/PlusIcon.stories.tsx +1151 -0
  272. package/dist/icons/plus-icon/index.tsx +24 -0
  273. package/dist/icons/plus-icon/meta.ts +8 -0
  274. package/dist/icons/search-icon/SearchIcon.stories.tsx +1181 -0
  275. package/dist/icons/search-icon/index.tsx +24 -0
  276. package/dist/icons/search-icon/meta.ts +8 -0
  277. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +1167 -0
  278. package/dist/icons/site-logo-icon/index.tsx +79 -0
  279. package/dist/icons/site-logo-icon/meta.ts +8 -0
  280. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +637 -0
  281. package/dist/icons/spinner-gradient-icon/index.tsx +53 -0
  282. package/dist/icons/spinner-gradient-icon/meta.ts +8 -0
  283. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +644 -0
  284. package/dist/icons/spinner-solid-icon/index.tsx +59 -0
  285. package/dist/icons/spinner-solid-icon/meta.ts +8 -0
  286. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +736 -0
  287. package/dist/icons/spinner-solid-neutral-icon/index.tsx +53 -0
  288. package/dist/icons/spinner-solid-neutral-icon/meta.ts +8 -0
  289. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +1204 -0
  290. package/dist/icons/tick-circle-icon/index.tsx +23 -0
  291. package/dist/icons/tick-circle-icon/meta.ts +8 -0
  292. package/dist/icons/tick-icon/TickIcon.stories.tsx +1340 -0
  293. package/dist/icons/tick-icon/index.tsx +24 -0
  294. package/dist/icons/tick-icon/meta.ts +8 -0
  295. package/dist/icons/trash-icon/TrashIcon.stories.tsx +996 -0
  296. package/dist/icons/trash-icon/index.tsx +24 -0
  297. package/dist/icons/trash-icon/meta.ts +8 -0
  298. package/dist/icons/upload-icon/UploadIcon.stories.tsx +947 -0
  299. package/dist/icons/upload-icon/index.tsx +24 -0
  300. package/dist/icons/upload-icon/meta.ts +8 -0
  301. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +1045 -0
  302. package/dist/icons/vertical-menu-icon/index.tsx +27 -0
  303. package/dist/icons/vertical-menu-icon/meta.ts +8 -0
  304. package/dist/index.d.ts +6 -0
  305. package/dist/index.js +206 -0
  306. package/dist/lib/utils.ts +6 -0
  307. package/dist/styles/aural-theme.css +1008 -0
  308. package/package.json +142 -0
@@ -0,0 +1,320 @@
1
+ import React, { useEffect } from "react"
2
+ import { Button } from "@components/button"
3
+ import { Checkbox } from "@components/checkbox"
4
+ import Input from "@components/input"
5
+ import { zodResolver } from "@hookform/resolvers/zod"
6
+ import type { Meta, StoryObj } from "@storybook/react"
7
+ import { useForm } from "react-hook-form"
8
+ import { z } from "zod"
9
+
10
+ import {
11
+ Form,
12
+ FormControl,
13
+ FormDescription,
14
+ FormField,
15
+ FormItem,
16
+ FormLabel,
17
+ FormMessage,
18
+ } from "."
19
+
20
+ const MAX_DESCRIPTION_LENGTH = 160
21
+
22
+ const formSchema = z.object({
23
+ username: z.string().min(2, {
24
+ message: "Username must be at least 2 characters.",
25
+ }),
26
+ email: z.string().email({
27
+ message: "Please enter a valid email address.",
28
+ }),
29
+ bio: z.string().max(MAX_DESCRIPTION_LENGTH).optional(),
30
+ terms: z.boolean().refine((val) => val === true, {
31
+ message: "You must accept the terms and conditions.",
32
+ }),
33
+ })
34
+
35
+ const FormExample = ({
36
+ onSubmit = () => {},
37
+ }: {
38
+ onSubmit?: (values: z.infer<typeof formSchema>) => void
39
+ }) => {
40
+ const form = useForm<z.infer<typeof formSchema>>({
41
+ resolver: zodResolver(formSchema),
42
+ defaultValues: {
43
+ username: "",
44
+ email: "",
45
+ bio: "",
46
+ terms: false,
47
+ },
48
+ })
49
+
50
+ function handleSubmit(values: z.infer<typeof formSchema>) {
51
+ onSubmit(values)
52
+ }
53
+
54
+ return (
55
+ <Form {...form}>
56
+ <form
57
+ onSubmit={form.handleSubmit(handleSubmit)}
58
+ className="text-fm-primary w-full max-w-md space-y-6"
59
+ >
60
+ <FormField
61
+ control={form.control}
62
+ name="username"
63
+ render={({ field, fieldState }) => (
64
+ <FormItem>
65
+ <FormLabel>Username</FormLabel>
66
+ <FormControl>
67
+ <Input
68
+ placeholder="username"
69
+ variant={fieldState.error ? "error" : "default"}
70
+ {...field}
71
+ />
72
+ </FormControl>
73
+ <FormMessage />
74
+ <FormDescription>
75
+ This is your public display name.
76
+ </FormDescription>
77
+ </FormItem>
78
+ )}
79
+ />
80
+ <FormField
81
+ control={form.control}
82
+ name="email"
83
+ render={({ field, fieldState }) => {
84
+ return (
85
+ <FormItem>
86
+ <FormLabel>Email</FormLabel>
87
+ <FormControl>
88
+ <Input
89
+ type="email"
90
+ placeholder="email@example.com"
91
+ helperText={
92
+ fieldState.error
93
+ ? fieldState.error.message
94
+ : "We'll never share your email with anyone else."
95
+ }
96
+ variant={fieldState.error ? "error" : "default"}
97
+ {...field}
98
+ />
99
+ </FormControl>
100
+ </FormItem>
101
+ )
102
+ }}
103
+ />
104
+ <FormField
105
+ control={form.control}
106
+ name="bio"
107
+ render={({ field }) => (
108
+ <FormItem>
109
+ <FormControl>
110
+ <Input
111
+ placeholder="Tell us about yourself"
112
+ label="Bio"
113
+ maxLength={MAX_DESCRIPTION_LENGTH}
114
+ helperText="Your bio will appear on your profile."
115
+ {...field}
116
+ />
117
+ </FormControl>
118
+ <FormMessage />
119
+ </FormItem>
120
+ )}
121
+ />
122
+ <FormField
123
+ control={form.control}
124
+ name="terms"
125
+ render={({ field }) => (
126
+ <FormItem className="flex flex-row flex-wrap items-start space-y-0 space-x-3 rounded-md border p-4">
127
+ <FormControl>
128
+ <Checkbox
129
+ checked={field.value}
130
+ onCheckedChange={field.onChange}
131
+ />
132
+ </FormControl>
133
+ <div className="space-y-1 leading-none">
134
+ <FormLabel>Accept terms and conditions</FormLabel>
135
+ <FormDescription>
136
+ You agree to our Terms of Service and Privacy Policy.
137
+ </FormDescription>
138
+ </div>
139
+ <FormMessage />
140
+ </FormItem>
141
+ )}
142
+ />
143
+ <Button type="submit">Submit</Button>
144
+ </form>
145
+ </Form>
146
+ )
147
+ }
148
+
149
+ const meta: Meta<typeof FormExample> = {
150
+ title: "Components/UI/Form",
151
+ component: FormExample,
152
+ parameters: {
153
+ layout: "centered",
154
+ },
155
+ tags: ["autodocs"],
156
+ argTypes: {
157
+ onSubmit: { action: "submitted" },
158
+ },
159
+ }
160
+
161
+ export default meta
162
+ type Story = StoryObj<typeof FormExample>
163
+
164
+ export const Default: Story = {}
165
+
166
+ export const WithErrors: Story = {
167
+ render: () => {
168
+ const form = useForm<z.infer<typeof formSchema>>({
169
+ resolver: zodResolver(formSchema),
170
+ defaultValues: {
171
+ username: "",
172
+ email: "",
173
+ bio: "",
174
+ terms: false,
175
+ },
176
+ })
177
+
178
+ useEffect(() => {
179
+ form.setError("username", {
180
+ type: "manual",
181
+ message: "Username is already taken.",
182
+ })
183
+
184
+ form.setError("email", {
185
+ type: "manual",
186
+ message: "Invalid email address.",
187
+ })
188
+ }, [])
189
+
190
+ return (
191
+ <Form {...form}>
192
+ <form className="w-full max-w-md space-y-6">
193
+ <FormField
194
+ control={form.control}
195
+ name="username"
196
+ render={({ field }) => (
197
+ <FormItem>
198
+ <FormLabel>Username</FormLabel>
199
+ <FormControl>
200
+ <Input placeholder="username" variant="error" {...field} />
201
+ </FormControl>
202
+ <FormDescription>
203
+ This is your public display name.
204
+ </FormDescription>
205
+ <FormMessage />
206
+ </FormItem>
207
+ )}
208
+ />
209
+ <FormField
210
+ control={form.control}
211
+ name="email"
212
+ render={({ field }) => (
213
+ <FormItem>
214
+ <FormLabel>Email</FormLabel>
215
+ <FormControl>
216
+ <Input
217
+ type="email"
218
+ variant="error"
219
+ placeholder="email@example.com"
220
+ {...field}
221
+ />
222
+ </FormControl>
223
+ <FormDescription>
224
+ We'll never share your email with anyone else.
225
+ </FormDescription>
226
+ <FormMessage />
227
+ </FormItem>
228
+ )}
229
+ />
230
+ </form>
231
+ </Form>
232
+ )
233
+ },
234
+ }
235
+
236
+ export const Filled: Story = {
237
+ render: () => {
238
+ const form = useForm<z.infer<typeof formSchema>>({
239
+ resolver: zodResolver(formSchema),
240
+ defaultValues: {
241
+ username: "johndoe",
242
+ email: "john.doe@example.com",
243
+ bio: "Frontend developer with 5+ years of experience",
244
+ terms: true,
245
+ },
246
+ })
247
+
248
+ return (
249
+ <Form {...form}>
250
+ <form className="w-full max-w-md space-y-6">
251
+ <FormField
252
+ control={form.control}
253
+ name="username"
254
+ render={({ field }) => (
255
+ <FormItem>
256
+ <FormLabel>Username</FormLabel>
257
+ <FormControl>
258
+ <Input {...field} />
259
+ </FormControl>
260
+ <FormDescription>
261
+ This is your public display name.
262
+ </FormDescription>
263
+ </FormItem>
264
+ )}
265
+ />
266
+ <FormField
267
+ control={form.control}
268
+ name="email"
269
+ render={({ field }) => (
270
+ <FormItem>
271
+ <FormLabel>Email</FormLabel>
272
+ <FormControl>
273
+ <Input type="email" {...field} />
274
+ </FormControl>
275
+ <FormDescription>
276
+ We'll never share your email with anyone else.
277
+ </FormDescription>
278
+ </FormItem>
279
+ )}
280
+ />
281
+ <FormField
282
+ control={form.control}
283
+ name="bio"
284
+ render={({ field }) => (
285
+ <FormItem>
286
+ <FormLabel>Bio</FormLabel>
287
+ <FormControl>
288
+ <Input {...field} />
289
+ </FormControl>
290
+ <FormDescription>
291
+ Your bio will appear on your profile.
292
+ </FormDescription>
293
+ </FormItem>
294
+ )}
295
+ />
296
+ <FormField
297
+ control={form.control}
298
+ name="terms"
299
+ render={({ field }) => (
300
+ <FormItem className="flex flex-row items-start space-y-0 space-x-3 rounded-md border p-4">
301
+ <FormControl>
302
+ <Checkbox
303
+ checked={field.value}
304
+ onCheckedChange={field.onChange}
305
+ />
306
+ </FormControl>
307
+ <div className="space-y-1 leading-none">
308
+ <FormLabel>Accept terms and conditions</FormLabel>
309
+ <FormDescription>
310
+ You agree to our Terms of Service and Privacy Policy.
311
+ </FormDescription>
312
+ </div>
313
+ </FormItem>
314
+ )}
315
+ />
316
+ </form>
317
+ </Form>
318
+ )
319
+ },
320
+ }
@@ -0,0 +1,183 @@
1
+ import * as React from "react"
2
+ import HelperText from "@components/helper-text"
3
+ import { Label } from "@components/label"
4
+ import { cn } from "@lib/utils"
5
+ import * as LabelPrimitive from "@radix-ui/react-label"
6
+ import { Slot } from "@radix-ui/react-slot"
7
+ import {
8
+ Controller,
9
+ FormProvider,
10
+ useFormContext,
11
+ type ControllerProps,
12
+ type FieldPath,
13
+ type FieldValues,
14
+ } from "react-hook-form"
15
+
16
+ const Form = FormProvider
17
+
18
+ type FormFieldContextValue<
19
+ TFieldValues extends FieldValues = FieldValues,
20
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
21
+ > = {
22
+ name: TName
23
+ }
24
+
25
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
26
+ {} as FormFieldContextValue
27
+ )
28
+
29
+ const FormField = <
30
+ TFieldValues extends FieldValues = FieldValues,
31
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
32
+ >({
33
+ ...props
34
+ }: ControllerProps<TFieldValues, TName>) => {
35
+ return (
36
+ <FormFieldContext.Provider value={{ name: props.name }}>
37
+ <Controller {...props} />
38
+ </FormFieldContext.Provider>
39
+ )
40
+ }
41
+
42
+ const useFormField = () => {
43
+ const fieldContext = React.useContext(FormFieldContext)
44
+ const itemContext = React.useContext(FormItemContext)
45
+ const { getFieldState, formState } = useFormContext()
46
+
47
+ const fieldState = getFieldState(fieldContext.name, formState)
48
+
49
+ if (!fieldContext) {
50
+ throw new Error("useFormField should be used within <FormField>")
51
+ }
52
+
53
+ const { id } = itemContext
54
+
55
+ return {
56
+ id,
57
+ name: fieldContext.name,
58
+ formItemId: `${id}-form-item`,
59
+ formDescriptionId: `${id}-form-item-description`,
60
+ formMessageId: `${id}-form-item-message`,
61
+ ...fieldState,
62
+ }
63
+ }
64
+
65
+ type FormItemContextValue = {
66
+ id: string
67
+ }
68
+
69
+ const FormItemContext = React.createContext<FormItemContextValue>(
70
+ {} as FormItemContextValue
71
+ )
72
+
73
+ const FormItem = React.forwardRef<
74
+ HTMLDivElement,
75
+ React.HTMLAttributes<HTMLDivElement>
76
+ >(({ className, ...props }, ref) => {
77
+ const id = React.useId()
78
+
79
+ return (
80
+ <FormItemContext.Provider value={{ id }}>
81
+ <div ref={ref} className={className} {...props} />
82
+ </FormItemContext.Provider>
83
+ )
84
+ })
85
+ FormItem.displayName = "FormItem"
86
+
87
+ const FormLabel = React.forwardRef<
88
+ React.ElementRef<typeof LabelPrimitive.Root>,
89
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
90
+ >(({ className, ...props }, ref) => {
91
+ const { error, formItemId } = useFormField()
92
+
93
+ return (
94
+ <Label
95
+ ref={ref}
96
+ className={cn({ "text-fm-negative": error }, className)}
97
+ data-state={error ? "invalid" : "valid"}
98
+ htmlFor={formItemId}
99
+ {...props}
100
+ >
101
+ {props.children}
102
+ </Label>
103
+ )
104
+ })
105
+ FormLabel.displayName = "FormLabel"
106
+
107
+ const FormControl = React.forwardRef<
108
+ React.ElementRef<typeof Slot>,
109
+ React.ComponentPropsWithoutRef<typeof Slot>
110
+ >(({ ...props }, ref) => {
111
+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
112
+
113
+ return (
114
+ <Slot
115
+ ref={ref}
116
+ id={formItemId}
117
+ aria-describedby={
118
+ !error
119
+ ? `${formDescriptionId}`
120
+ : `${formDescriptionId} ${formMessageId}`
121
+ }
122
+ aria-invalid={!!error}
123
+ {...props}
124
+ />
125
+ )
126
+ })
127
+ FormControl.displayName = "FormControl"
128
+
129
+ const FormDescription = React.forwardRef<
130
+ HTMLParagraphElement,
131
+ React.HTMLAttributes<HTMLParagraphElement>
132
+ >(({ className, ...props }, ref) => {
133
+ const { formDescriptionId } = useFormField()
134
+
135
+ return (
136
+ <HelperText
137
+ ref={ref}
138
+ id={formDescriptionId}
139
+ className={className}
140
+ variant="default"
141
+ {...props}
142
+ >
143
+ {props.children}
144
+ </HelperText>
145
+ )
146
+ })
147
+ FormDescription.displayName = "FormDescription"
148
+
149
+ const FormMessage = React.forwardRef<
150
+ HTMLParagraphElement,
151
+ React.HTMLAttributes<HTMLParagraphElement>
152
+ >(({ className, children, ...props }, ref) => {
153
+ const { error, formMessageId } = useFormField()
154
+ const body = error ? String(error?.message ?? "") : children
155
+
156
+ if (!body) {
157
+ return null
158
+ }
159
+
160
+ return (
161
+ <HelperText
162
+ ref={ref}
163
+ id={formMessageId}
164
+ className={className}
165
+ variant="error"
166
+ {...props}
167
+ >
168
+ {body}
169
+ </HelperText>
170
+ )
171
+ })
172
+ FormMessage.displayName = "FormMessage"
173
+
174
+ export {
175
+ useFormField,
176
+ Form,
177
+ FormItem,
178
+ FormLabel,
179
+ FormControl,
180
+ FormDescription,
181
+ FormMessage,
182
+ FormField,
183
+ }
@@ -0,0 +1,11 @@
1
+ export const meta = {
2
+ dependencies: {
3
+ "@radix-ui/react-slot": "^1.2.3",
4
+ "react-hook-form": "^7.56.4",
5
+ "@hookform/resolvers": "^5.0.1",
6
+ zod: "^3.25.32",
7
+ },
8
+ devDependencies: {},
9
+ internalDependencies: ["label", "helper-text"],
10
+ tokens: ["--color-fm-negative"],
11
+ }