65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
import Typography, { ETypo, ETypoColor } from "../Typography";
|
|
import classes from "./classes.module.scss";
|
|
import classNames from "classnames";
|
|
|
|
type IProps = {
|
|
percentage: number;
|
|
className?: string;
|
|
};
|
|
|
|
export default function CircleProgress(props: IProps) {
|
|
const { percentage, className } = props;
|
|
|
|
const [animatedProgress, setAnimatedProgress] = useState(0);
|
|
const requestRef = useRef<number>();
|
|
|
|
const animate = useCallback(() => {
|
|
setAnimatedProgress((prev) => {
|
|
if (prev < percentage) {
|
|
return prev + 1;
|
|
} else {
|
|
if (requestRef.current) {
|
|
cancelAnimationFrame(requestRef.current);
|
|
}
|
|
return prev;
|
|
}
|
|
});
|
|
requestRef.current = requestAnimationFrame(animate);
|
|
}, [percentage]);
|
|
|
|
useEffect(() => {
|
|
setAnimatedProgress(0); // Reset progress
|
|
requestRef.current = requestAnimationFrame(animate);
|
|
return () => {
|
|
if (requestRef.current) {
|
|
cancelAnimationFrame(requestRef.current);
|
|
}
|
|
};
|
|
}, [percentage, animate]);
|
|
|
|
const radius = 11;
|
|
const circumference = 2 * Math.PI * radius;
|
|
const offset = circumference - (animatedProgress / 100) * circumference;
|
|
|
|
return (
|
|
<div className={classNames(classes["root"], className)}>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27" viewBox="0 0 27 27" fill="none">
|
|
<circle className={classes["circleBackground"]} cx="13.5" cy="13.5" r={radius} strokeWidth="3" />
|
|
<circle
|
|
className={classes["circleProgress"]}
|
|
cx="13.5"
|
|
cy="13.5"
|
|
r={radius}
|
|
strokeWidth="3"
|
|
strokeDasharray={circumference}
|
|
strokeDashoffset={offset}
|
|
/>
|
|
</svg>
|
|
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_950}>
|
|
{Math.round(percentage)}%
|
|
</Typography>
|
|
</div>
|
|
);
|
|
}
|