// components.jsx — primitivos de UI compartilhados const { useState, useEffect, useRef } = React; /* ---------- Ícones (formas simples / abstratas) ---------- */ function Icon({ name, size = 24, stroke = 2, className }) { const p = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round', className }; switch (name) { case 'home': return ; case 'flame': return ; case 'avatar': return ; case 'chart': return ; case 'play': return ; case 'pause': return ; case 'check': return ; case 'arrow': return ; case 'back': return ; case 'plus': return ; case 'minus': return ; case 'next': return ; case 'music': return ; case 'bell': return ; case 'fire': return ; case 'clock': return ; case 'bolt': return ; // glyphs de objetivo case 'push': return ; case 'bar': return ; case 'core': return ; case 'burn': return ; case 'power': return ; // glyphs de badge (abstratos) case 'diamond':return ; case 'crown': return ; case 'peak': return ; case 'balance':return ; case 'fist': return ; case 'target': return ; default: return null; } } /* ---------- Status bar (relógio dinâmico) ---------- */ function StatusBar() { const [time, setTime] = useState(''); useEffect(() => { const t = () => setTime(new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })); t(); const id = setInterval(t, 1000 * 20); return () => clearInterval(id); }, []); return (
{time}
); } /* ---------- Phone frame (auto-fit to viewport) ---------- */ function PhoneFrame({ children }) { const [scale, setScale] = useState(1); useEffect(() => { const fit = () => { const m = 24; const s = Math.min(1, (window.innerHeight - m * 2) / 866, (window.innerWidth - m * 2) / 412); setScale(s > 0 ? s : 1); }; fit(); window.addEventListener('resize', fit); return () => window.removeEventListener('resize', fit); }, []); return (
{children}
); } /* ---------- Bottom nav ---------- */ function BottomNav({ active, onNav }) { const items = [ { id: 'dashboard', icon: 'home', label: 'Início' }, { id: 'workout', icon: 'bolt', label: 'Treino' }, { id: 'avatar', icon: 'avatar', label: 'Zenith' }, { id: 'history', icon: 'chart', label: 'Progresso' }, ]; return ( ); } /* ---------- Progress ring ---------- */ function ProgressRing({ size = 220, stroke = 12, progress = 0, track = 'var(--line)', color = 'var(--accent)', glow = true, children, animate = true }) { const r = (size - stroke) / 2; const c = 2 * Math.PI * r; const off = c * (1 - Math.max(0, Math.min(1, progress))); return (
{children}
); } /* ---------- Avatar Zenith (placeholder marcado dentro de aura) ---------- */ function AvatarSlot({ size = 200, level = 1, progress = 0.4, pulse = true }) { return (
{/* aura/glow */}
ARTE ZENITH NÍVEL {level} drop da arte aqui
); } /* ---------- Badge ---------- */ function BadgeChip({ badge, earned }) { return (
{badge.name}
{badge.desc}
); } Object.assign(window, { Icon, StatusBar, PhoneFrame, BottomNav, ProgressRing, AvatarSlot, BadgeChip });