'use client'; import React, { useEffect, useRef, useState } from 'react'; import { ComponentShareButton } from './ComponentShareButton'; import { Reveal } from './Reveal'; interface StatItem { value: string; label: string; subtext?: string; } interface StatsGridProps { /** * Pipe-delimited stats. Each stat: "value|label|subtext" separated by ~ * Example: "53%|Mehr Umsatz|Rakuten 24~33%|Conversion Boost|nach CWV Fix" */ stats: string; className?: string; } function parseStats(raw: string): StatItem[] { return raw .split('~') .map(s => s.trim()) .filter(Boolean) .map(s => { const [value = '', label = '', subtext] = s.split('|').map(p => p.trim()); return { value, label, subtext }; }); } function AnimatedValue({ value, isVisible }: { value: string; isVisible: boolean }) { const [display, setDisplay] = useState(''); const numMatch = value.match(/^([+-]?)(\d+(?:[.,]\d+)?)(.*)/); const prefix = numMatch?.[1] ?? ''; const numStr = numMatch?.[2] ?? ''; const suffix = numMatch?.[3] ?? value; const target = parseFloat(numStr.replace(',', '.')) || 0; const hasDecimals = numStr.includes('.') || numStr.includes(','); useEffect(() => { if (!isVisible || !numStr) { setDisplay(value); return; } const duration = 1500; const steps = 60; const stepTime = duration / steps; let step = 0; const timer = setInterval(() => { step++; const progress = Math.min(step / steps, 1); // Ease out expo const eased = 1 - Math.pow(2, -10 * progress); const current = target * eased; const formatted = hasDecimals ? current.toFixed(1) : Math.round(current).toString(); setDisplay(`${prefix}${formatted}${suffix}`); if (step >= steps) { clearInterval(timer); setDisplay(value); } }, stepTime); return () => clearInterval(timer); }, [isVisible, value, prefix, suffix, target, hasDecimals, numStr]); return <>{display || value}; } const gradients = [ 'from-blue-500/10 to-indigo-500/5', 'from-emerald-500/10 to-teal-500/5', 'from-violet-500/10 to-purple-500/5', 'from-amber-500/10 to-orange-500/5', ]; export const StatsGrid: React.FC = ({ stats, className = '' }) => { if (!stats || typeof stats !== 'string') return null; const items = parseStats(stats); const ref = useRef(null); const [isVisible, setIsVisible] = useState(false); const shareId = `statsgrid-${React.useId().replace(/:/g, "")}`; useEffect(() => { const el = ref.current; if (!el) return; const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setIsVisible(true); observer.disconnect(); } }, { threshold: 0.2 } ); observer.observe(el); return () => observer.disconnect(); }, []); const cols = (items?.length || 0) <= 2 ? 'grid-cols-2' : (items?.length || 0) === 3 ? 'grid-cols-3' : 'grid-cols-2 md:grid-cols-4'; return (
{/* Ambient Glow for the entire grid */}
{/* Main Grid Container */}
{/* Share Button top right */}
{items.map((item, i) => (
{item.label} {item.subtext && ( {item.subtext} )} {/* Item-specific ambient glow on hover */}
))}
); };