import { useTheme } from '@emotion/react'; import type { ReactElement } from 'react'; import React, { useEffect, useRef } from 'react'; import { Animated, Easing, View } from 'react-native'; import Svg, { Circle, G } from 'react-native-svg'; import type { ViewProps, StyleProp, ViewStyle } from 'react-native'; import { StyledContainer, StyledDonutCircle } from './StyledProgressCircle'; import Typography from '../Typography'; import { THEME_INTENT_MAP } from './constants'; import { hexToRgba } from '../../utils/helpers'; export interface ProgressCircleProps extends ViewProps { /** * The progress completion percentage: 0-100. */ value: number; /* * Custom displayed value. */ renderValue?: (value: number) => React.ReactNode; /** * Set intent for your progress. */ intent?: | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'archived' | 'primary-inverted' | 'success-inverted' | 'warning-inverted' | 'danger-inverted' | 'info-inverted' | 'archived-inverted'; /** * Additional style. */ style?: StyleProp; /* * Testing id of the component. */ testID?: string; } const defaultRenderValue = (value: number) => `${value}%`; const AnimatedCircle = Animated.createAnimatedComponent(Circle); const ProgressCircle = ({ value, renderValue = defaultRenderValue, intent = 'primary', style, testID, ...nativeProps }: ProgressCircleProps): ReactElement => { const theme = useTheme(); const size = theme.__hd__.progress.sizes.circleDiameter; const strokeWidth = theme.__hd__.progress.sizes.circleCompletenessHeight; const radius = size / 2 - strokeWidth; const circumference = 2 * Math.PI * radius; const progressAnimatedValue = useRef(new Animated.Value(0)).current; const minArc = strokeWidth / 2 / Math.PI; // Minimum arc length useEffect(() => { Animated.timing(progressAnimatedValue, { toValue: value, useNativeDriver: true, easing: Easing.inOut(Easing.ease), }).start(); }, [value]); const strokeDashoffset = progressAnimatedValue.interpolate({ inputRange: [0, 100], outputRange: [circumference - minArc, 0], // Full circle to zero offset }); return ( {/* rotation -90 in order to move start point from 3 o'clock to 12 o'clock */} {/* Background Circle */} {/* Progress Circle */} {renderValue(value)} ); }; export default ProgressCircle;