@boxcustodia/library 2.0.0-alpha.11 → 2.0.0-alpha.12

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 (349) hide show
  1. package/dist/index.css +1 -1
  2. package/package.json +4 -3
  3. package/src/__doc__/Changelog.mdx +6 -0
  4. package/src/__doc__/Components.mdx +73 -0
  5. package/src/__doc__/Examples.tsx +69 -0
  6. package/src/__doc__/Icons.mdx +41 -0
  7. package/src/__doc__/Intro.mdx +138 -0
  8. package/src/__doc__/MCP.mdx +71 -0
  9. package/src/__doc__/Migration.mdx +475 -0
  10. package/src/__doc__/Theme.mdx +132 -0
  11. package/src/__doc__/Types.mdx +252 -0
  12. package/src/components/alert/alert.stories.tsx +142 -0
  13. package/src/components/alert/alert.tsx +109 -0
  14. package/src/components/alert/index.ts +7 -0
  15. package/src/components/alert-dialog/alert-dialog.stories.tsx +173 -0
  16. package/src/components/alert-dialog/alert-dialog.test.tsx +49 -0
  17. package/src/components/alert-dialog/alert-dialog.tsx +265 -0
  18. package/src/components/alert-dialog/index.ts +1 -0
  19. package/src/components/auto-complete/auto-complete-primitives.tsx +155 -0
  20. package/src/components/auto-complete/auto-complete.stories.tsx +241 -0
  21. package/src/components/auto-complete/auto-complete.tsx +82 -0
  22. package/src/components/auto-complete/index.ts +2 -0
  23. package/src/components/avatar/avatar.stories.tsx +84 -0
  24. package/src/components/avatar/avatar.test.tsx +61 -0
  25. package/src/components/avatar/avatar.tsx +104 -0
  26. package/src/components/avatar/index.ts +1 -0
  27. package/src/components/background-image/background-image.stories.tsx +21 -0
  28. package/src/components/background-image/background-image.test.tsx +29 -0
  29. package/src/components/background-image/background-image.tsx +23 -0
  30. package/src/components/background-image/index.ts +1 -0
  31. package/src/components/button/button.stories.tsx +396 -0
  32. package/src/components/button/button.test.tsx +58 -0
  33. package/src/components/button/button.tsx +31 -0
  34. package/src/components/button/button.variants.ts +44 -0
  35. package/src/components/button/components/base-button.tsx +86 -0
  36. package/src/components/button/components/loader-overlay.tsx +21 -0
  37. package/src/components/button/components/loading-icon.tsx +47 -0
  38. package/src/components/button/index.ts +3 -0
  39. package/src/components/calendar/calendar.model.ts +86 -0
  40. package/src/components/calendar/calendar.stories.tsx +155 -0
  41. package/src/components/calendar/calendar.test.tsx +12 -0
  42. package/src/components/calendar/calendar.tsx +185 -0
  43. package/src/components/calendar/components/calendar-navigation.tsx +141 -0
  44. package/src/components/calendar/components/day.tsx +61 -0
  45. package/src/components/calendar/components/decade-view.tsx +45 -0
  46. package/src/components/calendar/components/index.ts +6 -0
  47. package/src/components/calendar/components/month-view.tsx +58 -0
  48. package/src/components/calendar/components/week-days.tsx +27 -0
  49. package/src/components/calendar/components/year-view.tsx +29 -0
  50. package/src/components/calendar/hooks/index.ts +4 -0
  51. package/src/components/calendar/hooks/use-calendar-navigation.ts +79 -0
  52. package/src/components/calendar/hooks/use-calendar.ts +90 -0
  53. package/src/components/calendar/hooks/use-multiple-calendar.ts +34 -0
  54. package/src/components/calendar/hooks/use-range-calendar.ts +91 -0
  55. package/src/components/calendar/hooks/use-single-calendar.ts +18 -0
  56. package/src/components/calendar/index.ts +1 -0
  57. package/src/components/calendar/utils/typeguards.ts +7 -0
  58. package/src/components/card/card.stories.tsx +116 -0
  59. package/src/components/card/card.tsx +74 -0
  60. package/src/components/card/index.ts +1 -0
  61. package/src/components/center/center.stories.tsx +81 -0
  62. package/src/components/center/center.tsx +24 -0
  63. package/src/components/center/index.ts +1 -0
  64. package/src/components/checkbox/checkbox.stories.tsx +307 -0
  65. package/src/components/checkbox/checkbox.tsx +273 -0
  66. package/src/components/checkbox/index.ts +1 -0
  67. package/src/components/checkbox-group/checkbox-group.stories.tsx +104 -0
  68. package/src/components/checkbox-group/checkbox-group.tsx +16 -0
  69. package/src/components/checkbox-group/index.ts +1 -0
  70. package/src/components/combobox/combobox.stories.tsx +339 -0
  71. package/src/components/combobox/combobox.tsx +892 -0
  72. package/src/components/combobox/index.ts +1 -0
  73. package/src/components/date-picker/date-input.stories.tsx +158 -0
  74. package/src/components/date-picker/date-input.tsx +163 -0
  75. package/src/components/date-picker/date-picker.model.ts +90 -0
  76. package/src/components/date-picker/date-picker.stories.tsx +200 -0
  77. package/src/components/date-picker/date-picker.test.tsx +23 -0
  78. package/src/components/date-picker/date-picker.tsx +298 -0
  79. package/src/components/date-picker/date-picker.utils.ts +260 -0
  80. package/src/components/date-picker/index.ts +3 -0
  81. package/src/components/date-picker/use-date-input-popover.ts +48 -0
  82. package/src/components/date-picker/use-date-input.ts +125 -0
  83. package/src/components/dialog/dialog.stories.tsx +171 -0
  84. package/src/components/dialog/dialog.test.tsx +68 -0
  85. package/src/components/dialog/dialog.tsx +277 -0
  86. package/src/components/dialog/index.ts +1 -0
  87. package/src/components/divider/divider.stories.tsx +70 -0
  88. package/src/components/divider/divider.test.tsx +22 -0
  89. package/src/components/divider/divider.tsx +23 -0
  90. package/src/components/divider/index.ts +1 -0
  91. package/src/components/dropzone/dropzone.stories.tsx +210 -0
  92. package/src/components/dropzone/dropzone.tsx +154 -0
  93. package/src/components/dropzone/file-types.ts +64 -0
  94. package/src/components/dropzone/index.ts +3 -0
  95. package/src/components/dropzone/upload-primitives.tsx +310 -0
  96. package/src/components/dropzone/use-dropzone.ts +122 -0
  97. package/src/components/empty-state/empty-state.stories.tsx +56 -0
  98. package/src/components/empty-state/empty-state.tsx +39 -0
  99. package/src/components/empty-state/index.ts +1 -0
  100. package/src/components/field/field.stories.tsx +223 -0
  101. package/src/components/field/field.tsx +229 -0
  102. package/src/components/field/index.ts +1 -0
  103. package/src/components/form/form.stories.tsx +594 -0
  104. package/src/components/form/form.tsx +30 -0
  105. package/src/components/form/index.ts +1 -0
  106. package/src/components/heading/heading.stories.tsx +74 -0
  107. package/src/components/heading/heading.tsx +28 -0
  108. package/src/components/heading/heading.variants.ts +27 -0
  109. package/src/components/heading/index.ts +1 -0
  110. package/src/components/index.ts +46 -0
  111. package/src/components/input/index.ts +1 -0
  112. package/src/components/input/input.stories.tsx +104 -0
  113. package/src/components/input/input.tsx +75 -0
  114. package/src/components/kbd/index.ts +1 -0
  115. package/src/components/kbd/kbd.stories.tsx +40 -0
  116. package/src/components/kbd/kbd.tsx +31 -0
  117. package/src/components/kbd/kbd.variants.ts +26 -0
  118. package/src/components/label/index.ts +1 -0
  119. package/src/components/label/label.stories.tsx +68 -0
  120. package/src/components/label/label.test.tsx +61 -0
  121. package/src/components/label/label.tsx +62 -0
  122. package/src/components/loader/index.ts +1 -0
  123. package/src/components/loader/loader.stories.tsx +60 -0
  124. package/src/components/loader/loader.test.tsx +26 -0
  125. package/src/components/loader/loader.tsx +60 -0
  126. package/src/components/menu/index.ts +2 -0
  127. package/src/components/menu/menu-primitives.tsx +248 -0
  128. package/src/components/menu/menu.stories.tsx +203 -0
  129. package/src/components/menu/menu.tsx +100 -0
  130. package/src/components/menu/util/render-menu-item.tsx +54 -0
  131. package/src/components/multi-select/hooks/use-multi-select.ts +66 -0
  132. package/src/components/multi-select/index.ts +1 -0
  133. package/src/components/multi-select/multi-select.stories.tsx +294 -0
  134. package/src/components/multi-select/multi-select.tsx +300 -0
  135. package/src/components/multi-select/multi-select.variants.ts +22 -0
  136. package/src/components/number-input/index.ts +1 -0
  137. package/src/components/number-input/number-input.stories.tsx +209 -0
  138. package/src/components/number-input/number-input.test.tsx +87 -0
  139. package/src/components/number-input/number-input.tsx +230 -0
  140. package/src/components/pagination/components/pagination-option.tsx +27 -0
  141. package/src/components/pagination/index.ts +1 -0
  142. package/src/components/pagination/pagination.stories.tsx +80 -0
  143. package/src/components/pagination/pagination.test.tsx +76 -0
  144. package/src/components/pagination/pagination.tsx +102 -0
  145. package/src/components/password/index.ts +1 -0
  146. package/src/components/password/password.stories.tsx +104 -0
  147. package/src/components/password/password.tsx +71 -0
  148. package/src/components/popover/index.ts +1 -0
  149. package/src/components/popover/popover.stories.tsx +213 -0
  150. package/src/components/popover/popover.tsx +203 -0
  151. package/src/components/progress/index.ts +1 -0
  152. package/src/components/progress/progress.stories.tsx +124 -0
  153. package/src/components/progress/progress.test.tsx +25 -0
  154. package/src/components/progress/progress.tsx +124 -0
  155. package/src/components/scroll-area/index.ts +1 -0
  156. package/src/components/scroll-area/scroll-area.stories.tsx +166 -0
  157. package/src/components/scroll-area/scroll-area.tsx +64 -0
  158. package/src/components/select/index.ts +1 -0
  159. package/src/components/select/select.stories.tsx +253 -0
  160. package/src/components/select/select.tsx +430 -0
  161. package/src/components/show/index.ts +1 -0
  162. package/src/components/show/show.stories.tsx +197 -0
  163. package/src/components/show/show.test.tsx +41 -0
  164. package/src/components/show/show.tsx +16 -0
  165. package/src/components/skeleton/index.ts +1 -0
  166. package/src/components/skeleton/skeleton.stories.tsx +36 -0
  167. package/src/components/skeleton/skeleton.test.tsx +14 -0
  168. package/src/components/skeleton/skeleton.tsx +15 -0
  169. package/src/components/stack/index.ts +1 -0
  170. package/src/components/stack/stack.stories.tsx +194 -0
  171. package/src/components/stack/stack.tsx +52 -0
  172. package/src/components/stepper/Stepper.tsx +190 -0
  173. package/src/components/stepper/context/stepper-context.tsx +11 -0
  174. package/src/components/stepper/index.ts +1 -0
  175. package/src/components/stepper/stepper.stories.tsx +130 -0
  176. package/src/components/stepper/stepper.test.tsx +91 -0
  177. package/src/components/switch/index.ts +1 -0
  178. package/src/components/switch/switch.stories.tsx +122 -0
  179. package/src/components/switch/switch.test.tsx +30 -0
  180. package/src/components/switch/switch.tsx +86 -0
  181. package/src/components/table/index.ts +3 -0
  182. package/src/components/table/table-primitives.tsx +122 -0
  183. package/src/components/table/table.model.ts +20 -0
  184. package/src/components/table/table.stories.tsx +169 -0
  185. package/src/components/table/table.test.tsx +91 -0
  186. package/src/components/table/table.tsx +109 -0
  187. package/src/components/table-pagination/index.ts +2 -0
  188. package/src/components/table-pagination/table-pagination.model.ts +2 -0
  189. package/src/components/table-pagination/table-pagination.stories.tsx +23 -0
  190. package/src/components/table-pagination/table-pagination.test.tsx +32 -0
  191. package/src/components/table-pagination/table-pagination.tsx +108 -0
  192. package/src/components/tabs/context/tabs-context.tsx +14 -0
  193. package/src/components/tabs/index.ts +1 -0
  194. package/src/components/tabs/tabs.stories.tsx +182 -0
  195. package/src/components/tabs/tabs.test.tsx +61 -0
  196. package/src/components/tabs/tabs.tsx +175 -0
  197. package/src/components/tag/index.ts +2 -0
  198. package/src/components/tag/tag.stories.tsx +170 -0
  199. package/src/components/tag/tag.test.tsx +18 -0
  200. package/src/components/tag/tag.tsx +99 -0
  201. package/src/components/tag/tag.variants.ts +31 -0
  202. package/src/components/textarea/index.ts +1 -0
  203. package/src/components/textarea/textarea.stories.tsx +73 -0
  204. package/src/components/textarea/textarea.tsx +105 -0
  205. package/src/components/timeline/index.ts +1 -0
  206. package/src/components/timeline/timeline-status.ts +5 -0
  207. package/src/components/timeline/timeline.stories.tsx +84 -0
  208. package/src/components/timeline/timeline.tsx +147 -0
  209. package/src/components/toast/index.ts +1 -0
  210. package/src/components/toast/toast.stories.tsx +392 -0
  211. package/src/components/toast/toast.test.tsx +50 -0
  212. package/src/components/toast/toast.tsx +411 -0
  213. package/src/components/tooltip/index.ts +1 -0
  214. package/src/components/tooltip/tooltip.stories.tsx +226 -0
  215. package/src/components/tooltip/tooltip.test.tsx +46 -0
  216. package/src/components/tooltip/tooltip.tsx +171 -0
  217. package/src/components/tree/hooks/use-controllable-tree-state.ts +80 -0
  218. package/src/components/tree/index.ts +2 -0
  219. package/src/components/tree/tree-primitives.tsx +126 -0
  220. package/src/components/tree/tree.stories.tsx +468 -0
  221. package/src/components/tree/tree.tsx +42 -0
  222. package/src/hooks/index.ts +26 -0
  223. package/src/hooks/useArray/__doc__/useArray.stories.tsx +100 -0
  224. package/src/hooks/useArray/__test__/useArray.test.tsx +88 -0
  225. package/src/hooks/useArray/index.ts +1 -0
  226. package/src/hooks/useArray/useArray.ts +76 -0
  227. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +149 -0
  228. package/src/hooks/useAsync/__test__/useAsync.test.tsx +68 -0
  229. package/src/hooks/useAsync/index.ts +1 -0
  230. package/src/hooks/useAsync/useAsync.ts +58 -0
  231. package/src/hooks/useClickOutside/__doc__/useClickOutside.stories.tsx +40 -0
  232. package/src/hooks/useClickOutside/__test__/useClickOutside.test.tsx +33 -0
  233. package/src/hooks/useClickOutside/index.ts +1 -0
  234. package/src/hooks/useClickOutside/useClickOutside.ts +26 -0
  235. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +45 -0
  236. package/src/hooks/useClipboard/__test__/useClipboard.test.tsx +19 -0
  237. package/src/hooks/useClipboard/index.ts +1 -0
  238. package/src/hooks/useClipboard/useClipboard.tsx +28 -0
  239. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +84 -0
  240. package/src/hooks/useDebounceCallback/index.ts +1 -0
  241. package/src/hooks/useDebounceCallback/useDebouncedCallback.ts +23 -0
  242. package/src/hooks/useDebounceValue/__doc__/useDebouncedValue.stories.tsx +75 -0
  243. package/src/hooks/useDebounceValue/index.ts +1 -0
  244. package/src/hooks/useDebounceValue/useDebouncedValue.ts +17 -0
  245. package/src/hooks/useDisclosure/__doc__/useDisclosure.stories.tsx +39 -0
  246. package/src/hooks/useDisclosure/__test__/useDisclosure.test.ts +43 -0
  247. package/src/hooks/useDisclosure/index.ts +1 -0
  248. package/src/hooks/useDisclosure/useDisclosure.ts +37 -0
  249. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +26 -0
  250. package/src/hooks/useDocumentTitle/index.ts +1 -0
  251. package/src/hooks/useDocumentTitle/useDocumentTitle.tsx +11 -0
  252. package/src/hooks/useEventListener/__doc__/useEventListener.stories.tsx +28 -0
  253. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +26 -0
  254. package/src/hooks/useEventListener/index.ts +1 -0
  255. package/src/hooks/useEventListener/useEventListener.ts +25 -0
  256. package/src/hooks/useFocusTrap/__doc__/useFocusTrap.stories.tsx +37 -0
  257. package/src/hooks/useFocusTrap/index.ts +1 -0
  258. package/src/hooks/useFocusTrap/scopeTab.ts +38 -0
  259. package/src/hooks/useFocusTrap/tabbable.ts +70 -0
  260. package/src/hooks/useFocusTrap/useFocusTrap.ts +78 -0
  261. package/src/hooks/useHotkey/__docs__/useHotkey.stories.tsx +116 -0
  262. package/src/hooks/useHotkey/__test__/useHotkey.test.tsx +105 -0
  263. package/src/hooks/useHotkey/__utils__/create-hotkey-listener.ts +25 -0
  264. package/src/hooks/useHotkey/__utils__/index.ts +3 -0
  265. package/src/hooks/useHotkey/__utils__/is-input-field.ts +14 -0
  266. package/src/hooks/useHotkey/__utils__/match-key-modifiers.ts +25 -0
  267. package/src/hooks/useHotkey/index.ts +1 -0
  268. package/src/hooks/useHotkey/useHotkey.ts +34 -0
  269. package/src/hooks/useHover/__doc__/useHover.stories.tsx +41 -0
  270. package/src/hooks/useHover/__test__/useHover.test.tsx +45 -0
  271. package/src/hooks/useHover/index.ts +1 -0
  272. package/src/hooks/useHover/useHover.tsx +40 -0
  273. package/src/hooks/useIsVisible/__doc__/useIsVisible.stories.tsx +60 -0
  274. package/src/hooks/useIsVisible/index.ts +1 -0
  275. package/src/hooks/useIsVisible/useIsVisible.tsx +50 -0
  276. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +86 -0
  277. package/src/hooks/useLocalStorage/__test__/useLocalStorage.test.ts +85 -0
  278. package/src/hooks/useLocalStorage/index.ts +1 -0
  279. package/src/hooks/useLocalStorage/useLocalStorage.ts +57 -0
  280. package/src/hooks/useMediaQuery/__doc__/useMediaQuery.stories.tsx +39 -0
  281. package/src/hooks/useMediaQuery/index.ts +1 -0
  282. package/src/hooks/useMediaQuery/useMediaQuery.ts +22 -0
  283. package/src/hooks/useMemoizedFn/index.ts +1 -0
  284. package/src/hooks/useMemoizedFn/useMemoizedFn.ts +32 -0
  285. package/src/hooks/useMutation/__doc__/useMutation.stories.tsx +111 -0
  286. package/src/hooks/useMutation/__test__/useMutation.test.tsx +83 -0
  287. package/src/hooks/useMutation/index.ts +1 -0
  288. package/src/hooks/useMutation/useMutation.tsx +60 -0
  289. package/src/hooks/useObject/__doc__/useObject.stories.tsx +119 -0
  290. package/src/hooks/useObject/__test__/useObject.test.tsx +87 -0
  291. package/src/hooks/useObject/index.ts +1 -0
  292. package/src/hooks/useObject/useObject.tsx +48 -0
  293. package/src/hooks/usePagination/__doc__/usePagination.stories.tsx +72 -0
  294. package/src/hooks/usePagination/__test__/usePagination.test.tsx +98 -0
  295. package/src/hooks/usePagination/index.ts +2 -0
  296. package/src/hooks/usePagination/usePagination.tsx +74 -0
  297. package/src/hooks/usePortal/__doc__/usePortal.stories.tsx +19 -0
  298. package/src/hooks/usePortal/__test__/usePortal.test.tsx +20 -0
  299. package/src/hooks/usePortal/index.ts +1 -0
  300. package/src/hooks/usePortal/usePortal.ts +40 -0
  301. package/src/hooks/usePreventCloseWindow/__doc__/usePreventCloseWindow.stories.tsx +32 -0
  302. package/src/hooks/usePreventCloseWindow/index.ts +1 -0
  303. package/src/hooks/usePreventCloseWindow/usePreventCloseWindow.ts +33 -0
  304. package/src/hooks/useRangePagination/__test__/useRangePagination.test.tsx +63 -0
  305. package/src/hooks/useRangePagination/index.ts +2 -0
  306. package/src/hooks/useRangePagination/useRangePagination.tsx +72 -0
  307. package/src/hooks/useSelection/__doc__/useSelection.stories.tsx +140 -0
  308. package/src/hooks/useSelection/__test__/useSelection.test.tsx +57 -0
  309. package/src/hooks/useSelection/index.ts +1 -0
  310. package/src/hooks/useSelection/useSelection.ts +121 -0
  311. package/src/hooks/useStep/__doc__/useStep.stories.tsx +98 -0
  312. package/src/hooks/useStep/__test__/useStep.test.ts +51 -0
  313. package/src/hooks/useStep/index.ts +1 -0
  314. package/src/hooks/useStep/useStep.ts +57 -0
  315. package/src/hooks/useToggle/__doc__/useToggle.stories.tsx +25 -0
  316. package/src/hooks/useToggle/__test__/useToggle.test.tsx +43 -0
  317. package/src/hooks/useToggle/index.ts +1 -0
  318. package/src/hooks/useToggle/useToggle.ts +16 -0
  319. package/src/index.ts +6 -0
  320. package/src/lib/cn.ts +8 -0
  321. package/src/lib/index.ts +1 -0
  322. package/src/models/Generic.model.ts +67 -0
  323. package/src/models/index.ts +1 -0
  324. package/src/providers/index.ts +2 -0
  325. package/src/providers/library-provider.tsx +44 -0
  326. package/src/providers/theme/ThemeProvider.tsx +25 -0
  327. package/src/providers/theme/index.ts +3 -0
  328. package/src/providers/theme/types.ts +11 -0
  329. package/src/providers/theme/useThemeProps.ts +25 -0
  330. package/src/stores/theme.store.ts +31 -0
  331. package/src/styles/components.css +4 -0
  332. package/src/styles/index.css +2 -0
  333. package/src/styles/library.css +2 -0
  334. package/src/styles/theme.css +232 -0
  335. package/src/utils/dates/parseDateRange.utility.ts +39 -0
  336. package/src/utils/form.tsx +91 -0
  337. package/src/utils/functions/createSafeContext.ts +17 -0
  338. package/src/utils/functions/ensureReactElement.tsx +30 -0
  339. package/src/utils/functions/getFormData.ts +19 -0
  340. package/src/utils/functions/index.ts +4 -0
  341. package/src/utils/functions/mergeRefs.ts +18 -0
  342. package/src/utils/index.ts +3 -0
  343. package/src/utils/strings/extractInitials.utility.ts +10 -0
  344. package/src/utils/strings/index.ts +1 -0
  345. package/src/utils/tests/click.ts +3 -0
  346. package/src/utils/tests/index.ts +2 -0
  347. package/src/utils/tests/keyboard.ts +21 -0
  348. package/src/utils/tests/type.ts +6 -0
  349. package/dist/components.css +0 -2
