import React, { useEffect, useRef } from 'react'; import { Animated, Easing, StyleSheet } from 'react-native'; import type { StyleProp, ViewStyle, ViewProps } from 'react-native'; import { StyledLoadingDot, StyledLoadingIndicatorWrapper, } from './StyledLoadingIndicator'; type ThemeVariant = | 'basic-transparent' | 'filled-primary' | 'filled-secondary' | 'filled-danger' | 'filled-white' | 'filled-inverted' | 'outlined-primary' | 'outlined-secondary' | 'outlined-danger' | 'outlined-white' | 'outlined-inverted' | 'text-primary' | 'text-secondary' | 'text-danger' | 'text-white' | 'text-inverted'; const AnimatedLoadingIndicatorWrapper = Animated.createAnimatedComponent( StyledLoadingIndicatorWrapper ); const AnimatedLoadingDot = Animated.createAnimatedComponent(StyledLoadingDot); const renderDotComponent = ({ index, size, count, animation, themeVariant, }: { index: number; size: number; count: number; animation: React.MutableRefObject; themeVariant: ThemeVariant; }) => { const interpolatedProgressAnimation = animation.current.interpolate({ inputRange: [ 0.0, (index + 0.5) / (count + 1), (index + 1.0) / (count + 1), (index + 1.5) / (count + 1), 1.0, ], outputRange: [1.0, 1.36, 1.56, 1.06, 1.0], }); return ( ); }; interface LoadingIndicatorProps extends ViewProps { /** * Size of the loading dot. */ count?: number; /** * Size of the loading dot. */ size?: number; /** * Additional style. */ style?: StyleProp; /** * Testing id of the component. */ testID?: string; /** * Testing id of the component. */ themeVariant: ThemeVariant; } const LoadingIndicator = ({ count = 3, size = 12, testID, themeVariant, ...nativeProps }: LoadingIndicatorProps) => { const progressAnimation = useRef(new Animated.Value(0)); useEffect(() => { const animation = Animated.loop( Animated.timing(progressAnimation.current, { toValue: 1, duration: 1200, easing: Easing.linear, useNativeDriver: true, }) ); animation.start(); return () => { animation.stop(); }; }, []); const renderLoadingDot = (_item: any, index: number) => { if (typeof renderDotComponent === 'function') { return renderDotComponent({ index, count, size, animation: progressAnimation, themeVariant, }); } return null; }; return ( {Array.from(new Array(count), renderLoadingDot, themeVariant)} ); }; export default LoadingIndicator;