// Small reusable visual widgets (icons, sparklines, funnel rows, etc.)
// Expose React hooks globally so all Babel scripts share them safely.
window.useState = React.useState;
window.useEffect = React.useEffect;
window.useRef = React.useRef;
window.useMemo = React.useMemo;
// ============ Icons (Lucide-style inline SVGs) ============
function Icon({ name, size = 16, strokeWidth = 2, className = "" }) {
const paths = {
home: <>>,
inbox: <>>,
pipeline: <>>,
campaign: <>>,
persona: <>>,
analytics: <>>,
settings: <>>,
bell: <>>,
search: <>>,
plus: <>>,
send: <>>,
reply: <>>,
sparkles: <>>,
arrow_up: <>>,
arrow_down: <>>,
arrow_right: <>>,
pause: <>>,
play: <>>,
filter: <>>,
download: <>>,
refresh: <>>,
check: <>>,
x: <>>,
flag: <>>,
sliders: <>>,
bolt: <>>,
user_plus: <>>,
clock: <>>,
mail: <>>,
pen: <>>,
sparkle: <>>,
cpu: <>>,
log: <>>,
menu: <>>,
arrow_left: <>>,
};
return (
);
}
// ============ Sparkline ============
function Sparkline({ data, height = 40, color }) {
if (!data || !data.length) return null;
const w = 100, h = 100;
const min = Math.min(...data), max = Math.max(...data);
const range = max - min || 1;
const points = data.map((v, i) => [
(i / (data.length - 1)) * w,
h - ((v - min) / range) * h * 0.9 - 5,
]);
const line = points.map((p, i) => `${i ? 'L' : 'M'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' ');
const area = `${line} L${w},${h} L0,${h} Z`;
return (
);
}
// ============ Funnel ============
function Funnel({ rows }) {
const max = rows[0].value;
return (
{rows.map((r) => (
{r.label}
{r.value.toLocaleString()}
{(r.pct * 100).toFixed(r.pct < 0.01 ? 2 : 1)}%
))}
);
}
// ============ Status badge ============
function Status({ kind, children }) {
return {children || kind};
}
// ============ Tiny ring meter ============
function Ring({ pct = 0.5, size = 36, stroke = 4, color = "var(--primary)", label }) {
const r = (size - stroke) / 2;
const c = 2 * Math.PI * r;
const off = c * (1 - pct);
return (
);
}
Object.assign(window, { Icon, Sparkline, Funnel, Status, Ring });