import setColor from 'color'; import * as React from 'react'; import { Animated, I18nManager, LayoutChangeEvent, Platform, StyleProp, StyleSheet, View, ViewStyle, } from 'react-native'; import { withTheme } from '../core/theming'; import isNativeAnimationSupported from '../utils/isNativeAnimationSupported'; type Props = React.ComponentPropsWithRef & { /** * Progress value (between 0 and 1). */ progress?: number; /** * Color of the progress bar. The background color will be calculated based on this but you can change it by passing `backgroundColor` to `style` prop. */ color?: string; /** * If the progress bar will show indeterminate progress. */ indeterminate?: boolean; /** * Whether to show the ProgressBar (true, the default) or hide it (false). */ visible?: boolean; style?: StyleProp; /** * @optional */ theme: ReactNativePaper.Theme; }; const INDETERMINATE_DURATION = 2000; const INDETERMINATE_MAX_WIDTH = 0.6; const { isRTL } = I18nManager; /** * Progress bar is an indicator used to present progress of some activity in the app. * *
* *
* * ## Usage * ```js * import * as React from 'react'; * import { ProgressBar, Colors } from 'react-native-paper'; * * const MyComponent = () => ( * * ); * * export default MyComponent; * ``` */ const ProgressBar = ({ color, indeterminate, style, progress = 0, visible = true, theme, ...rest }: Props) => { const { current: timer } = React.useRef( new Animated.Value(0) ); const { current: fade } = React.useRef(new Animated.Value(0)); const [width, setWidth] = React.useState(0); const [prevWidth, setPrevWidth] = React.useState(0); const indeterminateAnimation = React.useRef(null); const { scale } = theme.animation; const startAnimation = React.useCallback(() => { // Show progress bar Animated.timing(fade, { duration: 200 * scale, toValue: 1, useNativeDriver: isNativeAnimationSupported(), isInteraction: false, }).start(); // Animate progress bar if (indeterminate) { if (!indeterminateAnimation.current) { indeterminateAnimation.current = Animated.timing(timer, { duration: INDETERMINATE_DURATION, toValue: 1, // Animated.loop does not work if useNativeDriver is true on web useNativeDriver: Platform.OS !== 'web' && isNativeAnimationSupported(), isInteraction: false, }); } // Reset timer to the beginning timer.setValue(0); Animated.loop(indeterminateAnimation.current).start(); } else { Animated.timing(timer, { duration: 200 * scale, toValue: progress ? progress : 0, useNativeDriver: isNativeAnimationSupported(), isInteraction: false, }).start(); } }, [scale, timer, progress, indeterminate, fade]); const stopAnimation = React.useCallback(() => { // Stop indeterminate animation if (indeterminateAnimation.current) { indeterminateAnimation.current.stop(); } Animated.timing(fade, { duration: 200 * scale, toValue: 0, useNativeDriver: isNativeAnimationSupported(), isInteraction: false, }).start(); }, [fade, scale]); React.useEffect(() => { if (visible) startAnimation(); else stopAnimation(); }, [visible, startAnimation, stopAnimation]); React.useEffect(() => { // Start animation the very first time when previously the width was unclear if (visible && prevWidth === 0) { startAnimation(); } }, [prevWidth, startAnimation, visible]); const onLayout = (event: LayoutChangeEvent) => { setPrevWidth(width); setWidth(event.nativeEvent.layout.width); }; const tintColor = color || theme.colors.primary; const trackTintColor = setColor(tintColor).alpha(0.38).rgb().string(); return ( {width ? ( ) : null} ); }; const styles = StyleSheet.create({ container: { height: 4, overflow: 'hidden', }, progressBar: { flex: 1, }, }); export default withTheme(ProgressBar);