@@ -7,12 +7,15 @@ import {
77import  Animated ,  { 
88  cancelAnimation , 
99  Easing , 
10+   FadeIn , 
11+   FadeOut , 
1012  interpolate , 
1113  ReduceMotion , 
1214  useAnimatedStyle , 
1315  useSharedValue , 
1416  withRepeat , 
1517  withTiming , 
18+   type  SharedValue , 
1619}  from  'react-native-reanimated' ; 
1720
1821import  {  colorKit ,  useTheme  }  from  '../../providers/theme' ; 
@@ -29,12 +32,115 @@ import {
2932  DEFAULT_SPEED , 
3033  DISPLAY_NAME , 
3134}  from  './skeleton.constants' ; 
32- import  skeletonStyles  from  './skeleton.styles' ; 
33- import  type  {  SkeletonProps  }  from  './skeleton.types' ; 
35+ import  skeletonStyles ,  {  nativeStyles  }  from  './skeleton.styles' ; 
36+ import  type  { 
37+   PulseConfig , 
38+   ShimmerConfig , 
39+   SkeletonProps , 
40+ }  from  './skeleton.types' ; 
41+ 
42+ // -------------------------------------------------- 
43+ 
44+ interface  ShimmerAnimationProps  { 
45+   isLoading : boolean ; 
46+   shimmerConfig ?: ShimmerConfig ; 
47+   componentWidth : number ; 
48+   offset : number ; 
49+   progress : SharedValue < number > ; 
50+   screenWidth : number ; 
51+   isDark : boolean ; 
52+   colors : any ; 
53+ } 
54+ 
55+ const  ShimmerAnimation : React . FC < ShimmerAnimationProps >  =  ( { 
56+   shimmerConfig, 
57+   componentWidth, 
58+   offset, 
59+   progress, 
60+   screenWidth, 
61+   isDark, 
62+   colors, 
63+ } )  =>  { 
64+   const  highlightColor  =  isDark 
65+     ? colorKit 
66+         . setAlpha ( colorKit . increaseBrightness ( colors . background ,  10 ) . hex ( ) ,  0.5 ) 
67+         . hex ( ) 
68+     : colorKit 
69+         . setAlpha ( colorKit . decreaseBrightness ( colors . background ,  5 ) . hex ( ) ,  0.5 ) 
70+         . hex ( ) ; 
71+ 
72+   const  gradientColors  =  shimmerConfig ?. gradientConfig ?. colors  ??  [ 
73+     'transparent' , 
74+     highlightColor , 
75+     'transparent' , 
76+   ] ; 
77+   const  gradientStart  = 
78+     shimmerConfig ?. gradientConfig ?. start  ??  DEFAULT_GRADIENT_START ; 
79+   const  gradientEnd  = 
80+     shimmerConfig ?. gradientConfig ?. end  ??  DEFAULT_GRADIENT_END ; 
81+ 
82+   const  shimmerAnimatedStyle  =  useAnimatedStyle ( ( )  =>  { 
83+     const  translateX  =  interpolate ( 
84+       progress . get ( ) , 
85+       [ 0 ,  1 ] , 
86+       [ - ( componentWidth  +  offset ) ,  screenWidth ] 
87+     ) ; 
88+ 
89+     return  { 
90+       opacity : 1 , 
91+       transform : [ {  translateX } ] , 
92+     } ; 
93+   } ) ; 
94+ 
95+   return  ( 
96+     < Animated . View 
97+       style = { [ 
98+         StyleSheet . absoluteFill , 
99+         nativeStyles . borderCurve , 
100+         shimmerAnimatedStyle , 
101+       ] } 
102+     > 
103+       < LinearGradientComponent 
104+         colors = { gradientColors } 
105+         start = { gradientStart } 
106+         end = { gradientEnd } 
107+         style = { StyleSheet . absoluteFill } 
108+       /> 
109+     </ Animated . View > 
110+   ) ; 
111+ } ; 
112+ 
113+ // -------------------------------------------------- 
114+ 
115+ interface  PulseAnimationProps  { 
116+   pulseConfig ?: PulseConfig ; 
117+   progress : SharedValue < number > ; 
118+ } 
119+ 
120+ const  usePulseAnimation  =  ( {  pulseConfig,  progress } : PulseAnimationProps )  =>  { 
121+   const  pulseMinOpacity  =  pulseConfig ?. minOpacity  ??  DEFAULT_PULSE_MIN_OPACITY ; 
122+   const  pulseMaxOpacity  =  pulseConfig ?. maxOpacity  ??  DEFAULT_PULSE_MAX_OPACITY ; 
123+ 
124+   return  useAnimatedStyle ( ( )  =>  { 
125+     const  opacity  =  interpolate ( 
126+       progress . get ( ) , 
127+       [ 0 ,  1 ] , 
128+       [ pulseMinOpacity ,  pulseMaxOpacity ] 
129+     ) ; 
130+ 
131+     return  { 
132+       opacity, 
133+     } ; 
134+   } ) ; 
135+ } ; 
136+ 
137+ // -------------------------------------------------- 
34138
35139const  Skeleton : React . FC < SkeletonProps >  =  ( props )  =>  { 
36140  const  { 
37141    children, 
142+     entering =  FadeIn , 
143+     exiting =  FadeOut , 
38144    isLoading =  true , 
39145    animationType =  'shimmer' , 
40146    shimmerConfig, 
@@ -49,41 +155,18 @@ const Skeleton: React.FC<SkeletonProps> = (props) => {
49155  const  progress  =  useSharedValue ( 0 ) ; 
50156
51157  const  {  width : SCREEN_WIDTH  }  =  useWindowDimensions ( ) ; 
52- 
53158  const  {  colors,  isDark }  =  useTheme ( ) ; 
54159
55160  const  shimmerDuration  =  shimmerConfig ?. duration  ??  DEFAULT_SHIMMER_DURATION ; 
56161  const  shimmerEasing  =  shimmerConfig ?. easing  ??  DEFAULT_EASING ; 
57162  const  shimmerSpeed  =  shimmerConfig ?. speed  ??  DEFAULT_SPEED ; 
58163
59-   const  highlightColor  =  isDark 
60-     ? colorKit 
61-         . setAlpha ( colorKit . increaseBrightness ( colors . background ,  10 ) . hex ( ) ,  0.5 ) 
62-         . hex ( ) 
63-     : colorKit 
64-         . setAlpha ( colorKit . decreaseBrightness ( colors . background ,  5 ) . hex ( ) ,  0.5 ) 
65-         . hex ( ) ; 
66- 
67-   const  gradientColors  =  shimmerConfig ?. gradientConfig ?. colors  ??  [ 
68-     'transparent' , 
69-     highlightColor , 
70-     'transparent' , 
71-   ] ; 
72-   const  gradientStart  = 
73-     shimmerConfig ?. gradientConfig ?. start  ??  DEFAULT_GRADIENT_START ; 
74-   const  gradientEnd  = 
75-     shimmerConfig ?. gradientConfig ?. end  ??  DEFAULT_GRADIENT_END ; 
76- 
77-   // Pulse configuration 
78164  const  pulseDuration  =  pulseConfig ?. duration  ??  DEFAULT_PULSE_DURATION ; 
79165  const  pulseEasing  =  pulseConfig ?. easing  ??  Easing . inOut ( Easing . ease ) ; 
80-   const  pulseMinOpacity  =  pulseConfig ?. minOpacity  ??  DEFAULT_PULSE_MIN_OPACITY ; 
81-   const  pulseMaxOpacity  =  pulseConfig ?. maxOpacity  ??  DEFAULT_PULSE_MAX_OPACITY ; 
82166
83-   // 5. Styles 
84167  const  tvStyles  =  skeletonStyles . skeleton ( {  className } ) ; 
168+   const  pulseAnimatedStyle  =  usePulseAnimation ( {  pulseConfig,  progress } ) ; 
85169
86-   // 6. Effects 
87170  useEffect ( ( )  =>  { 
88171    if  ( isLoading  &&  animationType  !==  'none' )  { 
89172      progress . set ( 0 ) ; 
@@ -130,46 +213,6 @@ const Skeleton: React.FC<SkeletonProps> = (props) => {
130213    pulseEasing , 
131214  ] ) ; 
132215
133-   // 7. Animation styles 
134-   const  shimmerAnimatedStyle  =  useAnimatedStyle ( ( )  =>  { 
135-     if  ( animationType  !==  'shimmer'  ||  ! isLoading )  { 
136-       return  { 
137-         opacity : 0 , 
138-         transform : [ {  translateX : 0  } ] , 
139-       } ; 
140-     } 
141- 
142-     const  translateX  =  interpolate ( 
143-       progress . get ( ) , 
144-       [ 0 ,  1 ] , 
145-       [ - ( componentWidth  +  offset ) ,  SCREEN_WIDTH ] 
146-     ) ; 
147- 
148-     return  { 
149-       opacity : 1 , 
150-       transform : [ {  translateX } ] , 
151-     } ; 
152-   } ) ; 
153- 
154-   const  pulseAnimatedStyle  =  useAnimatedStyle ( ( )  =>  { 
155-     if  ( animationType  !==  'pulse'  ||  ! isLoading )  { 
156-       return  { 
157-         opacity : 1 , 
158-       } ; 
159-     } 
160- 
161-     const  opacity  =  interpolate ( 
162-       progress . get ( ) , 
163-       [ 0 ,  1 ] , 
164-       [ pulseMinOpacity ,  pulseMaxOpacity ] 
165-     ) ; 
166- 
167-     return  { 
168-       opacity, 
169-     } ; 
170-   } ) ; 
171- 
172-   // 8. Callbacks 
173216  const  handleLayout  =  useCallback ( 
174217    ( event : LayoutChangeEvent )  =>  { 
175218      if  ( componentWidth  ===  0 )  { 
@@ -181,27 +224,39 @@ const Skeleton: React.FC<SkeletonProps> = (props) => {
181224    [ componentWidth ] 
182225  ) ; 
183226
184-   // 9. Render 
185227  if  ( ! isLoading )  { 
186-     return  < > { children } </ > ; 
228+     return  ( 
229+       < Animated . View  key = "content"  entering = { entering }  exiting = { exiting } > 
230+         { children } 
231+       </ Animated . View > 
232+     ) ; 
187233  } 
188234
189235  return  ( 
190236    < Animated . View 
237+       key = "skeleton" 
238+       entering = { entering } 
239+       exiting = { exiting } 
191240      onLayout = { handleLayout } 
192-       style = { [ animationType  ===  'pulse'  &&  pulseAnimatedStyle ,  style ] } 
241+       style = { [ 
242+         animationType  ===  'pulse'  &&  pulseAnimatedStyle , 
243+         nativeStyles . borderCurve , 
244+         style , 
245+       ] } 
193246      className = { tvStyles } 
194247      { ...restProps } 
195248    > 
196249      { animationType  ===  'shimmer'  &&  componentWidth  >  0  &&  ( 
197-         < Animated . View  style = { [ StyleSheet . absoluteFill ,  shimmerAnimatedStyle ] } > 
198-           < LinearGradientComponent 
199-             colors = { gradientColors } 
200-             start = { gradientStart } 
201-             end = { gradientEnd } 
202-             style = { StyleSheet . absoluteFill } 
203-           /> 
204-         </ Animated . View > 
250+         < ShimmerAnimation 
251+           isLoading = { isLoading } 
252+           shimmerConfig = { shimmerConfig } 
253+           componentWidth = { componentWidth } 
254+           offset = { offset } 
255+           progress = { progress } 
256+           screenWidth = { SCREEN_WIDTH } 
257+           isDark = { isDark } 
258+           colors = { colors } 
259+         /> 
205260      ) } 
206261    </ Animated . View > 
207262  ) ; 
0 commit comments