Jade-Infra-test / src /components /StatsCounter.jsx
rushiljain's picture
Upload 29 files
691cdd0 verified
import React, { useEffect, useRef, useState } from 'react';
export default function StatsCounter({ value, label, duration = 1200, suffix = '', accent = false, decimals = 0 }) {
const [display, setDisplay] = useState(0);
const elRef = useRef(null);
const startedRef = useRef(false);
useEffect(() => {
const el = elRef.current;
if (!el) return;
const onEnter = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !startedRef.current) {
startedRef.current = true;
const start = performance.now();
const target = Number(value);
const step = (t) => {
const p = Math.min(1, (t - start) / duration);
const raw = p * target;
if (decimals > 0) {
setDisplay(Number(raw.toFixed(decimals)));
} else {
setDisplay(Math.floor(raw));
}
if (p < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
}
});
};
const obs = new IntersectionObserver(onEnter, { threshold: 0.4 });
obs.observe(el);
return () => obs.disconnect();
}, [value, duration]);
const formatNumber = (n) => {
if (decimals > 0) {
// Keep fixed decimals but still use locale for thousands if ever needed
return Number(n).toLocaleString(undefined, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
});
}
return Number(n).toLocaleString();
};
const counterContent = (
<span className="text-4xl font-extrabold text-brand-700">
{formatNumber(display)}{suffix}
</span>
);
return (
<div ref={elRef} className="text-center">
{accent ? (
<div className="inline-block rounded-full bg-accent/20 px-6 py-2 shadow-[0_0_18px_rgba(211,155,35,0.35)] backdrop-blur-sm">
{counterContent}
</div>
) : (
counterContent
)}
<div className="mt-3 text-sm text-slate-600">{label}</div>
</div>
);
}