SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
3.0 KB · 54 lines tsx
Raw Blame History
1import { t } from '@/i18n';2import type { CompareState } from '@/lib/compare-state';3import type { CompareSeries, CompareSnapshotRow, CountryLite } from '@/lib/types-compare';4import { subtopicAnchor } from '@/lib/anchors';5import { CollapsibleGroup, SubtopicJumpNav } from '@/components/data/collapsible-group';6import { CompareChart } from './compare-chart';7import { SnapshotCompact } from './snapshot-table';89/** Above this many charts a topic tab is split into collapsible subtopic blocks (first one expanded). */10const SECTION_THRESHOLD = 8;1112export function ChartGrid({ rows, countries, state, eager, maxYear }: { rows: CompareSnapshotRow[]; countries: CountryLite[]; state: CompareState; eager: Map<string, CompareSeries[]>; maxYear: number }) {13  return (14    <div className="grid gap-x-10 gap-y-8 lg:grid-cols-2">15      {rows.map((row) => (16        <CompareChart key={row.indicator.slug} indicator={row.indicator} countries={countries} state={state} initial={eager.get(row.indicator.slug)} snapshot={row.values} maxYear={maxYear} />17      ))}18    </div>19  );20}2122/** Group rows by `indicator.subtopic`, preserving first-seen order. */23export function groupBySubtopic<T extends { indicator: { subtopic: string | null } }>(rows: T[]): Array<{ subtopic: string; rows: T[] }> {24  const m = new Map<string, T[]>();25  for (const r of rows) {26    const k = r.indicator.subtopic ?? t('common.other');27    const cur = m.get(k);28    if (cur) cur.push(r);29    else m.set(k, [r]);30  }31  return Array.from(m, ([subtopic, rs]) => ({ subtopic, rows: rs }));32}3334/**35 * Charts of a compare topic tab. Short tabs → plain grid. Long tabs (> 8 charts, ≥ 2 subtopics) → a jump nav +36 * one CollapsibleGroup per subtopic: the first expanded (charts), the others collapsed showing the latest37 * values inline (SnapshotCompact) so nothing is hidden, just deferred. Charts still lazy-mount on scroll.38 */39export function ChartSections({ rows, countries, slugs, state, eager, maxYear, anchorPrefix }: { rows: CompareSnapshotRow[]; countries: CountryLite[]; slugs: string[]; state: CompareState; eager: Map<string, CompareSeries[]>; maxYear: number; anchorPrefix: string }) {40  const groups = groupBySubtopic(rows);41  if (rows.length <= SECTION_THRESHOLD || groups.length < 2) return <ChartGrid rows={rows} countries={countries} state={state} eager={eager} maxYear={maxYear} />;42  const items = groups.map((g) => ({ id: subtopicAnchor(anchorPrefix, g.subtopic), label: g.subtopic, count: g.rows.length }));43  return (44    <div className="min-w-0">45      <SubtopicJumpNav items={items} label={t('topic.jump')} className="mb-2" />46      {groups.map((g, i) => (47        <CollapsibleGroup key={g.subtopic} id={items[i]!.id} title={g.subtopic} count={g.rows.length} defaultOpen={i === 0} summary={<SnapshotCompact rows={g.rows} countries={countries} slugs={slugs} state={state} />}>48          <ChartGrid rows={g.rows} countries={countries} state={state} eager={eager} maxYear={maxYear} />49        </CollapsibleGroup>50      ))}51    </div>52  );53}54