@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,119 @@
1
+ "use client";
2
+
3
+ import { motion } from "framer-motion";
4
+
5
+ import { cn } from "../../../lib/utils";
6
+
7
+ import { skeletonAnimationPresets } from "./animations";
8
+ import type {
9
+ SkeletonAnimatedProps,
10
+ SkeletonAvatarAnimatedProps,
11
+ SkeletonButtonAnimatedProps,
12
+ SkeletonCardAnimatedProps,
13
+ SkeletonTextAnimatedProps,
14
+ } from "./types";
15
+ import {
16
+ SkeletonAvatarBase,
17
+ SkeletonBase,
18
+ SkeletonButtonBase,
19
+ SkeletonTextBase,
20
+ useSkeletonAnimation,
21
+ } from "../skeleton-base";
22
+
23
+ export function SkeletonAnimated(props: SkeletonAnimatedProps) {
24
+ const { animation = "shimmer" } = props;
25
+ const motionProps = skeletonAnimationPresets[animation];
26
+ return (
27
+ <SkeletonBase {...props} as={motion.div} initial={false} {...motionProps} />
28
+ );
29
+ }
30
+
31
+ SkeletonAnimated.displayName = "Skeleton";
32
+
33
+ export function SkeletonTextAnimated(props: SkeletonTextAnimatedProps) {
34
+ const { animation = "shimmer" } = props;
35
+ const effectiveAnimation = useSkeletonAnimation(animation);
36
+ const motionProps = skeletonAnimationPresets[effectiveAnimation];
37
+
38
+ return <SkeletonTextBase {...props} as={motion.div} initial={false} {...motionProps} />;
39
+ }
40
+
41
+ SkeletonTextAnimated.displayName = "SkeletonText";
42
+
43
+ export function SkeletonAvatarAnimated(props: SkeletonAvatarAnimatedProps) {
44
+ const { animation = "shimmer" } = props;
45
+ const effectiveAnimation = useSkeletonAnimation(animation);
46
+ const motionProps = skeletonAnimationPresets[effectiveAnimation];
47
+ return <SkeletonAvatarBase {...props} as={motion.div} initial={false} {...motionProps} />;
48
+ }
49
+
50
+ SkeletonAvatarAnimated.displayName = "SkeletonAvatar";
51
+
52
+ export function SkeletonCardAnimated(props: SkeletonCardAnimatedProps) {
53
+ const {
54
+ className,
55
+ busy,
56
+ animation = "shimmer",
57
+ shimmerTone,
58
+ appearance,
59
+ size,
60
+ rounded,
61
+ ref,
62
+ } = props;
63
+
64
+ return (
65
+ <div
66
+ ref={ref as never}
67
+ data-slot="skeleton-card"
68
+ className={cn("w-full max-w-sm", className)}
69
+ aria-busy={busy ? true : undefined}
70
+ >
71
+ <SkeletonAnimated
72
+ rounded="lg"
73
+ animation={animation}
74
+ shimmerTone={shimmerTone}
75
+ appearance={appearance}
76
+ size={size}
77
+ busy={busy}
78
+ className="flex flex-col gap-4 p-4"
79
+ >
80
+ <div className="flex items-center gap-3">
81
+ <SkeletonAvatarAnimated
82
+ appearance={appearance}
83
+ size={size}
84
+ animation={animation}
85
+ shimmerTone={shimmerTone}
86
+ rounded={rounded}
87
+ />
88
+ <div className="flex flex-1 flex-col gap-2">
89
+ <SkeletonTextAnimated
90
+ lines={2}
91
+ appearance={appearance}
92
+ size={size}
93
+ animation={animation}
94
+ shimmerTone={shimmerTone}
95
+ />
96
+ </div>
97
+ </div>
98
+ <SkeletonTextAnimated
99
+ lines={4}
100
+ appearance={appearance}
101
+ size={size}
102
+ animation={animation}
103
+ shimmerTone={shimmerTone}
104
+ />
105
+ </SkeletonAnimated>
106
+ </div>
107
+ );
108
+ }
109
+
110
+ SkeletonCardAnimated.displayName = "SkeletonCard";
111
+
112
+ export function SkeletonButtonAnimated(props: SkeletonButtonAnimatedProps) {
113
+ const { animation = "shimmer" } = props;
114
+ const effectiveAnimation = useSkeletonAnimation(animation);
115
+ const motionProps = skeletonAnimationPresets[effectiveAnimation];
116
+ return <SkeletonButtonBase {...props} as={motion.div} initial={false} {...motionProps} />;
117
+ }
118
+
119
+ SkeletonButtonAnimated.displayName = "SkeletonButton";
@@ -0,0 +1,49 @@
1
+ import type { VariantProps } from "class-variance-authority";
2
+ import type { HTMLMotionProps } from "framer-motion";
3
+ import type { ReactNode } from "react";
4
+
5
+ import type { SkeletonAnimation } from "../types";
6
+ import type { skeletonVariants } from "../variants";
7
+
8
+ export type SkeletonVariantProps = VariantProps<typeof skeletonVariants>;
9
+
10
+ export type SkeletonPresetMotionProps = Pick<
11
+ HTMLMotionProps<"div">,
12
+ "initial" | "animate" | "transition"
13
+ >;
14
+
15
+ export type SkeletonAnimationPresets = Record<
16
+ SkeletonAnimation,
17
+ SkeletonPresetMotionProps
18
+ >;
19
+
20
+
21
+ export type SkeletonAnimatedProps = SkeletonVariantProps &
22
+ Omit<HTMLMotionProps<"div">, "children"> & {
23
+ animation?: SkeletonAnimation;
24
+ children?: ReactNode;
25
+ busy?: boolean;
26
+ };
27
+
28
+ export type SkeletonCardAnimatedProps = SkeletonAnimatedProps;
29
+
30
+ export type SkeletonTextAnimatedProps = SkeletonVariantProps &
31
+ Omit<HTMLMotionProps<"div">, "children"> & {
32
+ animation?: SkeletonAnimation;
33
+ lines?: number;
34
+ busy?: boolean;
35
+ };
36
+
37
+ export type SkeletonAvatarAnimatedProps = SkeletonVariantProps &
38
+ Omit<HTMLMotionProps<"div">, "children"> & {
39
+ animation?: SkeletonAnimation;
40
+ avatarSize?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
41
+ busy?: boolean;
42
+ };
43
+
44
+ export type SkeletonButtonAnimatedProps = SkeletonVariantProps &
45
+ Omit<HTMLMotionProps<"div">, "children"> & {
46
+ animation?: SkeletonAnimation;
47
+ buttonSize?: "sm" | "md" | "lg";
48
+ busy?: boolean;
49
+ };
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ export {
4
+ Skeleton,
5
+ SkeletonAvatar,
6
+ SkeletonButton,
7
+ SkeletonCard,
8
+ SkeletonText,
9
+ } from "./skeleton";
10
+ export type {
11
+ SkeletonAnimation,
12
+ SkeletonAvatarProps,
13
+ SkeletonButtonProps,
14
+ SkeletonCardProps,
15
+ SkeletonProps,
16
+ SkeletonTextProps,
17
+ SkeletonVariantProps,
18
+ } from "./types";
19
+ export {
20
+ skeletonShimmerGradientClasses,
21
+ skeletonTextLineVariants,
22
+ skeletonVariants,
23
+ } from "./variants";
24
+ export type { SkeletonShimmerTone } from "./variants";
@@ -0,0 +1,288 @@
1
+ "use client";
2
+
3
+ import { createContext, useContext, useMemo } from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ import type {
8
+ SkeletonAnimation,
9
+ SkeletonAvatarProps,
10
+ SkeletonButtonProps,
11
+ SkeletonCardProps,
12
+ SkeletonProps,
13
+ SkeletonTextProps,
14
+ } from "./types";
15
+ import { skeletonTextLineVariants, skeletonVariants } from "./variants";
16
+
17
+ export const SkeletonAnimationContext = createContext<
18
+ SkeletonAnimation | undefined
19
+ >(undefined);
20
+
21
+ export function useSkeletonAnimation(
22
+ fallback: SkeletonAnimation,
23
+ ): SkeletonAnimation {
24
+ const value = useContext(SkeletonAnimationContext);
25
+ return value ?? fallback;
26
+ }
27
+
28
+ function skeletonMotionClass(animation: SkeletonAnimation): string {
29
+ if (animation === "none") {
30
+ return "";
31
+ }
32
+ return "animate-pulse";
33
+ }
34
+
35
+ export function SkeletonBase(props: SkeletonProps) {
36
+ const {
37
+ className,
38
+ appearance,
39
+ size,
40
+ rounded,
41
+ animation = "shimmer",
42
+ shimmerTone,
43
+ busy,
44
+ children,
45
+ ref,
46
+ as:Wrapper = "div",
47
+ ...rest
48
+ } = props;
49
+ const ctx = useMemo(() => animation, [animation]);
50
+
51
+ return (
52
+ <SkeletonAnimationContext.Provider value={ctx}>
53
+ <Wrapper
54
+ ref={ref}
55
+ data-slot="skeleton"
56
+ aria-hidden
57
+ aria-busy={busy ? true : undefined}
58
+ className={cn(
59
+ skeletonVariants({
60
+ appearance,
61
+ size,
62
+ rounded,
63
+ animation,
64
+ shimmerTone,
65
+ }),
66
+ skeletonMotionClass(animation),
67
+ className,
68
+ )}
69
+ {...rest}
70
+ >
71
+ {children}
72
+ </Wrapper>
73
+ </SkeletonAnimationContext.Provider>
74
+ );
75
+ }
76
+
77
+ SkeletonBase.displayName = "Skeleton";
78
+
79
+ export function SkeletonTextBase(props: SkeletonTextProps) {
80
+ const {
81
+ lines = 3,
82
+ className,
83
+ appearance,
84
+ size,
85
+ rounded,
86
+ animation = "shimmer",
87
+ shimmerTone,
88
+ busy,
89
+ ref,
90
+ as:Wrapper = "div",
91
+ } = props;
92
+ const effectiveAnimation = useSkeletonAnimation(animation);
93
+ const motionClass = skeletonMotionClass(effectiveAnimation);
94
+
95
+ return (
96
+ <div
97
+ data-slot="skeleton-text"
98
+ className={cn("flex w-full flex-col gap-2", className)}
99
+ aria-hidden
100
+ aria-busy={busy ? true : undefined}
101
+ >
102
+ {Array.from({ length: lines }).map((_, index) => (
103
+ <Wrapper
104
+ key={index}
105
+ ref={index === 0 ? ref : undefined}
106
+ className={cn(
107
+ skeletonVariants({
108
+ appearance,
109
+ size,
110
+ rounded,
111
+ animation: effectiveAnimation,
112
+ shimmerTone,
113
+ }),
114
+ skeletonTextLineVariants({ size }),
115
+ index === lines - 1 ? "w-3/5" : "w-full",
116
+ motionClass,
117
+ )}
118
+ />
119
+ ))}
120
+ </div>
121
+ );
122
+ }
123
+
124
+ SkeletonTextBase.displayName = "SkeletonText";
125
+
126
+ const avatarSizeClass: Record<
127
+ NonNullable<SkeletonAvatarProps["avatarSize"]>,
128
+ string
129
+ > = {
130
+ xs: "size-6",
131
+ sm: "size-8",
132
+ md: "size-10",
133
+ lg: "size-12",
134
+ xl: "size-14",
135
+ "2xl": "size-16",
136
+ };
137
+
138
+ export function SkeletonAvatarBase(props: SkeletonAvatarProps) {
139
+ const {
140
+ avatarSize = "md",
141
+ className,
142
+ appearance,
143
+ size,
144
+ rounded = "full",
145
+ animation,
146
+ shimmerTone,
147
+ busy,
148
+ ref,
149
+ as:Wrapper = "div",
150
+ ...rest
151
+ } = props;
152
+ const effectiveAnimation = useSkeletonAnimation(animation ?? "shimmer");
153
+ const motionClass = skeletonMotionClass(effectiveAnimation);
154
+
155
+ return (
156
+ <Wrapper
157
+ ref={ref}
158
+ data-slot="skeleton-avatar"
159
+ aria-hidden
160
+ aria-busy={busy ? true : undefined}
161
+ className={cn(
162
+ skeletonVariants({
163
+ appearance,
164
+ size,
165
+ rounded,
166
+ animation: effectiveAnimation,
167
+ shimmerTone,
168
+ }),
169
+ avatarSizeClass[avatarSize],
170
+ motionClass,
171
+ className,
172
+ )}
173
+ {...rest}
174
+ />
175
+ );
176
+ }
177
+
178
+ SkeletonAvatarBase.displayName = "SkeletonAvatar";
179
+
180
+ export function SkeletonCardBase(props: SkeletonCardProps) {
181
+ const {
182
+ className,
183
+ busy,
184
+ animation = "shimmer",
185
+ shimmerTone,
186
+ appearance,
187
+ size,
188
+ rounded,
189
+ ref,
190
+ } = props;
191
+
192
+ return (
193
+ <div
194
+ ref={ref as never}
195
+ data-slot="skeleton-card"
196
+ className={cn("w-full max-w-sm", className)}
197
+ aria-busy={busy ? true : undefined}
198
+ >
199
+ <SkeletonBase
200
+ rounded="lg"
201
+ animation={animation}
202
+ shimmerTone={shimmerTone}
203
+ appearance={appearance}
204
+ size={size}
205
+ busy={busy}
206
+ className="flex flex-col gap-4 p-4"
207
+ >
208
+ <div className="flex items-center gap-3">
209
+ <SkeletonAvatarBase
210
+ appearance={appearance}
211
+ size={size}
212
+ animation={animation}
213
+ shimmerTone={shimmerTone}
214
+ rounded={rounded}
215
+ />
216
+ <div className="flex flex-1 flex-col gap-2">
217
+ <SkeletonTextBase
218
+ lines={2}
219
+ appearance={appearance}
220
+ size={size}
221
+ animation={animation}
222
+ shimmerTone={shimmerTone}
223
+ />
224
+ </div>
225
+ </div>
226
+ <SkeletonTextBase
227
+ lines={4}
228
+ appearance={appearance}
229
+ size={size}
230
+ animation={animation}
231
+ shimmerTone={shimmerTone}
232
+ />
233
+ </SkeletonBase>
234
+ </div>
235
+ );
236
+ }
237
+
238
+ SkeletonCardBase.displayName = "SkeletonCard";
239
+
240
+ const buttonHeight: Record<
241
+ NonNullable<SkeletonButtonProps["buttonSize"]>,
242
+ string
243
+ > = {
244
+ sm: "h-8 w-24",
245
+ md: "h-10 w-32",
246
+ lg: "h-12 w-40",
247
+ };
248
+
249
+ export function SkeletonButtonBase(props: SkeletonButtonProps) {
250
+ const {
251
+ buttonSize = "md",
252
+ className,
253
+ appearance,
254
+ size,
255
+ rounded = "md",
256
+ animation,
257
+ shimmerTone,
258
+ busy,
259
+ ref,
260
+ ...rest
261
+ } = props;
262
+ const effectiveAnimation = useSkeletonAnimation(animation ?? "shimmer");
263
+ const motionClass = skeletonMotionClass(effectiveAnimation);
264
+
265
+ return (
266
+ <div
267
+ ref={ref}
268
+ data-slot="skeleton-button"
269
+ aria-hidden
270
+ aria-busy={busy ? true : undefined}
271
+ className={cn(
272
+ skeletonVariants({
273
+ appearance,
274
+ size,
275
+ rounded,
276
+ animation: effectiveAnimation,
277
+ shimmerTone,
278
+ }),
279
+ buttonHeight[buttonSize],
280
+ motionClass,
281
+ className,
282
+ )}
283
+ {...rest}
284
+ />
285
+ );
286
+ }
287
+
288
+ SkeletonButtonBase.displayName = "SkeletonButton";
@@ -0,0 +1,8 @@
1
+ // skeleton.tsx — default static entry (no framer-motion)
2
+ export {
3
+ SkeletonBase as Skeleton,
4
+ SkeletonTextBase as SkeletonText,
5
+ SkeletonAvatarBase as SkeletonAvatar,
6
+ SkeletonCardBase as SkeletonCard,
7
+ SkeletonButtonBase as SkeletonButton,
8
+ } from "./skeleton-base";
@@ -0,0 +1,31 @@
1
+ import type { VariantProps } from "class-variance-authority";
2
+ import type { ComponentPropsWithRef, ElementType, ReactNode } from "react";
3
+
4
+ import type { skeletonVariants } from "./variants";
5
+
6
+ export type SkeletonAnimation = "none" | "shimmer" | "pulse";
7
+
8
+ export type SkeletonVariantProps = VariantProps<typeof skeletonVariants>;
9
+
10
+ export type SkeletonProps = SkeletonVariantProps &
11
+ (Omit<ComponentPropsWithRef<ElementType>, "children"> & {
12
+ animation?: SkeletonAnimation;
13
+ children?: ReactNode;
14
+ /** When true, parent regions can expose busy state to assistive tech. */
15
+ busy?: boolean;
16
+ });
17
+
18
+ export type SkeletonTextProps = SkeletonProps & {
19
+ lines?: number;
20
+ };
21
+
22
+ export type SkeletonAvatarProps = SkeletonProps & {
23
+ /** Mirrors Avatar sizes for layout parity. */
24
+ avatarSize?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
25
+ };
26
+
27
+ export type SkeletonCardProps = SkeletonProps;
28
+
29
+ export type SkeletonButtonProps = SkeletonProps & {
30
+ buttonSize?: "sm" | "md" | "lg";
31
+ };