Move into folders

This commit is contained in:
Andras Schmelczer 2026-02-07 13:34:50 +00:00
parent ee73ab77fd
commit 5cbb180c57
24 changed files with 181 additions and 185 deletions

View file

@ -0,0 +1,83 @@
import { useMemo } from 'react';
import { SEGMENT_COLORS } from '../../lib/consts';
import { formatValue } from '../../lib/format';
interface Segment {
name: string;
value: number;
}
interface StackedBarChartProps {
segments: Segment[];
total: number;
}
/** Strip common suffixes/prefixes to produce short legend labels */
function shortenLabel(name: string): string {
return name
.replace(' (avg/yr)', '')
.replace(/^% /, '')
.replace('and sexual offences', '')
.replace('and arson', '')
.replace('from the person', '')
.replace('Possession of weapons', 'Weapons')
.replace('Anti-social behaviour', 'Anti-social')
.replace('Criminal damage', 'Damage')
.trim();
}
export default function StackedBarChart({ segments, total }: StackedBarChartProps) {
const sortedSegments = useMemo(
() => [...segments].sort((a, b) => b.value - a.value),
[segments]
);
if (total === 0) {
return (
<div className="text-xs text-warm-400 dark:text-warm-500 italic">No data</div>
);
}
return (
<div className="space-y-1.5">
{/* Stacked bar */}
<div className="flex h-4 rounded overflow-hidden bg-warm-200 dark:bg-warm-700">
{sortedSegments.map((segment, i) => {
const pct = (segment.value / total) * 100;
if (pct < 0.5) return null;
return (
<div
key={segment.name}
className="h-full"
style={{
width: `${pct}%`,
backgroundColor: SEGMENT_COLORS[i % SEGMENT_COLORS.length],
}}
title={`${shortenLabel(segment.name)}: ${formatValue(segment.value)} (${pct.toFixed(1)}%)`}
/>
);
})}
</div>
{/* Legend */}
<div className="flex flex-wrap gap-x-3 gap-y-0.5">
{sortedSegments.map((segment, i) => (
<div key={segment.name} className="flex items-center gap-1">
<span
className="w-2 h-2 rounded-sm shrink-0"
style={{
backgroundColor: SEGMENT_COLORS[i % SEGMENT_COLORS.length],
}}
/>
<span className="text-[10px] text-warm-600 dark:text-warm-400">
{shortenLabel(segment.name)}
</span>
<span className="text-[10px] text-warm-400 dark:text-warm-500">
{formatValue(segment.value)}
</span>
</div>
))}
</div>
</div>
);
}