Files
GMW/services/frontend/src/components/charts/donut.tsx
T

51 lines
1.6 KiB
TypeScript
Raw Normal View History

/** Stacked donut for moderation overview / composition. */
export function Donut({
segments,
size = 132,
thickness = 14,
centerLabel,
centerSub,
}: {
segments: { value: number; color: string; label: string }[];
size?: number;
thickness?: number;
centerLabel?: string;
centerSub?: string;
}) {
const total = segments.reduce((s, x) => s + x.value, 0) || 1;
const r = size / 2 - thickness / 2;
const c = 2 * Math.PI * r;
let offset = 0;
return (
<div className="relative inline-flex items-center justify-center" style={{ width: size, height: size }}>
<svg width={size} height={size} className="-rotate-90">
<circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--color-hairline)" strokeWidth={thickness} />
{segments.map((s, i) => {
const len = (s.value / total) * c;
const el = (
<circle
key={i}
cx={size / 2}
cy={size / 2}
r={r}
fill="none"
stroke={s.color}
strokeWidth={thickness}
strokeDasharray={`${len} ${c - len}`}
strokeDashoffset={-offset}
style={{ transition: "stroke-dashoffset 0.6s ease" }}
/>
);
offset += len;
return el;
})}
</svg>
{(centerLabel || centerSub) && (
<div className="absolute inset-0 flex flex-col items-center justify-center">
{centerLabel && <span className="display text-lg">{centerLabel}</span>}
{centerSub && <span className="mono text-[0.6rem] text-ink-faint">{centerSub}</span>}
</div>
)}
</div>
);
}