import { useRef } from "react"; import type { OrbitPassInfo } from "../tgu-map-2d/geometry/passes"; type MapPassSelectorProps = { passes: OrbitPassInfo[]; fromIndex: number; toIndex: number; onFromChange: (idx: number) => void; onToChange: (idx: number) => void; }; function utcHM(ms: number): string { const d = new Date(ms); return `${String(d.getUTCHours()).padStart(2, "0")}:${String(d.getUTCMinutes()).padStart(2, "0")}`; } function utcDM(ms: number): string { const d = new Date(ms); return `${String(d.getUTCDate()).padStart(2, "0")}.${String(d.getUTCMonth() + 1).padStart(2, "0")}`; } export function MapPassSelector({ passes, fromIndex, toIndex, onFromChange, onToChange }: MapPassSelectorProps) { const trackRef = useRef(null); const n = passes.length; if (n === 0) return null; const fromPass = passes[Math.max(0, Math.min(fromIndex - 1, n - 1))]; const toPass = passes[Math.max(0, Math.min(toIndex - 1, n - 1))]; const multiDay = utcDM(passes[0].startMs) !== utcDM(passes[n - 1].startMs); const isFullRange = fromIndex === 1 && toIndex === n; const fromRev = fromPass.revolution ?? fromIndex; const toRev = toPass.revolution ?? toIndex; const pct = (idx: number): number => (n <= 1 ? 0 : ((idx - 1) / (n - 1)) * 100); const fromPct = pct(fromIndex); const toPct = pct(toIndex); const indexAt = (clientX: number): number => { const rect = trackRef.current?.getBoundingClientRect(); if (!rect || n <= 1) return 1; return Math.round(Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) * (n - 1)) + 1; }; const makeHandlers = (isFrom: boolean) => ({ onPointerDown(e: React.PointerEvent) { e.stopPropagation(); (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId); }, onPointerMove(e: React.PointerEvent) { if (!(e.currentTarget as HTMLDivElement).hasPointerCapture(e.pointerId)) return; const idx = indexAt(e.clientX); if (isFrom) { onFromChange(Math.max(1, Math.min(idx, toIndex))); } else { onToChange(Math.min(n, Math.max(idx, fromIndex))); } }, onPointerUp(e: React.PointerEvent) { (e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId); }, }); const onTrackPointerDown = (e: React.PointerEvent) => { const idx = indexAt(e.clientX); if (Math.abs(idx - fromIndex) <= Math.abs(idx - toIndex)) { onFromChange(Math.max(1, Math.min(idx, toIndex))); } else { onToChange(Math.min(n, Math.max(idx, fromIndex))); } }; return (
Витки {fromRev}–{toRev} {multiDay ? `${utcDM(fromPass.startMs)} ` : ""}{utcHM(fromPass.startMs)} {" – "} {multiDay ? `${utcDM(toPass.startMs)} ` : ""}{utcHM(toPass.startMs)} {" UTC"} {!isFullRange && ( )}
{/* Track line */}
{/* Tick marks */} {passes.map((p) => (
= fromIndex && p.index <= toIndex ? " is-active" : ""}`} style={{ left: `${pct(p.index)}%` }} /> ))} {/* From handle */}
{/* To handle */}
); }