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,480 @@
1
+ import React, {
2
+ forwardRef,
3
+ ForwardRefExoticComponent,
4
+ ReactNode,
5
+ RefAttributes,
6
+ useCallback,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react"
11
+ import { CharCount } from "@components/char-count"
12
+ import { HelperText } from "@components/helper-text"
13
+ import { Label } from "@components/label"
14
+ import { cn } from "@lib/utils"
15
+ import { cva } from "class-variance-authority"
16
+
17
+ // Types
18
+ type TextAreaVariant = "default" | "error" | "warning" | "success"
19
+ type TextAreaDecoration = "underline" | "outline" | "filled"
20
+
21
+ interface TextAreaBaseProps {
22
+ variant?: TextAreaVariant
23
+ decoration?: TextAreaDecoration
24
+ disabled?: boolean
25
+ className?: string
26
+ placeholder?: string
27
+ value?: string
28
+ onChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
29
+ onBlur?: (e: React.FocusEvent<HTMLTextAreaElement>) => void
30
+ onFocus?: (e: React.FocusEvent<HTMLTextAreaElement>) => void
31
+ id?: string
32
+ name?: string
33
+ required?: boolean
34
+ maxLength?: number
35
+ rows?: number
36
+ autoGrow?: boolean
37
+ maxHeight?: number
38
+ minHeight?: number
39
+ unstyled?: boolean
40
+ "aria-describedby"?: string
41
+ "aria-invalid"?: boolean
42
+ "aria-labelledby"?: string
43
+ }
44
+
45
+ interface TextAreaProps extends TextAreaBaseProps {
46
+ label?: ReactNode
47
+ helperText?: ReactNode
48
+ fullWidth?: boolean
49
+ showCharCount?: boolean
50
+ classes?: {
51
+ root?: string
52
+ label?: string
53
+ wrapper?: string
54
+ textarea?: string
55
+ helperText?: string
56
+ charCount?: string
57
+ }
58
+ }
59
+
60
+ interface TextAreaComponent
61
+ extends ForwardRefExoticComponent<
62
+ TextAreaProps & RefAttributes<HTMLTextAreaElement>
63
+ > {
64
+ Root: typeof TextAreaRoot
65
+ Label: typeof TextAreaLabel
66
+ Wrapper: typeof TextAreaWrapper
67
+ Base: typeof TextAreaBase
68
+ HelperText: typeof HelperText
69
+ CharCount: typeof CharCount
70
+ }
71
+
72
+ // CVA for textarea variants with decoration support
73
+ const textareaVariants = cva(
74
+ "block w-full focus:outline-none transition-all duration-200 border-solid resize-none overflow-hidden tracking-wide placeholder:text-fm-placeholder font-fm-text text-fm-primary",
75
+ {
76
+ variants: {
77
+ variant: {
78
+ default: "border-fm-divider-primary focus:border-fm-divider-contrast",
79
+ error: "border-fm-divider-negative focus:border-fm-divider-negative",
80
+ warning: "border-fm-divider-warning focus:border-fm-divider-warning",
81
+ success: "border-fm-divider-positive focus:border-fm-divider-positive",
82
+ },
83
+ decoration: {
84
+ underline:
85
+ "border-b leading-fm-xl [font-size:var(--text-fm-xl)] px-0 py-2",
86
+ outline:
87
+ "border rounded-fm-s leading-fm-md [font-size:var(--text-fm-md)] px-3 py-2",
88
+ filled:
89
+ "rounded-fm-s bg-fm-surface-frosted/20 border leading-fm-md [font-size:var(--text-fm-md)] px-4 py-2",
90
+ },
91
+ state: {
92
+ default: "",
93
+ focused: "",
94
+ disabled:
95
+ "border-fm-divider-tertiary !text-fm-inactive cursor-not-allowed",
96
+ },
97
+ },
98
+ defaultVariants: {
99
+ variant: "default",
100
+ decoration: "filled",
101
+ state: "default",
102
+ },
103
+ }
104
+ )
105
+
106
+ // Auto-grow hook
107
+ const useAutoGrow = (
108
+ textareaRef: React.RefObject<HTMLTextAreaElement>,
109
+ value: string,
110
+ minHeight?: number,
111
+ maxHeight?: number,
112
+ autoGrow: boolean = true
113
+ ) => {
114
+ const adjustHeight = useCallback(() => {
115
+ const textarea = textareaRef.current
116
+ if (!textarea || !autoGrow) return
117
+
118
+ // Reset height to auto to get the correct scrollHeight
119
+ textarea.style.height = "auto"
120
+
121
+ let newHeight = textarea.scrollHeight
122
+
123
+ // Apply min height constraint
124
+ if (minHeight && newHeight < minHeight) {
125
+ newHeight = minHeight
126
+ }
127
+
128
+ // Apply max height constraint
129
+ if (maxHeight && newHeight > maxHeight) {
130
+ newHeight = maxHeight
131
+ textarea.style.overflowY = "auto"
132
+ } else {
133
+ textarea.style.overflowY = "hidden"
134
+ }
135
+
136
+ textarea.style.height = `${newHeight}px`
137
+ }, [textareaRef, minHeight, maxHeight, autoGrow])
138
+
139
+ useEffect(() => {
140
+ adjustHeight()
141
+ }, [value, adjustHeight])
142
+
143
+ useEffect(() => {
144
+ const textarea = textareaRef.current
145
+ if (!textarea || !autoGrow) return
146
+
147
+ // Set initial min height
148
+ if (minHeight) {
149
+ textarea.style.minHeight = `${minHeight}px`
150
+ }
151
+
152
+ adjustHeight()
153
+
154
+ // Handle window resize
155
+ const handleResize = () => adjustHeight()
156
+ window.addEventListener("resize", handleResize)
157
+
158
+ return () => {
159
+ window.removeEventListener("resize", handleResize)
160
+ }
161
+ }, [adjustHeight, minHeight, autoGrow])
162
+
163
+ return adjustHeight
164
+ }
165
+
166
+ // Atomic components
167
+
168
+ // Root container
169
+ const TextAreaRoot = forwardRef<
170
+ HTMLDivElement,
171
+ React.PropsWithChildren<{
172
+ className?: string
173
+ fullWidth?: boolean
174
+ }>
175
+ >(({ children, className = "", fullWidth = false }, ref) => (
176
+ <div
177
+ ref={ref}
178
+ className={cn(
179
+ "space-y-1",
180
+ {
181
+ "w-full": fullWidth,
182
+ },
183
+ className
184
+ )}
185
+ >
186
+ {children}
187
+ </div>
188
+ ))
189
+ TextAreaRoot.displayName = "TextAreaRoot"
190
+
191
+ // Label component (wrapper around existing Label)
192
+ const TextAreaLabel = forwardRef<
193
+ HTMLLabelElement,
194
+ {
195
+ htmlFor?: string
196
+ className?: string
197
+ children: ReactNode
198
+ disabled?: boolean
199
+ required?: boolean
200
+ }
201
+ >((props, ref) => {
202
+ return <Label ref={ref} {...props} />
203
+ })
204
+ TextAreaLabel.displayName = "TextAreaLabel"
205
+
206
+ // Wrapper for textarea with relative positioning
207
+ const TextAreaWrapper = forwardRef<
208
+ HTMLDivElement,
209
+ React.PropsWithChildren<{ className?: string }>
210
+ >(({ children, className = "" }, ref) => (
211
+ <div ref={ref} className={cn("relative", className)}>
212
+ {children}
213
+ </div>
214
+ ))
215
+ TextAreaWrapper.displayName = "TextAreaWrapper"
216
+
217
+ // Base textarea component without any wrapper elements
218
+ const TextAreaBase = forwardRef<HTMLTextAreaElement, TextAreaBaseProps>(
219
+ (
220
+ {
221
+ variant = "default",
222
+ decoration = "filled",
223
+ disabled = false,
224
+ className = "",
225
+ placeholder = "",
226
+ value,
227
+ onChange,
228
+ onBlur,
229
+ onFocus,
230
+ id,
231
+ name,
232
+ required = false,
233
+ maxLength,
234
+ rows = 3,
235
+ autoGrow = true,
236
+ maxHeight,
237
+ minHeight,
238
+ unstyled,
239
+ "aria-describedby": ariaDescribedBy,
240
+ "aria-invalid": ariaInvalid,
241
+ "aria-labelledby": ariaLabelledBy,
242
+ ...props
243
+ },
244
+ ref
245
+ ) => {
246
+ const [isFocused, setIsFocused] = useState(false)
247
+ const [textareaValue, setTextareaValue] = useState(value || "")
248
+ const internalRef = useRef<HTMLTextAreaElement>(null)
249
+ const textareaRef =
250
+ (ref as React.RefObject<HTMLTextAreaElement>) || internalRef
251
+
252
+ // Auto-grow functionality
253
+ useAutoGrow(
254
+ textareaRef,
255
+ value !== undefined ? value : textareaValue,
256
+ minHeight,
257
+ maxHeight,
258
+ autoGrow
259
+ )
260
+
261
+ // Handle focus state
262
+ const handleFocus = (e: React.FocusEvent<HTMLTextAreaElement>) => {
263
+ setIsFocused(true)
264
+ if (onFocus) onFocus(e)
265
+ }
266
+
267
+ // Handle blur state
268
+ const handleBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
269
+ setIsFocused(false)
270
+ if (onBlur) onBlur(e)
271
+ }
272
+
273
+ // Handle textarea change
274
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
275
+ const newValue = e.target.value
276
+
277
+ // Respect maxLength
278
+ if (maxLength && newValue.length > maxLength) {
279
+ return
280
+ }
281
+
282
+ setTextareaValue(newValue)
283
+ if (onChange) onChange(e)
284
+ }
285
+
286
+ // Determine focus state
287
+ const state = disabled ? "disabled" : isFocused ? "focused" : "default"
288
+
289
+ // Apply styles only if not unstyled
290
+ const textareaClassName = unstyled
291
+ ? className
292
+ : textareaVariants({
293
+ variant,
294
+ decoration,
295
+ state,
296
+ className,
297
+ })
298
+
299
+ return (
300
+ <textarea
301
+ ref={textareaRef}
302
+ id={id}
303
+ name={name}
304
+ className={textareaClassName}
305
+ placeholder={placeholder}
306
+ disabled={disabled}
307
+ value={value !== undefined ? value : textareaValue}
308
+ onChange={handleChange}
309
+ onFocus={handleFocus}
310
+ onBlur={handleBlur}
311
+ required={required}
312
+ rows={autoGrow ? 1 : rows}
313
+ maxLength={maxLength}
314
+ aria-describedby={ariaDescribedBy}
315
+ aria-invalid={ariaInvalid || variant === "error"}
316
+ aria-labelledby={ariaLabelledBy}
317
+ {...props}
318
+ />
319
+ )
320
+ }
321
+ )
322
+ TextAreaBase.displayName = "TextAreaBase"
323
+
324
+ // Main TextArea component using composition
325
+ const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
326
+ (
327
+ {
328
+ label,
329
+ helperText,
330
+ placeholder = "",
331
+ variant = "default",
332
+ decoration = "filled",
333
+ disabled = false,
334
+ required = false,
335
+ fullWidth = false,
336
+ className = "",
337
+ value,
338
+ onChange,
339
+ onBlur,
340
+ onFocus,
341
+ id,
342
+ name,
343
+ rows = 3,
344
+ maxHeight,
345
+ minHeight,
346
+ maxLength,
347
+ showCharCount = false,
348
+ autoGrow = true,
349
+ "aria-describedby": ariaDescribedBy,
350
+ "aria-invalid": ariaInvalid,
351
+ "aria-labelledby": ariaLabelledBy,
352
+ classes,
353
+ unstyled = false,
354
+ ...props
355
+ },
356
+ ref
357
+ ) => {
358
+ const [textareaValue, setTextareaValue] = useState(value || "")
359
+
360
+ // Generate unique IDs for accessibility
361
+ const textareaId =
362
+ id || `textarea-${Math.random().toString(36).substr(2, 9)}`
363
+ const helperTextId = helperText ? `${textareaId}-helper` : undefined
364
+ const charCountId = showCharCount ? `${textareaId}-char-count` : undefined
365
+
366
+ // Handle textarea change
367
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
368
+ const newValue = e.target.value
369
+
370
+ // Respect maxLength
371
+ if (maxLength && newValue.length > maxLength) {
372
+ return
373
+ }
374
+
375
+ setTextareaValue(newValue)
376
+ if (onChange) onChange(e)
377
+ }
378
+
379
+ // Current character count
380
+ const currentValue = value !== undefined ? value : textareaValue
381
+ const currentLength = currentValue.length
382
+
383
+ // Build aria-describedby
384
+ const describedByIds = [ariaDescribedBy, helperTextId, charCountId]
385
+ .filter(Boolean)
386
+ .join(" ")
387
+
388
+ return (
389
+ <TextAreaRoot
390
+ fullWidth={fullWidth}
391
+ className={cn(className, classes?.root)}
392
+ >
393
+ <div className="flex items-center justify-between gap-2">
394
+ {/* Label */}
395
+ {label && (
396
+ <TextAreaLabel
397
+ htmlFor={textareaId}
398
+ disabled={disabled}
399
+ required={required}
400
+ className={classes?.label}
401
+ >
402
+ {label}
403
+ </TextAreaLabel>
404
+ )}
405
+ {showCharCount && (
406
+ <CharCount
407
+ currentLength={currentLength}
408
+ maxLength={maxLength}
409
+ id={charCountId}
410
+ className="ml-auto"
411
+ />
412
+ )}
413
+ </div>
414
+
415
+ {/* TextArea wrapper */}
416
+ <TextAreaWrapper className={classes?.wrapper}>
417
+ {/* TextArea element */}
418
+ <TextAreaBase
419
+ ref={ref}
420
+ id={textareaId}
421
+ name={name}
422
+ variant={variant}
423
+ decoration={decoration}
424
+ disabled={disabled}
425
+ placeholder={placeholder}
426
+ value={value}
427
+ onChange={handleChange}
428
+ onBlur={onBlur}
429
+ onFocus={onFocus}
430
+ required={required}
431
+ rows={rows}
432
+ maxHeight={maxHeight}
433
+ minHeight={minHeight}
434
+ maxLength={maxLength}
435
+ autoGrow={autoGrow}
436
+ aria-describedby={describedByIds || undefined}
437
+ aria-invalid={ariaInvalid || variant === "error"}
438
+ aria-labelledby={ariaLabelledBy}
439
+ className={unstyled ? className : classes?.textarea}
440
+ unstyled={unstyled}
441
+ {...props}
442
+ />
443
+ </TextAreaWrapper>
444
+
445
+ {/* Helper text and character count row */}
446
+ {helperText && (
447
+ <HelperText variant={variant} id={helperTextId} disabled={disabled}>
448
+ {helperText}
449
+ </HelperText>
450
+ )}
451
+ </TextAreaRoot>
452
+ )
453
+ }
454
+ ) as TextAreaComponent
455
+
456
+ TextArea.displayName = "TextArea"
457
+
458
+ // Compose TextArea with its atomic components
459
+ TextArea.Root = TextAreaRoot
460
+ TextArea.Label = TextAreaLabel
461
+ TextArea.Wrapper = TextAreaWrapper
462
+ TextArea.Base = TextAreaBase
463
+ TextArea.HelperText = HelperText
464
+ TextArea.CharCount = CharCount
465
+
466
+ export default TextArea
467
+ export {
468
+ TextAreaRoot,
469
+ TextAreaLabel,
470
+ TextAreaWrapper,
471
+ TextAreaBase,
472
+ textareaVariants,
473
+ }
474
+ export type {
475
+ TextAreaProps,
476
+ TextAreaVariant,
477
+ TextAreaComponent,
478
+ TextAreaBaseProps,
479
+ TextAreaDecoration,
480
+ }
@@ -0,0 +1,23 @@
1
+ export const meta = {
2
+ dependencies: {},
3
+ devDependencies: {},
4
+ internalDependencies: ["char-count", "helper-text", "label"],
5
+ tokens: [
6
+ "--color-fm-divider-contrast",
7
+ "--color-fm-divider-negative",
8
+ "--color-fm-divider-positive",
9
+ "--color-fm-divider-primary",
10
+ "--color-fm-divider-tertiary",
11
+ "--color-fm-divider-warning",
12
+ "--color-fm-inactive",
13
+ "--color-fm-placeholder",
14
+ "--color-fm-primary",
15
+ "--color-fm-surface-frosted",
16
+ "--font-fm-text",
17
+ "--leading-fm-md",
18
+ "--leading-fm-xl",
19
+ "--radius-fm-s",
20
+ "--text-fm-md",
21
+ "--text-fm-xl",
22
+ ],
23
+ }