@sovereignfs/ui 0.54.0 → 0.89.1

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 (473) hide show
  1. package/LICENSE +373 -661
  2. package/NOTICE +8 -0
  3. package/README.md +4 -6
  4. package/dist/Accordion.module.css +1 -1
  5. package/dist/Alert.module.css +1 -1
  6. package/dist/AppsLauncher.module.css +102 -0
  7. package/dist/Avatar.module.css +1 -1
  8. package/dist/Badge.module.css +30 -7
  9. package/dist/BalanceChip.module.css +1 -1
  10. package/dist/Button.module.css +23 -2
  11. package/dist/Card.module.css +7 -1
  12. package/dist/CardTile.module.css +187 -0
  13. package/dist/CheckableListRow.module.css +8 -0
  14. package/dist/Checkbox.module.css +8 -0
  15. package/dist/CodeTextarea.module.css +7 -1
  16. package/dist/Collapsible.module.css +1 -1
  17. package/dist/ColorPicker.module.css +138 -0
  18. package/dist/Combobox.module.css +8 -2
  19. package/dist/Command.module.css +7 -1
  20. package/dist/ConfirmDialog.module.css +8 -2
  21. package/dist/ConsentPrompt.module.css +21 -0
  22. package/dist/DatePicker.module.css +7 -1
  23. package/dist/Dialog.module.css +130 -21
  24. package/dist/DragHandleRow.module.css +6 -0
  25. package/dist/Drawer.module.css +36 -7
  26. package/dist/FileDropzone.module.css +25 -1
  27. package/dist/Header.module.css +68 -0
  28. package/dist/HeaderFooterLayout.module.css +21 -0
  29. package/dist/IconPicker.module.css +15 -5
  30. package/dist/Input.module.css +7 -1
  31. package/dist/Kbd.module.css +1 -1
  32. package/dist/LegalLinks.module.css +9 -2
  33. package/dist/Markdown.module.css +29 -0
  34. package/dist/Menu.module.css +1 -0
  35. package/dist/Message.module.css +10 -1
  36. package/dist/MessageScroller.module.css +12 -1
  37. package/dist/MobileAppsDrawer.module.css +19 -14
  38. package/dist/MobileFooter.module.css +7 -1
  39. package/dist/MobileHeader.module.css +14 -1
  40. package/dist/NavList.module.css +139 -0
  41. package/dist/NavTabs.module.css +8 -2
  42. package/dist/NotificationsPanel.module.css +268 -0
  43. package/dist/OverlayHeader.module.css +1 -1
  44. package/dist/PageContainer.module.css +62 -0
  45. package/dist/PageHeader.module.css +54 -0
  46. package/dist/PageLayout.module.css +51 -0
  47. package/dist/Popover.module.css +1 -1
  48. package/dist/QrCode.module.css +17 -0
  49. package/dist/QuantityStepper.module.css +5 -3
  50. package/dist/RadioGroup.module.css +6 -0
  51. package/dist/Resizable.module.css +3 -3
  52. package/dist/RootLayout.module.css +12 -0
  53. package/dist/SegmentedControl.module.css +7 -1
  54. package/dist/Select.module.css +7 -1
  55. package/dist/Sheet.module.css +20 -2
  56. package/dist/SplitPane.module.css +5 -5
  57. package/dist/StatusBadge.module.css +1 -1
  58. package/dist/StepStrip.module.css +55 -0
  59. package/dist/SuggestionInput.module.css +14 -3
  60. package/dist/SwipableMobileCarousel.module.css +12 -3
  61. package/dist/SwipableMobileCarouselDots.module.css +21 -0
  62. package/dist/SwipeStack.module.css +101 -0
  63. package/dist/SystemBanner.module.css +16 -0
  64. package/dist/Table.module.css +2 -2
  65. package/dist/Tabs.module.css +7 -1
  66. package/dist/TagInput.module.css +8 -2
  67. package/dist/Textarea.module.css +15 -1
  68. package/dist/ThreeColumnLayout.module.css +35 -0
  69. package/dist/Toast.module.css +67 -3
  70. package/dist/Toggle.module.css +7 -0
  71. package/dist/Tooltip.module.css +7 -1
  72. package/dist/UserMenu.module.css +95 -0
  73. package/dist/fonts/OFL.txt +93 -0
  74. package/dist/fonts/hanken-grotesk-cyrillic-ext-wght-normal.woff2 +0 -0
  75. package/dist/fonts/hanken-grotesk-latin-ext-wght-normal.woff2 +0 -0
  76. package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  77. package/dist/fonts/hanken-grotesk-vietnamese-wght-normal.woff2 +0 -0
  78. package/dist/index.d.ts +1156 -76
  79. package/dist/index.js +4223 -1513
  80. package/dist/tokens/fonts.css +53 -0
  81. package/dist/tokens/primitives.css +23 -12
  82. package/dist/tokens/semantic.css +32 -0
  83. package/dist/tokens/theme-presets.css +33 -0
  84. package/dist/tokens.css +2 -1
  85. package/package.json +46 -19
  86. package/src/__tests__/motion.test.tsx +0 -105
  87. package/src/components/Accordion/Accordion.module.css +0 -3
  88. package/src/components/Accordion/Accordion.stories.tsx +0 -110
  89. package/src/components/Accordion/Accordion.tsx +0 -53
  90. package/src/components/Accordion/__tests__/Accordion.test.tsx +0 -61
  91. package/src/components/Alert/Alert.module.css +0 -62
  92. package/src/components/Alert/Alert.stories.tsx +0 -96
  93. package/src/components/Alert/Alert.tsx +0 -58
  94. package/src/components/Alert/__tests__/Alert.test.tsx +0 -75
  95. package/src/components/AspectRatio/AspectRatio.module.css +0 -15
  96. package/src/components/AspectRatio/AspectRatio.stories.tsx +0 -78
  97. package/src/components/AspectRatio/AspectRatio.tsx +0 -21
  98. package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +0 -27
  99. package/src/components/Avatar/Avatar.module.css +0 -38
  100. package/src/components/Avatar/Avatar.tsx +0 -42
  101. package/src/components/Badge/Badge.module.css +0 -154
  102. package/src/components/Badge/Badge.stories.tsx +0 -226
  103. package/src/components/Badge/Badge.tsx +0 -81
  104. package/src/components/Badge/__tests__/Badge.test.tsx +0 -88
  105. package/src/components/BalanceChip/BalanceChip.module.css +0 -32
  106. package/src/components/BalanceChip/BalanceChip.stories.tsx +0 -45
  107. package/src/components/BalanceChip/BalanceChip.tsx +0 -33
  108. package/src/components/Breadcrumb/Breadcrumb.module.css +0 -49
  109. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -47
  110. package/src/components/Breadcrumb/Breadcrumb.tsx +0 -57
  111. package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +0 -52
  112. package/src/components/Button/Button.module.css +0 -153
  113. package/src/components/Button/Button.stories.tsx +0 -83
  114. package/src/components/Button/Button.tsx +0 -52
  115. package/src/components/Button/__tests__/Button.test.tsx +0 -66
  116. package/src/components/ButtonGroup/ButtonGroup.module.css +0 -29
  117. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -57
  118. package/src/components/ButtonGroup/ButtonGroup.tsx +0 -27
  119. package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +0 -29
  120. package/src/components/Calendar/Calendar.module.css +0 -126
  121. package/src/components/Calendar/Calendar.tsx +0 -213
  122. package/src/components/Calendar/__tests__/Calendar.test.tsx +0 -98
  123. package/src/components/Calendar/dateUtils.ts +0 -97
  124. package/src/components/Card/Card.module.css +0 -42
  125. package/src/components/Card/Card.tsx +0 -34
  126. package/src/components/CheckableListRow/CheckableListRow.module.css +0 -95
  127. package/src/components/CheckableListRow/CheckableListRow.stories.tsx +0 -99
  128. package/src/components/CheckableListRow/CheckableListRow.tsx +0 -89
  129. package/src/components/Checkbox/Checkbox.module.css +0 -111
  130. package/src/components/Checkbox/Checkbox.tsx +0 -78
  131. package/src/components/CodeTextarea/CodeTextarea.module.css +0 -53
  132. package/src/components/CodeTextarea/CodeTextarea.stories.tsx +0 -77
  133. package/src/components/CodeTextarea/CodeTextarea.tsx +0 -36
  134. package/src/components/Collapsible/Collapsible.module.css +0 -70
  135. package/src/components/Collapsible/Collapsible.stories.tsx +0 -62
  136. package/src/components/Collapsible/Collapsible.tsx +0 -66
  137. package/src/components/Collapsible/__tests__/Collapsible.test.tsx +0 -71
  138. package/src/components/Combobox/Combobox.module.css +0 -122
  139. package/src/components/Combobox/Combobox.stories.tsx +0 -79
  140. package/src/components/Combobox/Combobox.tsx +0 -186
  141. package/src/components/Combobox/__tests__/Combobox.test.tsx +0 -158
  142. package/src/components/Command/Command.module.css +0 -80
  143. package/src/components/Command/Command.stories.tsx +0 -104
  144. package/src/components/Command/Command.tsx +0 -168
  145. package/src/components/Command/__tests__/Command.test.tsx +0 -162
  146. package/src/components/ConfirmDialog/ConfirmDialog.module.css +0 -113
  147. package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -124
  148. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +0 -146
  149. package/src/components/ContextMenu/ContextMenu.stories.tsx +0 -81
  150. package/src/components/ContextMenu/ContextMenu.tsx +0 -100
  151. package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +0 -87
  152. package/src/components/CurrencyInput/CurrencyInput.stories.tsx +0 -50
  153. package/src/components/CurrencyInput/CurrencyInput.tsx +0 -60
  154. package/src/components/DataTable/DataTable.module.css +0 -28
  155. package/src/components/DataTable/DataTable.stories.tsx +0 -74
  156. package/src/components/DataTable/DataTable.tsx +0 -154
  157. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -123
  158. package/src/components/DatePicker/DatePicker.module.css +0 -61
  159. package/src/components/DatePicker/DatePicker.tsx +0 -103
  160. package/src/components/DatePicker/__tests__/DatePicker.test.tsx +0 -89
  161. package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +0 -58
  162. package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +0 -45
  163. package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +0 -100
  164. package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +0 -119
  165. package/src/components/Dialog/Dialog.module.css +0 -226
  166. package/src/components/Dialog/Dialog.stories.tsx +0 -147
  167. package/src/components/Dialog/Dialog.tsx +0 -160
  168. package/src/components/Dialog/__tests__/Dialog.test.tsx +0 -137
  169. package/src/components/DragHandleRow/DragHandleRow.module.css +0 -57
  170. package/src/components/DragHandleRow/DragHandleRow.tsx +0 -61
  171. package/src/components/Drawer/Drawer.module.css +0 -92
  172. package/src/components/Drawer/Drawer.stories.tsx +0 -132
  173. package/src/components/Drawer/Drawer.tsx +0 -176
  174. package/src/components/Drawer/__tests__/Drawer.test.tsx +0 -199
  175. package/src/components/EmptyState/EmptyState.module.css +0 -27
  176. package/src/components/EmptyState/EmptyState.tsx +0 -27
  177. package/src/components/FileDropzone/FileDropzone.module.css +0 -63
  178. package/src/components/FileDropzone/FileDropzone.tsx +0 -109
  179. package/src/components/FileDropzone/__tests__/FileDropzone.test.tsx +0 -67
  180. package/src/components/FormField/FormField.module.css +0 -33
  181. package/src/components/FormField/FormField.tsx +0 -78
  182. package/src/components/FormField/__tests__/FormField.test.tsx +0 -83
  183. package/src/components/HoverCard/HoverCard.module.css +0 -3
  184. package/src/components/HoverCard/HoverCard.stories.tsx +0 -66
  185. package/src/components/HoverCard/HoverCard.tsx +0 -111
  186. package/src/components/HoverCard/__tests__/HoverCard.test.tsx +0 -99
  187. package/src/components/Icon/Icon.module.css +0 -27
  188. package/src/components/Icon/Icon.stories.tsx +0 -95
  189. package/src/components/Icon/Icon.tsx +0 -43
  190. package/src/components/Icon/__tests__/Icon.test.tsx +0 -40
  191. package/src/components/Icon/icons/activity.tsx +0 -20
  192. package/src/components/Icon/icons/alert-triangle.tsx +0 -22
  193. package/src/components/Icon/icons/apple.tsx +0 -21
  194. package/src/components/Icon/icons/banana.tsx +0 -21
  195. package/src/components/Icon/icons/beef.tsx +0 -22
  196. package/src/components/Icon/icons/beer.tsx +0 -24
  197. package/src/components/Icon/icons/bell.tsx +0 -21
  198. package/src/components/Icon/icons/calendar.tsx +0 -23
  199. package/src/components/Icon/icons/candy.tsx +0 -24
  200. package/src/components/Icon/icons/carrot.tsx +0 -22
  201. package/src/components/Icon/icons/check.tsx +0 -20
  202. package/src/components/Icon/icons/chevron-down.tsx +0 -20
  203. package/src/components/Icon/icons/chevron-left.tsx +0 -20
  204. package/src/components/Icon/icons/chevron-right.tsx +0 -20
  205. package/src/components/Icon/icons/chevron-up.tsx +0 -20
  206. package/src/components/Icon/icons/circle-check.tsx +0 -21
  207. package/src/components/Icon/icons/circle-x.tsx +0 -22
  208. package/src/components/Icon/icons/coffee.tsx +0 -23
  209. package/src/components/Icon/icons/cookie.tsx +0 -25
  210. package/src/components/Icon/icons/croissant.tsx +0 -24
  211. package/src/components/Icon/icons/cup-soda.tsx +0 -23
  212. package/src/components/Icon/icons/drumstick.tsx +0 -21
  213. package/src/components/Icon/icons/egg.tsx +0 -20
  214. package/src/components/Icon/icons/ellipsis-vertical.tsx +0 -22
  215. package/src/components/Icon/icons/external-link.tsx +0 -22
  216. package/src/components/Icon/icons/eye-off.tsx +0 -23
  217. package/src/components/Icon/icons/eye.tsx +0 -21
  218. package/src/components/Icon/icons/file.tsx +0 -21
  219. package/src/components/Icon/icons/fish.tsx +0 -25
  220. package/src/components/Icon/icons/grid-2x2.tsx +0 -22
  221. package/src/components/Icon/icons/house.tsx +0 -21
  222. package/src/components/Icon/icons/index.ts +0 -234
  223. package/src/components/Icon/icons/info.tsx +0 -22
  224. package/src/components/Icon/icons/lock.tsx +0 -21
  225. package/src/components/Icon/icons/log-out.tsx +0 -22
  226. package/src/components/Icon/icons/mail.tsx +0 -21
  227. package/src/components/Icon/icons/menu.tsx +0 -22
  228. package/src/components/Icon/icons/milk.tsx +0 -22
  229. package/src/components/Icon/icons/package.tsx +0 -23
  230. package/src/components/Icon/icons/pencil.tsx +0 -21
  231. package/src/components/Icon/icons/pizza.tsx +0 -24
  232. package/src/components/Icon/icons/plus.tsx +0 -21
  233. package/src/components/Icon/icons/rotate-ccw.tsx +0 -21
  234. package/src/components/Icon/icons/salad.tsx +0 -24
  235. package/src/components/Icon/icons/search.tsx +0 -21
  236. package/src/components/Icon/icons/settings.tsx +0 -21
  237. package/src/components/Icon/icons/shield.tsx +0 -20
  238. package/src/components/Icon/icons/shopping-basket.tsx +0 -26
  239. package/src/components/Icon/icons/sliders-horizontal.tsx +0 -28
  240. package/src/components/Icon/icons/smartphone.tsx +0 -21
  241. package/src/components/Icon/icons/snowflake.tsx +0 -31
  242. package/src/components/Icon/icons/spray-can.tsx +0 -29
  243. package/src/components/Icon/icons/trash-2.tsx +0 -24
  244. package/src/components/Icon/icons/upload.tsx +0 -22
  245. package/src/components/Icon/icons/user.tsx +0 -21
  246. package/src/components/Icon/icons/wine.tsx +0 -23
  247. package/src/components/Icon/icons/x.tsx +0 -21
  248. package/src/components/IconPicker/IconPicker.module.css +0 -63
  249. package/src/components/IconPicker/IconPicker.stories.tsx +0 -84
  250. package/src/components/IconPicker/IconPicker.tsx +0 -90
  251. package/src/components/Input/Input.module.css +0 -52
  252. package/src/components/Input/Input.stories.tsx +0 -86
  253. package/src/components/Input/Input.tsx +0 -14
  254. package/src/components/Input/__tests__/Input.test.tsx +0 -22
  255. package/src/components/Item/Item.module.css +0 -70
  256. package/src/components/Item/Item.stories.tsx +0 -62
  257. package/src/components/Item/Item.tsx +0 -54
  258. package/src/components/Item/__tests__/Item.test.tsx +0 -39
  259. package/src/components/Kbd/Kbd.module.css +0 -14
  260. package/src/components/Kbd/Kbd.stories.tsx +0 -32
  261. package/src/components/Kbd/Kbd.tsx +0 -12
  262. package/src/components/Kbd/__tests__/Kbd.test.tsx +0 -14
  263. package/src/components/Label/Label.module.css +0 -11
  264. package/src/components/Label/Label.stories.tsx +0 -40
  265. package/src/components/Label/Label.tsx +0 -24
  266. package/src/components/Label/__tests__/Label.test.tsx +0 -32
  267. package/src/components/LegalLinks/LegalLinks.module.css +0 -24
  268. package/src/components/LegalLinks/LegalLinks.stories.tsx +0 -22
  269. package/src/components/LegalLinks/LegalLinks.tsx +0 -40
  270. package/src/components/Markdown/Markdown.module.css +0 -49
  271. package/src/components/Markdown/Markdown.stories.tsx +0 -30
  272. package/src/components/Markdown/Markdown.tsx +0 -168
  273. package/src/components/Marker/Marker.module.css +0 -33
  274. package/src/components/Marker/Marker.stories.tsx +0 -39
  275. package/src/components/Marker/Marker.tsx +0 -34
  276. package/src/components/Marker/__tests__/Marker.test.tsx +0 -23
  277. package/src/components/MemberMultiSelect/MemberMultiSelect.module.css +0 -27
  278. package/src/components/MemberMultiSelect/MemberMultiSelect.stories.tsx +0 -74
  279. package/src/components/MemberMultiSelect/MemberMultiSelect.tsx +0 -67
  280. package/src/components/Menu/Menu.module.css +0 -89
  281. package/src/components/Menu/Menu.tsx +0 -159
  282. package/src/components/Menu/__tests__/Menu.test.tsx +0 -197
  283. package/src/components/Menubar/Menubar.stories.tsx +0 -52
  284. package/src/components/Menubar/Menubar.tsx +0 -42
  285. package/src/components/Menubar/__tests__/Menubar.test.tsx +0 -45
  286. package/src/components/Message/Message.module.css +0 -99
  287. package/src/components/Message/Message.stories.tsx +0 -79
  288. package/src/components/Message/Message.tsx +0 -41
  289. package/src/components/Message/__tests__/Message.test.tsx +0 -46
  290. package/src/components/MessageScroller/MessageScroller.module.css +0 -54
  291. package/src/components/MessageScroller/MessageScroller.stories.tsx +0 -84
  292. package/src/components/MessageScroller/MessageScroller.tsx +0 -72
  293. package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +0 -105
  294. package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +0 -81
  295. package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +0 -78
  296. package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +0 -80
  297. package/src/components/MobileFooter/MobileFooter.module.css +0 -73
  298. package/src/components/MobileFooter/MobileFooter.stories.tsx +0 -47
  299. package/src/components/MobileFooter/MobileFooter.tsx +0 -109
  300. package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +0 -71
  301. package/src/components/MobileHeader/MobileHeader.module.css +0 -63
  302. package/src/components/MobileHeader/MobileHeader.stories.tsx +0 -57
  303. package/src/components/MobileHeader/MobileHeader.tsx +0 -77
  304. package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +0 -58
  305. package/src/components/NavTabs/NavTabs.module.css +0 -50
  306. package/src/components/NavTabs/NavTabs.tsx +0 -61
  307. package/src/components/NavigationMenu/NavigationMenu.module.css +0 -53
  308. package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -58
  309. package/src/components/NavigationMenu/NavigationMenu.tsx +0 -125
  310. package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +0 -93
  311. package/src/components/OfflineGate/OfflineGate.tsx +0 -44
  312. package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +0 -66
  313. package/src/components/OverlayHeader/OverlayHeader.module.css +0 -98
  314. package/src/components/OverlayHeader/OverlayHeader.tsx +0 -62
  315. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +0 -51
  316. package/src/components/PageContainer/PageContainer.module.css +0 -20
  317. package/src/components/PageContainer/PageContainer.tsx +0 -31
  318. package/src/components/PageContainer/__tests__/PageContainer.test.tsx +0 -37
  319. package/src/components/PageHeader/PageHeader.module.css +0 -32
  320. package/src/components/PageHeader/PageHeader.tsx +0 -35
  321. package/src/components/Pagination/Pagination.module.css +0 -98
  322. package/src/components/Pagination/Pagination.stories.tsx +0 -49
  323. package/src/components/Pagination/Pagination.tsx +0 -87
  324. package/src/components/Pagination/__tests__/Pagination.test.tsx +0 -56
  325. package/src/components/Popover/Popover.module.css +0 -74
  326. package/src/components/Popover/Popover.stories.tsx +0 -357
  327. package/src/components/Popover/Popover.tsx +0 -208
  328. package/src/components/Popover/__tests__/Popover.test.tsx +0 -98
  329. package/src/components/Progress/Progress.module.css +0 -20
  330. package/src/components/Progress/Progress.stories.tsx +0 -40
  331. package/src/components/Progress/Progress.tsx +0 -35
  332. package/src/components/Progress/__tests__/Progress.test.tsx +0 -31
  333. package/src/components/QuantityStepper/QuantityStepper.module.css +0 -67
  334. package/src/components/QuantityStepper/QuantityStepper.stories.tsx +0 -79
  335. package/src/components/QuantityStepper/QuantityStepper.tsx +0 -107
  336. package/src/components/RadioGroup/RadioGroup.module.css +0 -88
  337. package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -69
  338. package/src/components/RadioGroup/RadioGroup.tsx +0 -99
  339. package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +0 -59
  340. package/src/components/Resizable/Resizable.module.css +0 -70
  341. package/src/components/Resizable/Resizable.stories.tsx +0 -82
  342. package/src/components/Resizable/Resizable.tsx +0 -313
  343. package/src/components/Resizable/__tests__/Resizable.test.tsx +0 -128
  344. package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +0 -24
  345. package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +0 -73
  346. package/src/components/ScrollArea/ScrollArea.module.css +0 -20
  347. package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -57
  348. package/src/components/ScrollArea/ScrollArea.tsx +0 -27
  349. package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +0 -39
  350. package/src/components/SegmentedControl/SegmentedControl.module.css +0 -55
  351. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -118
  352. package/src/components/SegmentedControl/SegmentedControl.tsx +0 -56
  353. package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +0 -63
  354. package/src/components/Select/Select.module.css +0 -82
  355. package/src/components/Select/Select.stories.tsx +0 -115
  356. package/src/components/Select/Select.tsx +0 -48
  357. package/src/components/Select/__tests__/Select.test.tsx +0 -59
  358. package/src/components/Sheet/Sheet.module.css +0 -55
  359. package/src/components/Sheet/Sheet.tsx +0 -109
  360. package/src/components/Sheet/__tests__/Sheet.test.tsx +0 -99
  361. package/src/components/Slider/Slider.module.css +0 -95
  362. package/src/components/Slider/Slider.stories.tsx +0 -64
  363. package/src/components/Slider/Slider.tsx +0 -77
  364. package/src/components/Slider/__tests__/Slider.test.tsx +0 -45
  365. package/src/components/Spinner/Spinner.module.css +0 -39
  366. package/src/components/Spinner/Spinner.tsx +0 -23
  367. package/src/components/SplitMethodSelector/SplitMethodSelector.stories.tsx +0 -33
  368. package/src/components/SplitMethodSelector/SplitMethodSelector.tsx +0 -43
  369. package/src/components/SplitPane/SplitPane.module.css +0 -85
  370. package/src/components/SplitPane/SplitPane.stories.tsx +0 -160
  371. package/src/components/SplitPane/SplitPane.tsx +0 -149
  372. package/src/components/SplitPane/__tests__/SplitPane.test.tsx +0 -51
  373. package/src/components/StatusBadge/StatusBadge.module.css +0 -59
  374. package/src/components/StatusBadge/StatusBadge.stories.tsx +0 -74
  375. package/src/components/StatusBadge/StatusBadge.tsx +0 -65
  376. package/src/components/SuggestionInput/SuggestionInput.module.css +0 -103
  377. package/src/components/SuggestionInput/SuggestionInput.stories.tsx +0 -144
  378. package/src/components/SuggestionInput/SuggestionInput.tsx +0 -207
  379. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +0 -55
  380. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +0 -227
  381. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +0 -35
  382. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +0 -42
  383. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +0 -18
  384. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +0 -21
  385. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +0 -29
  386. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +0 -133
  387. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +0 -61
  388. package/src/components/SwipableMobileCarousel/context.ts +0 -13
  389. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +0 -49
  390. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +0 -62
  391. package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +0 -63
  392. package/src/components/SystemBanner/SystemBanner.module.css +0 -69
  393. package/src/components/SystemBanner/SystemBanner.stories.tsx +0 -86
  394. package/src/components/SystemBanner/SystemBanner.tsx +0 -50
  395. package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +0 -50
  396. package/src/components/Table/Table.module.css +0 -45
  397. package/src/components/Table/Table.stories.tsx +0 -125
  398. package/src/components/Table/Table.tsx +0 -51
  399. package/src/components/Table/__tests__/Table.test.tsx +0 -50
  400. package/src/components/Tabs/Tabs.module.css +0 -56
  401. package/src/components/Tabs/Tabs.stories.tsx +0 -100
  402. package/src/components/Tabs/Tabs.tsx +0 -46
  403. package/src/components/Tabs/__tests__/Tabs.test.tsx +0 -45
  404. package/src/components/TagInput/TagInput.module.css +0 -141
  405. package/src/components/TagInput/TagInput.stories.tsx +0 -109
  406. package/src/components/TagInput/TagInput.tsx +0 -174
  407. package/src/components/TagInput/__tests__/TagInput.test.tsx +0 -69
  408. package/src/components/Textarea/Textarea.module.css +0 -39
  409. package/src/components/Textarea/Textarea.tsx +0 -15
  410. package/src/components/Textarea/__tests__/Textarea.test.tsx +0 -29
  411. package/src/components/Toast/Toast.module.css +0 -132
  412. package/src/components/Toast/Toast.stories.tsx +0 -191
  413. package/src/components/Toast/Toast.tsx +0 -139
  414. package/src/components/Toggle/Toggle.module.css +0 -57
  415. package/src/components/Toggle/Toggle.stories.tsx +0 -95
  416. package/src/components/Toggle/Toggle.tsx +0 -39
  417. package/src/components/Toggle/__tests__/Toggle.test.tsx +0 -47
  418. package/src/components/Tooltip/Tooltip.module.css +0 -56
  419. package/src/components/Tooltip/Tooltip.tsx +0 -30
  420. package/src/components/Typography/Typography.module.css +0 -47
  421. package/src/components/Typography/Typography.stories.tsx +0 -62
  422. package/src/components/Typography/Typography.tsx +0 -30
  423. package/src/components/Typography/__tests__/Typography.test.tsx +0 -34
  424. package/src/css-modules.d.ts +0 -4
  425. package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +0 -126
  426. package/src/hooks/__tests__/useDoubleTap.test.tsx +0 -117
  427. package/src/hooks/__tests__/useIsMobile.test.tsx +0 -80
  428. package/src/hooks/__tests__/useIsOffline.test.tsx +0 -53
  429. package/src/hooks/__tests__/useLongPress.test.tsx +0 -158
  430. package/src/hooks/__tests__/useOfflineTileState.test.tsx +0 -69
  431. package/src/hooks/__tests__/useResponsiveLayout.test.tsx +0 -87
  432. package/src/hooks/__tests__/useSnapCarousel.test.tsx +0 -93
  433. package/src/hooks/__tests__/useSwipeReveal.test.tsx +0 -110
  434. package/src/hooks/index.ts +0 -24
  435. package/src/hooks/useCarouselRouteSync.ts +0 -92
  436. package/src/hooks/useCommitOnEnterOrBlur.ts +0 -39
  437. package/src/hooks/useDoubleTap.ts +0 -91
  438. package/src/hooks/useIsMobile.ts +0 -33
  439. package/src/hooks/useIsOffline.ts +0 -41
  440. package/src/hooks/useLongPress.ts +0 -185
  441. package/src/hooks/useOfflineTileState.ts +0 -45
  442. package/src/hooks/useResponsiveLayout.ts +0 -36
  443. package/src/hooks/useSnapCarousel.ts +0 -79
  444. package/src/hooks/useSwipeReveal.ts +0 -107
  445. package/src/index.ts +0 -251
  446. package/src/motion.ts +0 -78
  447. package/src/overlay-shell.ts +0 -94
  448. package/src/scroll-lock.ts +0 -22
  449. package/src/stories/Avatar.stories.tsx +0 -44
  450. package/src/stories/Card.stories.tsx +0 -35
  451. package/src/stories/Checkbox.stories.tsx +0 -45
  452. package/src/stories/DesignSystemOverview.stories.tsx +0 -3187
  453. package/src/stories/DeviceOnlyGate.stories.tsx +0 -35
  454. package/src/stories/DeviceStorageKeyGate.stories.tsx +0 -57
  455. package/src/stories/DragHandleRow.stories.tsx +0 -41
  456. package/src/stories/EmptyState.stories.tsx +0 -33
  457. package/src/stories/FileDropzone.stories.tsx +0 -69
  458. package/src/stories/FormField.stories.tsx +0 -47
  459. package/src/stories/InteractionHooks.stories.tsx +0 -925
  460. package/src/stories/MobilePatterns.stories.tsx +0 -1536
  461. package/src/stories/NavTabs.stories.tsx +0 -53
  462. package/src/stories/OfflineGate.stories.tsx +0 -42
  463. package/src/stories/PageContainer.stories.tsx +0 -37
  464. package/src/stories/PageHeader.stories.tsx +0 -42
  465. package/src/stories/ResponsiveSurface.stories.tsx +0 -47
  466. package/src/stories/Spinner.stories.tsx +0 -25
  467. package/src/stories/SwipableMobileCarousel.stories.tsx +0 -304
  468. package/src/stories/Textarea.stories.tsx +0 -51
  469. package/src/stories/TokenGallery.stories.tsx +0 -602
  470. package/src/stories/Tooltip.stories.tsx +0 -59
  471. package/src/tokens/primitives.css +0 -157
  472. package/src/tokens/semantic.css +0 -130
  473. package/src/tokens.css +0 -5
