// aerOS — real SVG charts. Exports to window. All responsive via viewBox.
const { useState: _cS, useId: _cId } = React;
function useUid() { return React.useId ? React.useId().replace(/:/g, '') : Math.random().toString(36).slice(2); }
/* ----------------------------- LineChart (area) ----------------------------- */
function LineChart({ data, height = 220, color = 'var(--brand)', labels, valueFmt = v => v, showDots = true, area = true }) {
const [hover, setHover] = _cS(null);
const uid = useUid();
const W = 600, H = height, padL = 8, padR = 8, padT = 16, padB = 28;
const xs = data.length;
const max = Math.max(...data, 1) * 1.12;
const min = Math.min(...data, 0);
const range = max - min || 1;
const px = i => padL + (i * (W - padL - padR)) / Math.max(1, xs - 1);
const py = v => padT + (1 - (v - min) / range) * (H - padT - padB);
const pts = data.map((v, i) => [px(i), py(v)]);
const line = pts.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
const areaPath = line + ` L${px(xs - 1).toFixed(1)} ${H - padB} L${padL} ${H - padB} Z`;
const gy = [0, 0.25, 0.5, 0.75, 1].map(f => padT + f * (H - padT - padB));
return (
);
}
/* ----------------------------- BarChart ----------------------------- */
function BarChart({ data, height = 220, color = 'var(--brand)', labels, valueFmt = v => v, horizontal = false }) {
const [hover, setHover] = _cS(null);
const max = Math.max(...data.map(d => typeof d === 'object' ? d.value : d), 1) * 1.1;
const vals = data.map(d => typeof d === 'object' ? d : { value: d });
if (horizontal) {
return (
{vals.map((d, i) => (
{labels ? labels[i] : d.label}
{valueFmt(d.value)}
))}
);
}
const W = 600, H = height, padB = 28, padT = 12, gap = 0.34;
const n = vals.length;
const bw = (W / n) * (1 - gap);
return (
);
}
/* ----------------------------- DonutChart ----------------------------- */
function DonutChart({ data, size = 180, thickness = 26, centerLabel, centerValue }) {
const [hover, setHover] = _cS(null);
const total = data.reduce((s, d) => s + d.value, 0) || 1;
const R = size / 2, r = R - thickness / 2;
const C = 2 * Math.PI * r;
let offset = 0;
return (
{hover != null ? data[hover].value : (centerValue ?? total)}
{hover != null ? data[hover].label : centerLabel}
{data.map((d, i) => (
setHover(i)} onMouseLeave={() => setHover(null)} style={{ display: 'flex', alignItems: 'center', gap: 9, cursor: 'pointer', opacity: hover == null || hover === i ? 1 : 0.5, transition: 'opacity 120ms' }}>
{d.label}
{Math.round(d.value / total * 100)}%
))}
);
}
/* ----------------------------- Sparkline ----------------------------- */
function Sparkline({ data, width = 90, height = 30, color = 'var(--brand)' }) {
const max = Math.max(...data, 1), min = Math.min(...data, 0), range = max - min || 1;
const px = i => (i * width) / Math.max(1, data.length - 1);
const py = v => height - ((v - min) / range) * (height - 4) - 2;
const line = data.map((v, i) => (i === 0 ? 'M' : 'L') + px(i).toFixed(1) + ' ' + py(v).toFixed(1)).join(' ');
return (
);
}
Object.assign(window, { LineChart, BarChart, DonutChart, Sparkline });