import { useMemo, useState } from 'react' import type { LiveDriverData, LiveDriverInfo, LivePosition, LiveTelemetry, TrackOutline, } from '../../types' import { buildOutlinePath, canvasToSvg, isOnTrack, normalizeRawPoint, } from '../../lib/trackmap' interface Props { outline?: TrackOutline | null positions: Record telemetry?: Record drivers?: Record driverInfo?: Record loading?: boolean } export function TrackMap({ outline, positions, telemetry = {}, drivers = {}, driverInfo = {}, loading = false, }: Props) { const [selected, setSelected] = useState(null) const outlinePath = useMemo(() => buildOutlinePath(outline?.points ?? []), [outline]) const cars = useMemo(() => { if (!outline?.bounds) return [] return Object.entries(positions) .map(([number, position]) => { const canvas = normalizeRawPoint(position, outline.bounds) return { number, position, svg: canvasToSvg(canvas), info: driverInfo[number], driver: drivers[number], telemetry: telemetry[number], active: isOnTrack(position.status) && !drivers[number]?.Retired, } }) .sort((a, b) => Number(a.number) - Number(b.number)) }, [driverInfo, drivers, outline?.bounds, positions, telemetry]) const selectedCar = selected ? cars.find((car) => car.number === selected) : null if (loading) { return (
Track Map
loading cached circuit outline...
) } if (!outline || !outlinePath) { return (
Track Map
track outline unavailable for this live session
) } return (
Track Map {cars.length ? `${cars.length} cars` : 'waiting for GPS'}
{cars.map((car) => { const label = car.info?.Tla || car.number return ( setSelected(car.number)} onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault() setSelected(car.number) } }} > {label} ) })} {cars.length === 0 &&
waiting for live GPS
} {selectedCar && (
{selectedCar.info?.Tla || selectedCar.number} {selectedCar.position.status || 'OnTrack'}
)}
) } function Metric({ label, value, suffix = '' }: { label: string; value: number | undefined; suffix?: string }) { return ( <>
{label}
{value === undefined ? '-' : `${value}${suffix}`}
) } function teamColor(info: LiveDriverInfo | undefined): string { const raw = info?.TeamColour?.trim() if (!raw) return '#777777' return raw.startsWith('#') ? raw : `#${raw}` }