- 빈 곳 누름 해제 듣개 뗌 — 3D 와 같은 한 규칙 - 풀기는 Esc · 같은 것 다시 누름 한 길 · 도구 중 빈 곳 누름은 그대로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
145 lines
6.0 KiB
JavaScript
145 lines
6.0 KiB
JavaScript
/* 233-4 빈 곳 누름 = 어디서도 안 풂 — 진짜 소스의 누름 처리 본문을 떼어(TS 그 자리 트랜스파일) 돌림.
|
|
* ① 종단 그래프 빈 곳 누름(`_Profile_Render` chartWrap click) ② 횡단 그리드 빈 곳 누름(`_Section_View` grid click)
|
|
* ③ 같은 것 다시 누름 = 풂(종단 측점선 `design_profile` selectStation · 횡단 카드 `_Section_View` selectStation)
|
|
* ④ Esc = 풂(`design_pick_escape` + `design_selection`). 표준출력 JSON 한 벌 · 짝 = test_233_4_empty_click.py.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const ts = require(path.join(ROOT, "config", "node_modules", "typescript"));
|
|
|
|
const read = (...parts) => fs.readFileSync(path.join(ROOT, ...parts), "utf8").replace(/\r/g, "");
|
|
const run = (code, names, values) => {
|
|
const js = ts.transpileModule(code, {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
return new Function(...names, js)(...values);
|
|
};
|
|
/** `start` 줄부터 같은 들여쓰기로 닫는 `end` 줄까지 떼어 냄. */
|
|
const block = (src, start, end) => {
|
|
const from = src.indexOf(start);
|
|
if (from < 0) throw new Error(`본문 없음: ${start}`);
|
|
const to = src.indexOf(end, from);
|
|
return src.slice(from, to + end.length);
|
|
};
|
|
|
|
const out = {};
|
|
const target = (inStation = false, inCard = false) => ({
|
|
closest: (sel) =>
|
|
(sel.includes("b06-chart__station") && inStation) || (sel.includes("b06-cross-card") && inCard)
|
|
? {}
|
|
: null,
|
|
});
|
|
|
|
/* ① 종단 그래프 빈 곳 누름 */
|
|
const render = read("B05_Profile", "B05_Profile_UI_Profile_Render.ts");
|
|
const chartClick = block(render, ` chartWrap.addEventListener("click", (event) => {`, "\n });");
|
|
const calls = [];
|
|
let tool = false;
|
|
const ctx = {
|
|
toolActive: () => tool,
|
|
handleToolPick: (at) => (calls.push(`tool:${at}`), true),
|
|
selectedStationId: () => "s120",
|
|
selectedStructureId: () => "wall",
|
|
selectStation: (id) => calls.push(`station:${id}`),
|
|
setSelectedStructureId: (id) => calls.push(`structure:${id}`),
|
|
redraw: () => calls.push("redraw"),
|
|
};
|
|
const callbacks = { onStructureSelect: (id) => calls.push(`onStructureSelect:${id}`) };
|
|
let handler = null;
|
|
const chartWrap = {
|
|
addEventListener: (_type, fn) => (handler = fn),
|
|
getBoundingClientRect: () => ({ left: 0 }),
|
|
scrollLeft: 0,
|
|
};
|
|
run(
|
|
chartClick,
|
|
["ctx", "callbacks", "chartWrap", "chainageInverter", "longitudinal", "width", "layout"],
|
|
[ctx, callbacks, chartWrap, () => (x) => x, null, 100, { originOffset: 0 }],
|
|
);
|
|
handler({ target: target(), clientX: 42 });
|
|
out.profileEmpty = calls.splice(0);
|
|
tool = true; // 도구 중 빈 곳 누름은 도구 몫 그대로
|
|
handler({ target: target(), clientX: 42 });
|
|
out.profileEmptyTool = calls.splice(0);
|
|
|
|
/* ② 횡단 그리드 빈 곳 누름 — 그리드에 걸린 누름 듣개를 모두 돌림 */
|
|
const view = read("B06_Section", "B06_Section_UI_Section_View.ts");
|
|
const gridStart = view.indexOf(' grid.className = "b06-section__grid";');
|
|
const gridCode = view.slice(gridStart, view.indexOf(" // 열 수 = 사용자 값", gridStart)); // 그리드 누름 듣개 전부
|
|
const gridCalls = [];
|
|
const grid = { listeners: [], addEventListener: (_t, fn) => grid.listeners.push(fn) };
|
|
run(
|
|
gridCode.replace(' grid.className = "b06-section__grid";', ""),
|
|
["grid", "clearSelection", "pavementAt", "spanMarks", "isPavement", "pavementSelectListener"],
|
|
[grid, () => gridCalls.push("clearSelection"), () => null, [], () => false, null],
|
|
);
|
|
for (const fn of grid.listeners)
|
|
fn({ target: target(), stopPropagation() {}, stopImmediatePropagation() {} });
|
|
out.gridListeners = grid.listeners.length;
|
|
out.gridEmpty = gridCalls.splice(0);
|
|
|
|
/* ③ 같은 것 다시 누름 = 풂 */
|
|
const dpSrc = read("A00_Common", "design", "design_profile.ts");
|
|
const dpSelect = block(
|
|
dpSrc,
|
|
" function selectStation(stationId: string | null): void {",
|
|
"\n }",
|
|
);
|
|
const profileState = run(
|
|
`let selectedStationId = null; let selectedStructureId = null;
|
|
${dpSelect}
|
|
return { selectStation, line: () => selectedStationId };`,
|
|
["structureIdAtStation", "draw", "onSelectStation"],
|
|
[() => null, () => undefined, () => undefined],
|
|
);
|
|
profileState.selectStation("s120");
|
|
out.profileFirst = profileState.line();
|
|
profileState.selectStation("s120");
|
|
out.profileAgain = profileState.line();
|
|
|
|
const svSelect = block(
|
|
view,
|
|
" const selectStation = (stationId: string, scroll: boolean): void => {",
|
|
"\n };",
|
|
);
|
|
const cardState = run(
|
|
`let selectedStationId = null; let activeAreaKey = null;
|
|
${svSelect}
|
|
return { selectStation, card: () => selectedStationId };`,
|
|
["applySelection", "revealCard"],
|
|
[() => undefined, () => undefined],
|
|
);
|
|
cardState.selectStation("s120", true);
|
|
out.cardFirst = cardState.card();
|
|
cardState.selectStation("s120", true);
|
|
out.cardAgain = cardState.card();
|
|
|
|
/* ④ Esc = 풂 */
|
|
const store = new Map();
|
|
const loadTs = (file, stubs) => {
|
|
const box = { exports: {} };
|
|
const js = ts.transpileModule(read(...file.split("/")), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
new Function("exports", "module", "require", js)(box.exports, box, (r) => stubs[r]);
|
|
return box.exports;
|
|
};
|
|
const DSel = loadTs("A00_Common/design/design_selection.ts", {
|
|
"../b_page_state": {
|
|
readState: (k, p) => store.get(`${k}:${p}`) ?? null,
|
|
writeState: (k, v, p) => store.set(`${k}:${p}`, v),
|
|
},
|
|
});
|
|
let onKey = null;
|
|
globalThis.window = { addEventListener: (_t, fn) => (onKey = fn), removeEventListener() {} };
|
|
globalThis.document = { querySelector: () => null };
|
|
const ESC = loadTs("A00_Common/design/design_pick_escape.ts", { "./design_selection": DSel });
|
|
ESC.bindEscapeRelease("p", { visible: () => true, onDispose() {} });
|
|
DSel.selectDesign("p", { chainageM: 120, structureId: null }, { from: "b06" });
|
|
out.beforeEsc = DSel.readSelection("p");
|
|
onKey({ key: "Escape", defaultPrevented: false, target: { closest: () => null } });
|
|
out.afterEsc = DSel.readSelection("p");
|
|
|
|
process.stdout.write(JSON.stringify(out));
|