// 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 (
);
}
/* ---------- 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 (
);
}
/* ---------- 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 (
);
}
/* ---------- 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 });