- 종단 보기 구독이 배수유역도 관 마커 · 유역 · 마름모까지 알림 없이 칠함 · 재계산 뒤 다시 얹음 - 지도 고름 · 목록 관 고름 · 목록 다시 누름도 고름 한 칸에 적고 칠하기는 구독 몫 - 목록 관 줄 폭 PIPE_MATCH_M · 조금 옮겨진 관도 고름 따라감 · 저장 뒤 목록 주입이 관 폼 안 닫음 - design_pick_escape — Esc = 고름 한 칸 비움 → 종단 · 3D · 지도 · 폼 · 횡단 카드 · 범위 · 형상 모두 풂 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
172 lines
5.8 KiB
JavaScript
172 lines
5.8 KiB
JavaScript
/* 233-3 풀기 한 길(Esc) · 배수유역도 다시 얹음 — 진짜 `design_pick_escape` · `design_pick_profile` ·
|
|
* `B06_Section_UI_Page_Pick_Sync` · `design_selection` 을 Node 로 돌림. 창 키 · 폼 · 지도 · 카드는 흉내.
|
|
* 표준출력 JSON 한 벌 · 짝 = test_233_3_release.py.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const ts = require(path.join(ROOT, "config", "node_modules", "typescript"));
|
|
|
|
function loadTs(file, stubs = {}) {
|
|
const js = ts.transpileModule(fs.readFileSync(path.join(ROOT, file), "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
const localRequire = (request) => {
|
|
if (stubs[request]) return stubs[request];
|
|
throw new Error(`stub 없음: ${request}`);
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
return box.exports;
|
|
}
|
|
|
|
/* 창 키 듣기 · 창(모달) 흉내 */
|
|
const keyListeners = new Set();
|
|
globalThis.window = {
|
|
addEventListener: (type, fn) => type === "keydown" && keyListeners.add(fn),
|
|
removeEventListener: (type, fn) => keyListeners.delete(fn),
|
|
};
|
|
let modalOpen = false;
|
|
globalThis.document = { querySelector: () => (modalOpen ? {} : null) };
|
|
const press = (key, { inInput = false, prevented = false } = {}) => {
|
|
const event = {
|
|
key,
|
|
defaultPrevented: prevented,
|
|
target: { closest: () => (inInput ? {} : null) },
|
|
};
|
|
for (const fn of [...keyListeners]) fn(event);
|
|
};
|
|
|
|
const store = new Map();
|
|
const DSel = loadTs("A00_Common/design/design_selection.ts", {
|
|
"../b_page_state": {
|
|
readState: (key, pid) => store.get(`${key}:${pid}`) ?? null,
|
|
writeState: (key, value, pid) => store.set(`${key}:${pid}`, value),
|
|
},
|
|
});
|
|
const ESC = loadTs("A00_Common/design/design_pick_escape.ts", { "./design_selection": DSel });
|
|
const IRR = {
|
|
irregularStationId: (id) => `irregular:${id}`,
|
|
isPipeStation: (s) => s.origin === "pipe",
|
|
};
|
|
const SY = loadTs("B05_Profile/B05_Profile_UI_Selection.ts", {
|
|
"../A00_Common/design/design_selection": DSel,
|
|
"./B05_Profile_UI_IrregularStations": IRR,
|
|
});
|
|
const PP = loadTs("A00_Common/design/design_pick_profile.ts", {
|
|
"./design_selection": DSel,
|
|
"./design_pick_escape": ESC,
|
|
"../../B05_Profile/B05_Profile_UI_Selection": SY,
|
|
"../../B05_Profile/B05_Profile_UI_IrregularStations": IRR,
|
|
});
|
|
const SYNC = loadTs("B06_Section/B06_Section_UI_Page_Pick_Sync.ts", {
|
|
"../A00_Common/design/design_selection": DSel,
|
|
"../A00_Common/design/design_pick_escape": ESC,
|
|
});
|
|
|
|
const PID = "p233c";
|
|
const sel = (chainageM, structureId = null, from = "b05") =>
|
|
DSel.selectDesign(PID, chainageM === null ? DSel.NO_SELECTION : { chainageM, structureId }, {
|
|
from,
|
|
});
|
|
|
|
/* ── 종단 보기(B05) — 종단도 · 배수유역도 · 좌측 폼 흉내 ── */
|
|
const map = { pipe: null, mark: null, notifies: 0 };
|
|
const profile = { station: null, structure: null };
|
|
const form = { open: "wall", resets: 0 };
|
|
let b05Visible = true;
|
|
const b05 = PP.bindProfilePick(
|
|
PID,
|
|
{
|
|
profile: {
|
|
setSelectedStation: (id) => Object.assign(profile, { station: id, structure: null }),
|
|
setSelectedStructure: (id) => Object.assign(profile, { station: null, structure: id }),
|
|
drainage: {
|
|
// 진짜 판 꼴 — 0.51 m 안 관을 고름 · notify 거짓이면 밖으로 안 알림
|
|
selectPipeAtChainage: (at, notify = true) => {
|
|
map.pipe = at === null ? null : Math.abs(at - 80.17) < 0.51 ? 80.17 : null;
|
|
if (notify) map.notifies += 1;
|
|
},
|
|
markStation: (at) => (map.mark = at),
|
|
},
|
|
},
|
|
form: {
|
|
hasSelection: () => form.open !== null,
|
|
selectById: () => {
|
|
form.open = null;
|
|
form.resets += 1;
|
|
},
|
|
},
|
|
},
|
|
{ visible: () => b05Visible, onDispose() {} },
|
|
{
|
|
irregular: () => [{ id: "p80", chainage_m: 80.17, origin: "pipe" }],
|
|
pipes: () => [{ chainage_m: 80.17 }],
|
|
sections: () => [80, 100, 120].map((at) => ({ station_id: `s${at}`, chainage_m: at })),
|
|
},
|
|
);
|
|
|
|
/* ── 횡단 보기(B06) — 카드 · 형상 · 폼 흉내 ── */
|
|
const cross = [];
|
|
let b06Visible = false;
|
|
SYNC.bindCrossPick(
|
|
PID,
|
|
{ visible: () => b06Visible, onDispose() {} },
|
|
{
|
|
sections: () => [],
|
|
controls: {
|
|
revetOffset: { highlightFor: () => false, selectedFor: () => false, select() {} },
|
|
ford: { selectedFor: () => false, select() {} },
|
|
box: { selectedFor: () => false, select() {} },
|
|
},
|
|
releaseForm: () => cross.push("releaseForm"),
|
|
view: {
|
|
focusStation: (id) => cross.push(`focus:${id}`),
|
|
refreshCards: () => undefined,
|
|
selectMark: (id) => cross.push(`mark:${id}`),
|
|
},
|
|
reapply: () => cross.push("reapply"),
|
|
},
|
|
);
|
|
|
|
const snap = () => ({
|
|
session: DSel.readSelection(PID),
|
|
profile: { ...profile },
|
|
map: { ...map },
|
|
form: { ...form },
|
|
});
|
|
const out = {};
|
|
sel(80.17);
|
|
out.pipePick = snap();
|
|
// 배수유역도 재계산이 관 · 유역 고름을 지움 → 같은 값으로 다시 얹음(알림 없이)
|
|
map.pipe = null;
|
|
map.mark = null;
|
|
b05.paint();
|
|
out.afterRecompute = snap();
|
|
press("Escape", { inInput: true });
|
|
out.escInInput = snap();
|
|
modalOpen = true;
|
|
press("Escape");
|
|
modalOpen = false;
|
|
out.escModal = snap();
|
|
press("Escape", { prevented: true });
|
|
out.escPrevented = snap();
|
|
press("Escape");
|
|
out.esc = snap();
|
|
out.crossWhileHidden = cross.splice(0);
|
|
// 목록 다시 누름 같은 B05 풀기(값 비움)는 폼을 그 길이 이미 닫음 — 구독은 폼을 또 닫지 않음
|
|
form.open = "sign";
|
|
sel(100, "sign");
|
|
sel(null);
|
|
out.listRelease = snap();
|
|
// 횡단 보기가 보일 때 Esc = 카드 · 폼 · 범위 · 형상 모두 풂
|
|
b05Visible = false;
|
|
b06Visible = true;
|
|
sel(120, null, "b06");
|
|
press("Escape");
|
|
out.crossEsc = cross.splice(0);
|
|
out.crossSession = DSel.readSelection(PID);
|
|
out.listeners = keyListeners.size;
|
|
|
|
process.stdout.write(JSON.stringify(out));
|