/* 369-3 [업데이트] 진짜 누름 헬퍼 — `B05_Profile_UI_Drainage_Interact.ts` 를 그 자리에서 * 트랜스파일해 가짜 viewport · 단추 · 관 표지로 pointerdown → pointerup → click 차례를 돌림. * 사로잡기(setPointerCapture)가 걸리면 pointerup · click 은 viewport 로 감(브라우저와 같음). */ const fs = require("fs"); const path = require("path"); const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); const B05 = path.join(__dirname, "..", "..", "B05_Profile"); class Element { constructor(tag, parent = null) { this.tag = tag; this.parent = parent; this.className = ""; this.listeners = {}; } matches(selector) { return selector.split(",").some((part) => { const s = part.trim(); return s.startsWith(".") ? this.className.split(" ").includes(s.slice(1)) : s === this.tag; }); } closest(selector) { for (let node = this; node; node = node.parent) if (node.matches(selector)) return node; return null; } addEventListener(type, fn) { (this.listeners[type] ??= []).push(fn); } fire(type, event) { (this.listeners[type] ?? []).forEach((fn) => fn(event)); } } global.Element = Element; const stubs = { "../A00_Common/touch_bridge": { wheelZoomsIn: () => true }, "../B04_PreProcess/B04_PreProcess_UI_MapRender": { computeMaxScale: () => 10, lonLatToScreen: () => ({ x: 0, y: 0 }), }, "../B04_PreProcess/B04_PreProcess_UI_MapOverlays": { pointInRings: () => true }, "./B05_Profile_UI_Drainage_ZoomRange": { clampWheelScale: (value) => value }, }; const file = path.join(B05, "B05_Profile_UI_Drainage_Interact.ts"); const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, }).outputText; const box = { exports: {} }; new Function("exports", "module", "require", js)( box.exports, box, (name) => stubs[name] ?? require(name), ); function setup() { const viewport = new Element("div"); viewport.className = "b05-drainage__viewport"; viewport.style = { removeProperty() {} }; viewport.getBoundingClientRect = () => ({ left: 0, top: 0, width: 400, height: 300 }); viewport.captured = null; viewport.setPointerCapture = (id) => (viewport.captured = id); const canvas = new Element("canvas", viewport); const veil = new Element("div", viewport); veil.className = "b05-drainage__veil"; const update = new Element("button", veil); update.className = "b05-drainage__analyze b05-drainage__update"; const log = { live: 0, grabs: 0, picks: 0 }; update.addEventListener("click", () => log.live++); // [업데이트] = drainage/live 한 번 let dragging = false; const state = { scale: 1, offset: { x: 0, y: 0 } }; box.exports.bindDrainageInteractions({ viewport, contextMenu: { contains: () => false, close() {} }, // 가짜 표지 — 단추 바로 아래(어디를 눌러도 집힘) pipeEditor: { handleDown: () => ((dragging = true), log.grabs++, true), handleMove: () => dragging, handleUp: () => ((dragging = false), false), }, currentView: () => ({ mapRect: { width: 400 }, width: 400 }), getScale: () => state.scale, setScale: (v) => (state.scale = v), getMeta: () => null, getMinScale: () => 0.1, getOffset: () => state.offset, setOffset: (x, y) => (state.offset = { x, y }), scheduleDraw() {}, getNormalizer: () => ({}), getBasins: () => [ { index: 0, polygon_lonlat: [ [0, 0], [1, 0], [1, 1], ], area_m2: 1, }, ], getSelectedBasin: () => null, selectBasin: () => log.picks++, }); return { viewport, canvas, update, log, state }; } /** 진짜 마우스 누름 — 사건은 target 에서 viewport 로 거품 · 사로잡히면 up · click 이 viewport 로. */ function press(world, target, button = 0) { const event = { target, button, pointerId: 1, pointerType: "mouse", clientX: 200, clientY: 150 }; const bubble = (type, from) => { for (let node = from; node; node = node.parent) node.fire(type, { ...event, preventDefault() {} }); }; world.viewport.captured = null; bubble("pointerdown", target); const upTarget = world.viewport.captured !== null ? world.viewport : target; bubble("pointerup", upTarget); bubble("click", upTarget); } const onButton = setup(); press(onButton, onButton.update); const onMap = setup(); press(onMap, onMap.canvas); const panMap = setup(); const panEvent = { target: panMap.canvas, button: 1, pointerId: 2, pointerType: "mouse", preventDefault() {}, }; panMap.viewport.fire("pointerdown", { ...panEvent, clientX: 0, clientY: 0 }); panMap.viewport.fire("pointermove", { ...panEvent, clientX: 30, clientY: 40 }); process.stdout.write( JSON.stringify({ button: { ...onButton.log, captured: onButton.viewport.captured, offset: onButton.state.offset, }, map: { ...onMap.log, captured: onMap.viewport.captured }, pan: { captured: panMap.viewport.captured, offset: panMap.state.offset }, }), );