Import source: src/components/StatsBar

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:05:54 +03:30
parent c12cbc0a8d
commit a924032605
+78
View File
@@ -0,0 +1,78 @@
'use client';
import { useState, useEffect, useRef } from 'react';
import { useInView } from 'framer-motion';
import { useLocale, useTranslations } from 'next-intl';
type CountUpProps = {
end: number;
duration?: number;
};
function CountUp({ end, duration = 1800 }: CountUpProps) {
const [count, setCount] = useState(0);
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-50px' });
const locale = useLocale();
useEffect(() => {
if (!isInView) return;
let startTime: number | null = null;
const startValue = 0;
const animate = (timestamp: number) => {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing function: easeOutQuad
const easeProgress = progress * (2 - progress);
const current = Math.floor(easeProgress * (end - startValue) + startValue);
setCount(current);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
setCount(end);
}
};
requestAnimationFrame(animate);
}, [isInView, end, duration]);
return <span ref={ref}>{count.toLocaleString(locale === 'fa' ? 'fa-IR' : 'en-US')}</span>;
}
export default function StatsBar() {
const t = useTranslations('Stats');
const stats = [
{ value: 28, key: 'articles' },
{ value: 118280, key: 'views' },
{ value: 4789, key: 'reactions' },
];
return (
<section className="w-full bg-sand/20 dark:bg-charcoal/20 border-y border-sand/40 dark:border-charcoal/40 py-8 transition-colors duration-300">
<div className="max-w-[1200px] mx-auto px-6 grid grid-cols-1 md:grid-cols-3 gap-8 text-center">
{stats.map((stat, idx) => (
<div
key={stat.key}
className={`flex flex-col items-center justify-center ${
idx !== stats.length - 1 ? 'md:border-r border-sand/50 dark:border-charcoal/50 rtl:md:border-r-0 rtl:md:border-l' : ''
}`}
>
<div className="text-3xl md:text-5xl font-black text-teal-brand dark:text-teal-brand tracking-tight mb-2">
<CountUp end={stat.value} />
</div>
<div className="text-xs uppercase tracking-widest text-charcoal/60 dark:text-warm-white/50 font-semibold">
{t(stat.key)}
</div>
</div>
))}
</div>
</section>
);
}