@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,430 @@
1
+ "use client";
2
+
3
+ import { mergeProps } from "@base-ui/react/merge-props";
4
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
5
+ import { useRender } from "@base-ui/react/use-render";
6
+ import {
7
+ ChevronDownIcon,
8
+ ChevronsUpDownIcon,
9
+ ChevronUpIcon,
10
+ } from "lucide-react";
11
+ import * as React from "react";
12
+ import { cn } from "../../lib";
13
+ import { inputBaseClasses } from "../input";
14
+
15
+ // ─── Primitives ───────────────────────────────────────────────────────────────
16
+
17
+ export const selectTriggerClasses = cn(
18
+ inputBaseClasses,
19
+ "relative inline-flex items-center justify-between gap-2 select-none text-left text-foreground",
20
+ "focus-visible:border-ring",
21
+ "aria-invalid:border-error focus-visible:aria-invalid:ring-error/20",
22
+ "has-aria-invalid:border-error has-focus-visible:has-aria-invalid:ring-error/20",
23
+ "data-disabled:cursor-not-allowed data-disabled:opacity-50",
24
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0",
25
+ );
26
+
27
+ export const selectTriggerIconClassName = "-me-1 size-4.5 opacity-80 sm:size-4";
28
+
29
+ export function SelectRoot<
30
+ Value = string,
31
+ Multiple extends boolean | undefined = false,
32
+ >({
33
+ onChange,
34
+ ...props
35
+ }: Omit<SelectPrimitive.Root.Props<Value, Multiple>, "onValueChange"> & {
36
+ onChange?: SelectPrimitive.Root.Props<Value, Multiple>["onValueChange"];
37
+ }): React.ReactElement {
38
+ return <SelectPrimitive.Root onValueChange={onChange} {...props} />;
39
+ }
40
+
41
+ export function SelectTrigger({
42
+ className,
43
+ children,
44
+ ...props
45
+ }: SelectPrimitive.Trigger.Props): React.ReactElement {
46
+ return (
47
+ <SelectPrimitive.Trigger
48
+ className={cn(selectTriggerClasses, className)}
49
+ data-slot="select-trigger"
50
+ {...props}
51
+ >
52
+ {children}
53
+ <SelectPrimitive.Icon
54
+ data-slot="select-icon"
55
+ className="text-muted-foreground hover:text-foreground transition-colors"
56
+ >
57
+ <ChevronsUpDownIcon className={selectTriggerIconClassName} />
58
+ </SelectPrimitive.Icon>
59
+ </SelectPrimitive.Trigger>
60
+ );
61
+ }
62
+
63
+ export type SelectButtonProps = useRender.ComponentProps<"button"> & {
64
+ ref?: React.Ref<HTMLButtonElement>;
65
+ };
66
+
67
+ export function SelectButton({
68
+ className,
69
+ render,
70
+ children,
71
+ ...props
72
+ }: SelectButtonProps): React.ReactElement {
73
+ const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>["type"] =
74
+ render ? undefined : "button";
75
+
76
+ const defaultProps = {
77
+ children: (
78
+ <>
79
+ <span className="flex-1 truncate in-data-placeholder:text-muted-foreground/72">
80
+ {children}
81
+ </span>
82
+ <ChevronsUpDownIcon className={selectTriggerIconClassName} />
83
+ </>
84
+ ),
85
+ className: cn(selectTriggerClasses, "min-w-0", className),
86
+ "data-slot": "select-button",
87
+ type: typeValue,
88
+ };
89
+
90
+ return useRender({
91
+ defaultTagName: "button",
92
+ props: mergeProps<"button">(defaultProps, props),
93
+ render,
94
+ });
95
+ }
96
+
97
+ export function SelectValue({
98
+ className,
99
+ ...props
100
+ }: SelectPrimitive.Value.Props): React.ReactElement {
101
+ return (
102
+ <SelectPrimitive.Value
103
+ className={cn(
104
+ "flex-1 truncate data-placeholder:text-muted-foreground",
105
+ className,
106
+ )}
107
+ data-slot="select-value"
108
+ {...props}
109
+ />
110
+ );
111
+ }
112
+
113
+ export function SelectPopup({
114
+ className,
115
+ children,
116
+ side = "bottom",
117
+ sideOffset = 4,
118
+ align = "start",
119
+ alignOffset = 0,
120
+ alignItemWithTrigger = true,
121
+ anchor,
122
+ portalProps,
123
+ ...props
124
+ }: SelectPrimitive.Popup.Props & {
125
+ portalProps?: SelectPrimitive.Portal.Props;
126
+ side?: SelectPrimitive.Positioner.Props["side"];
127
+ sideOffset?: SelectPrimitive.Positioner.Props["sideOffset"];
128
+ align?: SelectPrimitive.Positioner.Props["align"];
129
+ alignOffset?: SelectPrimitive.Positioner.Props["alignOffset"];
130
+ alignItemWithTrigger?: SelectPrimitive.Positioner.Props["alignItemWithTrigger"];
131
+ anchor?: SelectPrimitive.Positioner.Props["anchor"];
132
+ }): React.ReactElement {
133
+ return (
134
+ <SelectPrimitive.Portal {...portalProps}>
135
+ <SelectPrimitive.Positioner
136
+ align={align}
137
+ alignItemWithTrigger={alignItemWithTrigger}
138
+ alignOffset={alignOffset}
139
+ anchor={anchor}
140
+ className="z-50 select-none"
141
+ data-slot="select-positioner"
142
+ side={side}
143
+ sideOffset={sideOffset}
144
+ >
145
+ <SelectPrimitive.Popup
146
+ className="origin-(--transform-origin) text-foreground outline-none"
147
+ data-slot="select-popup"
148
+ {...props}
149
+ >
150
+ <SelectPrimitive.ScrollUpArrow
151
+ className="top-0 z-50 flex h-6 w-full cursor-default items-center justify-center before:pointer-events-none before:absolute before:inset-x-px before:top-px before:h-[200%] before:rounded-t-[calc(var(--radius-lg)-1px)] before:bg-linear-to-b before:from-50% before:from-popover"
152
+ data-slot="select-scroll-up-arrow"
153
+ >
154
+ <ChevronUpIcon className="relative size-4.5 sm:size-4" />
155
+ </SelectPrimitive.ScrollUpArrow>
156
+ <div className="relative h-full min-w-(--anchor-width) rounded-lg border bg-popover not-dark:bg-clip-padding shadow-lg/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]">
157
+ <SelectPrimitive.List
158
+ className={cn(
159
+ "max-h-(--available-height) overflow-y-auto p-1",
160
+ className,
161
+ )}
162
+ data-slot="select-list"
163
+ >
164
+ {children}
165
+ </SelectPrimitive.List>
166
+ </div>
167
+ <SelectPrimitive.ScrollDownArrow
168
+ className="bottom-0 z-50 flex h-6 w-full cursor-default items-center justify-center before:pointer-events-none before:absolute before:inset-x-px before:bottom-px before:h-[200%] before:rounded-b-[calc(var(--radius-lg)-1px)] before:bg-linear-to-t before:from-50% before:from-popover"
169
+ data-slot="select-scroll-down-arrow"
170
+ >
171
+ <ChevronDownIcon className="relative size-4.5 sm:size-4" />
172
+ </SelectPrimitive.ScrollDownArrow>
173
+ </SelectPrimitive.Popup>
174
+ </SelectPrimitive.Positioner>
175
+ </SelectPrimitive.Portal>
176
+ );
177
+ }
178
+
179
+ export function SelectItem({
180
+ className,
181
+ children,
182
+ ...props
183
+ }: SelectPrimitive.Item.Props): React.ReactElement {
184
+ return (
185
+ <SelectPrimitive.Item
186
+ className={cn(
187
+ "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[1fr_1rem] items-center gap-2 rounded-sm py-1 px-2 text-base outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
188
+ className,
189
+ )}
190
+ data-slot="select-item"
191
+ {...props}
192
+ >
193
+ <SelectPrimitive.ItemText className="col-start-1 min-w-0">
194
+ {children}
195
+ </SelectPrimitive.ItemText>
196
+ <SelectPrimitive.ItemIndicator className="col-start-2">
197
+ <svg
198
+ aria-hidden="true"
199
+ fill="none"
200
+ height="24"
201
+ stroke="currentColor"
202
+ strokeLinecap="round"
203
+ strokeLinejoin="round"
204
+ strokeWidth="2"
205
+ viewBox="0 0 24 24"
206
+ width="24"
207
+ xmlns="http://www.w3.org/2000/svg"
208
+ >
209
+ <path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
210
+ </svg>
211
+ </SelectPrimitive.ItemIndicator>
212
+ </SelectPrimitive.Item>
213
+ );
214
+ }
215
+
216
+ export function SelectSeparator({
217
+ className,
218
+ ...props
219
+ }: SelectPrimitive.Separator.Props): React.ReactElement {
220
+ return (
221
+ <SelectPrimitive.Separator
222
+ className={cn("mx-2 my-1 h-px bg-border", className)}
223
+ data-slot="select-separator"
224
+ {...props}
225
+ />
226
+ );
227
+ }
228
+
229
+ export function SelectGroup(
230
+ props: SelectPrimitive.Group.Props,
231
+ ): React.ReactElement {
232
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />;
233
+ }
234
+
235
+ export function SelectGroupLabel(
236
+ props: SelectPrimitive.GroupLabel.Props,
237
+ ): React.ReactElement {
238
+ return (
239
+ <SelectPrimitive.GroupLabel
240
+ className="px-2 py-1.5 font-medium text-muted-foreground text-xs"
241
+ data-slot="select-group-label"
242
+ {...props}
243
+ />
244
+ );
245
+ }
246
+
247
+ export function SelectLabel({
248
+ className,
249
+ ...props
250
+ }: SelectPrimitive.Label.Props): React.ReactElement {
251
+ return (
252
+ <SelectPrimitive.Label
253
+ className={cn(
254
+ "not-in-data-[slot=field]:mb-2 inline-flex cursor-default items-center gap-2 font-medium text-base/4.5 text-foreground sm:text-sm/4",
255
+ className,
256
+ )}
257
+ data-slot="select-label"
258
+ {...props}
259
+ />
260
+ );
261
+ }
262
+
263
+ // ─── Composite ────────────────────────────────────────────────────────────────
264
+
265
+ function defaultGetLabel(item: unknown): string {
266
+ if (item == null) return "";
267
+ if (typeof item === "string" || typeof item === "number") return String(item);
268
+ if (typeof item === "object" && "label" in item)
269
+ return String((item as Record<string, unknown>).label);
270
+ return String(item);
271
+ }
272
+
273
+ function defaultGetValue(item: unknown): string {
274
+ if (item == null) return "";
275
+ if (typeof item === "string" || typeof item === "number") return String(item);
276
+ if (typeof item === "object" && "value" in item)
277
+ return String((item as Record<string, unknown>).value);
278
+ return String(item);
279
+ }
280
+
281
+ type SelectRootPropsAlias<V = string> = Omit<
282
+ SelectPrimitive.Root.Props<V, false>,
283
+ "onValueChange"
284
+ > & {
285
+ onChange?: SelectPrimitive.Root.Props<V, false>["onValueChange"];
286
+ };
287
+
288
+ type SelectBaseProps<TItem = unknown> = Omit<
289
+ SelectRootPropsAlias<string>,
290
+ "children" | "onChange" | "value" | "defaultValue" | "items" | "multiple"
291
+ > & {
292
+ items: readonly TItem[];
293
+ getLabel?: (item: TItem) => string;
294
+ getValue?: (item: TItem) => string;
295
+ renderItem?: (item: TItem) => React.ReactNode;
296
+ placeholder?: string;
297
+ triggerProps?: React.ComponentProps<typeof SelectTrigger>;
298
+ popupProps?: React.ComponentProps<typeof SelectPopup>;
299
+ popupClassName?: string;
300
+ itemProps?: SelectPrimitive.Item.Props;
301
+ itemClassName?: string;
302
+ className?: string;
303
+ };
304
+
305
+ export type SelectProps<TItem = unknown> =
306
+ | (SelectBaseProps<TItem> & {
307
+ multiple?: false;
308
+ value?: TItem | null;
309
+ defaultValue?: TItem | null;
310
+ onChange?: (value: TItem | null) => void;
311
+ })
312
+ | (SelectBaseProps<TItem> & {
313
+ multiple: true;
314
+ value?: TItem[];
315
+ defaultValue?: TItem[];
316
+ onChange?: (value: TItem[]) => void;
317
+ });
318
+
319
+ /**
320
+ * Composite select for single and multiple selection. Items shaped as
321
+ * `{ label, value }` or plain strings/numbers work with no extra props.
322
+ * For other shapes, provide `getLabel` and/or `getValue`.
323
+ *
324
+ * `renderItem` customizes the content **inside** each `SelectItem` (the
325
+ * checkmark indicator is always rendered by the item wrapper).
326
+ *
327
+ * Use `SelectRoot` + primitives directly when you need full structural
328
+ * control beyond what escape-hatch props offer.
329
+ */
330
+ export function Select<TItem = unknown>(
331
+ allProps: SelectProps<TItem>,
332
+ ): React.ReactElement {
333
+ const {
334
+ items,
335
+ getLabel: getLabelProp,
336
+ getValue: getValueProp,
337
+ renderItem,
338
+ placeholder,
339
+ onChange,
340
+ value,
341
+ defaultValue,
342
+ multiple,
343
+ triggerProps,
344
+ popupProps,
345
+ itemProps,
346
+ itemClassName,
347
+ className,
348
+ popupClassName,
349
+ ...rest
350
+ } = allProps as SelectBaseProps<TItem> & {
351
+ multiple?: boolean;
352
+ onChange?: (value: any) => void;
353
+ value?: any;
354
+ defaultValue?: any;
355
+ };
356
+
357
+ const getLabel: (item: TItem) => string = getLabelProp ?? defaultGetLabel;
358
+ const getValue: (item: TItem) => string = getValueProp ?? defaultGetValue;
359
+
360
+ const stringValue = multiple
361
+ ? (value as TItem[] | undefined)?.map(getValue)
362
+ : value != null
363
+ ? getValue(value as TItem)
364
+ : undefined;
365
+
366
+ const stringDefaultValue = multiple
367
+ ? (defaultValue as TItem[] | undefined)?.map(getValue)
368
+ : defaultValue != null
369
+ ? getValue(defaultValue as TItem)
370
+ : undefined;
371
+
372
+ const handleChange = (stringVal: string | string[] | null) => {
373
+ if (multiple) {
374
+ const vals = (stringVal as string[]) ?? [];
375
+ const found = vals
376
+ .map((sv) => items.find((i) => getValue(i) === sv) ?? null)
377
+ .filter(Boolean) as TItem[];
378
+ onChange?.(found);
379
+ } else {
380
+ if (stringVal == null) {
381
+ onChange?.(null);
382
+ return;
383
+ }
384
+ const item =
385
+ items.find((i) => getValue(i) === (stringVal as string)) ?? null;
386
+ onChange?.(item);
387
+ }
388
+ };
389
+
390
+ const itemToStringLabel = (stringVal: string) => {
391
+ const item = items.find((i) => getValue(i) === stringVal);
392
+ return item ? getLabel(item) : stringVal;
393
+ };
394
+
395
+ return (
396
+ <SelectRoot
397
+ itemToStringLabel={itemToStringLabel}
398
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
399
+ multiple={multiple as any}
400
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
401
+ value={stringValue as any}
402
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
403
+ defaultValue={stringDefaultValue as any}
404
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
405
+ onChange={handleChange as any}
406
+ {...(rest as any)}
407
+ >
408
+ <SelectTrigger className={className} {...triggerProps}>
409
+ <SelectValue placeholder={placeholder} />
410
+ </SelectTrigger>
411
+ <SelectPopup className={popupClassName} {...popupProps}>
412
+ {items.map((item) => (
413
+ <SelectItem
414
+ key={getValue(item)}
415
+ value={getValue(item)}
416
+ className={itemClassName}
417
+ {...itemProps}
418
+ >
419
+ {renderItem ? renderItem(item) : getLabel(item)}
420
+ </SelectItem>
421
+ ))}
422
+ </SelectPopup>
423
+ </SelectRoot>
424
+ );
425
+ }
426
+
427
+ // ─── Exports ──────────────────────────────────────────────────────────────────
428
+
429
+ export { SelectPrimitive };
430
+ export { SelectPopup as SelectContent };
@@ -0,0 +1 @@
1
+ export * from "./show";
@@ -0,0 +1,197 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Flame, Phone, Rat, SprayCan } from "lucide-react";
3
+ import { Button, Show } from "../../components";
4
+ import { useHover, useToggle } from "../../hooks";
5
+ import { cn } from "../../lib";
6
+
7
+ /**
8
+ * Componente que permite animar la entrada y salida de un componente.
9
+ *
10
+ * Expone dos estados `open` y `closed` para animar la entrada y salida respectivamente.
11
+ *
12
+ * ### Uso:
13
+ *
14
+ * ```tsx
15
+ * import { Show } from './Show';
16
+ *
17
+ * const Example = () => {
18
+ * const [open, toggle] = useToggle(false);
19
+ * return (
20
+ * <div>
21
+ * <button onClick={() => toggle()}>Toggle</button>
22
+ * <Show when={open}>
23
+ * <div className="animated-box">
24
+ * πŸ™
25
+ * </div>
26
+ * </Show>
27
+ * </div>
28
+ * );
29
+ * };
30
+ * ```
31
+ *
32
+ * ### CSS para animaciones:
33
+ *
34
+ * Define las animaciones en un archivo CSS. En este ejemplo, se crean animaciones para la entrada (`fadeInZoomIn`) y la salida (`fadeOutZoomOut`).
35
+ *
36
+ * ```css
37
+ * @keyframes fadeInZoomIn {
38
+ * 0% {
39
+ * opacity: 0;
40
+ * transform: scale(0.95);
41
+ * }
42
+ * 100% {
43
+ * opacity: 1;
44
+ * transform: scale(1);
45
+ * }
46
+ * }
47
+ *
48
+ * @keyframes fadeOutZoomOut {
49
+ * 0% {
50
+ * opacity: 1;
51
+ * transform: scale(1);
52
+ * }
53
+ * 100% {
54
+ * opacity: 0;
55
+ * transform: scale(0.95);
56
+ * }
57
+ * }
58
+ *
59
+ * .animated-box[data-state="open"] {
60
+ * animation: fadeInZoomIn 0.5s forwards;
61
+ * }
62
+ *
63
+ * .animated-box[data-state="closed"] {
64
+ * animation: fadeOutZoomOut 0.5s forwards;
65
+ * }
66
+ * ```
67
+ *
68
+ * ### Alternativa con TailwindCSS:
69
+ *
70
+ * Alternativamente, se puede usar el plugin [tailwindcss-animate](https://www.npmjs.com/package/tailwindcss-animate)
71
+ * que ya trae animaciones predefinidas. AcΓ‘ un ejemplo:
72
+ *
73
+ * ```tsx
74
+ * import { Show } from './Show';
75
+ *
76
+ * const Example = () => {
77
+ * const [open, toggle] = useToggle(false);
78
+ * return (
79
+ * <div>
80
+ * <button onClick={() => toggle()}>Toggle</button>
81
+ * <Show when={open}>
82
+ * <div
83
+ * className={cn(
84
+ * // Animaciones de entrada
85
+ * "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
86
+ * // Animaciones de salida
87
+ * "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95"
88
+ * )}
89
+ * >
90
+ * πŸ™
91
+ * </div>
92
+ * </Show>
93
+ * </div>
94
+ * );
95
+ * };
96
+ * ```
97
+ * ## Crea alias para acortar `data-[state-X]`
98
+ * En caso de que se quiera acortar el prefijo `data-[state-X]` se puede crear una variante en el archivo `tailwind.config.js`:
99
+ *
100
+ * ```tsx
101
+ * // tailwind.config.js
102
+ *
103
+ * // Importar plugin
104
+ * const plugin = require("tailwindcss/plugin");
105
+ *
106
+ * module.exports = {
107
+ * // ...
108
+ * plugins: [
109
+ * plugin(function ({ addVariant }) {
110
+ * addVariant("open", '&[data-state="open"]');
111
+ * addVariant("closed", '&[data-state="closed"]');
112
+ * }),
113
+ * ],
114
+ * };
115
+ *
116
+ * // Uso
117
+ * import { Show } from './Show';
118
+ *
119
+ * const Example = () => {
120
+ * const [open, toggle] = useToggle(false);
121
+ * return (
122
+ * <div>
123
+ * <button onClick={() => toggle()}>Toggle</button>
124
+ * <Show when={open}>
125
+ * <div
126
+ * className={cn(
127
+ * // Animaciones de entrada
128
+ * "open:animate-in open:fade-in-0 open:zoom-in-95",
129
+ * // Animaciones de salida
130
+ * "closed:animate-out closed:fade-out-0 closed:zoom-out-95"
131
+ * )}
132
+ * >
133
+ * πŸ™
134
+ * </div>
135
+ * </Show>
136
+ * </div>
137
+ * );
138
+ * };
139
+ * ```
140
+ */
141
+ const meta: Meta<typeof Show> = {
142
+ title: "others/Show",
143
+ component: Show,
144
+ };
145
+
146
+ export default meta;
147
+ type Story = StoryObj<typeof Show>;
148
+
149
+ export const Default: Story = {
150
+ render: () => {
151
+ const [open, toggle] = useToggle(false);
152
+ return (
153
+ <div>
154
+ <Button onClick={() => toggle()}>Toggle</Button>
155
+ <Show when={open}>
156
+ <div
157
+ className={cn(
158
+ "border rounded-md p-2 mt-2 w-[200px]",
159
+ "open:animate-in open:zoom-in-95",
160
+ "closed:animate-out closed:fade-out-0 closed:zoom-out-95",
161
+ )}
162
+ >
163
+ πŸ™
164
+ </div>
165
+ </Show>
166
+ </div>
167
+ );
168
+ },
169
+ };
170
+
171
+ export const Hover: Story = {
172
+ render: () => {
173
+ const { ref, isHovering } = useHover<HTMLDivElement>();
174
+ return (
175
+ <div
176
+ className="bg-gray-100 h-[300px] w-[300px] border border-dashed flex items-end overflow-hidden"
177
+ ref={ref}
178
+ >
179
+ <Show when={isHovering}>
180
+ <div
181
+ className={cn(
182
+ "rounded-md text-xl p-2 w-full flex justify-center gap-x-4",
183
+ "duration-300",
184
+ "open:animate-in open:slide-in-from-bottom",
185
+ "closed:animate-out closed:slide-out-to-bottom",
186
+ )}
187
+ >
188
+ <Phone />
189
+ <Rat />
190
+ <SprayCan />
191
+ <Flame />
192
+ </div>
193
+ </Show>
194
+ </div>
195
+ );
196
+ },
197
+ };
@@ -0,0 +1,41 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import "@testing-library/jest-dom";
3
+ import { ComponentProps } from "react";
4
+ import { describe, expect, test } from "vitest";
5
+ import { Show } from "../../components";
6
+
7
+ type PresenceProps = ComponentProps<typeof Show>;
8
+
9
+ describe("Show Component", () => {
10
+ const renderPresence = (props: PresenceProps) => {
11
+ return render(<Show {...props} />);
12
+ };
13
+
14
+ test('renders the component with "open" state when "when" prop is true', () => {
15
+ renderPresence({ when: true, children: <div>hello</div> });
16
+
17
+ const slotElement = screen.getByText("hello");
18
+ expect(slotElement).toBeInTheDocument();
19
+ expect(slotElement).toHaveAttribute("data-state", "open");
20
+ });
21
+
22
+ test('unmount the component when "when" prop is false', () => {
23
+ renderPresence({ when: false, children: <div>hello</div> });
24
+
25
+ const slotElement = screen.queryByText("hello");
26
+ expect(slotElement).not.toBeInTheDocument();
27
+ });
28
+
29
+ test("passes additional props to the Slot component", () => {
30
+ renderPresence({
31
+ when: true,
32
+ className: "test-class",
33
+ children: <div>hello</div>,
34
+ });
35
+
36
+ const slotElement = screen.getByText("hello");
37
+ expect(slotElement).toBeInTheDocument();
38
+
39
+ expect(slotElement).toHaveClass("test-class");
40
+ });
41
+ });
@@ -0,0 +1,16 @@
1
+ import { Presence as AnimatePresence } from "@radix-ui/react-presence";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { ComponentProps, ComponentPropsWithoutRef } from "react";
4
+
5
+ interface Props extends ComponentPropsWithoutRef<typeof Slot> {
6
+ /** Si el componente estΓ‘ presente o no */
7
+ when: ComponentProps<typeof AnimatePresence>["present"];
8
+ }
9
+
10
+ export const Show = ({ when, ...props }: Props) => {
11
+ return (
12
+ <AnimatePresence present={when}>
13
+ <Slot {...props} data-state={when ? "open" : "closed"} data-slot="show" />
14
+ </AnimatePresence>
15
+ );
16
+ };
@@ -0,0 +1 @@
1
+ export * from "./skeleton";