import React, { useEffect, useState } from 'react'; import classnames from 'classnames'; import styles from './ProgressRing.scss'; import IReactComponentProps from '../../../common/structures/IReactComponentProps'; interface IProps extends IReactComponentProps { /** Progress as a decimal number (0 to 1.0) **/ progress: number; progressColor?: 'auto' | 'light'; size?: 'large' | 'small'; textColor?: 'auto' | 'light' | 'dark'; } const defaultProps: Partial = { progressColor: 'auto', size: 'large', textColor: 'auto', }; const ProgressRing = (props: IProps) => { const [offset, setOffset] = useState(0); const [isAfterInit, setIsAfterInit] = useState(false); const { children, className, id, progress, progressColor, size, style, textColor, } = props; const sizeNum = size === 'large' ? 28 : 18; const strokeWidth = size === 'large' ? 4 : 2; const center = sizeNum / 2; const radius = sizeNum / 2 - strokeWidth / 2; const circumference = 2 * Math.PI * radius; useEffect(() => { const interval = setTimeout(() => { setIsAfterInit(true); }); return () => clearInterval(interval); }, []); useEffect(() => { // clamp progress value to 0-100 const progressNum = Math.max(0, Math.min(progress * 100, 100)); // incrementally decrease the overall % by using a tail reduction value // this is necessary because rounded stroke cap will otherwise make 95% visually look like 100% // the divisor here is completely arbitrary and whatever makes the ring look "full" at 100% const gradualRadialTailReduction = progressNum / 25; // calculate the svg offset value based on progress and circumference const progressOffset = ((100 - progressNum + gradualRadialTailReduction) / 100) * circumference; setOffset(progressOffset); }, [setOffset, progress, circumference, offset]); return (
{children}
); } ProgressRing.defaultProps = defaultProps; export default ProgressRing;