@@ -1,40 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Progress } from './Progress';
3
-
4
- const meta = {
5
- title: 'Components/Progress',
6
- component: Progress,
7
- parameters: {
8
- layout: 'padded',
9
- docs: {
10
- description: {
11
- component:
12
- 'Determinate progress bar. role="progressbar" with aria-valuenow/min/max so screen readers announce the current value. Indeterminate state is out of scope.',
13
- },
14
- },
15
- },
16
- args: {
17
- value: 40,
18
- label: 'Upload progress',
19
- },
20
- } satisfies Meta<typeof Progress>;
21
-
22
- export default meta;
23
- type Story = StoryObj<typeof meta>;
24
-
25
- // ---------------------------------------------------------------------------
26
-
27
- export const Default: Story = {};
28
-
29
- export const Empty: Story = {
30
- args: { value: 0 },
31
- };
32
-
33
- export const Complete: Story = {
34
- args: { value: 100 },
35
- };
36
-
37
- export const OverMax: Story = {
38
- name: 'Clamped (value > 100)',
39
- args: { value: 140 },
40
- };
@@ -1,35 +0,0 @@
1
- import styles from './Progress.module.css';
2
-
3
- export interface ProgressProps {
4
- /** 0–100. Values outside that range are clamped. */
5
- value: number;
6
- /** Accessible name — Progress has no visible text of its own. */
7
- label?: string;
8
- id?: string;
9
- className?: string;
10
- }
11
-
12
- /**
13
- * Progress — determinate bar.
14
- *
15
- * `role="progressbar"` with `aria-valuenow`/`aria-valuemin`/`aria-valuemax`
16
- * so screen readers announce the current value. Indeterminate state is out
17
- * of scope until a consumer needs it.
18
- */
19
- export function Progress({ value, label, id, className }: ProgressProps) {
20
- const clamped = Math.min(100, Math.max(0, value));
21
-
22
- return (
23
- <div
24
- id={id}
25
- role="progressbar"
26
- aria-valuenow={clamped}
27
- aria-valuemin={0}
28
- aria-valuemax={100}
29
- aria-label={label}
30
- className={[styles.track, className].filter(Boolean).join(' ')}
31
- >
32
- <div className={styles.fill} style={{ width: `${clamped}%` }} />
33
- </div>
34
- );
35
- }
@@ -1,31 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { afterEach, describe, expect, it } from 'vitest';
3
- import { cleanup, render, screen } from '@testing-library/react';
4
- import { Progress } from '../Progress';
5
-
6
- afterEach(cleanup);
7
-
8
- describe('Progress', () => {
9
- it('exposes progressbar ARIA attributes for the given value', () => {
10
- render(<Progress value={40} label="Upload progress" />);
11
- const bar = screen.getByRole('progressbar', { name: 'Upload progress' });
12
- expect(bar.getAttribute('aria-valuenow')).toBe('40');
13
- expect(bar.getAttribute('aria-valuemin')).toBe('0');
14
- expect(bar.getAttribute('aria-valuemax')).toBe('100');
15
- });
16
-
17
- it('clamps a value above 100', () => {
18
- render(<Progress value={140} label="Upload progress" />);
19
- expect(screen.getByRole('progressbar').getAttribute('aria-valuenow')).toBe('100');
20
- });
21
-
22
- it('clamps a negative value to 0', () => {
23
- render(<Progress value={-10} label="Upload progress" />);
24
- expect(screen.getByRole('progressbar').getAttribute('aria-valuenow')).toBe('0');
25
- });
26
-
27
- it('renders without an accessible name when label is omitted', () => {
28
- render(<Progress value={40} />);
29
- expect(screen.getByRole('progressbar').getAttribute('aria-label')).toBeNull();
30
- });
31
- });
@@ -1,67 +0,0 @@
1
- .root {
2
- display: inline-flex;
3
- align-items: center;
4
- gap: var(--sv-space-1);
5
- padding: var(--sv-space-1);
6
- border: 1px solid var(--sv-color-border-strong);
7
- border-radius: var(--sv-radius-full);
8
- background: var(--sv-color-surface-sunken);
9
- }
10
-
11
- .button {
12
- all: unset;
13
- display: flex;
14
- align-items: center;
15
- justify-content: center;
16
- width: var(--sv-space-6);
17
- height: var(--sv-space-6);
18
- border-radius: var(--sv-radius-full);
19
- color: var(--sv-color-text-primary);
20
- font-size: var(--sv-font-size-md);
21
- line-height: 1;
22
- cursor: pointer;
23
- }
24
-
25
- .button:hover:not(:disabled) {
26
- background: var(--sv-color-surface);
27
- }
28
-
29
- .button:disabled {
30
- color: var(--sv-color-text-subtle);
31
- cursor: not-allowed;
32
- }
33
-
34
- .input {
35
- all: unset;
36
- width: 3ch;
37
- text-align: center;
38
- font-family: var(--sv-font-family);
39
- font-size: var(--sv-font-size-sm);
40
- color: var(--sv-color-text-primary);
41
- /* Hide the native spinner — the +/- buttons are the stepper affordance,
42
- a second one from the browser would be redundant and often looks
43
- inconsistent across browsers anyway. */
44
- -moz-appearance: textfield;
45
- }
46
-
47
- .input::-webkit-outer-spin-button,
48
- .input::-webkit-inner-spin-button {
49
- -webkit-appearance: none;
50
- margin: 0;
51
- }
52
-
53
- .input:focus-visible {
54
- outline: none;
55
- box-shadow: 0 0 0 2px var(--sv-color-focus-ring);
56
- border-radius: var(--sv-radius-sm);
57
- }
58
-
59
- .input:disabled {
60
- color: var(--sv-color-text-muted);
61
- }
62
-
63
- .unit {
64
- font-size: var(--sv-font-size-xs);
65
- color: var(--sv-color-text-subtle);
66
- white-space: nowrap;
67
- }
@@ -1,79 +0,0 @@
1
- import { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react-vite';
3
- import { QuantityStepper } from './QuantityStepper';
4
-
5
- const meta = {
6
- title: 'Components/QuantityStepper',
7
- component: QuantityStepper,
8
- parameters: {
9
- layout: 'padded',
10
- docs: {
11
- description: {
12
- component:
13
- 'A numeric input with +/- buttons and an optional read-only unit suffix. Supports fractional step values for quantities like "1.5 kg".',
14
- },
15
- },
16
- },
17
- args: {
18
- value: 1,
19
- onChange: () => {},
20
- 'aria-label': 'Quantity',
21
- },
22
- } satisfies Meta<typeof QuantityStepper>;
23
-
24
- export default meta;
25
- type Story = StoryObj<typeof meta>;
26
-
27
- function ControlledStepper({
28
- initial = 1,
29
- step = 1,
30
- min = 0,
31
- max,
32
- unit,
33
- }: {
34
- initial?: number;
35
- step?: number;
36
- min?: number;
37
- max?: number;
38
- unit?: string;
39
- }) {
40
- const [value, setValue] = useState(initial);
41
- return (
42
- <QuantityStepper
43
- value={value}
44
- onChange={setValue}
45
- step={step}
46
- min={min}
47
- max={max}
48
- unit={unit}
49
- aria-label="Quantity"
50
- />
51
- );
52
- }
53
-
54
- export const Default: Story = {
55
- render: () => <ControlledStepper initial={1} />,
56
- };
57
-
58
- export const WithUnit: Story = {
59
- render: () => <ControlledStepper initial={6} unit="pcs" />,
60
- };
61
-
62
- export const FractionalStep: Story = {
63
- name: 'Fractional step (0.5 kg)',
64
- render: () => <ControlledStepper initial={1.5} step={0.5} unit="kg" />,
65
- };
66
-
67
- export const AtMinimum: Story = {
68
- render: () => <ControlledStepper initial={0} min={0} unit="pcs" />,
69
- };
70
-
71
- export const WithMax: Story = {
72
- render: () => <ControlledStepper initial={9} max={10} unit="pcs" />,
73
- };
74
-
75
- export const Disabled: Story = {
76
- render: () => (
77
- <QuantityStepper value={3} onChange={() => {}} unit="pcs" aria-label="Quantity" disabled />
78
- ),
79
- };
@@ -1,107 +0,0 @@
1
- 'use client';
2
-
3
- import { useId, type ChangeEvent } from 'react';
4
- import styles from './QuantityStepper.module.css';
5
-
6
- export interface QuantityStepperProps {
7
- value: number;
8
- onChange: (value: number) => void;
9
- /** Smallest selectable value. Default `0`. */
10
- min?: number;
11
- /** Largest selectable value. Default unbounded. */
12
- max?: number;
13
- /** Amount +/- adjusts by. Default `1` — pass e.g. `0.5` for fractional
14
- * quantities (kg, L). */
15
- step?: number;
16
- /** Read-only suffix shown after the number, e.g. "pcs" or "kg" — for
17
- * compact contexts (a list row) where a separate unit selector doesn't
18
- * fit. Editable unit selection (a `<Select>` of units) lives outside
19
- * this component. */
20
- unit?: string;
21
- 'aria-label': string;
22
- disabled?: boolean;
23
- id?: string;
24
- className?: string;
25
- }
26
-
27
- /**
28
- * QuantityStepper — a numeric input with +/- buttons and an optional
29
- * read-only unit suffix. Built for grocery/inventory-style quantities
30
- * (Sovereign Shopper, SHP-06) — supports fractional `step` values since
31
- * quantities like "1.5 kg" are common, unlike a typical integer counter.
32
- *
33
- * The middle field is a real `<input type="number">` so keyboard entry,
34
- * paste, and the browser's native numeric affordances all work — the
35
- * buttons are a convenience on top, not the only way to change the value.
36
- */
37
- export function QuantityStepper({
38
- value,
39
- onChange,
40
- min = 0,
41
- max,
42
- step = 1,
43
- unit,
44
- 'aria-label': ariaLabel,
45
- disabled = false,
46
- id,
47
- className,
48
- }: QuantityStepperProps) {
49
- const generatedId = useId();
50
- const inputId = id ?? generatedId;
51
-
52
- function clamp(next: number): number {
53
- let result = next;
54
- if (min !== undefined) result = Math.max(min, result);
55
- if (max !== undefined) result = Math.min(max, result);
56
- // Avoid float drift (0.1 + 0.2 style errors) from repeated +/- clicks.
57
- return Math.round(result * 1000) / 1000;
58
- }
59
-
60
- function handleInputChange(e: ChangeEvent<HTMLInputElement>) {
61
- const parsed = Number.parseFloat(e.target.value);
62
- if (!Number.isNaN(parsed)) onChange(clamp(parsed));
63
- }
64
-
65
- const decrementDisabled = disabled || (min !== undefined && value <= min);
66
- const incrementDisabled = disabled || (max !== undefined && value >= max);
67
-
68
- return (
69
- <div className={[styles.root, className].filter(Boolean).join(' ')}>
70
- <button
71
- type="button"
72
- className={styles.button}
73
- onClick={() => onChange(clamp(value - step))}
74
- disabled={decrementDisabled}
75
- aria-label={`Decrease ${ariaLabel}`}
76
- >
77
- −
78
- </button>
79
- <input
80
- id={inputId}
81
- type="number"
82
- className={styles.input}
83
- value={value}
84
- onChange={handleInputChange}
85
- min={min}
86
- max={max}
87
- step={step}
88
- disabled={disabled}
89
- aria-label={ariaLabel}
90
- />
91
- {unit && (
92
- <span className={styles.unit} aria-hidden>
93
- {unit}
94
- </span>
95
- )}
96
- <button
97
- type="button"
98
- className={styles.button}
99
- onClick={() => onChange(clamp(value + step))}
100
- disabled={incrementDisabled}
101
- aria-label={`Increase ${ariaLabel}`}
102
- >
103
- +
104
- </button>
105
- </div>
106
- );
107
- }
@@ -1,88 +0,0 @@
1
- .root {
2
- display: flex;
3
- flex-direction: column;
4
- gap: var(--sv-space-2);
5
- }
6
-
7
- .option {
8
- display: inline-flex;
9
- align-items: center;
10
- gap: var(--sv-space-2);
11
- cursor: pointer;
12
- }
13
-
14
- /* The visual circle — same 18px size and touch-target-expansion technique
15
- as Checkbox's box, radius-full instead of radius-sm. */
16
- .circle {
17
- --radio-circle-size: 18px;
18
- position: relative;
19
- flex-shrink: 0;
20
- width: var(--radio-circle-size);
21
- height: var(--radio-circle-size);
22
- border-radius: var(--sv-radius-full);
23
- border: 1.5px solid var(--sv-color-border-strong);
24
- background: var(--sv-color-surface);
25
- transition:
26
- background-color 0.15s ease,
27
- border-color 0.15s ease;
28
- display: flex;
29
- align-items: center;
30
- justify-content: center;
31
- }
32
-
33
- .checked {
34
- border-color: var(--sv-color-accent);
35
- }
36
-
37
- .dot {
38
- width: 8px;
39
- height: 8px;
40
- border-radius: var(--sv-radius-full);
41
- background: var(--sv-color-accent);
42
- pointer-events: none;
43
- }
44
-
45
- /* Hide the native input visually but keep it accessible */
46
- .input {
47
- position: absolute;
48
- inset: 0;
49
- opacity: 0;
50
- margin: 0;
51
- width: 100%;
52
- height: 100%;
53
- cursor: pointer;
54
- }
55
-
56
- .input:disabled {
57
- cursor: not-allowed;
58
- }
59
-
60
- /* Expand the tappable region to the platform's minimum touch target without
61
- growing the visible circle. Gated to (pointer: coarse), the *primary*
62
- pointer, so a touchscreen laptop with mouse/trackpad as primary keeps
63
- desktop density — same reasoning as Checkbox. */
64
- @media (pointer: coarse) {
65
- .input {
66
- inset: calc((var(--radio-circle-size) - var(--sv-touch-target-min, 44px)) / 2);
67
- }
68
- }
69
-
70
- .input:focus-visible {
71
- outline: none;
72
- }
73
-
74
- .circle:has(.input:focus-visible) {
75
- outline: 2px solid var(--sv-color-focus-ring);
76
- outline-offset: 2px;
77
- }
78
-
79
- .optionLabel {
80
- font-size: var(--sv-font-size-sm);
81
- color: var(--sv-color-text-primary);
82
- user-select: none;
83
- }
84
-
85
- .disabled {
86
- opacity: 0.5;
87
- cursor: not-allowed;
88
- }
@@ -1,69 +0,0 @@
1
- import { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react-vite';
3
- import { RadioGroup } from './RadioGroup';
4
- import { FormField } from '../FormField/FormField';
5
-
6
- const meta = {
7
- title: 'Components/RadioGroup',
8
- component: RadioGroup,
9
- parameters: {
10
- layout: 'padded',
11
- docs: {
12
- description: {
13
- component:
14
- 'Single-select list of options. Renders real `<input type="radio">` elements sharing one `name`, so keyboard arrow-key navigation between options is native browser behavior.',
15
- },
16
- },
17
- },
18
- args: {
19
- items: [
20
- { label: 'Small', value: 'sm' },
21
- { label: 'Medium', value: 'md' },
22
- { label: 'Large', value: 'lg' },
23
- ],
24
- value: 'md',
25
- onChange: () => {},
26
- 'aria-label': 'Size',
27
- },
28
- } satisfies Meta<typeof RadioGroup>;
29
-
30
- export default meta;
31
- type Story = StoryObj<typeof meta>;
32
-
33
- // ---------------------------------------------------------------------------
34
-
35
- export const Default: Story = {
36
- render: (args) => {
37
- const [value, setValue] = useState(args.value);
38
- return <RadioGroup {...args} value={value} onChange={setValue} />;
39
- },
40
- };
41
-
42
- export const WithDisabledItem: Story = {
43
- render: (args) => {
44
- const [value, setValue] = useState(args.value);
45
- return (
46
- <RadioGroup
47
- {...args}
48
- items={[...args.items, { label: 'X-Large (out of stock)', value: 'xl', disabled: true }]}
49
- value={value}
50
- onChange={setValue}
51
- />
52
- );
53
- },
54
- };
55
-
56
- export const Disabled: Story = {
57
- args: { disabled: true },
58
- };
59
-
60
- export const InFormField: Story = {
61
- render: (args) => {
62
- const [value, setValue] = useState(args.value);
63
- return (
64
- <FormField label="Shirt size" error="Select a size to continue.">
65
- {(field) => <RadioGroup {...args} {...field} value={value} onChange={setValue} />}
66
- </FormField>
67
- );
68
- },
69
- };
@@ -1,99 +0,0 @@
1
- 'use client';
2
-
3
- import { useId } from 'react';
4
- import styles from './RadioGroup.module.css';
5
-
6
- export interface RadioGroupItem {
7
- label: string;
8
- value: string;
9
- disabled?: boolean;
10
- }
11
-
12
- export interface RadioGroupProps {
13
- items: RadioGroupItem[];
14
- value: string;
15
- onChange: (value: string) => void;
16
- /** Groups the underlying native radios. Auto-generated when omitted —
17
- * only pass this to participate in a native, non-React form submission. */
18
- name?: string;
19
- /** Disables every item. Use `items[].disabled` to disable one. */
20
- disabled?: boolean;
21
- id?: string;
22
- className?: string;
23
- 'aria-label'?: string;
24
- 'aria-labelledby'?: string;
25
- 'aria-describedby'?: string;
26
- 'aria-invalid'?: boolean;
27
- required?: boolean;
28
- }
29
-
30
- /**
31
- * RadioGroup — single-select list of options.
32
- *
33
- * Renders real `<input type="radio">` elements sharing one `name`, visually
34
- * hidden behind a custom circle (same technique as `Checkbox`). Keyboard
35
- * arrow-key navigation between options is native browser behavior for
36
- * grouped radio inputs — nothing to hand-roll here.
37
- */
38
- export function RadioGroup({
39
- items,
40
- value,
41
- onChange,
42
- name,
43
- disabled,
44
- id,
45
- className,
46
- 'aria-label': ariaLabel,
47
- 'aria-labelledby': ariaLabelledBy,
48
- 'aria-describedby': ariaDescribedBy,
49
- 'aria-invalid': ariaInvalid,
50
- required,
51
- }: RadioGroupProps) {
52
- const generatedName = useId();
53
- const groupName = name ?? generatedName;
54
-
55
- return (
56
- <div
57
- id={id}
58
- role="radiogroup"
59
- aria-label={ariaLabel}
60
- aria-labelledby={ariaLabelledBy}
61
- aria-describedby={ariaDescribedBy}
62
- aria-invalid={ariaInvalid}
63
- aria-required={required}
64
- className={[styles.root, className].filter(Boolean).join(' ')}
65
- >
66
- {items.map((item) => {
67
- const checked = item.value === value;
68
- const itemDisabled = disabled || item.disabled;
69
- const inputId = `${groupName}-${item.value}`;
70
-
71
- return (
72
- <label
73
- key={item.value}
74
- htmlFor={inputId}
75
- className={[styles.option, itemDisabled ? styles.disabled : '']
76
- .filter(Boolean)
77
- .join(' ')}
78
- >
79
- <span className={[styles.circle, checked ? styles.checked : ''].join(' ')}>
80
- <input
81
- id={inputId}
82
- type="radio"
83
- name={groupName}
84
- value={item.value}
85
- checked={checked}
86
- disabled={itemDisabled}
87
- required={required}
88
- className={styles.input}
89
- onChange={() => onChange(item.value)}
90
- />
91
- {checked && <span className={styles.dot} />}
92
- </span>
93
- <span className={styles.optionLabel}>{item.label}</span>
94
- </label>
95
- );
96
- })}
97
- </div>
98
- );
99
- }
@@ -1,59 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { afterEach, describe, expect, it, vi } from 'vitest';
3
- import { cleanup, render, screen, fireEvent } from '@testing-library/react';
4
- import { RadioGroup } from '../RadioGroup';
5
-
6
- afterEach(cleanup);
7
-
8
- const items = [
9
- { label: 'Small', value: 'sm' },
10
- { label: 'Medium', value: 'md' },
11
- { label: 'Large', value: 'lg' },
12
- ];
13
-
14
- describe('RadioGroup', () => {
15
- it('renders all items as radio inputs', () => {
16
- render(<RadioGroup items={items} value="sm" onChange={() => {}} aria-label="Size" />);
17
- expect(screen.getAllByRole('radio').length).toBe(3);
18
- });
19
-
20
- it('marks the matching item as checked', () => {
21
- render(<RadioGroup items={items} value="md" onChange={() => {}} aria-label="Size" />);
22
- expect(screen.getByRole('radio', { name: 'Medium' })).toHaveProperty('checked', true);
23
- expect(screen.getByRole('radio', { name: 'Small' })).toHaveProperty('checked', false);
24
- });
25
-
26
- it('calls onChange with the selected value', () => {
27
- const onChange = vi.fn();
28
- render(<RadioGroup items={items} value="sm" onChange={onChange} aria-label="Size" />);
29
- fireEvent.click(screen.getByRole('radio', { name: 'Large' }));
30
- expect(onChange).toHaveBeenCalledWith('lg');
31
- });
32
-
33
- it('exposes a radiogroup with the given aria-label', () => {
34
- render(<RadioGroup items={items} value="sm" onChange={() => {}} aria-label="Size" />);
35
- expect(screen.getByRole('radiogroup', { name: 'Size' })).toBeDefined();
36
- });
37
-
38
- it('shares one name across all items so native radio semantics apply', () => {
39
- render(
40
- <RadioGroup items={items} value="sm" onChange={() => {}} aria-label="Size" name="size" />,
41
- );
42
- const radios = screen.getAllByRole('radio') as HTMLInputElement[];
43
- expect(new Set(radios.map((r) => r.name)).size).toBe(1);
44
- });
45
-
46
- it('disables an individual item via items[].disabled', () => {
47
- const withDisabled = [...items, { label: 'X-Large', value: 'xl', disabled: true }];
48
- render(<RadioGroup items={withDisabled} value="sm" onChange={() => {}} aria-label="Size" />);
49
- expect(screen.getByRole('radio', { name: 'X-Large' })).toHaveProperty('disabled', true);
50
- expect(screen.getByRole('radio', { name: 'Small' })).toHaveProperty('disabled', false);
51
- });
52
-
53
- it('disables every item when the group-level disabled prop is set', () => {
54
- render(<RadioGroup items={items} value="sm" onChange={() => {}} aria-label="Size" disabled />);
55
- for (const radio of screen.getAllByRole('radio')) {
56
- expect(radio).toHaveProperty('disabled', true);
57
- }
58
- });
59
- });