import { useCallback, useRef, useState } from 'react'; import { useGeoStore } from '../../store/geoStore'; import { usePlayerStore } from '../../store/playerStore'; import styles from './RouteInfo.module.css'; /** 정적 에셋 경로 (Vite base 반영). */ const bgUrl = `${import.meta.env.BASE_URL}assets/title-panel-bg@2x.png`; /** 원본 디자인 무대 가로폭(px). 배너는 이 기준으로 만들어졌다. */ const STAGE_WIDTH = 1920; /** 초 → "M분 S초". */ function formatDuration(sec?: number | null): string { if (sec == null || !isFinite(sec) || sec <= 0) return ''; const m = Math.floor(sec / 60); const s = Math.round(sec % 60); return s > 0 ? `${m}분 ${s}초` : `${m}분`; } /** "158k700" → 158700 (m). 매칭 실패 시 -1. */ function stationKm(title: string): number { const m = title.match(/(\d+)[Kk](\d+)/); return m ? parseInt(m[1], 10) * 1000 + parseInt(m[2], 10) : -1; } /** * 영상 좌상단 노선 정보 배너 — videoplayer 의 RouteInfo 디자인 이식. * 값은 모두 선택한 폴더에서 가져온다(하드코딩 없음). * - 연장(lengthKm): route.json 우선 → 측점 CSV 구간(min~max km) 계산 폴백. * - 소요(durationSec): route.json 우선 → 실제 영상 길이 폴백. * - 방향/노선명: CSV에 없는 정보 → route.json(routeInfo). * 표출할 값이 하나도 없으면 렌더하지 않는다. */ export default function RouteInfoOverlay() { const routeInfo = useGeoStore((s) => s.routeMeta?.routeInfo); const stations = useGeoStore((s) => s.stations); const videoDuration = usePlayerStore((s) => s.duration); // 원본처럼 영상 폭/1920 비율로 배너를 스케일 (부모=영상 영역 폭 관측). const [scale, setScale] = useState(1); const roRef = useRef(null); const setPanelRef = useCallback((el: HTMLDivElement | null) => { roRef.current?.disconnect(); const parent = el?.parentElement; if (!parent) return; const update = () => setScale(parent.clientWidth / STAGE_WIDTH); update(); const ro = new ResizeObserver(update); ro.observe(parent); roRef.current = ro; }, []); const direction = routeInfo?.direction; const name = routeInfo?.name; // 연장: route.json 우선 → 측점 구간 계산 폴백 let lengthKm = routeInfo?.lengthKm ?? null; if (lengthKm == null && stations.length) { const kms = stations.map((s) => stationKm(s.title)).filter((k) => k >= 0); if (kms.length >= 2) { lengthKm = Math.round((Math.max(...kms) - Math.min(...kms)) / 10) / 100; // m→km, 소수2 } } // 소요시간: route.json 우선 → 실제 영상 길이 폴백 const dur = formatDuration(routeInfo?.durationSec ?? videoDuration); if (!direction && !name && lengthKm == null && !dur) return null; return (
{direction &&

{direction}

} {name &&

{name}

} {lengthKm != null && ( <>

연장

{lengthKm}

km

)} {dur && ( <>

{dur}

소요

)}
); }