import { useEffect, useMemo, useState } from 'react' import { BuildingLibraryIcon, ChartBarIcon, ChevronLeftIcon, HeartIcon, MinusIcon, PlusIcon, ShieldCheckIcon, Squares2X2Icon, UsersIcon, } from '@heroicons/react/24/outline' import { InfoHelpTrigger } from './InfoHelpTrigger' import { groupMetricsByTheme, themeIdForMetric } from '../utils/censusMetricGroups' /** * Leading glyph per top-level theme, keyed by the taxonomy's theme id * (../utils/censusMetricGroups). Anything without an explicit entry — including * the trailing "More measures" bucket — falls back to a neutral grid icon. */ const THEME_ICONS: Record = { economy: ChartBarIcon, people: UsersIcon, health: HeartIcon, crime: ShieldCheckIcon, government: BuildingLibraryIcon, } interface CensusMetricBrowserPanelProps { metrics: ReadonlyArray<{ slug: string; label: string }> metricSlug: string metricFullHelp: string onPick: (slug: string) => void /** Collapse the panel back to the bare rail. */ onCollapse: () => void } /** * Persistent, collapsible metric browser that sits beside the map's display * rail. Metrics are organised into two levels — top-level themes (Economy, * People, Health, …) each holding sub-grouped metrics (Income, Housing, …) — * from the shared taxonomy (../utils/censusMetricGroups). The theme owning the * active metric starts expanded so the current selection is always visible. * Collapsed themes show a "+"; expanded themes show "−". Themes with no metrics * yet (Crime, Government) still expand, to an empty placeholder. The header * dropdown remains the small-screen affordance — this panel is desktop-only. */ export default function CensusMetricBrowserPanel({ metrics, metricSlug, metricFullHelp, onPick, onCollapse, }: CensusMetricBrowserPanelProps) { const themes = useMemo(() => groupMetricsByTheme(metrics), [metrics]) // Track which themes are expanded. The theme holding the active metric is // opened on mount and whenever the selection jumps to a different theme (e.g. // via the URL), without stomping a user's manual expand/collapse elsewhere. const activeThemeId = themeIdForMetric(metricSlug) const [open, setOpen] = useState>(() => ({ [activeThemeId]: true })) useEffect(() => { setOpen((prev) => (prev[activeThemeId] ? prev : { ...prev, [activeThemeId]: true })) }, [activeThemeId]) return (
Metrics
{themes.map((t) => { const isOpen = !!open[t.id] const Icon = THEME_ICONS[t.id] ?? Squares2X2Icon return (
{isOpen ? (
{t.groups.length === 0 ? (

No metrics yet

) : ( t.groups.map((g) => (
{g.shortTitle ?? g.title}
    {g.metrics.map((m) => { const selected = m.slug === metricSlug return (
  • ) })}
)) )}
) : null}
) })}
) }