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,334 @@
1
+ import React, { useState } from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+
4
+ import CharCount from "."
5
+
6
+ const meta: Meta<typeof CharCount> = {
7
+ title: "Components/UI/CharCount",
8
+ component: CharCount,
9
+ parameters: {
10
+ layout: "centered",
11
+ },
12
+ tags: ["autodocs"],
13
+ argTypes: {
14
+ currentLength: {
15
+ control: { type: "number", min: 0 },
16
+ description: "Current character count",
17
+ },
18
+ maxLength: {
19
+ control: { type: "number", min: 0 },
20
+ description: "Maximum allowed characters (optional)",
21
+ },
22
+ className: {
23
+ control: { type: "text" },
24
+ description: "Additional CSS classes to apply",
25
+ },
26
+ "aria-live": {
27
+ control: { type: "select" },
28
+ options: ["polite", "assertive", "off"],
29
+ description: "ARIA live region announcement behavior",
30
+ },
31
+ id: {
32
+ control: { type: "text" },
33
+ description: "HTML id attribute for accessibility",
34
+ },
35
+ },
36
+ }
37
+
38
+ export default meta
39
+ type Story = StoryObj<typeof meta>
40
+
41
+ export const Default: Story = {
42
+ args: {
43
+ currentLength: 25,
44
+ maxLength: 100,
45
+ "aria-live": "polite",
46
+ },
47
+ }
48
+
49
+ export const WithoutMaxLength: Story = {
50
+ args: {
51
+ currentLength: 42,
52
+ "aria-live": "polite",
53
+ },
54
+ }
55
+
56
+ export const NearLimit: Story = {
57
+ args: {
58
+ currentLength: 92,
59
+ maxLength: 100,
60
+ "aria-live": "polite",
61
+ },
62
+ }
63
+
64
+ export const OverLimit: Story = {
65
+ args: {
66
+ currentLength: 105,
67
+ maxLength: 100,
68
+ "aria-live": "assertive",
69
+ },
70
+ }
71
+
72
+ export const Zero: Story = {
73
+ args: {
74
+ currentLength: 0,
75
+ maxLength: 50,
76
+ "aria-live": "polite",
77
+ },
78
+ }
79
+
80
+ export const ExactLimit: Story = {
81
+ args: {
82
+ currentLength: 100,
83
+ maxLength: 100,
84
+ "aria-live": "polite",
85
+ },
86
+ }
87
+
88
+ export const LargeNumbers: Story = {
89
+ args: {
90
+ currentLength: 2847,
91
+ maxLength: 5000,
92
+ "aria-live": "polite",
93
+ },
94
+ }
95
+
96
+ export const WithCustomClassName: Story = {
97
+ args: {
98
+ currentLength: 30,
99
+ maxLength: 100,
100
+ className: "!text-blue-600 !text-lg",
101
+ "aria-live": "polite",
102
+ },
103
+ }
104
+
105
+ // Showcase all states together
106
+ export const AllStates: Story = {
107
+ render: () => (
108
+ <div className="w-96 space-y-6">
109
+ <div>
110
+ <h3 className="mb-2 text-sm font-medium">Normal State (25/100)</h3>
111
+ <CharCount currentLength={25} maxLength={100} />
112
+ </div>
113
+
114
+ <div>
115
+ <h3 className="mb-2 text-sm font-medium">Warning State (92/100)</h3>
116
+ <CharCount currentLength={92} maxLength={100} />
117
+ </div>
118
+
119
+ <div>
120
+ <h3 className="mb-2 text-sm font-medium">Error State (105/100)</h3>
121
+ <CharCount currentLength={105} maxLength={100} />
122
+ </div>
123
+
124
+ <div>
125
+ <h3 className="mb-2 text-sm font-medium">Without Max Length (42)</h3>
126
+ <CharCount currentLength={42} />
127
+ </div>
128
+
129
+ <div>
130
+ <h3 className="mb-2 text-sm font-medium">Zero Characters (0/50)</h3>
131
+ <CharCount currentLength={0} maxLength={50} />
132
+ </div>
133
+
134
+ <div>
135
+ <h3 className="mb-2 text-sm font-medium">Exact Limit (100/100)</h3>
136
+ <CharCount currentLength={100} maxLength={100} />
137
+ </div>
138
+ </div>
139
+ ),
140
+ }
141
+
142
+ // Interactive example with textarea
143
+ export const WithTextarea: Story = {
144
+ render: () => {
145
+ const [text, setText] = useState("")
146
+ const maxLength = 140
147
+
148
+ return (
149
+ <div className="w-96 space-y-4">
150
+ <div>
151
+ <label className="mb-2 block text-sm font-medium">
152
+ Tweet Message
153
+ </label>
154
+ <textarea
155
+ value={text}
156
+ onChange={(e) => setText(e.target.value)}
157
+ className="w-full resize-none rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none"
158
+ placeholder="What's happening?"
159
+ rows={4}
160
+ aria-describedby="tweet-count"
161
+ />
162
+ <div className="mt-2 flex justify-end">
163
+ <CharCount
164
+ currentLength={text.length}
165
+ maxLength={maxLength}
166
+ id="tweet-count"
167
+ aria-live="polite"
168
+ />
169
+ </div>
170
+ </div>
171
+ </div>
172
+ )
173
+ },
174
+ }
175
+
176
+ // Interactive example with input
177
+ export const WithInput: Story = {
178
+ render: () => {
179
+ const [username, setUsername] = useState("")
180
+ const maxLength = 20
181
+
182
+ return (
183
+ <div className="w-96 space-y-4">
184
+ <div>
185
+ <label className="mb-2 block text-sm font-medium">Username</label>
186
+ <input
187
+ type="text"
188
+ value={username}
189
+ onChange={(e) => setUsername(e.target.value)}
190
+ className="w-full rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none"
191
+ placeholder="Enter username"
192
+ aria-describedby="username-count"
193
+ />
194
+ <div className="mt-2 flex items-center justify-between">
195
+ <span className="text-sm text-gray-500">
196
+ Must be unique and memorable
197
+ </span>
198
+ <CharCount
199
+ currentLength={username.length}
200
+ maxLength={maxLength}
201
+ id="username-count"
202
+ aria-live="polite"
203
+ />
204
+ </div>
205
+ </div>
206
+ </div>
207
+ )
208
+ },
209
+ }
210
+
211
+ // Multiple inputs with different limits
212
+ export const MultipleInputs: Story = {
213
+ render: () => {
214
+ const [title, setTitle] = useState("")
215
+ const [description, setDescription] = useState("")
216
+ const [tags, setTags] = useState("")
217
+
218
+ return (
219
+ <div className="w-96 space-y-6">
220
+ <div>
221
+ <label className="mb-2 block text-sm font-medium">Post Title</label>
222
+ <input
223
+ type="text"
224
+ value={title}
225
+ onChange={(e) => setTitle(e.target.value)}
226
+ className="w-full rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none"
227
+ placeholder="Enter post title"
228
+ aria-describedby="title-count"
229
+ />
230
+ <div className="mt-1 flex justify-end">
231
+ <CharCount
232
+ currentLength={title.length}
233
+ maxLength={60}
234
+ id="title-count"
235
+ />
236
+ </div>
237
+ </div>
238
+
239
+ <div>
240
+ <label className="mb-2 block text-sm font-medium">Description</label>
241
+ <textarea
242
+ value={description}
243
+ onChange={(e) => setDescription(e.target.value)}
244
+ className="w-full resize-none rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none"
245
+ placeholder="Describe your post"
246
+ rows={3}
247
+ aria-describedby="description-count"
248
+ />
249
+ <div className="mt-1 flex justify-end">
250
+ <CharCount
251
+ currentLength={description.length}
252
+ maxLength={200}
253
+ id="description-count"
254
+ />
255
+ </div>
256
+ </div>
257
+
258
+ <div>
259
+ <label className="mb-2 block text-sm font-medium">
260
+ Tags (comma separated)
261
+ </label>
262
+ <input
263
+ type="text"
264
+ value={tags}
265
+ onChange={(e) => setTags(e.target.value)}
266
+ className="w-full rounded-md border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none"
267
+ placeholder="react, typescript, ui"
268
+ aria-describedby="tags-count"
269
+ />
270
+ <div className="mt-1 flex items-center justify-between">
271
+ <span className="text-xs text-gray-500">
272
+ Separate tags with commas
273
+ </span>
274
+ <CharCount
275
+ currentLength={tags.length}
276
+ maxLength={50}
277
+ id="tags-count"
278
+ />
279
+ </div>
280
+ </div>
281
+ </div>
282
+ )
283
+ },
284
+ }
285
+
286
+ // Accessibility demonstration
287
+ export const AccessibilityExample: Story = {
288
+ render: () => {
289
+ const [message, setMessage] = useState("")
290
+ const maxLength = 100
291
+ const isOverLimit = message.length > maxLength
292
+
293
+ return (
294
+ <div className="w-96 space-y-4">
295
+ <div>
296
+ <label
297
+ htmlFor="accessible-message"
298
+ className="mb-2 block text-sm font-medium"
299
+ >
300
+ Message *
301
+ </label>
302
+ <textarea
303
+ id="accessible-message"
304
+ value={message}
305
+ onChange={(e) => setMessage(e.target.value)}
306
+ className={`w-full resize-none rounded-md border px-3 py-2 focus:ring-2 focus:outline-none ${
307
+ isOverLimit
308
+ ? "border-red-300 focus:ring-red-500"
309
+ : "border-gray-300 focus:ring-blue-500"
310
+ }`}
311
+ placeholder="Enter your message"
312
+ rows={4}
313
+ aria-describedby="message-count message-help"
314
+ aria-invalid={isOverLimit}
315
+ required
316
+ />
317
+ <div className="mt-2 flex items-start justify-between">
318
+ <div id="message-help" className="text-sm text-gray-500">
319
+ {isOverLimit
320
+ ? "Message is too long. Please shorten it."
321
+ : "Share your thoughts with the community."}
322
+ </div>
323
+ <CharCount
324
+ currentLength={message.length}
325
+ maxLength={maxLength}
326
+ id="message-count"
327
+ aria-live={isOverLimit ? "assertive" : "polite"}
328
+ />
329
+ </div>
330
+ </div>
331
+ </div>
332
+ )
333
+ },
334
+ }
@@ -0,0 +1,51 @@
1
+ import React, { forwardRef } from "react"
2
+ import { cn } from "@lib/utils"
3
+
4
+ interface CharCountProps {
5
+ currentLength: number
6
+ maxLength?: number
7
+ className?: string
8
+ "aria-live"?: "polite" | "assertive" | "off"
9
+ id?: string
10
+ }
11
+
12
+ const CharCount = forwardRef<HTMLSpanElement, CharCountProps>(
13
+ (
14
+ {
15
+ currentLength,
16
+ maxLength,
17
+ className = "",
18
+ "aria-live": ariaLive = "polite",
19
+ id,
20
+ },
21
+ ref
22
+ ) => {
23
+ // Determine if we should show warning/error styling
24
+ const isNearLimit = maxLength && currentLength > maxLength * 0.9
25
+ const isOverLimit = maxLength && currentLength > maxLength
26
+
27
+ return (
28
+ <span
29
+ ref={ref}
30
+ id={id}
31
+ className={cn(
32
+ "leading-fm-sm font-fm-brand [font-size:var(--text-fm-sm)] tracking-wide uppercase",
33
+ {
34
+ "text-fm-warning": isNearLimit && !isOverLimit,
35
+ "text-fm-negative": isOverLimit,
36
+ "text-fm-tertiary": !isNearLimit && !isOverLimit,
37
+ },
38
+ className
39
+ )}
40
+ aria-live={ariaLive}
41
+ >
42
+ {maxLength ? `${currentLength}/${maxLength}` : currentLength}
43
+ </span>
44
+ )
45
+ }
46
+ )
47
+ CharCount.displayName = "CharCount"
48
+
49
+ export default CharCount
50
+ export { CharCount }
51
+ export type { CharCountProps }
@@ -0,0 +1,13 @@
1
+ export const meta = {
2
+ dependencies: {},
3
+ devDependencies: {},
4
+ internalDependencies: [],
5
+ tokens: [
6
+ "--color-fm-text-warning",
7
+ "--color-fm-text-negative",
8
+ "--color-fm-text-tertiary",
9
+ "--text-fm-sm",
10
+ "--leading-fm-sm",
11
+ "--font-fm-brand",
12
+ ],
13
+ }
@@ -0,0 +1,209 @@
1
+ import React from "react"
2
+ import { Button } from "@components/button"
3
+ import { Label } from "@components/label"
4
+ import type { Meta, StoryObj } from "@storybook/react"
5
+
6
+ import { Checkbox } from "."
7
+
8
+ const meta: Meta<typeof Checkbox> = {
9
+ title: "Components/UI/Checkbox",
10
+ component: Checkbox,
11
+ parameters: {
12
+ layout: "centered",
13
+ },
14
+ tags: ["autodocs"],
15
+ }
16
+
17
+ export default meta
18
+ type Story = StoryObj<typeof Checkbox>
19
+
20
+ export const Default: Story = {
21
+ render: () => (
22
+ <div className="flex items-center space-x-2">
23
+ <Checkbox id="terms" />
24
+ <Label htmlFor="terms">Accept terms and conditions</Label>
25
+ </div>
26
+ ),
27
+ }
28
+
29
+ export const States: Story = {
30
+ render: () => (
31
+ <div className="text-fm-primary flex flex-col space-y-8">
32
+ {/* Unchecked */}
33
+ <div className="space-y-2">
34
+ <h3 className="text-lg font-medium">Unchecked</h3>
35
+ <div className="flex items-center space-x-2">
36
+ <Checkbox id="unchecked" />
37
+ <Label htmlFor="unchecked">Unchecked</Label>
38
+ </div>
39
+ </div>
40
+
41
+ {/* Checked */}
42
+ <div className="space-y-2">
43
+ <h3 className="text-lg font-medium">Checked</h3>
44
+ <div className="flex items-center space-x-2">
45
+ <Checkbox id="checked" defaultChecked />
46
+ <Label htmlFor="checked">Checked</Label>
47
+ </div>
48
+ </div>
49
+
50
+ {/* Indeterminate */}
51
+ <div className="space-y-2">
52
+ <h3 className="text-lg font-medium">Indeterminate</h3>
53
+ <div className="flex items-center space-x-2">
54
+ <Checkbox id="indeterminate" indeterminate />
55
+ <Label htmlFor="indeterminate">Indeterminate</Label>
56
+ </div>
57
+ </div>
58
+
59
+ {/* Disabled Unchecked */}
60
+ <div className="space-y-2">
61
+ <h3 className="text-lg font-medium">Disabled Unchecked</h3>
62
+ <div className="flex items-center space-x-2">
63
+ <Checkbox id="disabled" disabled />
64
+ <Label htmlFor="disabled" disabled>
65
+ Disabled Unchecked
66
+ </Label>
67
+ </div>
68
+ </div>
69
+
70
+ {/* Disabled Checked */}
71
+ <div className="space-y-2">
72
+ <h3 className="text-lg font-medium">Disabled Checked</h3>
73
+ <div className="flex items-center space-x-2">
74
+ <Checkbox id="disabled-checked" disabled defaultChecked />
75
+ <Label htmlFor="disabled-checked" disabled>
76
+ Disabled Checked
77
+ </Label>
78
+ </div>
79
+ </div>
80
+
81
+ {/* Disabled Indeterminate Unchecked */}
82
+ <div className="space-y-2">
83
+ <h3 className="text-lg font-medium">
84
+ Disabled Indeterminate Unchecked
85
+ </h3>
86
+ <div className="flex items-center space-x-2">
87
+ <Checkbox id="disabled-indeterminate" disabled indeterminate />
88
+ <Label htmlFor="disabled-indeterminate" disabled>
89
+ Disabled Indeterminate Unchecked
90
+ </Label>
91
+ </div>
92
+ </div>
93
+
94
+ {/* Disabled Indeterminate Checked */}
95
+ <div className="space-y-2">
96
+ <h3 className="text-lg font-medium">Disabled Indeterminate Checked</h3>
97
+ <div className="flex items-center space-x-2">
98
+ <Checkbox
99
+ id="disabled-indeterminate"
100
+ disabled
101
+ indeterminate
102
+ defaultChecked
103
+ />
104
+ <Label htmlFor="disabled-indeterminate" disabled>
105
+ Disabled Indeterminate Checked
106
+ </Label>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ ),
111
+ }
112
+
113
+ export const WithForm: Story = {
114
+ render: () => (
115
+ <form
116
+ onSubmit={(e) => {
117
+ e.preventDefault()
118
+ alert("Form submitted!")
119
+ }}
120
+ className="space-y-6"
121
+ >
122
+ <div className="space-y-4">
123
+ <div className="flex items-center space-x-2">
124
+ <Checkbox id="newsletter" />
125
+ <Label htmlFor="newsletter">Subscribe to newsletter</Label>
126
+ </div>
127
+ <div className="flex items-center space-x-2">
128
+ <Checkbox id="marketing" />
129
+ <Label htmlFor="marketing">Receive marketing emails</Label>
130
+ </div>
131
+ </div>
132
+ <Button type="submit">Submit</Button>
133
+ </form>
134
+ ),
135
+ }
136
+
137
+ export const IndeterminateExample: Story = {
138
+ render: () => {
139
+ // This example shows a parent-child relationship where the parent checkbox
140
+ // becomes indeterminate when some but not all children are checked
141
+ const [checkedItems, setCheckedItems] = React.useState({
142
+ option1: false,
143
+ option2: false,
144
+ option3: false,
145
+ })
146
+
147
+ const allChecked = Object.values(checkedItems).every(Boolean)
148
+ const indeterminate =
149
+ Object.values(checkedItems).some(Boolean) && !allChecked
150
+
151
+ const handleParentChange = () => {
152
+ const newValue = !allChecked
153
+ setCheckedItems({
154
+ option1: newValue,
155
+ option2: newValue,
156
+ option3: newValue,
157
+ })
158
+ }
159
+
160
+ const handleChildChange = (option: keyof typeof checkedItems) => {
161
+ setCheckedItems((prev) => ({
162
+ ...prev,
163
+ [option]: !prev[option],
164
+ }))
165
+ }
166
+
167
+ return (
168
+ <div className="space-y-4">
169
+ <div className="flex items-center space-x-2">
170
+ <Checkbox
171
+ id="parent"
172
+ checked={allChecked || indeterminate}
173
+ indeterminate={indeterminate}
174
+ onCheckedChange={handleParentChange}
175
+ />
176
+ <Label htmlFor="parent" className="font-medium">
177
+ Select all options
178
+ </Label>
179
+ </div>
180
+ <div className="ml-6 space-y-2">
181
+ <div className="flex items-center space-x-2">
182
+ <Checkbox
183
+ id="option1"
184
+ checked={checkedItems.option1}
185
+ onCheckedChange={() => handleChildChange("option1")}
186
+ />
187
+ <Label htmlFor="option1">Option 1</Label>
188
+ </div>
189
+ <div className="flex items-center space-x-2">
190
+ <Checkbox
191
+ id="option2"
192
+ checked={checkedItems.option2}
193
+ onCheckedChange={() => handleChildChange("option2")}
194
+ />
195
+ <Label htmlFor="option2">Option 2</Label>
196
+ </div>
197
+ <div className="flex items-center space-x-2">
198
+ <Checkbox
199
+ id="option3"
200
+ checked={checkedItems.option3}
201
+ onCheckedChange={() => handleChildChange("option3")}
202
+ />
203
+ <Label htmlFor="option3">Option 3</Label>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ )
208
+ },
209
+ }
@@ -0,0 +1,34 @@
1
+ import * as React from "react"
2
+ import { TickIcon } from "@icons/tick-icon"
3
+ import { cn } from "@lib/utils"
4
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5
+
6
+ type CheckboxProps = React.ComponentPropsWithoutRef<
7
+ typeof CheckboxPrimitive.Root
8
+ > & {
9
+ indeterminate?: boolean
10
+ }
11
+
12
+ const Checkbox = React.forwardRef<
13
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
14
+ CheckboxProps
15
+ >(({ className, indeterminate = false, ...props }, ref) => (
16
+ <CheckboxPrimitive.Root
17
+ ref={ref}
18
+ className={cn(
19
+ "data-[state=checked]:not-[:disabled]:border-fm-divider-positive data-[state=checked]:not-[:disabled]:bg-fm-green-50 data-[state=unchecked]:not-[:disabled]:border-fm-divider-primary data-[state=unchecked]:not-[:disabled]:bg-fm-surface-primary data-[state=unchecked]:disabled:border-fm-divider-tertiary data-[state=unchecked]:disabled:bg-fm-surface-secondary data-[state=checked]:disabled:border-fm-green-100 data-[state=checked]:disabled:bg-fm-green-50 text-fm-icon-active disabled:text-fm-icon-inactive focus-visible:ring-fm-primary focus-visible:ring-offset-fm-green-50 hover:bg-fm-surface-secondary data-[state=unchecked]:not-[:disabled]:hover:bg-fm-surface-secondary rounded-fm-l size-8 border border-solid transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
20
+ "data-[state=unchecked]:not-[:disabled]:hover:border-fm-divider-primary disabled:cursor-not-allowed",
21
+ className
22
+ )}
23
+ {...props}
24
+ >
25
+ <CheckboxPrimitive.Indicator className="flex items-center justify-center text-current">
26
+ {indeterminate ? <span className="h-0.5 w-4 bg-current" /> : <TickIcon />}
27
+ </CheckboxPrimitive.Indicator>
28
+ </CheckboxPrimitive.Root>
29
+ ))
30
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName
31
+
32
+ // Add a custom prop type for the indeterminate state
33
+ export type { CheckboxProps }
34
+ export { Checkbox }
@@ -0,0 +1,19 @@
1
+ export const meta = {
2
+ dependencies: {
3
+ "@radix-ui/react-checkbox": "^1.3.2",
4
+ },
5
+ devDependencies: {},
6
+ internalDependencies: ["tick-icon"],
7
+ tokens: [
8
+ "--color-fm-divider-positive",
9
+ "--color-fm-divider-primary",
10
+ "--color-fm-divider-tertiary",
11
+ "--color-fm-green-50",
12
+ "--color-fm-green-100",
13
+ "--color-fm-icon-active",
14
+ "--color-fm-icon-inactive",
15
+ "--color-fm-primary",
16
+ "--color-fm-surface-primary",
17
+ "--color-fm-surface-secondary",
18
+ ],
19
+ }