@@ -0,0 +1,81 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Inbox, Phone } from "lucide-react";
3
+ import { Stack } from "../stack";
4
+ import { Center } from ".";
5
+
6
+ /**
7
+ * Layout primitive that centers its children both horizontally and vertically using flexbox.
8
+ * Supports polymorphic rendering via `as` and an `inline` mode for `inline-flex` contexts.
9
+ */
10
+ const meta: Meta<typeof Center> = {
11
+ title: "Components/Center",
12
+ component: Center,
13
+ argTypes: {
14
+ children: { control: false },
15
+ className: { control: false },
16
+ },
17
+ };
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof Center>;
21
+
22
+ export const Default: Story = {
23
+ render: () => (
24
+ <Center className="h-[300px] w-full border rounded-lg border-dashed">
25
+ <Stack
26
+ direction="vertical"
27
+ align="center"
28
+ gap={8}
29
+ className="text-muted-foreground"
30
+ >
31
+ <Inbox className="size-10 stroke-1" />
32
+ <span className="text-sm font-medium">No results found</span>
33
+ <span className="text-xs">Try adjusting your search or filters</span>
34
+ </Stack>
35
+ </Center>
36
+ ),
37
+ };
38
+
39
+ export const Icon: Story = {
40
+ render: () => (
41
+ <Center className="w-fit p-4 bg-primary text-white rounded-full">
42
+ <Phone />
43
+ </Center>
44
+ ),
45
+ };
46
+
47
+ /**
48
+ * `inline` switches the display to `inline-flex`, allowing the Center to sit inline with text
49
+ * or other inline elements without breaking the flow.
50
+ */
51
+ export const Inline: Story = {
52
+ render: () => (
53
+ <p className="text-sm">
54
+ Text before{" "}
55
+ <Center
56
+ inline
57
+ className="w-8 h-8 bg-primary text-white rounded-full text-xs"
58
+ >
59
+ 42
60
+ </Center>{" "}
61
+ text after
62
+ </p>
63
+ ),
64
+ };
65
+
66
+ /**
67
+ * `as` renders the Center as a different HTML element for semantic markup.
68
+ * Common use cases: `main` for page root, `section` for content areas.
69
+ */
70
+ export const As: Story = {
71
+ render: () => (
72
+ <Center
73
+ as="main"
74
+ className="bg-secondary w-[400px] aspect-square rounded p-4"
75
+ >
76
+ <div className="bg-primary text-primary-foreground rounded p-4">
77
+ Rendered as &lt;main&gt;
78
+ </div>
79
+ </Center>
80
+ ),
81
+ };
@@ -0,0 +1,24 @@
1
+ import { ComponentProps, ElementType } from "react";
2
+ import { cn } from "../../lib";
3
+
4
+ interface CenterProps extends ComponentProps<"div"> {
5
+ as?: ElementType;
6
+ inline?: boolean;
7
+ }
8
+
9
+ export const Center = ({
10
+ as: Tag = "div",
11
+ inline = false,
12
+ className,
13
+ ...props
14
+ }: CenterProps) => (
15
+ <Tag
16
+ className={cn(
17
+ inline ? "inline-flex" : "flex",
18
+ "items-center justify-center",
19
+ className,
20
+ )}
21
+ {...props}
22
+ data-slot="center"
23
+ />
24
+ );
@@ -0,0 +1 @@
1
+ export * from "./center";
@@ -0,0 +1,307 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { useState } from "react";
3
+ import { action } from "storybook/actions";
4
+ import { Button } from "../../components";
5
+ import { useSelection } from "../../hooks";
6
+ import { Checkbox, CheckboxIndicator, CheckboxRoot } from "./checkbox";
7
+
8
+ /**
9
+ * Checkbox built on Base UI. Handles checked, unchecked, and indeterminate
10
+ * states natively. Built on `@base-ui/react/checkbox`.
11
+ *
12
+ * Compound API:
13
+ * - `Checkbox` — single checkbox, renders the control only
14
+ * - `Checkbox.Item` — labeled checkbox for use inside `Checkbox.Group`
15
+ * - `Checkbox.Group` — group wrapper with value management and a11y fieldset
16
+ * - `Checkbox.Legend` — composable legend for `Checkbox.Group`
17
+ *
18
+ * `controlFirst` (default `true`) on `Checkbox.Group` places the checkbox before
19
+ * the label on all `Checkbox.Item` children via context.
20
+ *
21
+ * When `Checkbox.Group` has an `error`, all `Checkbox.Item` children
22
+ * automatically receive `aria-invalid` so their border turns red.
23
+ *
24
+ * Reference: [Checkbox – Base UI](https://base-ui.com/react/components/checkbox)
25
+ */
26
+ const meta: Meta<typeof Checkbox> = {
27
+ title: "Components/Checkbox",
28
+ component: Checkbox,
29
+ parameters: { layout: "centered" },
30
+ args: {
31
+ name: "checkbox",
32
+ },
33
+ argTypes: {
34
+ onCheckedChange: { control: false },
35
+ indicatorProps: { control: false },
36
+ tooltip: { control: false },
37
+ },
38
+ };
39
+
40
+ export default meta;
41
+ type Story = StoryObj<typeof Checkbox>;
42
+
43
+ export const Default: Story = {};
44
+
45
+ /**
46
+ * Pass `label` for a linked `<label>` element. `children` works as an alias —
47
+ * `label` takes priority when both are provided.
48
+ */
49
+ export const WithLabel: Story = {
50
+ args: {
51
+ label: "Receive marketing emails",
52
+ },
53
+ };
54
+
55
+ /**
56
+ * Pass `children` as an alternative to `label`.
57
+ */
58
+ export const WithChildren: Story = {
59
+ render: () => <Checkbox>I accept the terms and conditions</Checkbox>,
60
+ };
61
+
62
+ /**
63
+ * `required` shows a `*` in the label. `tooltip` renders an info icon next
64
+ * to the label that opens a tooltip on hover/focus.
65
+ */
66
+ export const WithRequired: Story = {
67
+ args: {
68
+ label: "I accept the terms and conditions",
69
+ required: true,
70
+ tooltip: "You must accept the terms to continue.",
71
+ },
72
+ };
73
+
74
+ /**
75
+ * `controlFirst={false}` places the label before the checkbox.
76
+ * Default is `true` (checkbox → label).
77
+ */
78
+ export const ControlFirst: Story = {
79
+ args: {
80
+ controlFirst: false,
81
+ label: "I accept the terms and conditions",
82
+ },
83
+ };
84
+
85
+ export const Disabled: Story = {
86
+ args: {
87
+ disabled: true,
88
+ defaultChecked: true,
89
+ },
90
+ };
91
+
92
+ /**
93
+ * `aria-invalid="true"` applies error border and ring — no Field wrapper needed.
94
+ */
95
+ export const Invalid: Story = {
96
+ args: { "aria-invalid": "true" },
97
+ };
98
+
99
+ /**
100
+ * Use `checked` + `onCheckedChange` for controlled mode.
101
+ */
102
+ export const Controlled: Story = {
103
+ render: () => {
104
+ const [checked, setChecked] = useState(true);
105
+ return <Checkbox checked={checked} onCheckedChange={setChecked} />;
106
+ },
107
+ };
108
+
109
+ /**
110
+ * `indeterminate` is a first-class prop — Base UI sets `aria-checked="mixed"`
111
+ * and manages the visual state automatically. Clicking an indeterminate
112
+ * checkbox moves it to checked.
113
+ */
114
+ export const Indeterminate: Story = {
115
+ render: () => {
116
+ const ITEMS = [1, 2, 3, 4];
117
+ const { selected, toggle, toggleAll, isAllSelected, isSomeSelected } =
118
+ useSelection(ITEMS);
119
+
120
+ return (
121
+ <div className="space-y-2">
122
+ <Checkbox
123
+ checked={isAllSelected}
124
+ indeterminate={isSomeSelected}
125
+ onCheckedChange={() => toggleAll()}
126
+ >
127
+ Select all
128
+ </Checkbox>
129
+ <div className="ml-6 space-y-2 border-l-2 pl-4">
130
+ {ITEMS.map((item) => (
131
+ <Checkbox
132
+ key={item}
133
+ checked={selected.includes(item)}
134
+ onCheckedChange={() => toggle(item)}
135
+ >
136
+ Option {item}
137
+ </Checkbox>
138
+ ))}
139
+ </div>
140
+ </div>
141
+ );
142
+ },
143
+ };
144
+
145
+ /**
146
+ * Base UI injects a hidden `<input type="checkbox">` so `name` and `required`
147
+ * work with native `FormData` out of the box.
148
+ */
149
+ export const Required: Story = {
150
+ args: {
151
+ name: "terms",
152
+ required: true,
153
+ label: "I accept the terms and conditions",
154
+ },
155
+ render: (args) => (
156
+ <form
157
+ className="space-y-3"
158
+ onSubmit={(event) => {
159
+ event.preventDefault();
160
+ const data = new FormData(event.currentTarget);
161
+ action("submit")(Object.fromEntries(data));
162
+ }}
163
+ >
164
+ <Checkbox {...args} />
165
+ <Button type="submit">Submit</Button>
166
+ </form>
167
+ ),
168
+ };
169
+
170
+ /**
171
+ * `Checkbox.Group` manages selected values and provides an accessible
172
+ * `<fieldset>` wrapper. Each `Checkbox.Item` needs a `value` prop to
173
+ * participate in the group's state.
174
+ *
175
+ * Use `defaultValue` for uncontrolled groups.
176
+ */
177
+ export const Group: Story = {
178
+ render: () => (
179
+ <Checkbox.Group
180
+ legend="Notifications"
181
+ defaultValue={["email"]}
182
+ onChange={action("onChange")}
183
+ >
184
+ <Checkbox.Item value="email" label="Email" />
185
+ <Checkbox.Item value="sms" label="SMS" />
186
+ <Checkbox.Item value="push" label="Push notifications" />
187
+ </Checkbox.Group>
188
+ ),
189
+ };
190
+
191
+ /**
192
+ * `error` takes visual priority over `description` — both props can coexist,
193
+ * but description only renders when there is no error. When `error` is set,
194
+ * all items in the group receive `aria-invalid` and their borders turn red.
195
+ */
196
+ export const GroupWithError: Story = {
197
+ render: () => (
198
+ <Checkbox.Group
199
+ legend="Preferences"
200
+ description="Select at least one option."
201
+ error="You must select at least one option."
202
+ >
203
+ <Checkbox.Item value="email" label="Email" />
204
+ <Checkbox.Item value="sms" label="SMS" />
205
+ </Checkbox.Group>
206
+ ),
207
+ };
208
+
209
+ /**
210
+ * `allValues` enables the select-all pattern: Base UI derives the indeterminate
211
+ * state automatically when only some items are checked. The parent checkbox
212
+ * controls the entire group when clicked.
213
+ */
214
+ export const GroupParent: Story = {
215
+ render: () => {
216
+ const ALL = ["email", "sms", "push"];
217
+ const [value, setValue] = useState(["email"]);
218
+
219
+ const isAll = value.length === ALL.length;
220
+ const isSome = value.length > 0 && !isAll;
221
+
222
+ return (
223
+ <div className="space-y-2">
224
+ <Checkbox
225
+ checked={isAll}
226
+ indeterminate={isSome}
227
+ onCheckedChange={(checked) => setValue(checked ? ALL : [])}
228
+ />
229
+ <div className="ml-6 border-l-2 pl-4">
230
+ <Checkbox.Group value={value} onChange={setValue} allValues={ALL}>
231
+ <Checkbox.Item value="email" label="Email" />
232
+ <Checkbox.Item value="sms" label="SMS" />
233
+ <Checkbox.Item value="push" label="Push notifications" />
234
+ </Checkbox.Group>
235
+ </div>
236
+ </div>
237
+ );
238
+ },
239
+ };
240
+
241
+ /**
242
+ * Use `value` + `onChange` for a controlled group.
243
+ */
244
+ export const GroupControlled: Story = {
245
+ render: () => {
246
+ const [value, setValue] = useState<string[]>(["email"]);
247
+ return (
248
+ <Checkbox.Group
249
+ legend="Contact channel"
250
+ value={value}
251
+ onChange={setValue}
252
+ >
253
+ <Checkbox.Item value="email" label="Email" />
254
+ <Checkbox.Item value="sms" label="SMS" />
255
+ <Checkbox.Item value="push" label="Push notifications" />
256
+ </Checkbox.Group>
257
+ );
258
+ },
259
+ };
260
+
261
+ /**
262
+ * `Checkbox.Legend` can be placed as a direct child of `Checkbox.Group`
263
+ * instead of the `legend` prop when you need custom styling or want to
264
+ * visually hide it with `className="sr-only"`.
265
+ *
266
+ * ```tsx
267
+ * <Checkbox.Group>
268
+ * <Checkbox.Legend className="sr-only">Preferences</Checkbox.Legend>
269
+ * <Checkbox.Item value="a" label="Option A" />
270
+ * </Checkbox.Group>
271
+ * ```
272
+ */
273
+ export const ComposableLegend: Story = {
274
+ render: () => (
275
+ <Checkbox.Group>
276
+ <Checkbox.Legend className="text-base font-semibold">
277
+ Contact preferences
278
+ </Checkbox.Legend>
279
+ <Checkbox.Item value="email" label="Email" />
280
+ <Checkbox.Item value="sms" label="SMS" />
281
+ </Checkbox.Group>
282
+ ),
283
+ };
284
+
285
+ /**
286
+ * Direct composition with primitives for full structural control.
287
+ * Use when the composite props are not enough — custom ordering,
288
+ * extra elements between parts, or non-standard layouts.
289
+ *
290
+ * ```tsx
291
+ * <CheckboxRoot id="custom">
292
+ * <CheckboxIndicator />
293
+ * </CheckboxRoot>
294
+ * ```
295
+ */
296
+ export const Primitive: Story = {
297
+ render: () => (
298
+ <div className="flex items-center gap-2">
299
+ <CheckboxRoot id="primitive-example">
300
+ <CheckboxIndicator />
301
+ </CheckboxRoot>
302
+ <label htmlFor="primitive-example" className="text-sm select-none">
303
+ Manual composition
304
+ </label>
305
+ </div>
306
+ ),
307
+ };
@@ -0,0 +1,273 @@
1
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
2
+ import { CheckboxGroup as CheckboxGroupPrimitive } from "@base-ui/react/checkbox-group";
3
+ import { Field as FieldBase } from "@base-ui/react/field";
4
+ import { Fieldset } from "@base-ui/react/fieldset";
5
+ import type * as React from "react";
6
+ import { createContext, type ReactNode, useContext, useId } from "react";
7
+ import { cn } from "../../lib";
8
+ import { Label } from "../label";
9
+
10
+ // ── Primitives ─────────────────────────────────────────────────────────────────
11
+
12
+ export function CheckboxRoot({
13
+ className,
14
+ ...props
15
+ }: CheckboxPrimitive.Root.Props): React.ReactElement {
16
+ return (
17
+ <CheckboxPrimitive.Root
18
+ className={cn(
19
+ "peer relative inline-flex size-4.5 shrink-0 items-center justify-center rounded-[.25rem] border border-input bg-background not-dark:bg-clip-padding shadow-xs/5 outline-none ring-ring transition-shadow",
20
+ "before:pointer-events-none before:absolute before:inset-0 before:rounded-[3px]",
21
+ "not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)]",
22
+ "focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background",
23
+ "aria-invalid:border-error focus-visible:aria-invalid:border-error focus-visible:aria-invalid:ring-error",
24
+ "data-disabled:cursor-not-allowed data-disabled:opacity-64",
25
+ "sm:size-4",
26
+ "dark:not-data-disabled:not-data-checked:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]",
27
+ "[[data-disabled],[data-checked],[aria-invalid]]:shadow-none",
28
+ className,
29
+ )}
30
+ data-slot="checkbox"
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ export function CheckboxIndicator({
37
+ className,
38
+ ...props
39
+ }: CheckboxPrimitive.Indicator.Props): React.ReactElement {
40
+ return (
41
+ <CheckboxPrimitive.Indicator
42
+ className={cn(
43
+ "absolute -inset-px flex items-center justify-center rounded-[.25rem] text-primary-foreground",
44
+ "data-unchecked:hidden data-checked:bg-primary data-indeterminate:text-foreground",
45
+ className,
46
+ )}
47
+ data-slot="checkbox-indicator"
48
+ render={(
49
+ renderProps: React.ComponentProps<"span">,
50
+ state: CheckboxPrimitive.Indicator.State,
51
+ ) => (
52
+ <span {...renderProps}>
53
+ {state.indeterminate ? (
54
+ <svg
55
+ aria-hidden="true"
56
+ className="size-3.5 sm:size-3"
57
+ fill="none"
58
+ height="24"
59
+ stroke="currentColor"
60
+ strokeLinecap="round"
61
+ strokeLinejoin="round"
62
+ strokeWidth="3"
63
+ viewBox="0 0 24 24"
64
+ width="24"
65
+ xmlns="http://www.w3.org/2000/svg"
66
+ >
67
+ <path d="M5.252 12h13.496" />
68
+ </svg>
69
+ ) : (
70
+ <svg
71
+ aria-hidden="true"
72
+ className="size-3.5 sm:size-3"
73
+ fill="none"
74
+ height="24"
75
+ stroke="currentColor"
76
+ strokeLinecap="round"
77
+ strokeLinejoin="round"
78
+ strokeWidth="3"
79
+ viewBox="0 0 24 24"
80
+ width="24"
81
+ xmlns="http://www.w3.org/2000/svg"
82
+ >
83
+ <path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
84
+ </svg>
85
+ )}
86
+ </span>
87
+ )}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ // ── Group context ──────────────────────────────────────────────────────────────
94
+
95
+ const CheckboxGroupContext = createContext<{
96
+ controlFirst: boolean;
97
+ invalid: boolean;
98
+ }>({
99
+ controlFirst: true,
100
+ invalid: false,
101
+ });
102
+
103
+ // ── Composite: Checkbox (single) ───────────────────────────────────────────────
104
+
105
+ interface CheckboxProps extends Omit<CheckboxPrimitive.Root.Props, "children"> {
106
+ children?: ReactNode;
107
+ label?: ReactNode;
108
+ tooltip?: ReactNode;
109
+ indicatorProps?: CheckboxPrimitive.Indicator.Props;
110
+ controlFirst?: boolean;
111
+ }
112
+
113
+ function CheckboxSingle({
114
+ className,
115
+ indicatorProps,
116
+ label,
117
+ children,
118
+ tooltip,
119
+ controlFirst = true,
120
+ required,
121
+ id,
122
+ ...props
123
+ }: CheckboxProps): React.ReactElement {
124
+ const generatedId = useId();
125
+ const idToUse = id ?? generatedId;
126
+ const labelContent = label ?? children;
127
+
128
+ const checkbox = (
129
+ <CheckboxRoot
130
+ id={idToUse}
131
+ className={className}
132
+ required={required}
133
+ {...props}
134
+ >
135
+ <CheckboxIndicator {...indicatorProps} />
136
+ </CheckboxRoot>
137
+ );
138
+
139
+ if (labelContent !== undefined) {
140
+ return (
141
+ <div
142
+ className={cn(
143
+ "flex select-none items-center gap-x-2",
144
+ !controlFirst && "flex-row-reverse justify-end",
145
+ )}
146
+ >
147
+ {checkbox}
148
+ <Label htmlFor={idToUse} required={required} tooltip={tooltip}>
149
+ {labelContent}
150
+ </Label>
151
+ </div>
152
+ );
153
+ }
154
+
155
+ return checkbox;
156
+ }
157
+
158
+ // ── Composite: Checkbox.Item (for use in Checkbox.Group) ──────────────────────
159
+
160
+ export interface CheckboxItemProps
161
+ extends Omit<CheckboxPrimitive.Root.Props, "children"> {
162
+ label: ReactNode;
163
+ }
164
+
165
+ function CheckboxItem({
166
+ className,
167
+ label,
168
+ id,
169
+ ...props
170
+ }: CheckboxItemProps): React.ReactElement {
171
+ const { controlFirst, invalid } = useContext(CheckboxGroupContext);
172
+ const generatedId = useId();
173
+ const idToUse = id ?? generatedId;
174
+
175
+ return (
176
+ <FieldBase.Root invalid={invalid}>
177
+ <div
178
+ className={cn(
179
+ "flex select-none items-center gap-x-2",
180
+ !controlFirst && "flex-row-reverse justify-end",
181
+ )}
182
+ >
183
+ <CheckboxRoot id={idToUse} className={className} {...props}>
184
+ <CheckboxIndicator />
185
+ </CheckboxRoot>
186
+ <Label htmlFor={idToUse}>{label}</Label>
187
+ </div>
188
+ </FieldBase.Root>
189
+ );
190
+ }
191
+
192
+ // ── Composite: Checkbox.Legend ─────────────────────────────────────────────────
193
+
194
+ export interface CheckboxLegendProps {
195
+ children: ReactNode;
196
+ className?: string;
197
+ }
198
+
199
+ function CheckboxLegend({
200
+ children,
201
+ className,
202
+ }: CheckboxLegendProps): React.ReactElement {
203
+ return (
204
+ <Fieldset.Legend
205
+ className={cn("text-sm font-medium leading-none", className)}
206
+ >
207
+ {children}
208
+ </Fieldset.Legend>
209
+ );
210
+ }
211
+
212
+ // ── Composite: Checkbox.Group ──────────────────────────────────────────────────
213
+
214
+ export interface CheckboxGroupProps {
215
+ legend?: ReactNode;
216
+ children: ReactNode;
217
+ error?: string;
218
+ description?: ReactNode;
219
+ defaultValue?: string[];
220
+ value?: string[];
221
+ onChange?: (value: string[]) => void;
222
+ allValues?: string[];
223
+ disabled?: boolean;
224
+ controlFirst?: boolean;
225
+ className?: string;
226
+ }
227
+
228
+ function CheckboxGroup({
229
+ legend,
230
+ children,
231
+ error,
232
+ description,
233
+ defaultValue,
234
+ value,
235
+ onChange,
236
+ allValues,
237
+ disabled,
238
+ controlFirst = true,
239
+ className,
240
+ }: CheckboxGroupProps): React.ReactElement {
241
+ return (
242
+ <CheckboxGroupContext.Provider value={{ controlFirst, invalid: !!error }}>
243
+ <CheckboxGroupPrimitive
244
+ defaultValue={defaultValue}
245
+ value={value}
246
+ onValueChange={onChange}
247
+ allValues={allValues}
248
+ disabled={disabled}
249
+ >
250
+ <Fieldset.Root className={cn("flex flex-col gap-3", className)}>
251
+ {legend && <CheckboxLegend>{legend}</CheckboxLegend>}
252
+ <div className="flex flex-col gap-2">{children}</div>
253
+ {error && <p className="text-sm font-medium text-error">{error}</p>}
254
+ {!error && description && (
255
+ <p className="text-sm text-muted-foreground">{description}</p>
256
+ )}
257
+ </Fieldset.Root>
258
+ </CheckboxGroupPrimitive>
259
+ </CheckboxGroupContext.Provider>
260
+ );
261
+ }
262
+
263
+ // ── Compound export ────────────────────────────────────────────────────────────
264
+
265
+ export const Checkbox = Object.assign(CheckboxSingle, {
266
+ Item: CheckboxItem,
267
+ Group: CheckboxGroup,
268
+ Legend: CheckboxLegend,
269
+ });
270
+
271
+ // ── Primitive escape hatch ─────────────────────────────────────────────────────
272
+
273
+ export { CheckboxPrimitive };
@@ -0,0 +1 @@
1
+ export * from "./checkbox";