import Link from 'next/link'; import { t } from '@/i18n'; import { cn } from '@/lib/cn'; import { formatValue, isNum, ordinal } from '@/lib/format'; import { routes } from '@/lib/site'; import type { FormatSpec as Spec, Provenance } from '@/lib/types'; import { MARK } from './palette'; export interface RankedBarRow { id: string; label: string; flag?: string | null; href?: string | null; value: number | null; rank?: number | null; /** Secondary text shown right of the value (e.g. change); hidden on phones. */ hint?: string | null; /** Observation year — pass it only when it is older than the list's reference year (freshness honesty); always visible. */ year?: number | null; } /** * Horizontal ranked bars rendered as HTML (server component, fully responsive, no SVG text scaling). * One series → one colour (slot 1); `highlightId` marks the country of interest with the accent and bold label. * Bars ≤ 24 px thick, 4 px rounded data-end, square at the baseline; value labelled at the tip (text tokens). */ export function RankedBars({ rows, spec, highlightId, showRank = true, className, provenance, ariaLabel, linkRows = true, }: { rows: RankedBarRow[]; spec: Spec; highlightId?: string | null; showRank?: boolean; className?: string; provenance?: Provenance | null; ariaLabel?: string; linkRows?: boolean; }) { const values = rows.map((r) => r.value).filter(isNum); const max = values.length ? Math.max(...values.map(Math.abs)) : 0; const top = rows[0]; const summary = top ? t('chart.summary.bars', { top: top.label, value: formatValue(top.value, spec), n: rows.length }) : t('chart.noData'); if (rows.length === 0) return
{t('chart.noData')}
; return ({t('common.source')}: {[provenance.source_name, provenance.dataset].filter(Boolean).join(' — ')} · {provenance.series_code}
) : null}