import React, { useEffect, useMemo, useRef, useState } from 'react'; import { Animated, Easing, LayoutChangeEvent, StyleSheet, Text, View, ViewStyle } from 'react-native'; type ProgressBarProps = { progress: number; // 0 - 1 height?: number; style?: ViewStyle; trackColor?: string; fillColor?: string; animated?: boolean; showLabel?: boolean; }; export function ProgressBar({ progress, height = 18, style, trackColor = '#EDEDED', fillColor = '#BBF246', animated = true, showLabel = true, }: ProgressBarProps) { const [trackWidth, setTrackWidth] = useState(0); const animatedValue = useRef(new Animated.Value(0)).current; const clamped = useMemo(() => { if (Number.isNaN(progress)) return 0; return Math.min(1, Math.max(0, progress)); }, [progress]); useEffect(() => { if (!animated) { animatedValue.setValue(clamped); return; } Animated.timing(animatedValue, { toValue: clamped, duration: 650, easing: Easing.out(Easing.cubic), useNativeDriver: false, }).start(); }, [clamped, animated, animatedValue]); const onLayout = (e: LayoutChangeEvent) => { setTrackWidth(e.nativeEvent.layout.width); }; const fillWidth = Animated.multiply(animatedValue, trackWidth || 1); const percent = Math.round(clamped * 100); return ( {showLabel && ( {percent}% )} ); } const styles = StyleSheet.create({ container: { width: '100%', borderRadius: 10, overflow: 'hidden', backgroundColor: 'transparent', }, track: { ...StyleSheet.absoluteFillObject, borderRadius: 10, }, fill: { height: '100%', borderRadius: 10, justifyContent: 'center', paddingHorizontal: 10, }, label: { color: '#192126', fontWeight: '700', fontSize: 12, }, });