import React, { useCallback, useEffect, useState } from 'react'; import styles from './CircularProgressBar.css'; interface CircularProgressBarProps { className?: string; containerClassName?: string; size?: number; progress: number; strokeWidth?: number; strokeColor?: string; showProgressText?: boolean; } function CircularProgressBar({ className = styles.circularProgressBar, containerClassName = styles.circularProgressBarContainer, size = 60, strokeWidth = 5, strokeColor = '#ffc230', showProgressText = false, progress, }: CircularProgressBarProps) { const [currentProgress, setCurrentProgress] = useState(0); const raf = React.useRef(0); const center = size / 2; const radius = center - strokeWidth; const circumference = Math.PI * (radius * 2); const sizeInPixels = `${size}px`; const strokeDashoffset = ((100 - currentProgress) / 100) * circumference; const progressText = `${Math.round(currentProgress)}%`; const handleAnimation = useCallback( (p: number) => { setCurrentProgress((prevProgress) => { if (prevProgress < p) { return prevProgress + Math.min(1, p - prevProgress); } return prevProgress; }); }, [setCurrentProgress] ); useEffect(() => { if (progress > currentProgress) { cancelAnimationFrame(raf.current); raf.current = requestAnimationFrame(() => handleAnimation(progress)); } }, [progress, currentProgress, handleAnimation]); useEffect( () => { return () => cancelAnimationFrame(raf.current); }, // We only want to run this effect once // eslint-disable-next-line react-hooks/exhaustive-deps [] ); return (
{showProgressText && (
{progressText}
)}
); } export default CircularProgressBar;