- 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
93 lines
3.4 KiB
TypeScript
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();
|
|
},
|
|
};
|
|
}
|