/ components / ShimmerEffect.tsx
ShimmerEffect.tsx
  1  import React, { useEffect, useRef } from 'react';
  2  import { Animated, View, ViewStyle } from 'react-native';
  3  import { LinearGradient } from 'expo-linear-gradient';
  4  import { useTheme } from '@/constants/ThemeContext';
  5  // import { Colors } from '@/constants/Colors';
  6  
  7  import { DimensionValue } from 'react-native';
  8  
  9  interface ShimmerEffectProps {
 10    width?: DimensionValue;
 11    height?: number;
 12    borderRadius?: number;
 13    style?: ViewStyle;
 14  }
 15  
 16  export const ShimmerEffect: React.FC<ShimmerEffectProps> = ({
 17    width = 100,
 18    height = 20,
 19    borderRadius = 4,
 20    style,
 21  }) => {
 22    const { actualTheme } = useTheme();
 23    const shimmerAnimation = useRef(new Animated.Value(0)).current;
 24  
 25    useEffect(() => {
 26      const startShimmer = () => {
 27        shimmerAnimation.setValue(0);
 28        Animated.loop(
 29          Animated.timing(shimmerAnimation, {
 30            toValue: 1,
 31            duration: 1500,
 32            useNativeDriver: true,
 33          })
 34        ).start();
 35      };
 36  
 37      startShimmer();
 38    }, [shimmerAnimation]);
 39  
 40    const translateX = shimmerAnimation.interpolate({
 41      inputRange: [0, 1],
 42      outputRange: [-300, 300],
 43    });
 44  
 45    const baseColor = actualTheme === 'dark' ? '#2C2C2E' : '#F2F2F7';
 46    const highlightColor = actualTheme === 'dark' ? '#3C3C3E' : '#FFFFFF';
 47  
 48    return (
 49      <View
 50        style={[
 51          {
 52            width,
 53            height,
 54            borderRadius,
 55            backgroundColor: baseColor,
 56            overflow: 'hidden',
 57          },
 58          style,
 59        ]}
 60      >
 61        <Animated.View
 62          style={{
 63            width: '100%',
 64            height: '100%',
 65            transform: [{ translateX }],
 66          }}
 67        >
 68          <LinearGradient
 69            colors={[baseColor, highlightColor, baseColor]}
 70            start={{ x: 0, y: 0 }}
 71            end={{ x: 1, y: 0 }}
 72            style={{
 73              width: 300,
 74              height: '100%',
 75            }}
 76          />
 77        </Animated.View>
 78      </View>
 79    );
 80  };
 81  
 82  // Convenience components for common use cases
 83  export const ShimmerCard: React.FC<{ style?: ViewStyle }> = ({ style }) => (
 84    <View style={style}>
 85      <ShimmerEffect width="100%" height={200} borderRadius={12} />
 86      <ShimmerEffect width="80%" height={16} style={{ marginTop: 8 }} />
 87      <ShimmerEffect width="60%" height={12} style={{ marginTop: 4 }} />
 88    </View>
 89  );
 90  
 91  export const ShimmerText: React.FC<{
 92    lines?: number;
 93    width?: DimensionValue;
 94    style?: ViewStyle;
 95  }> = ({ lines = 3, width = '100%' as DimensionValue, style }) => (
 96    <View style={style}>
 97      {Array.from({ length: lines }).map((_, index) => (
 98        <ShimmerEffect
 99          key={index}
100          width={index === lines - 1 ? ('70%' as DimensionValue) : width}
101          height={16}
102          style={{ marginBottom: index < lines - 1 ? 8 : 0 }}
103        />
104      ))}
105    </View>
106  );