import React, { useMemo, useState, useEffect } from 'react'; import { View, StyleSheet, Animated } from 'react-native'; import { applyStyles, parseColor, parseSize } from '../../utils/styles'; const DEFAULT_INDETERMINATE_DURATION = 2; const DEFAULT_INDETERMINATE_WIDTH_PCT = 30; interface Props { component: any; scaleFactor: number; parentDirection?: string; } export const NamiProgressBar: React.FC = ({ component, scaleFactor, parentDirection }) => { const containerStyle = applyStyles(component, scaleFactor, false, parentDirection); const mode = component.mode ?? 'determinate'; const innerPadding = parseSize(component.innerPadding ?? 0, scaleFactor) ?? 0; const trackColor = parseColor(component.inactiveFillColor ?? component.inactiveFillColorFallback ?? component.trackColor) ?? '#E0E0E0'; const fillColor = parseColor(component.activeFillColor ?? component.activeFillColorFallback ?? component.progressColor ?? component.fillColor) ?? '#007AFF'; const barHeight = parseSize(component.height ?? component.barHeight ?? 4, scaleFactor) ?? 4; const inactiveRadius = parseSize(component.inactiveBorderRadius ?? component.borderRadius, scaleFactor) ?? barHeight / 2; const activeRadius = parseSize(component.activeBorderRadius ?? component.borderRadius, scaleFactor) ?? barHeight / 2; const [trackWidth, setTrackWidth] = useState(0); const translateX = useState(new Animated.Value(0))[0]; const progressPct = useMemo(() => { const raw = component.percentage ?? component.progress ?? 0; if (typeof raw === 'number') { if (!Number.isInteger(raw) && raw <= 1) return Math.max(0, Math.min(100, raw * 100)); return Math.max(0, Math.min(100, raw)); } return 0; }, [component.percentage, component.progress]); useEffect(() => { if (mode !== 'indeterminate' || trackWidth <= 0) return; const activeWidth = component.activeWidth ?? `${DEFAULT_INDETERMINATE_WIDTH_PCT}%`; const isPct = typeof activeWidth === 'string' && activeWidth.trim().endsWith('%'); const barWidth = isPct ? trackWidth * (parseFloat(activeWidth) / 100) : (typeof activeWidth === 'number' ? activeWidth : parseFloat(activeWidth)); const duration = (component.duration ?? DEFAULT_INDETERMINATE_DURATION) * 1000; translateX.setValue(-barWidth); const anim = Animated.loop( Animated.timing(translateX, { toValue: trackWidth, duration, useNativeDriver: true, }) ); anim.start(); return () => anim.stop(); }, [mode, trackWidth, component.activeWidth, component.duration, translateX]); return ( setTrackWidth(e.nativeEvent.layout.width)} > {mode === 'indeterminate' ? ( ) : ( )} ); }; const styles = StyleSheet.create({ track: { width: '100%', overflow: 'hidden' }, fill: { height: '100%' }, });