● Zion Boggan
repos/Docket/components/party-split-bar.tsx
zionboggan.com ↗
56 lines · tsx
History for this file →
1
import { partyToken, type PartyCode } from "@/lib/neutrality";
2
import { clsx } from "@/lib/cx";
3
 
4
export type PartySlice = { party: PartyCode; count: number };
5
 
6
 
7
export function PartySplitBar({
8
  slices,
9
  className,
10
  ariaLabel,
11
}: {
12
  slices: PartySlice[];
13
  className?: string;
14
  ariaLabel: string;
15
}) {
16
  const order: PartyCode[] = ["D", "I", "R"];
17
  const sorted = [...slices].sort(
18
    (a, b) => order.indexOf(a.party) - order.indexOf(b.party)
19
  );
20
  const total = Math.max(1, sorted.reduce((n, s) => n + s.count, 0));
21
 
22
  return (
23
    <div className={clsx("w-full", className)}>
24
      <div
25
        className="flex h-2 w-full overflow-hidden rounded-[2px] border border-line"
26
        role="img"
27
        aria-label={ariaLabel}
28
      >
29
        {sorted.map((s) => (
30
          <span
31
            key={s.party}
32
            style={{
33
              width: `${(s.count / total) * 100}%`,
34
              backgroundColor: partyToken(s.party),
35
            }}
36
          />
37
        ))}
38
      </div>
39
      <div className="mt-2 flex flex-wrap gap-x-4 gap-y-1 font-mono text-[12px]">
40
        {sorted.map((s) => (
41
          <span key={s.party} className="inline-flex items-center gap-1.5">
42
            <span
43
              aria-hidden="true"
44
              className="inline-block h-1.5 w-1.5 rounded-full"
45
              style={{ backgroundColor: partyToken(s.party) }}
46
            />
47
            <span className="label">
48
              {s.party === "D" ? "Dem." : s.party === "R" ? "Rep." : "Ind."}
49
            </span>
50
            <span className="tabular-nums text-paper">{s.count}</span>
51
          </span>
52
        ))}
53
      </div>
54
    </div>
55
  );
56
}