Files
Aislo/resources/tester/helper_313_clear_all.cjs
T
eomsangdonandClaude Opus 5.5 3de9ca043a feat(b05,b06): 좌측 구조물 목록 맨 아래 [구조물 전체 삭제] — 빈 목록 숨김 · 공용 확인창 한 번 · 하나씩 지우기와 같은 길 (313)
- 목록 그리기(renderStructureList)가 맨 아래에 위험 단추 줄 — 목록이 비면 그리지 않음(다시 생기면 보임)
- 확인 「구조물 N개를 모두 지울까요?」(showConfirmDialog) · 취소 = 그대로
- 관(자동 횡단배수 · 손 배치) = 보기마다 관 빼기 길(B05 배수유역도 · B06 관 초안) · 구조물 = 정본 배열 비우고 알림 한 번 → 캐시 · [저장] 때 작업본
- 저장 길(초안 읽기 · [저장] · 서버 · 자동 배치)은 안 고침(312 조사 몫)
- 시험: test_313_clear_all 새로 · test_116 기대값(return { startNew, clearAll })

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
2026-10-13 18:38:50 +09:00

182 lines
6.0 KiB
JavaScript

/* 313 [구조물 전체 삭제] 헬퍼 — 진짜 TS(목록 그리기 · 입력 배선)를 Node 로 돌려 표준출력 JSON 한 벌로.
* 확인창(@ui showConfirmDialog)은 답을 정해 둔 가짜로 막음.
*/
const fs = require("fs");
const path = require("path");
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
const ROOT = path.join(__dirname, "..", "..");
const cache = new Map();
const confirms = [];
let answer = true;
const UI = {
showConfirmDialog: async (message, ok) => {
confirms.push([message, ok]);
return answer;
},
showToast: () => {},
};
const load = (file) => {
if (cache.has(file)) return cache.get(file);
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = { exports: {} };
cache.set(file, box.exports);
const near = (m) => {
if (m.startsWith("@ui/")) return UI;
if (m.startsWith("@config/")) return load(path.join(ROOT, "config", `${m.slice(8)}.ts`));
if (m.startsWith("@util/")) return load(path.join(ROOT, "common_util", `${m.slice(6)}.ts`));
if (m.endsWith(".json")) return require(path.join(path.dirname(file), m));
if (m.startsWith(".")) return load(path.join(path.dirname(file), `${m}.ts`));
return new Proxy({}, { get: () => () => {} });
};
new Function("exports", "module", "require", js)(box.exports, box, near);
return box.exports;
};
// DOM 흉내 — 목록이 쓰는 것만
class Node {
constructor(tag) {
this.tag = tag;
this.children = [];
this.className = "";
this.textContent = "";
this.style = { setProperty() {} };
this.listeners = {};
this.title = "";
}
get classList() {
const names = () => this.className.split(" ").filter(Boolean);
return {
add: (c) => (this.className = [...names(), c].join(" ")),
toggle: (c, on) => {
const rest = names().filter((n) => n !== c);
this.className = (on ? [...rest, c] : rest).join(" ");
},
contains: (c) => names().includes(c),
};
}
append(...kids) {
this.children.push(...kids);
}
replaceChildren(...kids) {
this.children = [...kids];
}
addEventListener(type, fn) {
this.listeners[type] = fn;
}
scrollIntoView() {}
}
globalThis.document = { createElement: (tag) => new Node(tag), activeElement: null };
globalThis.requestAnimationFrame = (fn) => fn();
const memory = new Map();
globalThis.window = {
sessionStorage: {
getItem: (k) => memory.get(k) ?? null,
setItem: (k, v) => memory.set(k, String(v)),
removeItem: (k) => memory.delete(k),
},
requestAnimationFrame: (fn) => fn(),
};
globalThis.sessionStorage = globalThis.window.sessionStorage;
const B05 = (f) => load(path.join(ROOT, "B05_Profile", `${f}.ts`));
const { renderStructureList } = B05("B05_Profile_UI_Structures_List");
const { bindStructuresEvents } = B05("B05_Profile_UI_Structures_Panel_Events");
const typeMap = new Map([
["sign", { type_id: "sign", name: "표지", group: "E" }],
["pipe", { type_id: "pipe", name: "배수관", group: "A" }],
]);
const sign = { structure_id: "s1", type_id: "sign", placement: "point", chainage_m: 40 };
const pipe = { chainage_m: 80, facility: "pipe", source: "auto" };
const draw = (structures, pipes, withClear = true) => {
const list = new Node("ul");
let cleared = 0;
renderStructureList({
list,
structures,
pipeFacilities: pipes,
typeMap,
intervalM: 20,
editingId: null,
selectedPipeChainage: null,
onSelectStructure: () => {},
onSelectPipe: () => {},
...(withClear ? { onClearAll: () => (cleared += 1) } : {}),
});
const last = list.children.at(-1);
const button = last?.children?.[0];
const clear = last?.className === "b05-route__irregular-clear";
if (clear) button.listeners.click();
return {
rows: list.children.length,
lastClass: last?.className ?? null,
button: clear
? { text: button.textContent, className: button.className, type: button.type }
: null,
cleared,
};
};
const listCases = {
filled: draw([sign], [pipe]),
empty: draw([], []),
noHandler: draw([sign], [pipe], false),
};
// 입력 배선 — 가짜 ctx(누름 듣개는 담기만)
const el = () => new Node("div");
const makeCtx = (structures, pipes) => {
const calls = { reset: 0, release: 0, removed: [], emit: 0 };
let pipeList = pipes.map((p) => ({ ...p }));
const ctx = {
groupSelect: el(),
typeSelect: el(),
primary: el(),
removeButton: el(),
resetButton: el(),
positionRow: el(),
memoField: el(),
optionRow: el(),
optionInputs: [],
anchorFields: { station: el(), remainder: el() },
startFields: { station: el(), remainder: el() },
endFields: { station: el(), remainder: el() },
facilityOptions: { root: el() },
structures,
pipeFacilities: () => pipeList,
projectId: "p",
callbacks: {
onListRelease: () => (calls.release += 1),
onPipeRemove: (at) => {
calls.removed.push(at);
pipeList = pipeList.filter((p) => p.chainage_m !== at); // 하나씩 빼기 길이 목록을 갈아 끼움
},
},
resetForm: () => (calls.reset += 1),
emit: () => (calls.emit += 1),
};
return { ctx, calls, left: () => structures.length + pipeList.length };
};
const runClear = async (structures, pipes, ok) => {
answer = ok;
confirms.length = 0;
const { ctx, calls, left } = makeCtx(structures, pipes);
const { clearAll } = bindStructuresEvents(ctx);
await clearAll();
return { confirms: [...confirms], calls, left: left() };
};
(async () => {
const pipes = [pipe, { chainage_m: 160.37, facility: "ford_pavement", source: "user" }];
const result = {
listCases,
confirmed: await runClear([{ ...sign }, { ...sign, structure_id: "s2" }], pipes, true),
cancelled: await runClear([{ ...sign }], pipes, false),
pipesOnly: await runClear([], [pipe], true),
nothing: await runClear([], [], true),
};
process.stdout.write(JSON.stringify(result));
})();