react-native-skelo 0.0.1 → 0.0.2

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 (269) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -20
  3. package/lib/commonjs/Skelo.js +104 -0
  4. package/lib/commonjs/Skelo.js.map +1 -0
  5. package/lib/commonjs/animations/AnimationWrapper.js +76 -0
  6. package/lib/commonjs/animations/AnimationWrapper.js.map +1 -0
  7. package/lib/commonjs/animations/PulseAnimation.js +76 -0
  8. package/lib/commonjs/animations/PulseAnimation.js.map +1 -0
  9. package/lib/commonjs/animations/ShimmerAnimation.js +103 -0
  10. package/lib/commonjs/animations/ShimmerAnimation.js.map +1 -0
  11. package/lib/commonjs/animations/index.js +27 -0
  12. package/lib/commonjs/animations/index.js.map +1 -0
  13. package/lib/commonjs/components/MeasuredBlock.js +64 -0
  14. package/lib/commonjs/components/MeasuredBlock.js.map +1 -0
  15. package/lib/commonjs/components/Skeleton.js +144 -0
  16. package/lib/commonjs/components/Skeleton.js.map +1 -0
  17. package/lib/commonjs/components/SkeletonIgnore.js +30 -0
  18. package/lib/commonjs/components/SkeletonIgnore.js.map +1 -0
  19. package/lib/commonjs/components/SkeletonRenderer.js +270 -0
  20. package/lib/commonjs/components/SkeletonRenderer.js.map +1 -0
  21. package/lib/commonjs/components/primitives/SkeletonBox.js +47 -0
  22. package/lib/commonjs/components/primitives/SkeletonBox.js.map +1 -0
  23. package/lib/commonjs/components/primitives/SkeletonCircle.js +44 -0
  24. package/lib/commonjs/components/primitives/SkeletonCircle.js.map +1 -0
  25. package/lib/commonjs/components/primitives/SkeletonImage.js +46 -0
  26. package/lib/commonjs/components/primitives/SkeletonImage.js.map +1 -0
  27. package/lib/commonjs/components/primitives/SkeletonText.js +60 -0
  28. package/lib/commonjs/components/primitives/SkeletonText.js.map +1 -0
  29. package/lib/commonjs/components/primitives/index.js +34 -0
  30. package/lib/commonjs/components/primitives/index.js.map +1 -0
  31. package/lib/commonjs/components/withSkeleton.js +45 -0
  32. package/lib/commonjs/components/withSkeleton.js.map +1 -0
  33. package/lib/commonjs/constants/defaults.js +43 -0
  34. package/lib/commonjs/constants/defaults.js.map +1 -0
  35. package/lib/commonjs/core/deepRegistry.js +18 -0
  36. package/lib/commonjs/core/deepRegistry.js.map +1 -0
  37. package/lib/commonjs/core/generator/StyleSkeleton.js +101 -0
  38. package/lib/commonjs/core/generator/StyleSkeleton.js.map +1 -0
  39. package/lib/commonjs/core/parser/Parser.js +84 -0
  40. package/lib/commonjs/core/parser/Parser.js.map +1 -0
  41. package/lib/commonjs/core/parser/expandOffline.js +83 -0
  42. package/lib/commonjs/core/parser/expandOffline.js.map +1 -0
  43. package/lib/commonjs/core/parser/hostTreeReconciler.js +228 -0
  44. package/lib/commonjs/core/parser/hostTreeReconciler.js.map +1 -0
  45. package/lib/commonjs/core/parser/index.js +13 -0
  46. package/lib/commonjs/core/parser/index.js.map +1 -0
  47. package/lib/commonjs/core/registry/SkeletonRegistry.js +113 -0
  48. package/lib/commonjs/core/registry/SkeletonRegistry.js.map +1 -0
  49. package/lib/commonjs/core/registry/index.js +19 -0
  50. package/lib/commonjs/core/registry/index.js.map +1 -0
  51. package/lib/commonjs/core/resolver/StyleResolver.js +83 -0
  52. package/lib/commonjs/core/resolver/StyleResolver.js.map +1 -0
  53. package/lib/commonjs/core/resolver/index.js +13 -0
  54. package/lib/commonjs/core/resolver/index.js.map +1 -0
  55. package/lib/commonjs/deep.js +18 -0
  56. package/lib/commonjs/deep.js.map +1 -0
  57. package/lib/commonjs/index.js +66 -0
  58. package/lib/commonjs/index.js.map +1 -0
  59. package/lib/commonjs/plugins/ImagePlugin.js +60 -0
  60. package/lib/commonjs/plugins/ImagePlugin.js.map +1 -0
  61. package/lib/commonjs/plugins/TextPlugin.js +70 -0
  62. package/lib/commonjs/plugins/TextPlugin.js.map +1 -0
  63. package/lib/commonjs/plugins/ViewPlugin.js +71 -0
  64. package/lib/commonjs/plugins/ViewPlugin.js.map +1 -0
  65. package/lib/commonjs/plugins/index.js +27 -0
  66. package/lib/commonjs/plugins/index.js.map +1 -0
  67. package/lib/commonjs/types/index.js +2 -0
  68. package/lib/commonjs/types/index.js.map +1 -0
  69. package/lib/commonjs/types/plugin.js +6 -0
  70. package/lib/commonjs/types/plugin.js.map +1 -0
  71. package/lib/commonjs/types/skeleton.js +6 -0
  72. package/lib/commonjs/types/skeleton.js.map +1 -0
  73. package/lib/commonjs/utils/componentUtils.js +138 -0
  74. package/lib/commonjs/utils/componentUtils.js.map +1 -0
  75. package/lib/commonjs/utils/index.js +62 -0
  76. package/lib/commonjs/utils/index.js.map +1 -0
  77. package/lib/commonjs/utils/styleUtils.js +76 -0
  78. package/lib/commonjs/utils/styleUtils.js.map +1 -0
  79. package/lib/module/Skelo.js +98 -0
  80. package/lib/module/Skelo.js.map +1 -0
  81. package/lib/module/animations/AnimationWrapper.js +69 -0
  82. package/lib/module/animations/AnimationWrapper.js.map +1 -0
  83. package/lib/module/animations/PulseAnimation.js +69 -0
  84. package/lib/module/animations/PulseAnimation.js.map +1 -0
  85. package/lib/module/animations/ShimmerAnimation.js +96 -0
  86. package/lib/module/animations/ShimmerAnimation.js.map +1 -0
  87. package/lib/module/animations/index.js +4 -0
  88. package/lib/module/animations/index.js.map +1 -0
  89. package/lib/module/components/MeasuredBlock.js +57 -0
  90. package/lib/module/components/MeasuredBlock.js.map +1 -0
  91. package/lib/module/components/Skeleton.js +138 -0
  92. package/lib/module/components/Skeleton.js.map +1 -0
  93. package/lib/module/components/SkeletonIgnore.js +23 -0
  94. package/lib/module/components/SkeletonIgnore.js.map +1 -0
  95. package/lib/module/components/SkeletonRenderer.js +264 -0
  96. package/lib/module/components/SkeletonRenderer.js.map +1 -0
  97. package/lib/module/components/primitives/SkeletonBox.js +40 -0
  98. package/lib/module/components/primitives/SkeletonBox.js.map +1 -0
  99. package/lib/module/components/primitives/SkeletonCircle.js +37 -0
  100. package/lib/module/components/primitives/SkeletonCircle.js.map +1 -0
  101. package/lib/module/components/primitives/SkeletonImage.js +39 -0
  102. package/lib/module/components/primitives/SkeletonImage.js.map +1 -0
  103. package/lib/module/components/primitives/SkeletonText.js +53 -0
  104. package/lib/module/components/primitives/SkeletonText.js.map +1 -0
  105. package/lib/module/components/primitives/index.js +5 -0
  106. package/lib/module/components/primitives/index.js.map +1 -0
  107. package/lib/module/components/withSkeleton.js +38 -0
  108. package/lib/module/components/withSkeleton.js.map +1 -0
  109. package/lib/module/constants/defaults.js +37 -0
  110. package/lib/module/constants/defaults.js.map +1 -0
  111. package/lib/module/core/deepRegistry.js +11 -0
  112. package/lib/module/core/deepRegistry.js.map +1 -0
  113. package/lib/module/core/generator/StyleSkeleton.js +95 -0
  114. package/lib/module/core/generator/StyleSkeleton.js.map +1 -0
  115. package/lib/module/core/parser/Parser.js +77 -0
  116. package/lib/module/core/parser/Parser.js.map +1 -0
  117. package/lib/module/core/parser/expandOffline.js +77 -0
  118. package/lib/module/core/parser/expandOffline.js.map +1 -0
  119. package/lib/module/core/parser/hostTreeReconciler.js +222 -0
  120. package/lib/module/core/parser/hostTreeReconciler.js.map +1 -0
  121. package/lib/module/core/parser/index.js +2 -0
  122. package/lib/module/core/parser/index.js.map +1 -0
  123. package/lib/module/core/registry/SkeletonRegistry.js +106 -0
  124. package/lib/module/core/registry/SkeletonRegistry.js.map +1 -0
  125. package/lib/module/core/registry/index.js +2 -0
  126. package/lib/module/core/registry/index.js.map +1 -0
  127. package/lib/module/core/resolver/StyleResolver.js +77 -0
  128. package/lib/module/core/resolver/StyleResolver.js.map +1 -0
  129. package/lib/module/core/resolver/index.js +2 -0
  130. package/lib/module/core/resolver/index.js.map +1 -0
  131. package/lib/module/deep.js +11 -0
  132. package/lib/module/deep.js.map +1 -0
  133. package/lib/module/index.js +21 -0
  134. package/lib/module/index.js.map +1 -0
  135. package/lib/module/plugins/ImagePlugin.js +53 -0
  136. package/lib/module/plugins/ImagePlugin.js.map +1 -0
  137. package/lib/module/plugins/TextPlugin.js +63 -0
  138. package/lib/module/plugins/TextPlugin.js.map +1 -0
  139. package/lib/module/plugins/ViewPlugin.js +64 -0
  140. package/lib/module/plugins/ViewPlugin.js.map +1 -0
  141. package/lib/module/plugins/index.js +4 -0
  142. package/lib/module/plugins/index.js.map +1 -0
  143. package/lib/module/types/index.js +2 -0
  144. package/lib/module/types/index.js.map +1 -0
  145. package/lib/module/types/plugin.js +2 -0
  146. package/lib/module/types/plugin.js.map +1 -0
  147. package/lib/module/types/skeleton.js +2 -0
  148. package/lib/module/types/skeleton.js.map +1 -0
  149. package/lib/module/utils/componentUtils.js +123 -0
  150. package/lib/module/utils/componentUtils.js.map +1 -0
  151. package/lib/module/utils/index.js +3 -0
  152. package/lib/module/utils/index.js.map +1 -0
  153. package/lib/module/utils/styleUtils.js +66 -0
  154. package/lib/module/utils/styleUtils.js.map +1 -0
  155. package/lib/typescript/Skelo.d.ts +76 -0
  156. package/lib/typescript/Skelo.d.ts.map +1 -0
  157. package/lib/typescript/animations/AnimationWrapper.d.ts +66 -0
  158. package/lib/typescript/animations/AnimationWrapper.d.ts.map +1 -0
  159. package/lib/typescript/animations/PulseAnimation.d.ts +59 -0
  160. package/lib/typescript/animations/PulseAnimation.d.ts.map +1 -0
  161. package/lib/typescript/animations/ShimmerAnimation.d.ts +59 -0
  162. package/lib/typescript/animations/ShimmerAnimation.d.ts.map +1 -0
  163. package/lib/typescript/animations/index.d.ts +5 -0
  164. package/lib/typescript/animations/index.d.ts.map +1 -0
  165. package/lib/typescript/components/MeasuredBlock.d.ts +32 -0
  166. package/lib/typescript/components/MeasuredBlock.d.ts.map +1 -0
  167. package/lib/typescript/components/Skeleton.d.ts +37 -0
  168. package/lib/typescript/components/Skeleton.d.ts.map +1 -0
  169. package/lib/typescript/components/SkeletonIgnore.d.ts +26 -0
  170. package/lib/typescript/components/SkeletonIgnore.d.ts.map +1 -0
  171. package/lib/typescript/components/SkeletonRenderer.d.ts +39 -0
  172. package/lib/typescript/components/SkeletonRenderer.d.ts.map +1 -0
  173. package/lib/typescript/components/primitives/SkeletonBox.d.ts +21 -0
  174. package/lib/typescript/components/primitives/SkeletonBox.d.ts.map +1 -0
  175. package/lib/typescript/components/primitives/SkeletonCircle.d.ts +21 -0
  176. package/lib/typescript/components/primitives/SkeletonCircle.d.ts.map +1 -0
  177. package/lib/typescript/components/primitives/SkeletonImage.d.ts +21 -0
  178. package/lib/typescript/components/primitives/SkeletonImage.d.ts.map +1 -0
  179. package/lib/typescript/components/primitives/SkeletonText.d.ts +21 -0
  180. package/lib/typescript/components/primitives/SkeletonText.d.ts.map +1 -0
  181. package/lib/typescript/components/primitives/index.d.ts +5 -0
  182. package/lib/typescript/components/primitives/index.d.ts.map +1 -0
  183. package/lib/typescript/components/withSkeleton.d.ts +37 -0
  184. package/lib/typescript/components/withSkeleton.d.ts.map +1 -0
  185. package/lib/typescript/constants/defaults.d.ts +29 -0
  186. package/lib/typescript/constants/defaults.d.ts.map +1 -0
  187. package/lib/typescript/core/deepRegistry.d.ts +7 -0
  188. package/lib/typescript/core/deepRegistry.d.ts.map +1 -0
  189. package/lib/typescript/core/generator/StyleSkeleton.d.ts +36 -0
  190. package/lib/typescript/core/generator/StyleSkeleton.d.ts.map +1 -0
  191. package/lib/typescript/core/parser/Parser.d.ts +30 -0
  192. package/lib/typescript/core/parser/Parser.d.ts.map +1 -0
  193. package/lib/typescript/core/parser/expandOffline.d.ts +10 -0
  194. package/lib/typescript/core/parser/expandOffline.d.ts.map +1 -0
  195. package/lib/typescript/core/parser/hostTreeReconciler.d.ts +28 -0
  196. package/lib/typescript/core/parser/hostTreeReconciler.d.ts.map +1 -0
  197. package/lib/typescript/core/parser/index.d.ts +2 -0
  198. package/lib/typescript/core/parser/index.d.ts.map +1 -0
  199. package/lib/typescript/core/registry/SkeletonRegistry.d.ts +57 -0
  200. package/lib/typescript/core/registry/SkeletonRegistry.d.ts.map +1 -0
  201. package/lib/typescript/core/registry/index.d.ts +2 -0
  202. package/lib/typescript/core/registry/index.d.ts.map +1 -0
  203. package/lib/typescript/core/resolver/StyleResolver.d.ts +54 -0
  204. package/lib/typescript/core/resolver/StyleResolver.d.ts.map +1 -0
  205. package/lib/typescript/core/resolver/index.d.ts +2 -0
  206. package/lib/typescript/core/resolver/index.d.ts.map +1 -0
  207. package/lib/typescript/deep.d.ts +2 -0
  208. package/lib/typescript/deep.d.ts.map +1 -0
  209. package/lib/typescript/index.d.ts +17 -0
  210. package/lib/typescript/index.d.ts.map +1 -0
  211. package/lib/typescript/plugins/ImagePlugin.d.ts +11 -0
  212. package/lib/typescript/plugins/ImagePlugin.d.ts.map +1 -0
  213. package/lib/typescript/plugins/TextPlugin.d.ts +12 -0
  214. package/lib/typescript/plugins/TextPlugin.d.ts.map +1 -0
  215. package/lib/typescript/plugins/ViewPlugin.d.ts +11 -0
  216. package/lib/typescript/plugins/ViewPlugin.d.ts.map +1 -0
  217. package/lib/typescript/plugins/index.d.ts +4 -0
  218. package/lib/typescript/plugins/index.d.ts.map +1 -0
  219. package/lib/typescript/types/index.d.ts +3 -0
  220. package/lib/typescript/types/index.d.ts.map +1 -0
  221. package/lib/typescript/types/plugin.d.ts +144 -0
  222. package/lib/typescript/types/plugin.d.ts.map +1 -0
  223. package/lib/typescript/types/skeleton.d.ts +176 -0
  224. package/lib/typescript/types/skeleton.d.ts.map +1 -0
  225. package/lib/typescript/utils/componentUtils.d.ts +40 -0
  226. package/lib/typescript/utils/componentUtils.d.ts.map +1 -0
  227. package/lib/typescript/utils/index.d.ts +3 -0
  228. package/lib/typescript/utils/index.d.ts.map +1 -0
  229. package/lib/typescript/utils/styleUtils.d.ts +41 -0
  230. package/lib/typescript/utils/styleUtils.d.ts.map +1 -0
  231. package/package.json +171 -11
  232. package/src/Skelo.ts +103 -0
  233. package/src/animations/AnimationWrapper.tsx +139 -0
  234. package/src/animations/PulseAnimation.tsx +138 -0
  235. package/src/animations/ShimmerAnimation.tsx +169 -0
  236. package/src/animations/index.ts +4 -0
  237. package/src/components/MeasuredBlock.tsx +85 -0
  238. package/src/components/Skeleton.tsx +149 -0
  239. package/src/components/SkeletonIgnore.tsx +27 -0
  240. package/src/components/SkeletonRenderer.tsx +341 -0
  241. package/src/components/primitives/SkeletonBox.tsx +53 -0
  242. package/src/components/primitives/SkeletonCircle.tsx +48 -0
  243. package/src/components/primitives/SkeletonImage.tsx +50 -0
  244. package/src/components/primitives/SkeletonText.tsx +69 -0
  245. package/src/components/primitives/index.ts +4 -0
  246. package/src/components/withSkeleton.tsx +62 -0
  247. package/src/constants/defaults.ts +38 -0
  248. package/src/core/deepRegistry.ts +16 -0
  249. package/src/core/generator/StyleSkeleton.tsx +138 -0
  250. package/src/core/parser/Parser.ts +86 -0
  251. package/src/core/parser/expandOffline.tsx +83 -0
  252. package/src/core/parser/hostTreeReconciler.tsx +271 -0
  253. package/src/core/parser/index.ts +1 -0
  254. package/src/core/registry/SkeletonRegistry.ts +111 -0
  255. package/src/core/registry/index.ts +1 -0
  256. package/src/core/resolver/StyleResolver.ts +80 -0
  257. package/src/core/resolver/index.ts +1 -0
  258. package/src/deep.ts +12 -0
  259. package/src/index.ts +30 -0
  260. package/src/plugins/ImagePlugin.tsx +65 -0
  261. package/src/plugins/TextPlugin.tsx +71 -0
  262. package/src/plugins/ViewPlugin.tsx +67 -0
  263. package/src/plugins/index.ts +3 -0
  264. package/src/types/index.ts +14 -0
  265. package/src/types/plugin.ts +164 -0
  266. package/src/types/skeleton.ts +197 -0
  267. package/src/utils/componentUtils.ts +166 -0
  268. package/src/utils/index.ts +9 -0
  269. package/src/utils/styleUtils.ts +72 -0
