SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
2.6 KB · 57 lines tsx
Raw Blame History
1'use client';23import dynamic from 'next/dynamic';4import { useMemo, useState } from 'react';5import { LevelLegend } from '@/components/ui/primitives';6import { useLive } from '@/lib/live';7import type { Country, Front, Incident, LatencyPair, Probe, Region } from '@/lib/types';8import { ModeSelector } from './ModeSelector';9import { type MapMode } from './modes';1011const WorldMap = dynamic(() => import('./WorldMap').then((m) => m.WorldMap), {12  ssr: false,13  loading: () => <div className="flex h-full w-full items-center justify-center bg-neutral text-[11px] text-ink-3">Loading map…</div>,14});1516export interface MapData {17  regions: Region[] | null;18  countries: Country[] | null;19  probes: Probe[] | null;20  fronts: Front[] | null;21  incidents: Incident[] | null;22  matrix: LatencyPair[] | null;23}2425/** Client island: the map itself is lazy-loaded (no SSR); live slices override the SSR data. */26export function MapIsland({ initial, height = 'h-[300px] sm:h-[420px] lg:h-[520px]', initialMode = 'pressure' }: { initial: MapData; height?: string; initialMode?: MapMode }) {27  const [mode, setMode] = useState<MapMode>(initialMode);28  const liveRegions = useLive((s) => s.regions);29  const liveFronts = useLive((s) => s.fronts);30  const liveIncidents = useLive((s) => s.incidents);31  const liveCountries = useLive((s) => s.countries);3233  // Memoised so the map's data effect only re-runs when a real regional update arrived.34  const countries = useMemo(() => {35    if (!initial.countries) return null;36    if (!liveCountries) return initial.countries;37    const byCc = new Map(liveCountries.map((x) => [x.cc, x]));38    return initial.countries.map((c) => {39      const u = byCc.get(c.cc);40      return u ? { ...c, pressure: u.pressure, level: u.level, delta_1h: u.delta_1h } : c;41    });42  }, [initial.countries, liveCountries]);4344  return (45    <div>46      <ModeSelector mode={mode} onChange={setMode} />47      <div className={`relative mt-2 w-full overflow-hidden rounded-[4px] border border-line bg-neutral ${height}`}>48        <WorldMap mode={mode} regions={liveRegions ?? initial.regions} countries={countries} probes={initial.probes} fronts={liveFronts ?? initial.fronts} incidents={liveIncidents ?? initial.incidents} matrix={initial.matrix} />49      </div>50      <div className="mt-2 flex flex-wrap items-center justify-between gap-2">51        <LevelLegend compact />52        <span className="text-[10.5px] text-ink-3">{mode === 'probes' ? 'Arc thickness = |z| of the inter-region latency matrix' : mode === 'incidents' ? 'Markers = incidents with a geographic scope' : 'Countries coloured only where we hold measurements'}</span>53      </div>54    </div>55  );56}57