@zentauri-ui/zentauri-components 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/README.md +78 -0
  2. package/cli/cli.integration.test.ts +51 -0
  3. package/cli/index.mjs +664 -0
  4. package/cli/registry.json +36 -0
  5. package/cli/rewrite-imports.mjs +57 -0
  6. package/cli/rewrite-imports.test.ts +71 -0
  7. package/dist/ui/slider/slider.d.ts +18 -0
  8. package/dist/ui/slider/slider.d.ts.map +1 -1
  9. package/dist/ui/slider.js +21 -25
  10. package/dist/ui/slider.js.map +1 -1
  11. package/dist/ui/slider.mjs +21 -25
  12. package/dist/ui/slider.mjs.map +1 -1
  13. package/package.json +8 -2
  14. package/src/hooks/index.ts +48 -0
  15. package/src/hooks/useBodyScrollLock/index.ts +1 -0
  16. package/src/hooks/useBodyScrollLock/useBodyScrollLock.test.ts +51 -0
  17. package/src/hooks/useBodyScrollLock/useBodyScrollLock.ts +48 -0
  18. package/src/hooks/useClickOutside/index.ts +5 -0
  19. package/src/hooks/useClickOutside/useClickOutside.test.tsx +60 -0
  20. package/src/hooks/useClickOutside/useClickOutside.ts +52 -0
  21. package/src/hooks/useClipboard/index.ts +1 -0
  22. package/src/hooks/useClipboard/useClipboard.test.ts +101 -0
  23. package/src/hooks/useClipboard/useClipboard.ts +69 -0
  24. package/src/hooks/useControllableState/index.ts +4 -0
  25. package/src/hooks/useControllableState/useControllableState.test.ts +59 -0
  26. package/src/hooks/useControllableState/useControllableState.ts +49 -0
  27. package/src/hooks/useDebouncedValue/index.ts +1 -0
  28. package/src/hooks/useDebouncedValue/useDebouncedValue.test.ts +74 -0
  29. package/src/hooks/useDebouncedValue/useDebouncedValue.ts +29 -0
  30. package/src/hooks/useDisclosure/index.ts +5 -0
  31. package/src/hooks/useDisclosure/useDisclosure.test.ts +64 -0
  32. package/src/hooks/useDisclosure/useDisclosure.ts +62 -0
  33. package/src/hooks/useDocumentTitle/index.ts +4 -0
  34. package/src/hooks/useDocumentTitle/useDocumentTitle.test.ts +40 -0
  35. package/src/hooks/useDocumentTitle/useDocumentTitle.ts +58 -0
  36. package/src/hooks/useFocusManagement/index.ts +1 -0
  37. package/src/hooks/useFocusManagement/useFocusManagement.test.tsx +45 -0
  38. package/src/hooks/useFocusManagement/useFocusManagement.ts +77 -0
  39. package/src/hooks/useHover/index.ts +1 -0
  40. package/src/hooks/useHover/useHover.test.ts +45 -0
  41. package/src/hooks/useHover/useHover.ts +45 -0
  42. package/src/hooks/useInView/index.ts +1 -0
  43. package/src/hooks/useInView/useInView.test.ts +43 -0
  44. package/src/hooks/useInView/useInView.ts +28 -0
  45. package/src/hooks/useIntersectionObserver/index.ts +4 -0
  46. package/src/hooks/useIntersectionObserver/useIntersectionObserver.test.ts +75 -0
  47. package/src/hooks/useIntersectionObserver/useIntersectionObserver.ts +54 -0
  48. package/src/hooks/useIsMounted/index.ts +1 -0
  49. package/src/hooks/useIsMounted/useIsMounted.test.ts +25 -0
  50. package/src/hooks/useIsMounted/useIsMounted.ts +22 -0
  51. package/src/hooks/useIsomorphicLayoutEffect/index.ts +1 -0
  52. package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.test.ts +19 -0
  53. package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.ts +12 -0
  54. package/src/hooks/useLocalStorage/index.ts +4 -0
  55. package/src/hooks/useLocalStorage/useLocalStorage.test.ts +99 -0
  56. package/src/hooks/useLocalStorage/useLocalStorage.ts +109 -0
  57. package/src/hooks/useMediaQuery/index.ts +1 -0
  58. package/src/hooks/useMediaQuery/useMediaQuery.test.ts +63 -0
  59. package/src/hooks/useMediaQuery/useMediaQuery.ts +37 -0
  60. package/src/hooks/useNetworkStatus/index.ts +1 -0
  61. package/src/hooks/useNetworkStatus/useNetworkStatus.test.ts +53 -0
  62. package/src/hooks/useNetworkStatus/useNetworkStatus.ts +33 -0
  63. package/src/hooks/usePageVisibility/index.ts +1 -0
  64. package/src/hooks/usePageVisibility/usePageVisibility.test.ts +21 -0
  65. package/src/hooks/usePageVisibility/usePageVisibility.ts +31 -0
  66. package/src/hooks/usePagination/index.ts +6 -0
  67. package/src/hooks/usePagination/usePagination.test.ts +139 -0
  68. package/src/hooks/usePagination/usePagination.ts +153 -0
  69. package/src/hooks/usePrefersColorScheme/index.ts +4 -0
  70. package/src/hooks/usePrefersColorScheme/usePrefersColorScheme.test.ts +53 -0
  71. package/src/hooks/usePrefersColorScheme/usePrefersColorScheme.ts +21 -0
  72. package/src/hooks/usePrefersReducedMotion/index.ts +1 -0
  73. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.test.ts +27 -0
  74. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.ts +14 -0
  75. package/src/hooks/useResizeObserver/index.ts +4 -0
  76. package/src/hooks/useResizeObserver/useResizeObserver.test.ts +68 -0
  77. package/src/hooks/useResizeObserver/useResizeObserver.ts +58 -0
  78. package/src/hooks/useSessionStorage/index.ts +4 -0
  79. package/src/hooks/useSessionStorage/useSessionStorage.test.ts +54 -0
  80. package/src/hooks/useSessionStorage/useSessionStorage.ts +84 -0
  81. package/src/hooks/useThrottledCallback/index.ts +1 -0
  82. package/src/hooks/useThrottledCallback/useThrottledCallback.test.ts +75 -0
  83. package/src/hooks/useThrottledCallback/useThrottledCallback.ts +36 -0
  84. package/src/hooks/useToggle/index.ts +1 -0
  85. package/src/hooks/useToggle/useToggle.test.ts +40 -0
  86. package/src/hooks/useToggle/useToggle.ts +22 -0
  87. package/src/hooks/useWindowSize/index.ts +1 -0
  88. package/src/hooks/useWindowSize/useWindowSize.test.ts +23 -0
  89. package/src/hooks/useWindowSize/useWindowSize.ts +39 -0
  90. package/src/lib/utils.ts +25 -0
  91. package/src/ui/accordion/accordion-base.tsx +223 -0
  92. package/src/ui/accordion/accordion.test.tsx +146 -0
  93. package/src/ui/accordion/accordion.tsx +11 -0
  94. package/src/ui/accordion/animated/accordion-content-animated.tsx +46 -0
  95. package/src/ui/accordion/animated/accordion-root-animated.tsx +10 -0
  96. package/src/ui/accordion/animated/animations.ts +16 -0
  97. package/src/ui/accordion/animated/index.ts +7 -0
  98. package/src/ui/accordion/animated/types.ts +7 -0
  99. package/src/ui/accordion/index.ts +23 -0
  100. package/src/ui/accordion/types.ts +48 -0
  101. package/src/ui/accordion/variants.ts +115 -0
  102. package/src/ui/alert/alert-base.tsx +157 -0
  103. package/src/ui/alert/alert.test.tsx +150 -0
  104. package/src/ui/alert/alert.tsx +9 -0
  105. package/src/ui/alert/animated/alert-animated.tsx +20 -0
  106. package/src/ui/alert/animated/animations.ts +20 -0
  107. package/src/ui/alert/animated/index.ts +3 -0
  108. package/src/ui/alert/animated/types.ts +16 -0
  109. package/src/ui/alert/index.ts +22 -0
  110. package/src/ui/alert/types.ts +28 -0
  111. package/src/ui/alert/variants.ts +74 -0
  112. package/src/ui/avatar/animated/animations.ts +11 -0
  113. package/src/ui/avatar/animated/avatar-animated.tsx +25 -0
  114. package/src/ui/avatar/animated/index.ts +6 -0
  115. package/src/ui/avatar/animated/types.ts +16 -0
  116. package/src/ui/avatar/avatar-base.tsx +184 -0
  117. package/src/ui/avatar/avatar.test.tsx +51 -0
  118. package/src/ui/avatar/avatar.tsx +11 -0
  119. package/src/ui/avatar/index.ts +16 -0
  120. package/src/ui/avatar/types.ts +36 -0
  121. package/src/ui/avatar/variants.ts +52 -0
  122. package/src/ui/badge/animated/animations.ts +20 -0
  123. package/src/ui/badge/animated/badge-animated.tsx +28 -0
  124. package/src/ui/badge/animated/index.ts +5 -0
  125. package/src/ui/badge/animated/types.ts +18 -0
  126. package/src/ui/badge/badge-base.tsx +53 -0
  127. package/src/ui/badge/badge.test.tsx +48 -0
  128. package/src/ui/badge/badge.tsx +9 -0
  129. package/src/ui/badge/index.ts +5 -0
  130. package/src/ui/badge/types.ts +25 -0
  131. package/src/ui/badge/variants.ts +85 -0
  132. package/src/ui/breadcrumb/breadcrumb.test.tsx +62 -0
  133. package/src/ui/breadcrumb/breadcrumb.tsx +135 -0
  134. package/src/ui/breadcrumb/index.ts +28 -0
  135. package/src/ui/breadcrumb/types.ts +29 -0
  136. package/src/ui/breadcrumb/variants.ts +53 -0
  137. package/src/ui/buttons/animated/animations.ts +34 -0
  138. package/src/ui/buttons/animated/button-animated.tsx +70 -0
  139. package/src/ui/buttons/animated/index.ts +5 -0
  140. package/src/ui/buttons/animated/types.ts +29 -0
  141. package/src/ui/buttons/button-base.tsx +59 -0
  142. package/src/ui/buttons/button.test.tsx +480 -0
  143. package/src/ui/buttons/button.tsx +9 -0
  144. package/src/ui/buttons/index.ts +5 -0
  145. package/src/ui/buttons/types.ts +14 -0
  146. package/src/ui/buttons/variants.ts +77 -0
  147. package/src/ui/card/animated/animations.ts +32 -0
  148. package/src/ui/card/animated/card-animated.tsx +28 -0
  149. package/src/ui/card/animated/index.ts +12 -0
  150. package/src/ui/card/animated/types.ts +8 -0
  151. package/src/ui/card/card-base.tsx +146 -0
  152. package/src/ui/card/card.test.tsx +79 -0
  153. package/src/ui/card/card.tsx +11 -0
  154. package/src/ui/card/index.ts +21 -0
  155. package/src/ui/card/types.ts +42 -0
  156. package/src/ui/card/variants.ts +122 -0
  157. package/src/ui/divider/animated/animations.ts +27 -0
  158. package/src/ui/divider/animated/divider-animated.tsx +24 -0
  159. package/src/ui/divider/animated/index.ts +4 -0
  160. package/src/ui/divider/animated/types.ts +18 -0
  161. package/src/ui/divider/divider-base.tsx +80 -0
  162. package/src/ui/divider/divider.tsx +9 -0
  163. package/src/ui/divider/index.ts +14 -0
  164. package/src/ui/divider/types.ts +18 -0
  165. package/src/ui/divider/variants.ts +98 -0
  166. package/src/ui/drawer/animated/animations.ts +39 -0
  167. package/src/ui/drawer/animated/drawer-content-animated.tsx +101 -0
  168. package/src/ui/drawer/animated/index.ts +14 -0
  169. package/src/ui/drawer/animated/types.ts +18 -0
  170. package/src/ui/drawer/drawer-base.tsx +259 -0
  171. package/src/ui/drawer/drawer.test.tsx +132 -0
  172. package/src/ui/drawer/drawer.tsx +11 -0
  173. package/src/ui/drawer/index.ts +21 -0
  174. package/src/ui/drawer/types.ts +39 -0
  175. package/src/ui/drawer/variants.ts +122 -0
  176. package/src/ui/dropdown/dropdown.test.tsx +114 -0
  177. package/src/ui/dropdown/dropdown.tsx +179 -0
  178. package/src/ui/dropdown/index.ts +15 -0
  179. package/src/ui/dropdown/types.ts +68 -0
  180. package/src/ui/dropdown/variants.ts +138 -0
  181. package/src/ui/empty-state/animated/animations.ts +19 -0
  182. package/src/ui/empty-state/animated/empty-state-animated.tsx +23 -0
  183. package/src/ui/empty-state/animated/index.ts +7 -0
  184. package/src/ui/empty-state/animated/types.ts +26 -0
  185. package/src/ui/empty-state/empty-state-base.tsx +114 -0
  186. package/src/ui/empty-state/empty-state.tsx +9 -0
  187. package/src/ui/empty-state/index.ts +10 -0
  188. package/src/ui/empty-state/types.ts +19 -0
  189. package/src/ui/empty-state/variants.ts +51 -0
  190. package/src/ui/file-upload/file-upload.test.tsx +36 -0
  191. package/src/ui/file-upload/file-upload.tsx +119 -0
  192. package/src/ui/file-upload/index.ts +5 -0
  193. package/src/ui/file-upload/types.ts +21 -0
  194. package/src/ui/file-upload/variants.ts +29 -0
  195. package/src/ui/inputs/animated/animations.ts +36 -0
  196. package/src/ui/inputs/animated/index.ts +5 -0
  197. package/src/ui/inputs/animated/input-animated.tsx +124 -0
  198. package/src/ui/inputs/animated/types.ts +40 -0
  199. package/src/ui/inputs/index.ts +5 -0
  200. package/src/ui/inputs/input-base.tsx +114 -0
  201. package/src/ui/inputs/input.test.tsx +414 -0
  202. package/src/ui/inputs/input.tsx +8 -0
  203. package/src/ui/inputs/types.ts +18 -0
  204. package/src/ui/inputs/variants.ts +316 -0
  205. package/src/ui/modal/animated/animations.ts +29 -0
  206. package/src/ui/modal/animated/index.ts +5 -0
  207. package/src/ui/modal/animated/modal-content-animated.tsx +96 -0
  208. package/src/ui/modal/animated/types.ts +23 -0
  209. package/src/ui/modal/index.ts +21 -0
  210. package/src/ui/modal/modal-base.tsx +279 -0
  211. package/src/ui/modal/modal.test.tsx +129 -0
  212. package/src/ui/modal/modal.tsx +8 -0
  213. package/src/ui/modal/types.ts +31 -0
  214. package/src/ui/modal/variants.ts +109 -0
  215. package/src/ui/pagination/index.ts +13 -0
  216. package/src/ui/pagination/pagination.test.tsx +165 -0
  217. package/src/ui/pagination/pagination.tsx +237 -0
  218. package/src/ui/pagination/types.ts +66 -0
  219. package/src/ui/pagination/variants.ts +97 -0
  220. package/src/ui/progress/animated/animations.ts +9 -0
  221. package/src/ui/progress/animated/index.ts +17 -0
  222. package/src/ui/progress/animated/progress-animated.tsx +133 -0
  223. package/src/ui/progress/animated/types.ts +35 -0
  224. package/src/ui/progress/index.ts +10 -0
  225. package/src/ui/progress/progress-base.tsx +151 -0
  226. package/src/ui/progress/progress.test.tsx +84 -0
  227. package/src/ui/progress/progress.tsx +12 -0
  228. package/src/ui/progress/types.ts +33 -0
  229. package/src/ui/progress/variants.ts +105 -0
  230. package/src/ui/select/index.ts +25 -0
  231. package/src/ui/select/select.test.tsx +128 -0
  232. package/src/ui/select/select.tsx +221 -0
  233. package/src/ui/select/types.ts +77 -0
  234. package/src/ui/select/variants.ts +163 -0
  235. package/src/ui/skeleton/animated/animations.ts +15 -0
  236. package/src/ui/skeleton/animated/index.ts +20 -0
  237. package/src/ui/skeleton/animated/skeleton-animated.tsx +119 -0
  238. package/src/ui/skeleton/animated/types.ts +49 -0
  239. package/src/ui/skeleton/index.ts +24 -0
  240. package/src/ui/skeleton/skeleton-base.tsx +288 -0
  241. package/src/ui/skeleton/skeleton.tsx +8 -0
  242. package/src/ui/skeleton/types.ts +31 -0
  243. package/src/ui/skeleton/variants.ts +254 -0
  244. package/src/ui/slider/index.ts +22 -0
  245. package/src/ui/slider/slider.test.tsx +94 -0
  246. package/src/ui/slider/slider.tsx +728 -0
  247. package/src/ui/slider/types.ts +66 -0
  248. package/src/ui/slider/variants.ts +81 -0
  249. package/src/ui/spinner/animated/index.ts +5 -0
  250. package/src/ui/spinner/animated/spinner.test.tsx +41 -0
  251. package/src/ui/spinner/animated/spinner.tsx +143 -0
  252. package/src/ui/spinner/animated/types.ts +11 -0
  253. package/src/ui/spinner/animated/variants.ts +50 -0
  254. package/src/ui/stepper/index.ts +22 -0
  255. package/src/ui/stepper/stepper.test.tsx +183 -0
  256. package/src/ui/stepper/stepper.tsx +172 -0
  257. package/src/ui/stepper/types.ts +32 -0
  258. package/src/ui/stepper/variants.ts +69 -0
  259. package/src/ui/table/animated/animations.ts +9 -0
  260. package/src/ui/table/animated/index.ts +15 -0
  261. package/src/ui/table/animated/table-animated.tsx +15 -0
  262. package/src/ui/table/animated/types.ts +16 -0
  263. package/src/ui/table/index.ts +22 -0
  264. package/src/ui/table/table-base.tsx +197 -0
  265. package/src/ui/table/table.tsx +13 -0
  266. package/src/ui/table/types.ts +47 -0
  267. package/src/ui/table/variants.ts +105 -0
  268. package/src/ui/tabs/animated/animations.ts +48 -0
  269. package/src/ui/tabs/animated/index.ts +8 -0
  270. package/src/ui/tabs/animated/tabs-content-animated.tsx +46 -0
  271. package/src/ui/tabs/animated/types.ts +24 -0
  272. package/src/ui/tabs/index.ts +10 -0
  273. package/src/ui/tabs/tabs-base.tsx +185 -0
  274. package/src/ui/tabs/tabs.test.tsx +53 -0
  275. package/src/ui/tabs/tabs.tsx +2 -0
  276. package/src/ui/tabs/types.ts +88 -0
  277. package/src/ui/tabs/variants.ts +70 -0
  278. package/src/ui/toast/animated/animations.ts +17 -0
  279. package/src/ui/toast/animated/index.ts +9 -0
  280. package/src/ui/toast/animated/toast-animated.tsx +96 -0
  281. package/src/ui/toast/animated/types.ts +13 -0
  282. package/src/ui/toast/index.ts +26 -0
  283. package/src/ui/toast/toast-base.tsx +231 -0
  284. package/src/ui/toast/toast.test.tsx +102 -0
  285. package/src/ui/toast/toast.tsx +13 -0
  286. package/src/ui/toast/types.ts +57 -0
  287. package/src/ui/toast/variants.ts +73 -0
  288. package/src/ui/toggle/animated/animations.ts +9 -0
  289. package/src/ui/toggle/animated/index.ts +7 -0
  290. package/src/ui/toggle/animated/toggle-animated.tsx +76 -0
  291. package/src/ui/toggle/animated/types.ts +13 -0
  292. package/src/ui/toggle/index.ts +5 -0
  293. package/src/ui/toggle/toggle-base.tsx +70 -0
  294. package/src/ui/toggle/toggle.test.tsx +44 -0
  295. package/src/ui/toggle/toggle.tsx +9 -0
  296. package/src/ui/toggle/types.ts +18 -0
  297. package/src/ui/toggle/variants.ts +84 -0
  298. package/src/ui/tooltip/animated/animations.ts +16 -0
  299. package/src/ui/tooltip/animated/index.ts +10 -0
  300. package/src/ui/tooltip/animated/tooltip-content-animated.tsx +47 -0
  301. package/src/ui/tooltip/animated/types.ts +19 -0
  302. package/src/ui/tooltip/index.ts +17 -0
  303. package/src/ui/tooltip/tooltip-base.tsx +152 -0
  304. package/src/ui/tooltip/tooltip.test.tsx +84 -0
  305. package/src/ui/tooltip/tooltip.tsx +8 -0
  306. package/src/ui/tooltip/types.ts +57 -0
  307. package/src/ui/tooltip/variants.ts +61 -0
