perfect-postcode/frontend/src/components/map/StackedEnumChart.tsx

76 lines
2.4 KiB
TypeScript

import type { EnumFeatureStats } from '../../types';
interface StackedEnumChartProps {
components: { label: string; stats: EnumFeatureStats }[];
valueOrder: string[];
valueColors: string[];
}
/** Strip common suffixes to produce short row labels */
function shortenLabel(name: string): string {
return name.replace(/ risk$/, '');
}
export default function StackedEnumChart({
components,
valueOrder,
valueColors,
}: StackedEnumChartProps) {
const visibleRows = components.filter(({ stats }) => {
const total = Object.values(stats.counts).reduce((a, b) => a + b, 0);
if (total === 0) return false;
const lowCount = stats.counts[valueOrder[0]] ?? 0;
return total - lowCount > 0;
});
if (visibleRows.length === 0) {
return <div className="text-xs text-warm-400 dark:text-warm-500 italic mt-1">All low</div>;
}
return (
<div className="space-y-1.5">
{visibleRows.map(({ label, stats }) => {
const total = Object.values(stats.counts).reduce((a, b) => a + b, 0);
return (
<div key={label} className="flex items-center gap-2 text-xs">
<span className="w-24 truncate text-warm-500 dark:text-warm-400 text-right shrink-0">
{shortenLabel(label)}
</span>
<div className="flex-1 flex h-3.5 rounded overflow-hidden bg-warm-200 dark:bg-warm-700">
{valueOrder.map((value, i) => {
const count = stats.counts[value] ?? 0;
const pct = (count / total) * 100;
if (pct < 0.5) return null;
return (
<div
key={value}
className="h-full"
style={{
width: `${pct}%`,
backgroundColor: valueColors[i],
}}
title={`${value}: ${count} (${pct.toFixed(0)}%)`}
/>
);
})}
</div>
</div>
);
})}
{/* Legend */}
<div className="flex gap-x-3 gap-y-0.5 justify-center">
{valueOrder.map((value, i) => (
<div key={value} className="flex items-center gap-1">
<span
className="w-2 h-2 rounded-sm shrink-0"
style={{ backgroundColor: valueColors[i] }}
/>
<span className="text-[10px] text-warm-600 dark:text-warm-400">{value}</span>
</div>
))}
</div>
</div>
);
}