Files
Aislo/resources/tester/helper_345_2_3d_modal.cjs
T
eomsangdonandClaude Opus 5.5 f8fcb812d3 feat(종단보기): 345-2 3D → 큰 모달 — [3D] 단추 · 처음 열 때 지형 · 닫으면 숨긴 자리로 · 그리기 멈춤 · 떠날 때만 버림 · 3D 노선 점 옮김 · 지움 + 「선택 포인트 상세」 칸 지움
- 새 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
2026-10-16 11:11:10 +09:00

162 lines
4.8 KiB
JavaScript

/* 345-2 3D 큰 모달 헬퍼 — `design_view_profile_3d_modal.ts` 를 트랜스파일해 가짜 DOM · 가짜 뷰어로
* 닫힌 동안 그리기 0 · 열면 그림 · 닫으면 숨긴 자리로 되돌림 · 지형은 처음 열 때 한 번 · 버림은 떠날 때만을 본다.
* 결과는 JSON 한 벌(표준출력).
*/
const fs = require("fs");
const path = require("path");
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
const ROOT = path.join(__dirname, "..", "..");
class FakeNode {
constructor(tag) {
this.tagName = tag;
this.children = [];
this.parent = null;
this.hidden = false;
this.title = "";
this.listeners = {};
const classes = new Set();
this.classList = {
add: (...n) => n.forEach((x) => classes.add(x)),
contains: (n) => classes.has(n),
};
this._classes = classes;
}
set className(v) {
this._classes.clear();
String(v)
.split(/\s+/)
.filter(Boolean)
.forEach((x) => this._classes.add(x));
}
append(...kids) {
kids.forEach((kid) => {
if (kid.parent) kid.parent.children = kid.parent.children.filter((c) => c !== kid);
kid.parent = this;
this.children.push(kid);
});
}
remove() {
if (this.parent) this.parent.children = this.parent.children.filter((c) => c !== this);
this.parent = null;
}
addEventListener(type, fn) {
(this.listeners[type] ??= []).push(fn);
}
click() {
(this.listeners.click ?? []).forEach((fn) => fn({}));
}
}
global.document = { createElement: (tag) => new FakeNode(tag) };
const modals = [];
const STUBS = {
"@ui/ui_template_elements": {
createButton: ({ label, onClick }) => {
const button = new FakeNode("button");
button.label = label;
if (onClick) button.addEventListener("click", onClick);
return button;
},
},
"@ui/ui_template_modal": {
openModal: (opts) => {
const body = new FakeNode("div");
const record = { opts, body, open: true };
opts.mount(body, () => (record.open = false), new FakeNode("div"));
record.close = () => (record.open = false);
modals.push(record);
return { close: record.close };
},
},
"@ui/ui_template_progress": {
createProgressCircle: () => {
const root = new FakeNode("div");
return { root, set: () => undefined, remove: () => root.remove() };
},
},
};
function loadTs(file) {
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = { exports: {} };
const localRequire = (request) => STUBS[request] ?? require(request);
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
return box.exports;
}
const { createViewer3dModal } = loadTs(
path.join(ROOT, "A00_Common", "design", "design_view_profile_3d_modal.ts"),
);
// 가짜 뷰어 — 만들면 그리기 루프가 돎(진짜와 같음) · setActive 가 루프를 켜고 끔.
const viewer = {
root: new FakeNode("div"),
running: true,
draws: 0,
disposed: false,
setActive(active) {
this.running = active;
},
dispose() {
this.disposed = true;
},
};
const frames = (n) => {
for (let i = 0; i < n; i += 1) if (viewer.running) viewer.draws += 1;
};
let terrainLoads = 0;
let resolveTerrain;
const view3d = createViewer3dModal({
viewer,
loadTerrain: () => {
terrainLoads += 1;
return new Promise((resolve) => (resolveTerrain = resolve));
},
visible: () => true,
});
(async () => {
const out = {};
frames(10);
out.closedDraws = viewer.draws;
out.inPark = viewer.root.parent === view3d.park && view3d.park.hidden;
out.terrainBeforeOpen = terrainLoads;
view3d.button.click();
const first = modals[0];
out.wide = first.opts.wide === true;
out.inModal = viewer.root.parent === first.body;
out.spinnerWhileLoading = viewer.root.children.length === 1;
frames(3);
out.openDraws = viewer.draws;
resolveTerrain();
await new Promise((r) => setImmediate(r));
out.spinnerGone = viewer.root.children.length === 0;
// 닫기 단추 · Esc · 바깥 누름 = beforeClose 뒤 닫힘(공용 모달 규칙)
out.beforeCloseOk = (await first.opts.beforeClose()) === true;
first.close();
frames(10);
out.afterCloseDraws = viewer.draws;
out.backInPark = viewer.root.parent === view3d.park;
out.disposedOnClose = viewer.disposed;
view3d.open();
view3d.open(); // 열린 동안 또 눌러도 하나
out.modalCount = modals.length;
out.terrainLoads = terrainLoads;
frames(2);
out.reopenDraws = viewer.draws;
view3d.dispose(); // 페이지 떠남 — 열려 있으면 닫고 버림
out.closedOnDispose = modals[1].open === false;
out.disposed = viewer.disposed;
out.runningAfterDispose = viewer.running;
console.log(JSON.stringify(out));
})();