@@ -0,0 +1,105 @@
1
+ import { cva } from "class-variance-authority";
2
+
3
+ export const tableVariants = cva(
4
+ "w-full table-auto border-collapse caption-bottom text-sm text-slate-200 md:table-fixed",
5
+ {
6
+ variants: {
7
+ appearance: {
8
+ default: "",
9
+ striped: "",
10
+ bordered: "border border-white/10",
11
+ ghost: "",
12
+ sky: "border border-sky-600",
13
+ rose: "border border-rose-600",
14
+ purple: "border border-purple-600",
15
+ pink: "border border-pink-600",
16
+ orange: "border border-orange-600",
17
+ yellow: "border border-yellow-600",
18
+ teal: "border border-teal-600",
19
+ indigo: "border border-indigo-600",
20
+ emerald: "border border-emerald-600",
21
+ gray: "border border-gray-600",
22
+ amber: "border border-amber-600",
23
+ violet: "border border-violet-600",
24
+ },
25
+ size: {
26
+ sm: "text-xs",
27
+ md: "text-sm",
28
+ lg: "text-base",
29
+ },
30
+ stickyHeader: {
31
+ true: "",
32
+ false: "",
33
+ },
34
+ },
35
+ defaultVariants: {
36
+ appearance: "default",
37
+ size: "md",
38
+ stickyHeader: false,
39
+ },
40
+ },
41
+ );
42
+
43
+ export const tableRowVariants = cva(
44
+ "border-b border-white/5 transition-colors data-[state=selected]:bg-white/[0.06]",
45
+ {
46
+ variants: {
47
+ appearance: {
48
+ default: "",
49
+ striped: "odd:bg-white/[0.03]",
50
+ bordered: "",
51
+ ghost: "border-transparent hover:bg-white/[0.03]",
52
+ sky: "border-sky-600 hover:bg-sky-600",
53
+ rose: "border-rose-600 hover:bg-rose-900",
54
+ purple: "border-purple-600 hover:bg-purple-900",
55
+ pink: "border-pink-600 hover:bg-pink-900",
56
+ orange: "border-orange-600 hover:bg-orange-900",
57
+ yellow: "border-yellow-600 hover:bg-yellow-900",
58
+ teal: "border-teal-600 hover:bg-teal-900",
59
+ indigo: "border-indigo-600 hover:bg-indigo-900",
60
+ emerald: "border-emerald-600 hover:bg-emerald-900",
61
+ gray: "border-gray-600 hover:bg-gray-900",
62
+ amber: "border-amber-600 hover:bg-amber-900",
63
+ violet: "border-violet-600 hover:bg-violet-900",
64
+ },
65
+ },
66
+ defaultVariants: { appearance: "default" },
67
+ },
68
+ );
69
+
70
+ export const tableCellVariants = cva(
71
+ "min-w-0 border p-3 align-middle break-words",
72
+ {
73
+ variants: {
74
+ appearance: {
75
+ default: "border-white/10",
76
+ striped: "border-white/10",
77
+ bordered: "border-white/10",
78
+ ghost: "border-white/10",
79
+ sky: "border-sky-600",
80
+ rose: "border-rose-600",
81
+ purple: "border-purple-600",
82
+ pink: "border-pink-600",
83
+ orange: "border-orange-600",
84
+ yellow: "border-yellow-600",
85
+ teal: "border-teal-600",
86
+ indigo: "border-indigo-600",
87
+ emerald: "border-emerald-600",
88
+ gray: "border-gray-600",
89
+ amber: "border-amber-600",
90
+ violet: "border-violet-600",
91
+ },
92
+ size: {
93
+ sm: "p-2",
94
+ md: "p-3",
95
+ lg: "p-4",
96
+ },
97
+ textAlign: {
98
+ left: "text-left",
99
+ center: "text-center",
100
+ right: "text-right",
101
+ },
102
+ },
103
+ defaultVariants: { appearance: "default", size: "md" },
104
+ },
105
+ );
@@ -0,0 +1,48 @@
1
+ import type { TabsAnimationPresets, TabsOrientation, TabsPresetMotionProps } from "./types";
2
+ import type { TabsAnimation } from "./types";
3
+
4
+ const easeOutExpo: [number, number, number, number] = [0.16, 1, 0.3, 1];
5
+
6
+ export const tabsContentAnimationPresets: TabsAnimationPresets = {
7
+ none: {},
8
+ fade: {
9
+ initial: { opacity: 0, y: 6 },
10
+ animate: { opacity: 1, y: 0 },
11
+ transition: { duration: 0.2, ease: easeOutExpo },
12
+ },
13
+ slide: {
14
+ initial: { opacity: 0, y: 8 },
15
+ animate: { opacity: 1, y: 0 },
16
+ transition: {
17
+ type: "spring",
18
+ stiffness: 420,
19
+ damping: 34,
20
+ mass: 0.82,
21
+ },
22
+ },
23
+ };
24
+
25
+ export function getTabsContentMotionProps(
26
+ animation: TabsAnimation,
27
+ orientation: TabsOrientation,
28
+ reducedMotion: boolean,
29
+ ): TabsPresetMotionProps {
30
+ if (reducedMotion || animation === "none") {
31
+ return {};
32
+ }
33
+ if (animation === "fade") {
34
+ return tabsContentAnimationPresets.fade;
35
+ }
36
+ if (animation === "slide") {
37
+ const offset = 12;
38
+ return {
39
+ initial:
40
+ orientation === "horizontal"
41
+ ? { opacity: 0, x: offset, y: 0 }
42
+ : { opacity: 0, x: 0, y: offset },
43
+ animate: { opacity: 1, x: 0, y: 0 },
44
+ transition: { duration: 0.22, ease: easeOutExpo },
45
+ };
46
+ }
47
+ return {};
48
+ }
@@ -0,0 +1,8 @@
1
+ "use client";
2
+
3
+ export { TabsContentAnimated } from "./tabs-content-animated";
4
+ export type { TabsContentAnimatedProps, TabsAnimation, TabsAnimationPresets, TabsOrientation, TabsPresetMotionProps } from "./types";
5
+ export {
6
+ getTabsContentMotionProps,
7
+ tabsContentAnimationPresets,
8
+ } from "./animations";
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import { motion, prefersReducedMotion, useReducedMotion } from "framer-motion";
4
+
5
+ import { cn } from "../../../lib/utils";
6
+
7
+ import { getTabsContentMotionProps } from "./animations";
8
+ import type { TabsContentAnimatedProps } from "./types";
9
+ import { useTabs } from "../tabs-base";
10
+
11
+ export function TabsContentAnimated({
12
+ value,
13
+ children,
14
+ className,
15
+ animation = "fade",
16
+ }: TabsContentAnimatedProps) {
17
+ const {
18
+ value: activeValue,
19
+ orientation,
20
+ tabTriggerId,
21
+ tabPanelId,
22
+ } = useTabs();
23
+ const prefersReducedMotion = useReducedMotion();
24
+
25
+ if (activeValue !== value) return null;
26
+
27
+ const motionProps = getTabsContentMotionProps(
28
+ animation,
29
+ orientation,
30
+ Boolean(prefersReducedMotion),
31
+ );
32
+
33
+ return (
34
+ <motion.div
35
+ id={tabPanelId(value)}
36
+ role="tabpanel"
37
+ aria-labelledby={tabTriggerId(value)}
38
+ {...motionProps}
39
+ className={cn("mt-2", className)}
40
+ >
41
+ {children}
42
+ </motion.div>
43
+ );
44
+ }
45
+
46
+ TabsContentAnimated.displayName = "TabsContent";
@@ -0,0 +1,24 @@
1
+ import type { HTMLMotionProps } from "framer-motion";
2
+ import type { ReactNode } from "react";
3
+ import type { TabsValue } from "../types";
4
+
5
+ export type TabsAnimation = "none" | "fade" | "slide";
6
+
7
+ export type TabsPresetMotionProps = Pick<
8
+ HTMLMotionProps<"div">,
9
+ "initial" | "animate" | "transition"
10
+ >;
11
+
12
+ export type TabsAnimationPresets = Record<TabsAnimation, TabsPresetMotionProps>;
13
+
14
+ export type TabsOrientation = "horizontal" | "vertical";
15
+
16
+ export type TabsContentAnimatedProps = {
17
+ value: TabsValue;
18
+ children: ReactNode;
19
+ className?: string;
20
+ animation?: TabsAnimation;
21
+ } & Omit<
22
+ HTMLMotionProps<"div">,
23
+ "children" | "className" | "role" | "id" | "aria-labelledby"
24
+ >;
@@ -0,0 +1,10 @@
1
+ "use client";
2
+
3
+ export { Tabs, TabsContent, TabsList, TabsTrigger, TabsContext, useTabs } from "./tabs";
4
+ export type {
5
+ TabsContentProps,
6
+ TabsListProps,
7
+ TabsProps,
8
+ TabsTriggerProps,
9
+ } from "./types";
10
+ export { tabsListVariants, tabsTriggerVariants } from "./variants";
@@ -0,0 +1,185 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ KeyboardEvent,
6
+ useContext,
7
+ useId,
8
+ useState,
9
+ } from "react";
10
+
11
+ import { cn } from "../../lib/utils";
12
+
13
+ import {
14
+ TabsContentProps,
15
+ TabsListProps,
16
+ TabsProps,
17
+ TabsTriggerProps,
18
+ TabsValue,
19
+ TabsContextType,
20
+ } from "./types";
21
+ import { tabsListVariants, tabsTriggerVariants } from "./variants";
22
+
23
+ export const TabsContext = createContext<TabsContextType | null>(null);
24
+
25
+ export const useTabs = () => {
26
+ const ctx = useContext(TabsContext);
27
+ if (!ctx) throw new Error("Tabs components must be used within Tabs");
28
+ return ctx;
29
+ };
30
+
31
+ export function Tabs({
32
+ value,
33
+ defaultValue,
34
+ onValueChange,
35
+ orientation = "horizontal",
36
+ variant,
37
+ size,
38
+ appearance,
39
+ children,
40
+ className,
41
+ }: TabsProps) {
42
+ const [internalValue, setInternalValue] = useState(defaultValue);
43
+ const idPrefix = useId();
44
+
45
+ const isControlled = value !== undefined;
46
+ const currentValue = isControlled ? value : internalValue;
47
+
48
+ const setValue = (val: string) => {
49
+ if (!isControlled) setInternalValue(val);
50
+ onValueChange?.(val);
51
+ };
52
+
53
+ const tabTriggerId = (tabValue: TabsValue) =>
54
+ `${idPrefix}zentauri-tab-${tabValue}`;
55
+ const tabPanelId = (tabValue: TabsValue) =>
56
+ `${idPrefix}zentauri-panel-${tabValue}`;
57
+
58
+ return (
59
+ <TabsContext.Provider
60
+ value={{
61
+ value: currentValue,
62
+ setValue,
63
+ orientation,
64
+ size,
65
+ variant,
66
+ appearance,
67
+ tabTriggerId,
68
+ tabPanelId,
69
+ }}
70
+ >
71
+ <div data-slot="tabs" className={cn("w-full", className)}>
72
+ {children}
73
+ </div>
74
+ </TabsContext.Provider>
75
+ );
76
+ }
77
+
78
+ export function TabsList({ children, className, ...props }: TabsListProps) {
79
+ const { orientation, size } = useTabs();
80
+
81
+ return (
82
+ <div
83
+ role="tablist"
84
+ aria-orientation={orientation}
85
+ className={cn(tabsListVariants({ orientation, size }), className)}
86
+ {...props}
87
+ >
88
+ {children}
89
+ </div>
90
+ );
91
+ }
92
+
93
+ export function TabsTrigger({
94
+ value,
95
+ children,
96
+ disabled,
97
+ className,
98
+ ...props
99
+ }: TabsTriggerProps) {
100
+ const {
101
+ value: activeValue,
102
+ setValue,
103
+ tabTriggerId,
104
+ tabPanelId,
105
+ size,
106
+ appearance,
107
+ variant,
108
+ } = useTabs();
109
+
110
+ const isActive = activeValue === value;
111
+
112
+ const handleKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {
113
+ const triggers = Array.from(
114
+ document.querySelectorAll('[role="tab"]'),
115
+ ) as HTMLElement[];
116
+
117
+ const index = triggers.findIndex((el) => el === e.currentTarget);
118
+
119
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
120
+ e.preventDefault();
121
+ triggers[index + 1]?.focus();
122
+ }
123
+
124
+ if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
125
+ e.preventDefault();
126
+ triggers[index - 1]?.focus();
127
+ }
128
+
129
+ if (e.key === "Home") {
130
+ e.preventDefault();
131
+ triggers[0]?.focus();
132
+ }
133
+
134
+ if (e.key === "End") {
135
+ e.preventDefault();
136
+ triggers[triggers.length - 1]?.focus();
137
+ }
138
+ };
139
+
140
+ return (
141
+ <button
142
+ id={tabTriggerId(value)}
143
+ type="button"
144
+ role="tab"
145
+ data-state={isActive ? "active" : "inactive"}
146
+ aria-selected={isActive}
147
+ aria-controls={tabPanelId(value)}
148
+ disabled={disabled}
149
+ onClick={() => setValue(value)}
150
+ onKeyDown={handleKeyDown}
151
+ className={cn(
152
+ tabsTriggerVariants({ size, appearance, variant }),
153
+ className,
154
+ "cursor-pointer",
155
+ )}
156
+ {...props}
157
+ >
158
+ {children}
159
+ </button>
160
+ );
161
+ }
162
+
163
+ export function TabsContent({
164
+ value,
165
+ children,
166
+ className,
167
+ as: Wrapper = "div",
168
+ ...props
169
+ }: TabsContentProps) {
170
+ const { value: activeValue, tabTriggerId, tabPanelId } = useTabs();
171
+
172
+ if (activeValue !== value) return null;
173
+
174
+ return (
175
+ <Wrapper
176
+ id={tabPanelId(value)}
177
+ role="tabpanel"
178
+ aria-labelledby={tabTriggerId(value)}
179
+ className={cn("mt-2", className)}
180
+ {...props}
181
+ >
182
+ {children}
183
+ </Wrapper>
184
+ );
185
+ }
@@ -0,0 +1,53 @@
1
+ import { render, screen, waitFor } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
6
+
7
+ describe("Tabs", () => {
8
+ it("should stamp data-slot on root", () => {
9
+ render(
10
+ <Tabs defaultValue="a">
11
+ <TabsList>
12
+ <TabsTrigger value="a">A</TabsTrigger>
13
+ </TabsList>
14
+ <TabsContent value="a">Panel A</TabsContent>
15
+ </Tabs>,
16
+ );
17
+ expect(document.querySelector('[data-slot="tabs"]')).toBeTruthy();
18
+ });
19
+
20
+ it("should show matching tab panel", () => {
21
+ render(
22
+ <Tabs defaultValue="b">
23
+ <TabsList>
24
+ <TabsTrigger value="a">A</TabsTrigger>
25
+ <TabsTrigger value="b">B</TabsTrigger>
26
+ </TabsList>
27
+ <TabsContent value="a">Panel A</TabsContent>
28
+ <TabsContent value="b">Panel B</TabsContent>
29
+ </Tabs>,
30
+ );
31
+ expect(screen.getByRole("tabpanel", { name: "B" })).toHaveTextContent(
32
+ "Panel B",
33
+ );
34
+ });
35
+
36
+ it("should switch tab on trigger click", async () => {
37
+ const user = userEvent.setup();
38
+ render(
39
+ <Tabs defaultValue="a">
40
+ <TabsList>
41
+ <TabsTrigger value="a">A</TabsTrigger>
42
+ <TabsTrigger value="b">B</TabsTrigger>
43
+ </TabsList>
44
+ <TabsContent value="a">Panel A</TabsContent>
45
+ <TabsContent value="b">Panel B</TabsContent>
46
+ </Tabs>,
47
+ );
48
+ await user.click(screen.getByRole("tab", { name: "B" }));
49
+ await waitFor(() => {
50
+ expect(screen.getByText("Panel B")).toBeInTheDocument();
51
+ });
52
+ });
53
+ });
@@ -0,0 +1,2 @@
1
+ // tabs.tsx — default static entry (no framer-motion on content)
2
+ export { Tabs, TabsContent, TabsList, TabsTrigger, TabsContext, useTabs } from "./tabs-base";
@@ -0,0 +1,88 @@
1
+ import type { ElementType, HTMLAttributes, ReactNode } from "react";
2
+
3
+ export type TabsValue = string;
4
+
5
+ export type TabsProps = {
6
+ value?: TabsValue;
7
+ defaultValue?: TabsValue;
8
+ onValueChange?: (value: TabsValue) => void;
9
+ orientation?: "horizontal" | "vertical";
10
+ variant?: "default" | "underline" | "pills";
11
+ size?: "sm" | "md" | "lg";
12
+ appearance?:
13
+ | "default"
14
+ | "sky"
15
+ | "rose"
16
+ | "purple"
17
+ | "pink"
18
+ | "orange"
19
+ | "yellow"
20
+ | "teal"
21
+ | "indigo"
22
+ | "emerald"
23
+ | "gray"
24
+ | "gradient-blue"
25
+ | "gradient-green"
26
+ | "gradient-red"
27
+ | "gradient-yellow"
28
+ | "gradient-purple"
29
+ | "gradient-teal"
30
+ | "gradient-indigo"
31
+ | "gradient-pink"
32
+ | "gradient-orange";
33
+ children: ReactNode;
34
+ className?: string;
35
+ };
36
+
37
+ export type TabsListProps = {
38
+ children: ReactNode;
39
+ className?: string;
40
+ };
41
+
42
+ export type TabsTriggerProps = {
43
+ value: TabsValue;
44
+ children: ReactNode;
45
+ disabled?: boolean;
46
+ className?: string;
47
+ };
48
+
49
+ export type TabsContentProps = {
50
+ as?: ElementType;
51
+ value: TabsValue;
52
+ children: ReactNode;
53
+ className?: string;
54
+ } & Omit<
55
+ HTMLAttributes<HTMLDivElement>,
56
+ "children" | "className" | "role" | "id" | "aria-labelledby"
57
+ >;
58
+
59
+ export type TabsContextType = {
60
+ value: TabsValue | undefined;
61
+ setValue: (value: TabsValue) => void;
62
+ orientation: "horizontal" | "vertical";
63
+ size?: "sm" | "md" | "lg";
64
+ variant?: "default" | "underline" | "pills";
65
+ appearance?:
66
+ | "default"
67
+ | "sky"
68
+ | "rose"
69
+ | "purple"
70
+ | "pink"
71
+ | "orange"
72
+ | "yellow"
73
+ | "teal"
74
+ | "indigo"
75
+ | "emerald"
76
+ | "gray"
77
+ | "gradient-blue"
78
+ | "gradient-green"
79
+ | "gradient-red"
80
+ | "gradient-yellow"
81
+ | "gradient-purple"
82
+ | "gradient-teal"
83
+ | "gradient-indigo"
84
+ | "gradient-pink"
85
+ | "gradient-orange";
86
+ tabTriggerId: (value: TabsValue) => string;
87
+ tabPanelId: (value: TabsValue) => string;
88
+ };
@@ -0,0 +1,70 @@
1
+ import { cva } from "class-variance-authority";
2
+
3
+ export const tabsListVariants = cva("flex items-center gap-1", {
4
+ variants: {
5
+ variant: {
6
+ default: "bg-transparent",
7
+ underline: "border-b-2 border-transparent",
8
+ pills: "rounded-md",
9
+ },
10
+ size: {
11
+ sm: "text-sm p-1",
12
+ md: "text-base p-1.5",
13
+ lg: "text-lg p-2",
14
+ },
15
+ orientation: {
16
+ horizontal: "flex-row",
17
+ vertical: "flex-col",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ size: "md",
22
+ orientation: "horizontal",
23
+ },
24
+ });
25
+
26
+ export const tabsTriggerVariants = cva(
27
+ "px-3 py-1.5 rounded-md transition-all focus:outline-none focus:ring-2 focus:ring-ring",
28
+ {
29
+ variants: {
30
+ appearance: {
31
+ default: "bg-transparent",
32
+ sky: "bg-sky-500/75",
33
+ rose: "bg-rose-500/75",
34
+ purple: "bg-purple-500/75",
35
+ pink: "bg-pink-500/75",
36
+ orange: "bg-orange-500/75",
37
+ yellow: "bg-yellow-500/75",
38
+ teal: "bg-teal-500/75",
39
+ indigo: "bg-indigo-500/75",
40
+ emerald: "bg-emerald-500/75",
41
+ gray: "bg-gray-500/75",
42
+ "gradient-blue": "bg-gradient-to-r from-blue-600 to-purple-600",
43
+ "gradient-green": "bg-gradient-to-r from-green-600 to-lime-600",
44
+ "gradient-red": "bg-gradient-to-r from-red-600 to-pink-600",
45
+ "gradient-yellow": "bg-gradient-to-r from-yellow-600 to-orange-600",
46
+ "gradient-purple": "bg-gradient-to-r from-purple-600 to-pink-600",
47
+ "gradient-teal": "bg-gradient-to-r from-teal-600 to-cyan-600",
48
+ "gradient-indigo": "bg-gradient-to-r from-indigo-600 to-purple-600",
49
+ "gradient-pink": "bg-gradient-to-r from-pink-600 to-rose-600",
50
+ "gradient-orange": "bg-gradient-to-r from-orange-600 to-red-600",
51
+ },
52
+ variant: {
53
+ default: "data-[state=active]:bg-background",
54
+ underline:
55
+ "border-b-2 border-transparent data-[state=active]:border-primary rounded-none",
56
+ pills: "data-[state=active]:bg-primary data-[state=active]:text-white",
57
+ },
58
+ size: {
59
+ sm: "px-2 py-1",
60
+ md: "px-3 py-1.5",
61
+ lg: "px-4 py-2",
62
+ },
63
+ },
64
+ defaultVariants: {
65
+ appearance: "default",
66
+ variant: "default",
67
+ size: "md",
68
+ },
69
+ },
70
+ );
@@ -0,0 +1,17 @@
1
+ import type { ToastAnimationPresets } from "./types";
2
+
3
+ export const toastAnimationPresets: ToastAnimationPresets = {
4
+ none: {},
5
+ slide: {
6
+ initial: { opacity: 0, x: 24 },
7
+ animate: { opacity: 1, x: 0 },
8
+ exit: { opacity: 0, x: 16 },
9
+ transition: { type: "spring", stiffness: 380, damping: 28 },
10
+ },
11
+ fade: {
12
+ initial: { opacity: 0 },
13
+ animate: { opacity: 1 },
14
+ exit: { opacity: 0 },
15
+ transition: { duration: 0.18 },
16
+ },
17
+ };
@@ -0,0 +1,9 @@
1
+ "use client";
2
+
3
+ export { ToastViewportAnimated, ToastAnimated } from "./toast-animated";
4
+ export { toastAnimationPresets } from "./animations";
5
+ export type {
6
+ ToastAnimation,
7
+ ToastAnimationPresets,
8
+ ToastPresetMotionProps,
9
+ } from "./types";