'use client'; import { useMemo, useState } from 'react'; import { t } from '@/i18n'; import { formatValue } from '@/lib/format'; import type { FormatSpec } from '@/lib/types'; import { ChoroplethView, type ChoroplethFeature } from '@/components/charts/choropleth-view'; import { SourceLine } from '@/components/charts/source-line'; import { YearSlider } from '@/components/controls/year-slider'; import type { BaseFeature } from '@/components/indicators/indicator-map'; import type { Provenance } from '@/lib/types'; export interface StoryFrame { year: number; values: Record; n: number; } function classIndex(value: number, breaks: number[]): number { let i = 0; while (i < breaks.length && value >= breaks[i]!) i++; return i; } /** * Story map: a handful of year frames (server-fetched) behind one year slider with play, a legend shared by every * frame (pooled quantile breaks computed server-side) so colours mean the same thing in 1960 and today. */ export function StoryMapFrames({ geometry, sphere, frames, breaks, min, max, spec, provenance }: { geometry: BaseFeature[]; sphere: string; frames: StoryFrame[]; breaks: number[]; min: number | null; max: number | null; spec: FormatSpec; provenance: Provenance | null }) { const years = frames.map((f) => f.year); const [year, setYear] = useState(years[years.length - 1] ?? 0); const frame = frames.find((f) => f.year === year) ?? frames[frames.length - 1]; const k = breaks.length + 1; const features: ChoroplethFeature[] = useMemo( () => geometry.map((g) => { const v = g.iso3 && frame ? frame.values[g.iso3] : undefined; return { ...g, value: typeof v === 'number' ? v : null, cls: typeof v === 'number' ? classIndex(v, breaks) : null }; }), [geometry, frame, breaks], ); const legend = Array.from({ length: k }, (_, i) => { const lo = i === 0 ? min : breaks[i - 1]!; const hi = i === k - 1 ? max : breaks[i]!; return { cls: i, label: `${formatValue(lo, spec)} – ${formatValue(hi, spec)}` }; }); const summary = frame ? t('chart.summary.map', { name: spec.name ?? '', year: frame.year, n: frame.n, min: formatValue(min, spec), max: formatValue(max, spec) }) : t('chart.noData'); return (
{frame ? t('indicator.map.n', { n: frame.n }) : ''}

{t('stories.block.mapHint')}

); }