@boxcustodia/library 2.0.0-alpha.10 → 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 (352) hide show
  1. package/dist/index.cjs.js +70 -70
  2. package/dist/index.css +2 -0
  3. package/dist/index.d.ts +420 -272
  4. package/dist/index.es.js +34448 -27816
  5. package/dist/theme.css +1 -1
  6. package/package.json +11 -6
  7. package/src/__doc__/Changelog.mdx +6 -0
  8. package/src/__doc__/Components.mdx +73 -0
  9. package/src/__doc__/Examples.tsx +69 -0
  10. package/src/__doc__/Icons.mdx +41 -0
  11. package/src/__doc__/Intro.mdx +138 -0
  12. package/src/__doc__/MCP.mdx +71 -0
  13. package/src/__doc__/Migration.mdx +475 -0
  14. package/src/__doc__/Theme.mdx +132 -0
  15. package/src/__doc__/Types.mdx +252 -0
  16. package/src/components/alert/alert.stories.tsx +142 -0
  17. package/src/components/alert/alert.tsx +109 -0
  18. package/src/components/alert/index.ts +7 -0
  19. package/src/components/alert-dialog/alert-dialog.stories.tsx +173 -0
  20. package/src/components/alert-dialog/alert-dialog.test.tsx +49 -0
  21. package/src/components/alert-dialog/alert-dialog.tsx +265 -0
  22. package/src/components/alert-dialog/index.ts +1 -0
  23. package/src/components/auto-complete/auto-complete-primitives.tsx +155 -0
  24. package/src/components/auto-complete/auto-complete.stories.tsx +241 -0
  25. package/src/components/auto-complete/auto-complete.tsx +82 -0
  26. package/src/components/auto-complete/index.ts +2 -0
  27. package/src/components/avatar/avatar.stories.tsx +84 -0
  28. package/src/components/avatar/avatar.test.tsx +61 -0
  29. package/src/components/avatar/avatar.tsx +104 -0
  30. package/src/components/avatar/index.ts +1 -0
  31. package/src/components/background-image/background-image.stories.tsx +21 -0
  32. package/src/components/background-image/background-image.test.tsx +29 -0
  33. package/src/components/background-image/background-image.tsx +23 -0
  34. package/src/components/background-image/index.ts +1 -0
  35. package/src/components/button/button.stories.tsx +396 -0
  36. package/src/components/button/button.test.tsx +58 -0
  37. package/src/components/button/button.tsx +31 -0
  38. package/src/components/button/button.variants.ts +44 -0
  39. package/src/components/button/components/base-button.tsx +86 -0
  40. package/src/components/button/components/loader-overlay.tsx +21 -0
  41. package/src/components/button/components/loading-icon.tsx +47 -0
  42. package/src/components/button/index.ts +3 -0
  43. package/src/components/calendar/calendar.model.ts +86 -0
  44. package/src/components/calendar/calendar.stories.tsx +155 -0
  45. package/src/components/calendar/calendar.test.tsx +12 -0
  46. package/src/components/calendar/calendar.tsx +185 -0
  47. package/src/components/calendar/components/calendar-navigation.tsx +141 -0
  48. package/src/components/calendar/components/day.tsx +61 -0
  49. package/src/components/calendar/components/decade-view.tsx +45 -0
  50. package/src/components/calendar/components/index.ts +6 -0
  51. package/src/components/calendar/components/month-view.tsx +58 -0
  52. package/src/components/calendar/components/week-days.tsx +27 -0
  53. package/src/components/calendar/components/year-view.tsx +29 -0
  54. package/src/components/calendar/hooks/index.ts +4 -0
  55. package/src/components/calendar/hooks/use-calendar-navigation.ts +79 -0
  56. package/src/components/calendar/hooks/use-calendar.ts +90 -0
  57. package/src/components/calendar/hooks/use-multiple-calendar.ts +34 -0
  58. package/src/components/calendar/hooks/use-range-calendar.ts +91 -0
  59. package/src/components/calendar/hooks/use-single-calendar.ts +18 -0
  60. package/src/components/calendar/index.ts +1 -0
  61. package/src/components/calendar/utils/typeguards.ts +7 -0
  62. package/src/components/card/card.stories.tsx +116 -0
  63. package/src/components/card/card.tsx +74 -0
  64. package/src/components/card/index.ts +1 -0
  65. package/src/components/center/center.stories.tsx +81 -0
  66. package/src/components/center/center.tsx +24 -0
  67. package/src/components/center/index.ts +1 -0
  68. package/src/components/checkbox/checkbox.stories.tsx +307 -0
  69. package/src/components/checkbox/checkbox.tsx +273 -0
  70. package/src/components/checkbox/index.ts +1 -0
  71. package/src/components/checkbox-group/checkbox-group.stories.tsx +104 -0
  72. package/src/components/checkbox-group/checkbox-group.tsx +16 -0
  73. package/src/components/checkbox-group/index.ts +1 -0
  74. package/src/components/combobox/combobox.stories.tsx +339 -0
  75. package/src/components/combobox/combobox.tsx +892 -0
  76. package/src/components/combobox/index.ts +1 -0
  77. package/src/components/date-picker/date-input.stories.tsx +158 -0
  78. package/src/components/date-picker/date-input.tsx +163 -0
  79. package/src/components/date-picker/date-picker.model.ts +90 -0
  80. package/src/components/date-picker/date-picker.stories.tsx +200 -0
  81. package/src/components/date-picker/date-picker.test.tsx +23 -0
  82. package/src/components/date-picker/date-picker.tsx +298 -0
  83. package/src/components/date-picker/date-picker.utils.ts +260 -0
  84. package/src/components/date-picker/index.ts +3 -0
  85. package/src/components/date-picker/use-date-input-popover.ts +48 -0
  86. package/src/components/date-picker/use-date-input.ts +125 -0
  87. package/src/components/dialog/dialog.stories.tsx +171 -0
  88. package/src/components/dialog/dialog.test.tsx +68 -0
  89. package/src/components/dialog/dialog.tsx +277 -0
  90. package/src/components/dialog/index.ts +1 -0
  91. package/src/components/divider/divider.stories.tsx +70 -0
  92. package/src/components/divider/divider.test.tsx +22 -0
  93. package/src/components/divider/divider.tsx +23 -0
  94. package/src/components/divider/index.ts +1 -0
  95. package/src/components/dropzone/dropzone.stories.tsx +210 -0
  96. package/src/components/dropzone/dropzone.tsx +154 -0
  97. package/src/components/dropzone/file-types.ts +64 -0
  98. package/src/components/dropzone/index.ts +3 -0
  99. package/src/components/dropzone/upload-primitives.tsx +310 -0
  100. package/src/components/dropzone/use-dropzone.ts +122 -0
  101. package/src/components/empty-state/empty-state.stories.tsx +56 -0
  102. package/src/components/empty-state/empty-state.tsx +39 -0
  103. package/src/components/empty-state/index.ts +1 -0
  104. package/src/components/field/field.stories.tsx +223 -0
  105. package/src/components/field/field.tsx +229 -0
  106. package/src/components/field/index.ts +1 -0
  107. package/src/components/form/form.stories.tsx +594 -0
  108. package/src/components/form/form.tsx +30 -0
  109. package/src/components/form/index.ts +1 -0
  110. package/src/components/heading/heading.stories.tsx +74 -0
  111. package/src/components/heading/heading.tsx +28 -0
  112. package/src/components/heading/heading.variants.ts +27 -0
  113. package/src/components/heading/index.ts +1 -0
  114. package/src/components/index.ts +46 -0
  115. package/src/components/input/index.ts +1 -0
  116. package/src/components/input/input.stories.tsx +104 -0
  117. package/src/components/input/input.tsx +75 -0
  118. package/src/components/kbd/index.ts +1 -0
  119. package/src/components/kbd/kbd.stories.tsx +40 -0
  120. package/src/components/kbd/kbd.tsx +31 -0
  121. package/src/components/kbd/kbd.variants.ts +26 -0
  122. package/src/components/label/index.ts +1 -0
  123. package/src/components/label/label.stories.tsx +68 -0
  124. package/src/components/label/label.test.tsx +61 -0
  125. package/src/components/label/label.tsx +62 -0
  126. package/src/components/loader/index.ts +1 -0
  127. package/src/components/loader/loader.stories.tsx +60 -0
  128. package/src/components/loader/loader.test.tsx +26 -0
  129. package/src/components/loader/loader.tsx +60 -0
  130. package/src/components/menu/index.ts +2 -0
  131. package/src/components/menu/menu-primitives.tsx +248 -0
  132. package/src/components/menu/menu.stories.tsx +203 -0
  133. package/src/components/menu/menu.tsx +100 -0
  134. package/src/components/menu/util/render-menu-item.tsx +54 -0
  135. package/src/components/multi-select/hooks/use-multi-select.ts +66 -0
  136. package/src/components/multi-select/index.ts +1 -0
  137. package/src/components/multi-select/multi-select.stories.tsx +294 -0
  138. package/src/components/multi-select/multi-select.tsx +300 -0
  139. package/src/components/multi-select/multi-select.variants.ts +22 -0
  140. package/src/components/number-input/index.ts +1 -0
  141. package/src/components/number-input/number-input.stories.tsx +209 -0
  142. package/src/components/number-input/number-input.test.tsx +87 -0
  143. package/src/components/number-input/number-input.tsx +230 -0
  144. package/src/components/pagination/components/pagination-option.tsx +27 -0
  145. package/src/components/pagination/index.ts +1 -0
  146. package/src/components/pagination/pagination.stories.tsx +80 -0
  147. package/src/components/pagination/pagination.test.tsx +76 -0
  148. package/src/components/pagination/pagination.tsx +102 -0
  149. package/src/components/password/index.ts +1 -0
  150. package/src/components/password/password.stories.tsx +104 -0
  151. package/src/components/password/password.tsx +71 -0
  152. package/src/components/popover/index.ts +1 -0
  153. package/src/components/popover/popover.stories.tsx +213 -0
  154. package/src/components/popover/popover.tsx +203 -0
  155. package/src/components/progress/index.ts +1 -0
  156. package/src/components/progress/progress.stories.tsx +124 -0
  157. package/src/components/progress/progress.test.tsx +25 -0
  158. package/src/components/progress/progress.tsx +124 -0
  159. package/src/components/scroll-area/index.ts +1 -0
  160. package/src/components/scroll-area/scroll-area.stories.tsx +166 -0
  161. package/src/components/scroll-area/scroll-area.tsx +64 -0
  162. package/src/components/select/index.ts +1 -0
  163. package/src/components/select/select.stories.tsx +253 -0
  164. package/src/components/select/select.tsx +430 -0
  165. package/src/components/show/index.ts +1 -0
  166. package/src/components/show/show.stories.tsx +197 -0
  167. package/src/components/show/show.test.tsx +41 -0
  168. package/src/components/show/show.tsx +16 -0
  169. package/src/components/skeleton/index.ts +1 -0
  170. package/src/components/skeleton/skeleton.stories.tsx +36 -0
  171. package/src/components/skeleton/skeleton.test.tsx +14 -0
  172. package/src/components/skeleton/skeleton.tsx +15 -0
  173. package/src/components/stack/index.ts +1 -0
  174. package/src/components/stack/stack.stories.tsx +194 -0
  175. package/src/components/stack/stack.tsx +52 -0
  176. package/src/components/stepper/Stepper.tsx +190 -0
  177. package/src/components/stepper/context/stepper-context.tsx +11 -0
  178. package/src/components/stepper/index.ts +1 -0
  179. package/src/components/stepper/stepper.stories.tsx +130 -0
  180. package/src/components/stepper/stepper.test.tsx +91 -0
  181. package/src/components/switch/index.ts +1 -0
  182. package/src/components/switch/switch.stories.tsx +122 -0
  183. package/src/components/switch/switch.test.tsx +30 -0
  184. package/src/components/switch/switch.tsx +86 -0
  185. package/src/components/table/index.ts +3 -0
  186. package/src/components/table/table-primitives.tsx +122 -0
  187. package/src/components/table/table.model.ts +20 -0
  188. package/src/components/table/table.stories.tsx +169 -0
  189. package/src/components/table/table.test.tsx +91 -0
  190. package/src/components/table/table.tsx +109 -0
  191. package/src/components/table-pagination/index.ts +2 -0
  192. package/src/components/table-pagination/table-pagination.model.ts +2 -0
  193. package/src/components/table-pagination/table-pagination.stories.tsx +23 -0
  194. package/src/components/table-pagination/table-pagination.test.tsx +32 -0
  195. package/src/components/table-pagination/table-pagination.tsx +108 -0
  196. package/src/components/tabs/context/tabs-context.tsx +14 -0
  197. package/src/components/tabs/index.ts +1 -0
  198. package/src/components/tabs/tabs.stories.tsx +182 -0
  199. package/src/components/tabs/tabs.test.tsx +61 -0
  200. package/src/components/tabs/tabs.tsx +175 -0
  201. package/src/components/tag/index.ts +2 -0
  202. package/src/components/tag/tag.stories.tsx +170 -0
  203. package/src/components/tag/tag.test.tsx +18 -0
  204. package/src/components/tag/tag.tsx +99 -0
  205. package/src/components/tag/tag.variants.ts +31 -0
  206. package/src/components/textarea/index.ts +1 -0
  207. package/src/components/textarea/textarea.stories.tsx +73 -0
  208. package/src/components/textarea/textarea.tsx +105 -0
  209. package/src/components/timeline/index.ts +1 -0
  210. package/src/components/timeline/timeline-status.ts +5 -0
  211. package/src/components/timeline/timeline.stories.tsx +84 -0
  212. package/src/components/timeline/timeline.tsx +147 -0
  213. package/src/components/toast/index.ts +1 -0
  214. package/src/components/toast/toast.stories.tsx +392 -0
  215. package/src/components/toast/toast.test.tsx +50 -0
  216. package/src/components/toast/toast.tsx +411 -0
  217. package/src/components/tooltip/index.ts +1 -0
  218. package/src/components/tooltip/tooltip.stories.tsx +226 -0
  219. package/src/components/tooltip/tooltip.test.tsx +46 -0
  220. package/src/components/tooltip/tooltip.tsx +171 -0
  221. package/src/components/tree/hooks/use-controllable-tree-state.ts +80 -0
  222. package/src/components/tree/index.ts +2 -0
  223. package/src/components/tree/tree-primitives.tsx +126 -0
  224. package/src/components/tree/tree.stories.tsx +468 -0
  225. package/src/components/tree/tree.tsx +42 -0
  226. package/src/hooks/index.ts +26 -0
  227. package/src/hooks/useArray/__doc__/useArray.stories.tsx +100 -0
  228. package/src/hooks/useArray/__test__/useArray.test.tsx +88 -0
  229. package/src/hooks/useArray/index.ts +1 -0
  230. package/src/hooks/useArray/useArray.ts +76 -0
  231. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +149 -0
  232. package/src/hooks/useAsync/__test__/useAsync.test.tsx +68 -0
  233. package/src/hooks/useAsync/index.ts +1 -0
  234. package/src/hooks/useAsync/useAsync.ts +58 -0
  235. package/src/hooks/useClickOutside/__doc__/useClickOutside.stories.tsx +40 -0
  236. package/src/hooks/useClickOutside/__test__/useClickOutside.test.tsx +33 -0
  237. package/src/hooks/useClickOutside/index.ts +1 -0
  238. package/src/hooks/useClickOutside/useClickOutside.ts +26 -0
  239. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +45 -0
  240. package/src/hooks/useClipboard/__test__/useClipboard.test.tsx +19 -0
  241. package/src/hooks/useClipboard/index.ts +1 -0
  242. package/src/hooks/useClipboard/useClipboard.tsx +28 -0
  243. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +84 -0
  244. package/src/hooks/useDebounceCallback/index.ts +1 -0
  245. package/src/hooks/useDebounceCallback/useDebouncedCallback.ts +23 -0
  246. package/src/hooks/useDebounceValue/__doc__/useDebouncedValue.stories.tsx +75 -0
  247. package/src/hooks/useDebounceValue/index.ts +1 -0
  248. package/src/hooks/useDebounceValue/useDebouncedValue.ts +17 -0
  249. package/src/hooks/useDisclosure/__doc__/useDisclosure.stories.tsx +39 -0
  250. package/src/hooks/useDisclosure/__test__/useDisclosure.test.ts +43 -0
  251. package/src/hooks/useDisclosure/index.ts +1 -0
  252. package/src/hooks/useDisclosure/useDisclosure.ts +37 -0
  253. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +26 -0
  254. package/src/hooks/useDocumentTitle/index.ts +1 -0
  255. package/src/hooks/useDocumentTitle/useDocumentTitle.tsx +11 -0
  256. package/src/hooks/useEventListener/__doc__/useEventListener.stories.tsx +28 -0
  257. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +26 -0
  258. package/src/hooks/useEventListener/index.ts +1 -0
  259. package/src/hooks/useEventListener/useEventListener.ts +25 -0
  260. package/src/hooks/useFocusTrap/__doc__/useFocusTrap.stories.tsx +37 -0
  261. package/src/hooks/useFocusTrap/index.ts +1 -0
  262. package/src/hooks/useFocusTrap/scopeTab.ts +38 -0
  263. package/src/hooks/useFocusTrap/tabbable.ts +70 -0
  264. package/src/hooks/useFocusTrap/useFocusTrap.ts +78 -0
  265. package/src/hooks/useHotkey/__docs__/useHotkey.stories.tsx +116 -0
  266. package/src/hooks/useHotkey/__test__/useHotkey.test.tsx +105 -0
  267. package/src/hooks/useHotkey/__utils__/create-hotkey-listener.ts +25 -0
  268. package/src/hooks/useHotkey/__utils__/index.ts +3 -0
  269. package/src/hooks/useHotkey/__utils__/is-input-field.ts +14 -0
  270. package/src/hooks/useHotkey/__utils__/match-key-modifiers.ts +25 -0
  271. package/src/hooks/useHotkey/index.ts +1 -0
  272. package/src/hooks/useHotkey/useHotkey.ts +34 -0
  273. package/src/hooks/useHover/__doc__/useHover.stories.tsx +41 -0
  274. package/src/hooks/useHover/__test__/useHover.test.tsx +45 -0
  275. package/src/hooks/useHover/index.ts +1 -0
  276. package/src/hooks/useHover/useHover.tsx +40 -0
  277. package/src/hooks/useIsVisible/__doc__/useIsVisible.stories.tsx +60 -0
  278. package/src/hooks/useIsVisible/index.ts +1 -0
  279. package/src/hooks/useIsVisible/useIsVisible.tsx +50 -0
  280. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +86 -0
  281. package/src/hooks/useLocalStorage/__test__/useLocalStorage.test.ts +85 -0
  282. package/src/hooks/useLocalStorage/index.ts +1 -0
  283. package/src/hooks/useLocalStorage/useLocalStorage.ts +57 -0
  284. package/src/hooks/useMediaQuery/__doc__/useMediaQuery.stories.tsx +39 -0
  285. package/src/hooks/useMediaQuery/index.ts +1 -0
  286. package/src/hooks/useMediaQuery/useMediaQuery.ts +22 -0
  287. package/src/hooks/useMemoizedFn/index.ts +1 -0
  288. package/src/hooks/useMemoizedFn/useMemoizedFn.ts +32 -0
  289. package/src/hooks/useMutation/__doc__/useMutation.stories.tsx +111 -0
  290. package/src/hooks/useMutation/__test__/useMutation.test.tsx +83 -0
  291. package/src/hooks/useMutation/index.ts +1 -0
  292. package/src/hooks/useMutation/useMutation.tsx +60 -0
  293. package/src/hooks/useObject/__doc__/useObject.stories.tsx +119 -0
  294. package/src/hooks/useObject/__test__/useObject.test.tsx +87 -0
  295. package/src/hooks/useObject/index.ts +1 -0
  296. package/src/hooks/useObject/useObject.tsx +48 -0
  297. package/src/hooks/usePagination/__doc__/usePagination.stories.tsx +72 -0
  298. package/src/hooks/usePagination/__test__/usePagination.test.tsx +98 -0
  299. package/src/hooks/usePagination/index.ts +2 -0
  300. package/src/hooks/usePagination/usePagination.tsx +74 -0
  301. package/src/hooks/usePortal/__doc__/usePortal.stories.tsx +19 -0
  302. package/src/hooks/usePortal/__test__/usePortal.test.tsx +20 -0
  303. package/src/hooks/usePortal/index.ts +1 -0
  304. package/src/hooks/usePortal/usePortal.ts +40 -0
  305. package/src/hooks/usePreventCloseWindow/__doc__/usePreventCloseWindow.stories.tsx +32 -0
  306. package/src/hooks/usePreventCloseWindow/index.ts +1 -0
  307. package/src/hooks/usePreventCloseWindow/usePreventCloseWindow.ts +33 -0
  308. package/src/hooks/useRangePagination/__test__/useRangePagination.test.tsx +63 -0
  309. package/src/hooks/useRangePagination/index.ts +2 -0
  310. package/src/hooks/useRangePagination/useRangePagination.tsx +72 -0
  311. package/src/hooks/useSelection/__doc__/useSelection.stories.tsx +140 -0
  312. package/src/hooks/useSelection/__test__/useSelection.test.tsx +57 -0
  313. package/src/hooks/useSelection/index.ts +1 -0
  314. package/src/hooks/useSelection/useSelection.ts +121 -0
  315. package/src/hooks/useStep/__doc__/useStep.stories.tsx +98 -0
  316. package/src/hooks/useStep/__test__/useStep.test.ts +51 -0
  317. package/src/hooks/useStep/index.ts +1 -0
  318. package/src/hooks/useStep/useStep.ts +57 -0
  319. package/src/hooks/useToggle/__doc__/useToggle.stories.tsx +25 -0
  320. package/src/hooks/useToggle/__test__/useToggle.test.tsx +43 -0
  321. package/src/hooks/useToggle/index.ts +1 -0
  322. package/src/hooks/useToggle/useToggle.ts +16 -0
  323. package/src/index.ts +6 -0
  324. package/src/lib/cn.ts +8 -0
  325. package/src/lib/index.ts +1 -0
  326. package/src/models/Generic.model.ts +67 -0
  327. package/src/models/index.ts +1 -0
  328. package/src/providers/index.ts +2 -0
  329. package/src/providers/library-provider.tsx +44 -0
  330. package/src/providers/theme/ThemeProvider.tsx +25 -0
  331. package/src/providers/theme/index.ts +3 -0
  332. package/src/providers/theme/types.ts +11 -0
  333. package/src/providers/theme/useThemeProps.ts +25 -0
  334. package/src/stores/theme.store.ts +31 -0
  335. package/src/styles/components.css +4 -0
  336. package/src/styles/index.css +2 -0
  337. package/src/styles/library.css +2 -0
  338. package/src/styles/theme.css +232 -0
  339. package/src/utils/dates/parseDateRange.utility.ts +39 -0
  340. package/src/utils/form.tsx +91 -0
  341. package/src/utils/functions/createSafeContext.ts +17 -0
  342. package/src/utils/functions/ensureReactElement.tsx +30 -0
  343. package/src/utils/functions/getFormData.ts +19 -0
  344. package/src/utils/functions/index.ts +4 -0
  345. package/src/utils/functions/mergeRefs.ts +18 -0
  346. package/src/utils/index.ts +3 -0
  347. package/src/utils/strings/extractInitials.utility.ts +10 -0
  348. package/src/utils/strings/index.ts +1 -0
  349. package/src/utils/tests/click.ts +3 -0
  350. package/src/utils/tests/index.ts +2 -0
  351. package/src/utils/tests/keyboard.ts +21 -0
  352. package/src/utils/tests/type.ts +6 -0
