Radarr/frontend/src/Components/CircularProgressBar.tsx
Bogdan f0019d622a Convert Progress Bars to TypeScript
(cherry picked from commit a9072ac460f971d3da737de6446153d8cbf1e1c2)

Closes #10404
2024-09-04 13:46:41 +03:00

99 lines
2.4 KiB
TypeScript

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<number>(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 (
<div
className={containerClassName}
style={{
width: sizeInPixels,
height: sizeInPixels,
lineHeight: sizeInPixels,
}}
>
<svg
className={className}
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
>
<circle
fill="transparent"
r={radius}
cx={center}
cy={center}
strokeDasharray={circumference}
style={{
stroke: strokeColor,
strokeWidth,
strokeDashoffset,
}}
/>
</svg>
{showProgressText && (
<div className={styles.circularProgressBarText}>{progressText}</div>
)}
</div>
);
}
export default CircularProgressBar;