76 lines
2.4 KiB
TypeScript
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>
|
|
);
|
|
}
|