TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1'use client';23import { useState } from 'react';4import { compactNumber, fullNumber } from './scale';56export interface BarDatum {7 label: string;8 value: number;9 sublabel?: string;10 color?: string;11 href?: string;12}1314/**15 * Horizontal bar chart (magnitude by category). Bars ≤ 24px thick, 4px rounded data-end, single16 * hue by default, per-mark hover tooltip, direct value label at the tip.17 */18export function BarChart({ data, currency = false, maxBars = 12, ariaLabel, className, percent = false }: { data: BarDatum[]; currency?: boolean; maxBars?: number; ariaLabel: string; className?: string; percent?: boolean }) {19 const [hover, setHover] = useState<number | null>(null);20 const rows = data.slice(0, maxBars);21 if (!rows.length) return <div className={`text-xs text-subtle ${className ?? ''}`}>No data</div>;22 const max = Math.max(...rows.map((r) => Math.abs(r.value)), 1e-9);23 const fmt = (v: number) => (percent ? `${(v * 100).toFixed(1)}%` : compactNumber(v, { currency }));24 const fmtFull = (v: number) => (percent ? `${(v * 100).toFixed(2)}%` : fullNumber(v, { currency }));25 return (26 <div className={`relative ${className ?? ''}`} role="img" aria-label={ariaLabel}>27 <ul className="space-y-1.5">28 {rows.map((r, i) => {29 const w = Math.max(2, (Math.abs(r.value) / max) * 100);30 return (31 <li key={`${r.label}-${i}`} className="grid grid-cols-[minmax(0,9rem)_1fr_auto] items-center gap-2 text-[12px]" onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)}>32 <span className="truncate text-muted" title={r.label}>33 {r.href ? (34 <a href={r.href} className="hover:text-fg">35 {r.label}36 </a>37 ) : (38 r.label39 )}40 </span>41 <span className="relative h-4">42 <span className="absolute inset-y-0 left-0 rounded-r-[4px]" style={{ width: `${w}%`, background: r.color ?? 'var(--ri-index)', opacity: hover === null || hover === i ? 1 : 0.55 }} />43 </span>44 <span className="num w-16 text-right font-medium text-fg" title={fmtFull(r.value)}>45 {fmt(r.value)}46 </span>47 </li>48 );49 })}50 </ul>51 {hover !== null && rows[hover]?.sublabel ? <div className="mt-1 text-[11px] text-subtle">{rows[hover]!.sublabel}</div> : null}52 </div>53 );54}55