@@ -0,0 +1,169 @@
1
+ import React, { useEffect } from 'react';
2
+ import type { DimensionValue, StyleProp, ViewStyle } from 'react-native';
3
+ import { StyleSheet, View } from 'react-native';
4
+ import Animated, {
5
+ useSharedValue,
6
+ useAnimatedStyle,
7
+ withRepeat,
8
+ withTiming,
9
+ interpolate,
10
+ Easing,
11
+ } from 'react-native-reanimated';
12
+ import { LinearGradient } from 'react-native-linear-gradient';
13
+
14
+ interface ShimmerProps {
15
+ /**
16
+ * Width of the shimmer container
17
+ */
18
+ width?: DimensionValue;
19
+
20
+ /**
21
+ * Height of the shimmer container
22
+ */
23
+ height?: DimensionValue;
24
+
25
+ /**
26
+ * Base color of the skeleton
27
+ * @default '#E1E9EE'
28
+ */
29
+ baseColor?: string;
30
+
31
+ /**
32
+ * Highlight color that shimmers across
33
+ * @default '#F2F8FC'
34
+ */
35
+ highlightColor?: string;
36
+
37
+ /**
38
+ * Animation duration in milliseconds
39
+ * @default 1500
40
+ */
41
+ duration?: number;
42
+
43
+ /**
44
+ * Border radius
45
+ */
46
+ borderRadius?: number;
47
+
48
+ /**
49
+ * Additional styles
50
+ */
51
+ style?: StyleProp<ViewStyle>;
52
+
53
+ /**
54
+ * Child content (rendered on top of shimmer)
55
+ */
56
+ children?: React.ReactNode;
57
+ }
58
+
59
+ /**
60
+ * Shimmer animation component using Reanimated 3
61
+ *
62
+ * Creates a smooth left-to-right shimmer effect using:
63
+ * - Shared values for performance
64
+ * - Worklets for 60 FPS on UI thread
65
+ * - Linear gradient for shimmer effect
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * <ShimmerAnimation
70
+ * width={200}
71
+ * height={20}
72
+ * borderRadius={4}
73
+ * />
74
+ * ```
75
+ */
76
+ export function ShimmerAnimation({
77
+ width,
78
+ height,
79
+ baseColor = '#E1E9EE',
80
+ highlightColor = '#F2F8FC',
81
+ duration = 1500,
82
+ borderRadius = 4,
83
+ style,
84
+ children,
85
+ }: ShimmerProps) {
86
+ // Shared value for animation progress (0 to 1)
87
+ const progress = useSharedValue(0);
88
+
89
+ useEffect(() => {
90
+ // Start infinite shimmer animation
91
+ progress.value = withRepeat(
92
+ withTiming(1, {
93
+ duration,
94
+ easing: Easing.linear,
95
+ }),
96
+ -1, // Infinite repeat
97
+ false // Don't reverse
98
+ );
99
+ }, [duration, progress]);
100
+
101
+ // Animated style for shimmer overlay
102
+ const animatedStyle = useAnimatedStyle(() => {
103
+ 'worklet';
104
+
105
+ // Calculate shimmer position
106
+ // Interpolate from -100% to 100% to create left-to-right sweep
107
+ const translateX = interpolate(progress.value, [0, 1], [-1, 1]);
108
+
109
+ return {
110
+ transform: [{ translateX: translateX * (typeof width === 'number' ? width : 200) }],
111
+ };
112
+ }, [width]);
113
+
114
+ return (
115
+ <View
116
+ style={[
117
+ styles.container,
118
+ {
119
+ width,
120
+ height,
121
+ borderRadius,
122
+ backgroundColor: baseColor,
123
+ },
124
+ style,
125
+ ]}
126
+ >
127
+ {/* Shimmer gradient overlay */}
128
+ <Animated.View
129
+ style={[
130
+ StyleSheet.absoluteFill,
131
+ {
132
+ overflow: 'hidden',
133
+ borderRadius,
134
+ },
135
+ ]}
136
+ >
137
+ <Animated.View
138
+ style={[
139
+ styles.shimmer,
140
+ {
141
+ width: typeof width === 'number' ? width : '100%',
142
+ height: '100%',
143
+ },
144
+ animatedStyle,
145
+ ]}
146
+ >
147
+ <LinearGradient
148
+ colors={[baseColor, highlightColor, highlightColor, baseColor]}
149
+ start={{ x: 0, y: 0 }}
150
+ end={{ x: 1, y: 0 }}
151
+ style={StyleSheet.absoluteFill}
152
+ />
153
+ </Animated.View>
154
+ </Animated.View>
155
+
156
+ {/* Child content */}
157
+ {children}
158
+ </View>
159
+ );
160
+ }
161
+
162
+ const styles = StyleSheet.create({
163
+ container: {
164
+ overflow: 'hidden',
165
+ },
166
+ shimmer: {
167
+ position: 'absolute',
168
+ },
169
+ });
@@ -0,0 +1,4 @@
1
+ export { ShimmerAnimation } from './ShimmerAnimation';
2
+ export { PulseAnimation } from './PulseAnimation';
3
+ export { AnimationWrapper } from './AnimationWrapper';
4
+ export type { AnimationType } from './AnimationWrapper';
@@ -0,0 +1,85 @@
1
+ import React, { useState } from 'react';
2
+ import type { LayoutChangeEvent } from 'react-native';
3
+ import { StyleSheet, View } from 'react-native';
4
+ import type { ReactNode } from 'react';
5
+ import type { SkeletonConfig } from '../types';
6
+ import { AnimationWrapper } from '../animations';
7
+
8
+ interface MeasuredBlockProps {
9
+ /**
10
+ * The real element to render (hidden) and measure.
11
+ */
12
+ children: ReactNode;
13
+
14
+ /**
15
+ * Skeleton configuration (animation, colors, etc.)
16
+ */
17
+ config: SkeletonConfig;
18
+ }
19
+
20
+ interface Size {
21
+ width: number;
22
+ height: number;
23
+ }
24
+
25
+ /**
26
+ * Renders an opaque (non-host) component hidden so it lays out at its real
27
+ * size, measures that size via `onLayout`, then overlays a shimmer block of
28
+ * exactly those dimensions.
29
+ *
30
+ * This is Skelo's runtime-measurement fallback: when the parser can't
31
+ * introspect a component (e.g. a user-defined function component), we can't
32
+ * know its internal View/Text/Image structure without native view-tree access,
33
+ * but we *can* render it off-screen and reproduce its footprint accurately.
34
+ *
35
+ * The result is a single, correctly-sized skeleton block rather than a
36
+ * misleading fixed-size placeholder.
37
+ */
38
+ export function MeasuredBlock({ children, config }: MeasuredBlockProps) {
39
+ const [size, setSize] = useState<Size | null>(null);
40
+
41
+ const handleLayout = (event: LayoutChangeEvent) => {
42
+ const { width, height } = event.nativeEvent.layout;
43
+ setSize(prev =>
44
+ prev && prev.width === width && prev.height === height ? prev : { width, height }
45
+ );
46
+ };
47
+
48
+ return (
49
+ <View>
50
+ {/* Real content, rendered invisibly purely to occupy space + measure. */}
51
+ <View
52
+ style={styles.hidden}
53
+ pointerEvents="none"
54
+ importantForAccessibility="no-hide-descendants"
55
+ accessibilityElementsHidden
56
+ onLayout={handleLayout}
57
+ >
58
+ {children}
59
+ </View>
60
+
61
+ {/* Size-matched shimmer overlay, shown once measured. */}
62
+ {size && size.width > 0 && size.height > 0 && (
63
+ <View style={StyleSheet.absoluteFill} pointerEvents="none">
64
+ <AnimationWrapper
65
+ animation={config.animation}
66
+ width={size.width}
67
+ height={size.height}
68
+ baseColor={config.baseColor}
69
+ highlightColor={config.highlightColor}
70
+ duration={config.duration}
71
+ borderRadius={config.borderRadius}
72
+ />
73
+ </View>
74
+ )}
75
+ </View>
76
+ );
77
+ }
78
+
79
+ const styles = StyleSheet.create({
80
+ hidden: {
81
+ opacity: 0,
82
+ },
83
+ });
84
+
85
+ MeasuredBlock.displayName = 'Skeleton.MeasuredBlock';
@@ -0,0 +1,149 @@
1
+ import React, { useMemo } from 'react';
2
+ import type { SkeletonProps } from '../types';
3
+ import { Parser } from '../core/parser';
4
+ import { SkeletonRenderer } from './SkeletonRenderer';
5
+ import { SkeletonIgnore } from './SkeletonIgnore';
6
+ import { StyleSkeleton } from '../core/generator/StyleSkeleton';
7
+ import { getDeepExpander } from '../core/deepRegistry';
8
+ import { DEFAULT_CONFIG } from '../constants/defaults';
9
+
10
+ /**
11
+ * Main Skeleton component
12
+ *
13
+ * Automatically generates skeleton loading states for your React Native components.
14
+ * Wraps your UI and shows skeleton placeholders while loading.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <Skeleton loading={isLoading}>
19
+ * <ProfileScreen />
20
+ * </Skeleton>
21
+ * ```
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * <Skeleton
26
+ * loading={isLoading}
27
+ * animation="pulse"
28
+ * baseColor="#f0f0f0"
29
+ * highlightColor="#ffffff"
30
+ * >
31
+ * <View>
32
+ * <Text>Hello World</Text>
33
+ * <Image source={avatar} />
34
+ * </View>
35
+ * </Skeleton>
36
+ * ```
37
+ */
38
+ export function Skeleton({
39
+ loading,
40
+ children,
41
+ styles,
42
+ excludeStyles,
43
+ deep = false,
44
+ animation = DEFAULT_CONFIG.animation,
45
+ duration = DEFAULT_CONFIG.duration,
46
+ baseColor = DEFAULT_CONFIG.baseColor,
47
+ highlightColor = DEFAULT_CONFIG.highlightColor,
48
+ borderRadius = DEFAULT_CONFIG.borderRadius,
49
+ accessible = true,
50
+ accessibilityLabel = 'Loading content',
51
+ debug = false,
52
+ }: SkeletonProps) {
53
+ // Parse component tree and generate skeleton.
54
+ // Hooks must run unconditionally (rules of hooks), but parsing stays lazy:
55
+ // we skip the work entirely when not loading or in styles-driven mode.
56
+ const skeletonTree = useMemo(() => {
57
+ if (!loading || styles) {
58
+ return [];
59
+ }
60
+
61
+ if (__DEV__ && debug) {
62
+ console.log('[Skelo] Parsing component tree...');
63
+ }
64
+
65
+ try {
66
+ // Deep mode: expand opaque components into their real host tree via the
67
+ // opt-in expander (registered by importing 'react-native-skelo/deep').
68
+ // Falls back to static parsing if unavailable or it returns nothing.
69
+ if (deep) {
70
+ const expand = getDeepExpander();
71
+ if (expand) {
72
+ const expanded = expand(<>{children}</>);
73
+ if (expanded && expanded.length > 0) {
74
+ return expanded;
75
+ }
76
+ if (__DEV__) {
77
+ console.warn('[Skelo] deep expansion returned nothing; falling back to static parsing.');
78
+ }
79
+ } else if (__DEV__) {
80
+ console.warn(
81
+ "[Skelo] `deep` requires react-reconciler. Install it and add `import 'react-native-skelo/deep';` in your app entry."
82
+ );
83
+ }
84
+ }
85
+
86
+ const parsed = Parser.parse(children);
87
+
88
+ if (__DEV__ && debug) {
89
+ // Log a lightweight summary: the raw nodes hold React elements
90
+ // (`element`) and arbitrary props that aren't safely serializable.
91
+ const summarize = (nodes: typeof parsed): unknown =>
92
+ nodes.map(node => ({
93
+ type: node.type,
94
+ style: node.style,
95
+ children: summarize(node.children),
96
+ }));
97
+ console.log('[Skelo] Parsed tree:', JSON.stringify(summarize(parsed), null, 2));
98
+ }
99
+
100
+ return parsed;
101
+ } catch (error) {
102
+ if (__DEV__) {
103
+ console.error('[Skelo] Failed to parse component tree:', error);
104
+ }
105
+ return [];
106
+ }
107
+ }, [loading, styles, deep, children, debug]);
108
+
109
+ // Create skeleton config
110
+ const config = useMemo(
111
+ () => ({
112
+ animation,
113
+ duration,
114
+ baseColor,
115
+ highlightColor,
116
+ borderRadius,
117
+ }),
118
+ [animation, duration, baseColor, highlightColor, borderRadius]
119
+ );
120
+
121
+ // If not loading, render the real children directly.
122
+ if (!loading) {
123
+ return <>{children}</>;
124
+ }
125
+
126
+ // Styles-driven mode: derive the skeleton from the provided styles instead of
127
+ // introspecting the child tree (useful when children are opaque components).
128
+ if (styles) {
129
+ return <StyleSkeleton styles={styles} config={config} exclude={excludeStyles} />;
130
+ }
131
+
132
+ return (
133
+ <SkeletonRenderer
134
+ tree={skeletonTree}
135
+ config={config}
136
+ accessible={accessible}
137
+ accessibilityLabel={accessibilityLabel}
138
+ debug={debug}
139
+ />
140
+ );
141
+ }
142
+
143
+ Skeleton.displayName = 'Skeleton';
144
+
145
+ /**
146
+ * Escape hatch to keep children as real content inside a loading `<Skeleton>`.
147
+ * @see SkeletonIgnore
148
+ */
149
+ Skeleton.Ignore = SkeletonIgnore;
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import type { ReactNode } from 'react';
3
+
4
+ interface SkeletonIgnoreProps {
5
+ children?: ReactNode;
6
+ }
7
+
8
+ /**
9
+ * Escape hatch that renders its children as real content, even inside a
10
+ * loading `<Skeleton>`. Use it for elements that should stay visible during
11
+ * loading — section headings, static labels, action buttons, etc.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <Skeleton loading={loading}>
16
+ * <Skeleton.Ignore>
17
+ * <Text>Profile</Text> // stays as real text
18
+ * </Skeleton.Ignore>
19
+ * <ProfileCard /> // becomes a skeleton
20
+ * </Skeleton>
21
+ * ```
22
+ */
23
+ export function SkeletonIgnore({ children }: SkeletonIgnoreProps) {
24
+ return <>{children}</>;
25
+ }
26
+
27
+ SkeletonIgnore.displayName = 'Skeleton.Ignore';