"use client"; import type { DensityView, MapLayer, TimeMachine } from "@dci/core"; import { DENSITY_VIEWS, MAP_LAYERS } from "@dci/core"; import { Activity, Building2, Clock, Cloud, Cpu, Gauge, Grid2x2, Network, Pause, Play, Share2, Zap } from "lucide-react"; import { useEffect, useRef, useState, type ComponentType } from "react"; import { cn } from "@/components/ui/cn"; import { apiGet, clientApiBase } from "@/lib/api"; import { fmtPct } from "@/lib/format"; import { DENSITY_LABEL, MAP_LAYER_LABEL } from "@/lib/labels"; /* ------------------------------------------------------------------------------------------ Map 2.0 controls: MODES rail (layer=), DENSITY toggle (density=), TIME MACHINE (year=). Pure presentational + URL callbacks; the page owns the state. ------------------------------------------------------------------------------------------ */ const LAYER_ICON: Record> = { facilities: Building2, capacity: Gauge, projects: Activity, ai: Cpu, cloud: Cloud, ixps: Share2, connectivity: Network, power: Zap, pipeline: Grid2x2, }; export const LAYER_HINT: Record = { facilities: "Every indexed facility, coloured by status", capacity: "Facilities with a published MW figure, sized by capacity", projects: "Announced, permitted and under-construction projects (square markers)", ai: "Facilities and projects with AI / HPC evidence", cloud: "Public cloud regions (hollow accent circles) — providers publish the metro, not the building", ixps: "Internet exchange points (triangles)", connectivity: "Carrier hotels, IXPs and cloud on-ramps", power: "Utility-scale loads, grid connections and power events", pipeline: "Pipeline by lifecycle stage", }; export function ModesRail({ layer, onChange, orientation = "vertical", className }: { layer: MapLayer; onChange: (l: MapLayer) => void; orientation?: "vertical" | "horizontal"; className?: string }) { const vertical = orientation === "vertical"; return ( ); } export function DensityToggle({ density, onChange, className }: { density: DensityView | null; onChange: (d: DensityView | null) => void; className?: string }) { return (
Density {density && ( )}
{DENSITY_VIEWS.map((d) => { const active = d === density; return ( ); })}
); } /** Density legend: graduated grid from light to dark (or accent for MW views). */ export function DensityLegend({ density, max }: { density: DensityView; max: number | null }) { const mw = density === "known_mw" || density === "pipeline_mw"; const fmt = (v: number) => (mw ? (v >= 1000 ? `${(v / 1000).toFixed(1)} GW` : `${Math.round(v)} MW`) : String(Math.round(v))); return (
{DENSITY_LABEL[density]} per cell
{[0.06, 0.18, 0.3, 0.42, 0.55].map((o) => ( ))}
0 {max !== null ? fmt(max) : "max"}

{mw ? "Known published MW only — cells without a figure stay empty." : "Counts of indexed records per cell."}

); } const THIS_YEAR = new Date().getUTCFullYear(); /** * Time machine (§20): year slider from earliestReliableYear to now, PLAY steps one year per tick, the caption states * the share of facilities whose opening date is known — so an early year is read as a lower bound, never as history. */ export function TimeMachineControl({ year, onChange, coverage, compact, className }: { year: number | null; onChange: (y: number | null) => void; coverage: number | null; compact?: boolean; className?: string }) { const [tm, setTm] = useState(null); const [playing, setPlaying] = useState(false); const timer = useRef | null>(null); useEffect(() => { const ctrl = new AbortController(); apiGet("/time-machine", undefined, { base: clientApiBase(), signal: ctrl.signal, timeoutMs: 8000 }).then((r) => { if (!ctrl.signal.aborted && r.ok) setTm(r.data); }); return () => ctrl.abort(); }, []); const min = tm?.earliestReliableYear ?? 1995; const max = THIS_YEAR; const y = year ?? max; useEffect(() => { if (!playing) { if (timer.current) clearInterval(timer.current); timer.current = null; return; } timer.current = setInterval(() => { if (y + 1 >= max) { setPlaying(false); onChange(null); } else onChange(y + 1); }, 800); return () => { if (timer.current) clearInterval(timer.current); }; }, [playing, y, min, max, onChange]); const frame = tm?.frames.find((f) => f.year === y) ?? null; const cov = coverage ?? tm?.openingDateCoverage ?? null; return (
Time machine {year !== null && ( )}
{ setPlaying(false); const v = Number(e.target.value); onChange(v >= max ? null : v); }} className="h-9 min-w-0 flex-1 accent-[var(--accent)] sm:h-6" /> {y}
{!compact && (
{year === null ? ( Drag or play to see facilities known open by a given year. ) : ( Facilities known open by {y} {frame && frame.facilities > 0 ? <> · {frame.facilities.toLocaleString("en-US")} facilities{frame.knownMw ? `, ${frame.knownMw >= 1000 ? `${(frame.knownMw / 1000).toFixed(1)} GW` : `${Math.round(frame.knownMw)} MW`} known` : ""} : null} )} {cov !== null && Opening date known for {fmtPct(cov, cov < 0.1 ? 1 : 0)} of facilities — earlier years are lower bounds.}
)}
); }