perfect-postcode/frontend/src/components/map/EnumBarChart.tsx
Andras Schmelczer 05a1f316e1
Some checks failed
CI / Check (push) Failing after 2m14s
Build and publish Docker image / build-and-push (push) Failing after 2m38s
More
2026-05-04 17:21:26 +01:00

80 lines
2.9 KiB
TypeScript

import { getEnumValueColor } from '../../lib/consts';
export default function EnumBarChart({
counts,
globalCounts,
featureName,
}: {
counts: Record<string, number>;
globalCounts?: Record<string, number>;
featureName?: string;
}) {
const entries = Object.entries(counts).sort(([, countA], [, countB]) => countB - countA);
const localTotal = entries.reduce((sum, [, c]) => sum + c, 0);
// When global counts are available, normalize both to percentages for comparison
const globalTotal = globalCounts ? Object.values(globalCounts).reduce((sum, c) => sum + c, 0) : 0;
const hasGlobal = globalCounts && globalTotal > 0;
// Compute max percentage across both datasets for consistent bar scaling
const maxPct = entries.reduce((max, [label, count]) => {
const localPct = localTotal > 0 ? count / localTotal : 0;
const globalPct = hasGlobal ? (globalCounts[label] ?? 0) / globalTotal : 0;
return Math.max(max, localPct, globalPct);
}, 0);
// Fallback to raw count scaling when no global data
const maxCount = Math.max(...entries.map(([, count]) => count), 1);
return (
<div className="space-y-1 mt-1">
{entries.map(([label, count]) => {
const localPct = localTotal > 0 ? count / localTotal : 0;
const globalPct = hasGlobal ? (globalCounts[label] ?? 0) / globalTotal : 0;
const localWidth = hasGlobal
? maxPct > 0
? (localPct / maxPct) * 100
: 0
: (count / maxCount) * 100;
const globalWidth = hasGlobal && maxPct > 0 ? (globalPct / maxPct) * 100 : 0;
const overrideColor = featureName ? getEnumValueColor(featureName, label) : null;
const barStyle = overrideColor
? `rgb(${overrideColor[0]},${overrideColor[1]},${overrideColor[2]})`
: undefined;
return (
<div key={label} className="flex items-center gap-2 text-xs">
<span className="w-16 truncate text-warm-500 dark:text-warm-400 text-right shrink-0">
{label}
</span>
<div className="flex-1 h-3 bg-warm-100 dark:bg-navy-700 rounded overflow-hidden relative">
{hasGlobal && (
<div
className="absolute inset-y-0 left-0 bg-warm-300/60 dark:bg-warm-600/60 rounded"
style={{ width: `${globalWidth}%` }}
/>
)}
<div
className={
barStyle
? 'h-full rounded relative'
: 'h-full bg-teal-500 dark:bg-teal-400 rounded relative'
}
style={{
width: `${localWidth}%`,
...(barStyle ? { backgroundColor: barStyle } : {}),
}}
/>
</div>
<span className="w-8 text-warm-500 dark:text-warm-400 text-right shrink-0">
{count}
</span>
</div>
);
})}
</div>
);
}