import { FC, useEffect, useMemo, useRef } from 'react'; import { Animated, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; interface SkeletonProps { count?: number; circle?: boolean; width?: ViewStyle['width']; height?: ViewStyle['height']; color?: ViewStyle['backgroundColor']; borderRadius?: ViewStyle['borderRadius']; style?: StyleProp; spacing?: number; containerStyle?: StyleProp; } interface SkeletonViewProps { skStyle?: StyleProp; appStyle: ViewStyle; style?: StyleProp; } const SkeletonView: FC = ({ skStyle, appStyle, style }) => ( ); export const Skeleton: FC = ({ count, circle, width = '100%', height = 14, color = '#ebebeb', borderRadius = 4, spacing = 10, style, containerStyle, }) => { const opacity = useRef(new Animated.Value(0.3)).current; const appStyle = useMemo(() => { const opacityValue = opacity as unknown as number; let radius = borderRadius; if (circle) { if (typeof width !== 'number') { console.warn('Circle width need to be number value.'); } else if (width !== height) { console.warn('Circle width and height need to be the same value'); } else { radius = width / 2; } } return { width: width, height: height, borderRadius: radius, backgroundColor: color, opacity: opacityValue, }; }, [borderRadius, circle, color, height, opacity, width]); useEffect(() => { Animated.loop( Animated.sequence([ Animated.timing(opacity, { toValue: 1, duration: 500, useNativeDriver: true, }), Animated.timing(opacity, { toValue: 0.3, duration: 800, useNativeDriver: true, }), ]) ).start(); }, [opacity]); if (!count || count === 1 || count === 0) { return ; } return ( {Array.from({ length: count }).map((_, i) => ( ))} ); }; const styles = StyleSheet.create({ wFull: { width: '100%', flexShrink: 1, }, });