// ===== SHARED UI COMPONENTS =====
const { useState, useEffect, useRef } = React;
function Card({ t, children, style, onClick, pad = 16 }) {
return (
{children}
);
}
function Badge({ t, color, children, style }) {
return (
{children}
);
}
// Slightly lighten a hex color for legibility on dark bg
function lighten(hex) {
try {
const n = parseInt(hex.slice(1), 16);
let r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;
r = Math.min(255, Math.round(r + (255 - r) * 0.35));
g = Math.min(255, Math.round(g + (255 - g) * 0.35));
b = Math.min(255, Math.round(b + (255 - b) * 0.35));
return `rgb(${r},${g},${b})`;
} catch (e) { return hex; }
}
function ProgressRing({ pct, size = 120, stroke = 12, color = '#2556a8', track = '#e6eaf2', children, animate = true }) {
const r = (size - stroke) / 2;
const circ = 2 * Math.PI * r;
const [shown, setShown] = useState(animate ? 0 : pct);
useEffect(() => {
if (!animate) { setShown(pct); return; }
const id = requestAnimationFrame(() => setShown(pct));
return () => cancelAnimationFrame(id);
}, [pct, animate]);
const offset = circ - (shown / 100) * circ;
return (
);
}
function Pill({ active, color, t, children, onClick, style }) {
const c = color || t.mid;
return (
);
}
function ProgressBar({ pct, color, t, height = 8 }) {
const [w, setW] = useState(0);
useEffect(() => { const id = requestAnimationFrame(() => setW(pct)); return () => cancelAnimationFrame(id); }, [pct]);
return (
);
}
// Screen wrapper: title + scrollable body
function ScreenHeader({ t, title, subtitle, right }) {
return (
{title}
{subtitle &&
{subtitle}
}
{right}
);
}
function SectionTitle({ t, children, action }) {
return (
{children}
{action}
);
}
// Icon tile with soft tinted background
function IconTile({ icon, color, t, size = 44, radius = 14, fontSize = 22 }) {
return (
{icon}
);
}
// Primary action button
function Btn({ t, children, onClick, color, full, variant = 'solid', style, icon }) {
const c = color || t.mid;
const base = {
border: 'none', borderRadius: 14, padding: '14px 18px',
fontSize: 15, fontWeight: 700, minHeight: 48,
display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
width: full ? '100%' : 'auto', transition: 'transform 0.12s ease, opacity 0.2s',
};
const variants = {
solid: { background: c, color: '#fff' },
soft: { background: c + (t.dark ? '33' : '1c'), color: t.dark ? lighten(c) : c },
outline: { background: 'transparent', color: t.text, border: `1.5px solid ${t.border}` },
};
return (
);
}
function Divider({ t }) {
return ;
}
Object.assign(window, { Card, Badge, ProgressRing, ProgressBar, Pill, ScreenHeader, SectionTitle, IconTile, Btn, Divider, lighten });