import React, { useEffect, useRef, useState } from 'react'; import { useSite } from '../../api'; import { useLang, formatNumber } from '../../i18n/lang'; import { useFieldSaver } from '../../admin/AdminContext'; import { AddItemButton, EditableItem } from '../../admin/Editable'; import { InlineEdit } from '../admin/InlineEdit'; function useCounter(targetNumber: number, duration: number = 2000) { const [count, setCount] = useState(0); const ref = useRef(null); useEffect(() => { const element = ref.current; if (!element) return; const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting) { observer.disconnect(); let startTime: number | null = null; const step = (timestamp: number) => { if (!startTime) startTime = timestamp; const progress = timestamp - startTime; // ease-out cubic const easeOut = progress >= duration ? 1 : 1 - Math.pow(1 - progress / duration, 3); setCount(Math.floor(easeOut * targetNumber)); if (progress < duration) { window.requestAnimationFrame(step); } else { setCount(targetNumber); } }; window.requestAnimationFrame(step); } }, { threshold: 0.1 } ); observer.observe(element); return () => observer.disconnect(); }, [targetNumber, duration]); return { count, ref }; } function Counter({ value }: { value: string | number }) { const { lang } = useLang(); const strValue = String(value); // Match the first sequence of digits const numMatch = strValue.match(/(\d+)/); const targetNumber = numMatch ? parseInt(numMatch[0], 10) : 0; const { count, ref } = useCounter(targetNumber, 1500); // If there's a number, replace it with the animated (and localized) count, otherwise render string as is. const formattedDisplay = formatNumber(count, lang); const renderValue = numMatch ? strValue.replace(numMatch[0], formattedDisplay) : strValue; return {renderValue}; } export function Stats() { const { stats } = useSite(); const { t } = useLang(); const save = useFieldSaver(); return (
{stats.map((stat) =>

save('stats', stat.id, { suffix: v }, (d) => { const s = d.stats.find((x) => x.id === stat.id); if (s) s.suffix = v; })} className="ltr:ml-1 rtl:mr-1 text-2xl" />

save('stats', stat.id, { label: v }, (d) => { const s = d.stats.find((x) => x.id === stat.id); if (s) s.label = v; })} />

)}
); }