Files
Aislo/A00_Common/design/design_view_profile_3d_modal.ts
T
eomsangdonandClaude Opus 5.5 519b54eb73 feat(종단보기): 345-4 3D 모달 열 때 낡았으면 자동 다시 짓기 — 닫혀 있는 동안 안 지음 · 「3D 늦음」 = setCorridorPending 그대로 · 단추 · 열기 겹쳐도 한 번
- ProfileViewState.corridorPending(표시와 한 칸) · sections.setCorridorPending · 모달 onOpen(지형 선 뒤 · 그 사이 닫으면 안 부름)
- 시험: test_345_4_corridor_worker 4(워커 = 주 스레드 · transfer 9/9 · 저장본 먼저) · test_345_2_3d_modal 5(닫힌 동안 짓기 0) · 관련 310 통과 · typecheck 내 파일 0 오류

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
2026-10-16 11:47:31 +09:00

93 lines
3.4 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;
/** 열 때마다(지형이 선 뒤) — 낡은 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<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) {
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();
},
};
}