Files
Aislo/B05_wf2_Route/B05_wf2_Route_UI_Profile_Panel.ts
T
2026-07-19 09:17:48 +09:00

82 lines
2.6 KiB
TypeScript

import type {
LongitudinalSection,
SectionDetailResponse,
} from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Api_Fetch";
import { createLongitudinalProfile } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_View";
import "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style.css";
const COLLAPSED_KEY = "b05-route-profile-collapsed";
function normalizedLongitudinal(data: LongitudinalSection): LongitudinalSection {
return {
...data,
samples: data.samples.map((sample) => ({
...sample,
elevation_m: sample.elevation_m ?? sample.z ?? null,
})),
};
}
export function createRouteProfilePanel(onSelectStation: (stationId: string) => void) {
const root = document.createElement("section");
root.className = "b05-route-profile";
const header = document.createElement("header");
const toggle = document.createElement("button");
toggle.type = "button";
toggle.className = "b05-route-profile__toggle";
const body = document.createElement("div");
body.className = "b05-route-profile__body";
const empty = document.createElement("p");
empty.className = "b05-route-profile__empty";
empty.textContent = "최적 경로를 계산하면 종단면도가 표시됩니다.";
body.append(empty);
header.append(toggle);
root.append(header, body);
let detail: SectionDetailResponse | null = null;
let selectedStationId: string | null = null;
let stationInterval: number | undefined;
function draw(): void {
if (!detail) return;
body.replaceChildren(
createLongitudinalProfile(
normalizedLongitudinal(detail.longitudinal),
selectedStationId,
1,
undefined,
onSelectStation,
stationInterval,
),
);
}
function setCollapsed(collapsed: boolean): void {
root.classList.toggle("is-collapsed", collapsed);
toggle.textContent = collapsed ? "⌃" : "⌄";
toggle.setAttribute("aria-label", collapsed ? "종단면도 펼치기" : "종단면도 접기");
toggle.setAttribute("aria-expanded", String(!collapsed));
sessionStorage.setItem(COLLAPSED_KEY, String(collapsed));
}
toggle.addEventListener("click", () => setCollapsed(!root.classList.contains("is-collapsed")));
setCollapsed(sessionStorage.getItem(COLLAPSED_KEY) === "true");
return {
root,
render(nextDetail: SectionDetailResponse, nextStationInterval?: number) {
detail = nextDetail;
stationInterval = nextStationInterval;
draw();
},
setSelectedStation(stationId: string | null) {
selectedStationId = stationId;
draw();
},
clear() {
detail = null;
selectedStationId = null;
body.replaceChildren(empty);
},
};
}