@@ -0,0 +1,223 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { CheckboxIndicator, CheckboxRoot } from "../checkbox/checkbox";
3
+ import { Input } from "../input/input";
4
+ import { SwitchRoot, SwitchThumb } from "../switch/switch";
5
+ import { Textarea } from "../textarea/textarea";
6
+ import {
7
+ Field,
8
+ FieldControl,
9
+ FieldDescription,
10
+ FieldError,
11
+ FieldItem,
12
+ FieldLabel,
13
+ FieldRoot,
14
+ FieldValidity,
15
+ } from "./field";
16
+
17
+ /**
18
+ * Layout wrapper that connects a label, description, and errors to any form
19
+ * control via Base UI's field context. Auto-wires `htmlFor`/`aria-describedby`.
20
+ *
21
+ * The composite `Field` promotes `label`, `description`, `error`, `required`,
22
+ * `tooltip`, and `inline` as top-level props — covers ~90% of use cases.
23
+ * `error` accepts a string, a `{ message, match }` object (scoped to a
24
+ * `ValidityState` key), or an array for multiple errors.
25
+ *
26
+ * For full structural control (custom ordering, extra elements between parts,
27
+ * non-standard error rendering), use `FieldRoot` + primitives directly.
28
+ *
29
+ * Reference: [Field – Base UI](https://base-ui.com/react/components/field.md)
30
+ */
31
+ const meta: Meta<typeof Field> = {
32
+ title: "Components/Field",
33
+ component: Field,
34
+ subcomponents: {
35
+ FieldRoot,
36
+ FieldLabel,
37
+ FieldItem,
38
+ FieldDescription,
39
+ FieldError,
40
+ FieldControl,
41
+ FieldValidity,
42
+ },
43
+ parameters: { layout: "centered" },
44
+ args: {
45
+ label: "Email",
46
+ description: "We'll never share your email.",
47
+ children: <Input type="email" placeholder="you@example.com" />,
48
+ },
49
+ argTypes: {
50
+ children: { control: false },
51
+ labelProps: { control: false },
52
+ descriptionProps: { control: false },
53
+ errorProps: { control: false },
54
+ error: { control: false },
55
+ },
56
+ };
57
+
58
+ export default meta;
59
+ type Story = StoryObj<typeof Field>;
60
+
61
+ export const Default: Story = {};
62
+
63
+ export const Required: Story = {
64
+ args: {
65
+ children: <Input type="email" required placeholder="you@example.com" />,
66
+ },
67
+ };
68
+
69
+ export const Disabled: Story = {
70
+ args: { disabled: true },
71
+ };
72
+
73
+ /**
74
+ * `tooltip` renders an info icon next to the label with an accessible popup.
75
+ */
76
+ export const WithTooltip: Story = {
77
+ args: {
78
+ tooltip: "Used to send you a confirmation link.",
79
+ },
80
+ };
81
+
82
+ /**
83
+ * `error` as a string auto-marks the field invalid — no `invalid` prop needed.
84
+ */
85
+ export const WithError: Story = {
86
+ args: {
87
+ error: "Enter a valid email address.",
88
+ },
89
+ };
90
+
91
+ /**
92
+ * `error` as an array maps each message to a specific `ValidityState` key via
93
+ * `match`. Submit the form to trigger native HTML validation.
94
+ */
95
+ export const WithMatchedErrors: Story = {
96
+ render: () => (
97
+ <form className="flex flex-col gap-3" onSubmit={(e) => e.preventDefault()}>
98
+ <Field
99
+ label="Email"
100
+ required
101
+ error={[
102
+ { message: "Email is required.", match: "valueMissing" },
103
+ { message: "Enter a valid email address.", match: "typeMismatch" },
104
+ ]}
105
+ >
106
+ <Input type="email" required placeholder="you@example.com" />
107
+ </Field>
108
+ <button
109
+ type="submit"
110
+ className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground"
111
+ >
112
+ Submit
113
+ </button>
114
+ </form>
115
+ ),
116
+ };
117
+
118
+ /**
119
+ * `FieldValidity` render prop exposes the full `ValidityState` for custom
120
+ * error rendering without going through the `error` prop.
121
+ */
122
+ export const WithValidity: Story = {
123
+ render: () => (
124
+ <form className="flex flex-col gap-3" onSubmit={(e) => e.preventDefault()}>
125
+ <Field label="Email" required className="w-72">
126
+ <Input type="email" required placeholder="you@example.com" />
127
+ <FieldValidity>
128
+ {({ validity }) =>
129
+ !validity.valid && (
130
+ <p className="text-destructive-foreground text-xs">
131
+ {validity.valueMissing
132
+ ? "Email is required."
133
+ : "Enter a valid email address."}
134
+ </p>
135
+ )
136
+ }
137
+ </FieldValidity>
138
+ </Field>
139
+ <button
140
+ type="submit"
141
+ className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground"
142
+ >
143
+ Submit
144
+ </button>
145
+ </form>
146
+ ),
147
+ };
148
+
149
+ export const TextareaField: Story = {
150
+ args: {
151
+ label: "Bio",
152
+ description: "Max 160 characters.",
153
+ children: <Textarea placeholder="Tell us about yourself..." />,
154
+ },
155
+ };
156
+
157
+ /**
158
+ * `inline` renders control + label side-by-side. Use for single checkboxes and
159
+ * switches. Description and error still wrap to the row below.
160
+ */
161
+ export const CheckboxField: Story = {
162
+ args: {
163
+ inline: true,
164
+ label: "I accept the terms and conditions",
165
+ description: undefined,
166
+ className: undefined,
167
+ error: { message: "You must accept the terms.", match: "valueMissing" },
168
+ children: (
169
+ <CheckboxRoot required>
170
+ <CheckboxIndicator />
171
+ </CheckboxRoot>
172
+ ),
173
+ },
174
+ };
175
+
176
+ export const SwitchField: Story = {
177
+ args: {
178
+ inline: true,
179
+ label: "Email notifications",
180
+ description: "Receive a weekly digest.",
181
+ className: undefined,
182
+ children: (
183
+ <SwitchRoot>
184
+ <SwitchThumb />
185
+ </SwitchRoot>
186
+ ),
187
+ },
188
+ };
189
+
190
+ /**
191
+ * `FieldRoot` + primitives for full structural control. `FieldControl` registers
192
+ * a non-Base-UI input with the field context so it participates in labelling,
193
+ * validation, and error state.
194
+ *
195
+ * ```tsx
196
+ * <FieldRoot>
197
+ * <FieldLabel>Website</FieldLabel>
198
+ * <FieldControl render={<MyCustomInput />} />
199
+ * <FieldDescription>Helper text</FieldDescription>
200
+ * <FieldError>Error message</FieldError>
201
+ * </FieldRoot>
202
+ * ```
203
+ */
204
+ export const Primitive: Story = {
205
+ render: () => (
206
+ <FieldRoot className="w-72">
207
+ <FieldLabel>Website</FieldLabel>
208
+ <FieldControl
209
+ render={
210
+ <input
211
+ className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50"
212
+ type="url"
213
+ required
214
+ placeholder="https://example.com"
215
+ />
216
+ }
217
+ />
218
+ <FieldDescription>Your public profile URL.</FieldDescription>
219
+ <FieldError match="valueMissing">Website is required.</FieldError>
220
+ <FieldError match="typeMismatch">Enter a valid URL.</FieldError>
221
+ </FieldRoot>
222
+ ),
223
+ };
@@ -0,0 +1,229 @@
1
+ import { Field as FieldPrimitive } from "@base-ui/react/field";
2
+ import { type ReactNode } from "react";
3
+ import { cn } from "../../lib";
4
+ import { Label } from "../label";
5
+
6
+ // ── Types ─────────────────────────────────────────────────────────────────────
7
+
8
+ export type FieldErrorMatch = FieldPrimitive.Error.Props["match"];
9
+
10
+ export type FieldErrorItem = {
11
+ message: ReactNode;
12
+ match?: NonNullable<FieldErrorMatch>;
13
+ };
14
+
15
+ export type FieldErrorProp = string | FieldErrorItem | FieldErrorItem[];
16
+
17
+ // ── Primitives ────────────────────────────────────────────────────────────────
18
+
19
+ export function FieldRoot({
20
+ className,
21
+ required,
22
+ ...props
23
+ }: FieldPrimitive.Root.Props & { required?: boolean }) {
24
+ return (
25
+ <FieldPrimitive.Root
26
+ className={cn("group flex flex-col gap-1", className)}
27
+ data-slot="field"
28
+ data-required={required || undefined}
29
+ {...props}
30
+ />
31
+ );
32
+ }
33
+
34
+ export function FieldLabel({
35
+ className,
36
+ ...props
37
+ }: FieldPrimitive.Label.Props) {
38
+ return (
39
+ <FieldPrimitive.Label
40
+ className={cn(
41
+ "inline-flex items-center font-medium text-base/4.5 text-foreground data-disabled:opacity-64 sm:text-sm/4",
42
+ className,
43
+ )}
44
+ data-slot="field-label"
45
+ {...props}
46
+ />
47
+ );
48
+ }
49
+
50
+ export function FieldItem({ className, ...props }: FieldPrimitive.Item.Props) {
51
+ return (
52
+ <FieldPrimitive.Item
53
+ className={cn("flex items-center gap-2", className)}
54
+ data-slot="field-item"
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ export function FieldDescription({
61
+ className,
62
+ ...props
63
+ }: FieldPrimitive.Description.Props) {
64
+ return (
65
+ <FieldPrimitive.Description
66
+ className={cn(
67
+ "text-muted-foreground text-xs group-data-[invalid]:hidden",
68
+ className,
69
+ )}
70
+ data-slot="field-description"
71
+ {...props}
72
+ />
73
+ );
74
+ }
75
+
76
+ export function FieldError({
77
+ className,
78
+ ...props
79
+ }: FieldPrimitive.Error.Props) {
80
+ return (
81
+ <FieldPrimitive.Error
82
+ className={cn("text-error text-xs", className)}
83
+ data-slot="field-error"
84
+ {...props}
85
+ />
86
+ );
87
+ }
88
+
89
+ export const FieldControl: typeof FieldPrimitive.Control =
90
+ FieldPrimitive.Control;
91
+ export const FieldValidity: typeof FieldPrimitive.Validity =
92
+ FieldPrimitive.Validity;
93
+
94
+ // ── Composite ─────────────────────────────────────────────────────────────────
95
+
96
+ function normalizeError(error: FieldErrorProp | undefined): FieldErrorItem[] {
97
+ if (error === undefined) return [];
98
+ if (typeof error === "string") return [{ message: error, match: true }];
99
+ if (Array.isArray(error)) return error;
100
+ return [error];
101
+ }
102
+
103
+ export interface FieldProps
104
+ extends Omit<FieldPrimitive.Root.Props, "children"> {
105
+ /** Text rendered inside `FieldLabel`. */
106
+ label?: ReactNode;
107
+ /** Helper text rendered below the control. */
108
+ description?: ReactNode;
109
+ /**
110
+ * Error message(s) rendered below the control:
111
+ * - `string` → always renders once the field is invalid.
112
+ * - `{ message, match }` → renders only when the `ValidityState` key matches.
113
+ * - `FieldErrorItem[]` → multiple errors with individual matches.
114
+ */
115
+ error?: FieldErrorProp;
116
+ /** Shows a red asterisk after the label text. */
117
+ required?: boolean;
118
+ /** Renders an info icon with a tooltip next to the label. */
119
+ tooltip?: ReactNode;
120
+ /**
121
+ * Renders the control before the label on the same row.
122
+ * Use for single checkboxes and switches.
123
+ */
124
+ inline?: boolean;
125
+ children?: ReactNode;
126
+ // Escape hatches for each sub-component
127
+ labelProps?: FieldPrimitive.Label.Props;
128
+ labelClassName?: string;
129
+ descriptionProps?: FieldPrimitive.Description.Props;
130
+ descriptionClassName?: string;
131
+ errorProps?: Omit<FieldPrimitive.Error.Props, "match" | "children">;
132
+ errorClassName?: string;
133
+ }
134
+
135
+ export function Field({
136
+ label,
137
+ description,
138
+ error,
139
+ required,
140
+ tooltip,
141
+ inline = false,
142
+ children,
143
+ className,
144
+ labelProps,
145
+ labelClassName,
146
+ descriptionProps,
147
+ descriptionClassName,
148
+ errorProps,
149
+ errorClassName,
150
+ invalid,
151
+ ...rootProps
152
+ }: FieldProps) {
153
+ const errors = normalizeError(error);
154
+
155
+ const derivedInvalid =
156
+ invalid || errors.some((e) => e.match === true) || undefined;
157
+
158
+ const labelNode = label && (
159
+ <FieldLabel
160
+ render={tooltip ? <Label tooltip={tooltip} /> : undefined}
161
+ className={labelClassName}
162
+ {...labelProps}
163
+ >
164
+ <span className="after:text-error group-has-[*:required]:after:content-['*'] group-data-[required]:after:content-['*']">
165
+ {label}
166
+ </span>
167
+ </FieldLabel>
168
+ );
169
+
170
+ return (
171
+ <FieldRoot
172
+ className={className}
173
+ required={required}
174
+ invalid={derivedInvalid}
175
+ {...rootProps}
176
+ >
177
+ {inline ? (
178
+ <div className="flex items-center gap-1">
179
+ {children}
180
+ {labelNode}
181
+ </div>
182
+ ) : (
183
+ <>
184
+ {labelNode}
185
+ {children}
186
+ </>
187
+ )}
188
+ {description && (
189
+ <FieldDescription
190
+ className={descriptionClassName}
191
+ {...descriptionProps}
192
+ >
193
+ {description}
194
+ </FieldDescription>
195
+ )}
196
+ {errors.map((item, i) => (
197
+ <FieldError
198
+ key={i}
199
+ match={item.match}
200
+ className={errorClassName}
201
+ {...errorProps}
202
+ >
203
+ {item.message}
204
+ </FieldError>
205
+ ))}
206
+ {/* Catch-all for server errors propagated via `Form.errors` (React context,
207
+ not `setCustomValidity`). When all user errors have a `match`, none of
208
+ them fire for server-side errors — render a plain `FieldError` guarded
209
+ by `FieldValidity` so it only shows when the field is invalid AND native
210
+ validation passed (validity.error === ""). */}
211
+ {!errors.some((item) => item.match === undefined) &&
212
+ (errors.length === 0 ? (
213
+ <FieldError {...errorProps} />
214
+ ) : (
215
+ <FieldValidity>
216
+ {({ validity, error }) =>
217
+ !validity.valid && error === "" ? (
218
+ <FieldError {...errorProps} />
219
+ ) : null
220
+ }
221
+ </FieldValidity>
222
+ ))}
223
+ </FieldRoot>
224
+ );
225
+ }
226
+
227
+ // ── Primitive escape hatch ────────────────────────────────────────────────────
228
+
229
+ export { FieldPrimitive };
@@ -0,0 +1 @@
1
+ export * from "./field";