- 새 design_view_profile_3d_modal.ts(공용 openModal wide · 보기 조작 줄 · [3D 업데이트] · 3D 고름이 같은 노드로 따라감) - 지형 올리기는 페이지 열기에서 빠짐 · 모달 처음 열 때(로딩 원) · 등고선 간격 바꿈도 그 값으로 - 시험: test_345_2_3d_modal 4 통과(닫힌 동안 그리기 0) · 칸 시험(parity · 311) 고쳐 씀 · typecheck 통과 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
89 lines
3.2 KiB
TypeScript
89 lines
3.2 KiB
TypeScript
/* =============================================================================
|
|
* 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<void>;
|
|
/** 종단 보기가 보이는가 — 숨은 보기에서는 안 연다. */
|
|
visible: () => boolean;
|
|
}
|
|
|
|
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<void> | 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) return;
|
|
const progress = createProgressCircle({ label: "3D 지형을 불러오는 중…", overlay: true });
|
|
progress.set(null);
|
|
viewer.root.append(progress.root);
|
|
terrain = deps.loadTerrain().finally(() => progress.remove());
|
|
}
|
|
|
|
/** 열려 있으면 닫는다 — 보기를 떠날 때 · 페이지를 떠날 때. */
|
|
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();
|
|
},
|
|
};
|
|
}
|