/* ============================================================================= * design_view_profile_3d_modal.ts * 종단 보기 3D 를 **큰 모달**로(345-2). 3D 화면은 숨긴 자리(`park`)에 살고 [3D] 를 누르면 공용 큰 * 모달(`openModal({ wide })`) 몸으로 옮겨 붙는다 — 같은 노드라 보기 조작 줄 · [3D 업데이트] · * 3D 안 고름(측점 · 구조물 · 우클릭 더함)이 그대로 따라간다. * * 처음 열 때 3D 지형을 올린다(아직이면 로딩 원). 닫으면 숨긴 자리로 되돌려 `setActive(false)` — * 그리기 루프 · 포인터 듣기만 멈추고 WebGL 문맥 · 지형은 살린다. 버림은 페이지를 떠날 때만. * ========================================================================== */ import { createButton } from "@ui/ui_template_elements"; import { openModal } from "@ui/ui_template_modal"; import { createProgressCircle } from "@ui/ui_template_progress"; export interface Viewer3dModalDeps { viewer: { root: HTMLElement; setActive: (active: boolean) => void; dispose: () => void }; /** 처음 열 때 한 번 — 3D 지형 올리기(실패해도 모달은 열림). */ loadTerrain: () => Promise; /** 종단 보기가 보이는가 — 숨은 보기에서는 안 연다. */ visible: () => boolean; /** 열 때마다(지형이 선 뒤) — 낡은 3D 길 모형 다시 짓기(345-4). */ onOpen?: () => void; } export function createViewer3dModal(deps: Viewer3dModalDeps) { const { viewer } = deps; const park = document.createElement("div"); park.className = "b05-route__3d-park"; park.hidden = true; park.append(viewer.root); viewer.setActive(false); // 닫힌 동안 그리기 0 let modal: { close: () => void } | null = null; let terrain: Promise | null = null; const putBack = (): void => { modal = null; park.append(viewer.root); viewer.setActive(false); }; function open(): void { if (modal || !deps.visible()) return; modal = openModal({ title: "3D 보기", closeLabel: "닫기", wide: true, dialogClass: "b05-route__3d-modal", mount: (body) => { body.classList.add("b05-route__3d-body"); body.append(viewer.root); }, // 닫기 단추 · Esc · 바깥 누름 — 묻지 않고 숨긴 자리로 되돌린다. beforeClose: () => { putBack(); return true; }, }); viewer.setActive(true); if (!terrain) { const progress = createProgressCircle({ label: "3D 지형을 불러오는 중…", overlay: true }); progress.set(null); viewer.root.append(progress.root); terrain = deps.loadTerrain().finally(() => progress.remove()); } void terrain.then(() => modal && deps.onOpen?.()); // 그 사이 닫았으면 안 지음 } /** 열려 있으면 닫는다 — 보기를 떠날 때 · 페이지를 떠날 때. */ function close(): void { if (!modal) return; const shown = modal; putBack(); shown.close(); } const button = createButton({ label: "3D", variant: "ghost", onClick: open }); button.classList.add("b05-route__3d-open"); button.title = "3D 화면을 큰 창으로 엽니다 — 닫으면 그리기를 멈춥니다"; return { park, button, open, close, isOpen: () => modal !== null, dispose(): void { close(); viewer.dispose(); park.remove(); }, }; }