spb/company-atlas
Public
Python 66.3%
TypeScript 22.7%
JavaScript 8.6%
HTML 1.4%
CSS 0.7%
1import Link from 'next/link';2import type { ReactNode } from 'react';3import { cn } from '@/lib/cn';4import { fmtInt } from '@/lib/format';56/**7 * Compact heatmap table (rows × columns) with sequential intensity from the accent hue. Server-renderable.8 * Cells with null are drawn hollow, never as 0.9 */10export function Heatmap({ rows, cols, get, rowHref, format = fmtInt, className, colLabel = (c) => c }: { rows: { key: string; label: ReactNode }[]; cols: string[]; get: (row: string, col: string) => number | null; rowHref?: (row: string) => string | undefined; format?: (v: number) => string; className?: string; colLabel?: (c: string) => ReactNode }) {11 let max = 0;12 for (const r of rows) for (const c of cols) max = Math.max(max, get(r.key, c) ?? 0);13 return (14 <div className={cn('table-scroll', className)}>15 <table className="w-full border-separate border-spacing-[2px] text-xs">16 <thead>17 <tr>18 <th className="sticky left-0 z-[2] bg-canvas text-left font-medium text-ink-2" />19 {cols.map((c) => (20 <th key={c} className="whitespace-nowrap px-1 pb-1 text-center font-medium text-ink-3">21 {colLabel(c)}22 </th>23 ))}24 </tr>25 </thead>26 <tbody>27 {rows.map((r) => {28 const href = rowHref?.(r.key);29 return (30 <tr key={r.key}>31 <th className="sticky left-0 z-[2] max-w-[10rem] truncate bg-canvas pr-2 text-left font-medium text-ink-2">32 {href ? (33 <Link href={href} className="hover:text-accent">34 {r.label}35 </Link>36 ) : (37 r.label38 )}39 </th>40 {cols.map((c) => {41 const v = get(r.key, c);42 const a = v === null || max === 0 ? 0 : 0.08 + 0.82 * Math.sqrt(v / max);43 return (44 <td key={c} className={cn('tnum h-7 min-w-[2.4rem] rounded-[2px] text-center', v === null && 'border border-dashed border-rule text-ink-3')} style={v === null ? undefined : { background: `color-mix(in srgb, var(--accent) ${Math.round(a * 100)}%, var(--surface-2))`, color: a > 0.55 ? 'var(--accent-ink)' : 'var(--ink)' }} title={v === null ? 'no data' : `${format(v)}`}>45 {v === null ? '·' : v === 0 ? '' : format(v)}46 </td>47 );48 })}49 </tr>50 );51 })}52 </tbody>53 </table>54 </div>55 );56}57