From 4b7402270d7e3edc540f97aea00d3fbad92263ce Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 10 Oct 2026 21:51:40 +0900 Subject: [PATCH] =?UTF-8?q?fix(b08):=20=EA=B1=B0=EB=A6=AC=EC=9D=B4?= =?UTF-8?q?=EC=A0=95=ED=91=9C=20=EB=8B=A4=EC=9D=B4=EC=96=B4=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=98=81=EC=97=AD=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20?= =?UTF-8?q?=EC=97=86=EC=95=B0=20=C2=B7=20=ED=9C=A0=20=3D=20=EC=A4=8C?= =?UTF-8?q?=EB=A7=8C=20(244-8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 틀 overflow hidden · max-height 없앰 — 그림 = 틀 안 맞춤(높이 48vh 넘으면 비율대로 좁힘) - 틀 wheel 듣개(passive false · preventDefault) — 그림 밖 여백 · 단추 줄 휠도 페이지 스크롤로 안 샘 - 새 시험 test_244_8 — 진짜 wheel 사건으로 줌 · 기본동작 막힘 판정 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz --- B08_Quantity/B08_Quantity_UI_Milestone.css | 5 +- .../B08_Quantity_UI_Milestone_Diagram.ts | 5 +- .../test_244_6_b08_milestone_pan_client.py | 2 +- .../test_244_8_b08_milestone_no_scroll.py | 85 +++++++++++++++++++ 4 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 resources/tester/test_244_8_b08_milestone_no_scroll.py diff --git a/B08_Quantity/B08_Quantity_UI_Milestone.css b/B08_Quantity/B08_Quantity_UI_Milestone.css index adef962c1..ffabeca3f 100644 --- a/B08_Quantity/B08_Quantity_UI_Milestone.css +++ b/B08_Quantity/B08_Quantity_UI_Milestone.css @@ -51,10 +51,9 @@ border: 1px solid var(--color-border); } -/* 244-2 다이어그램 — 상자 · 선 · km 글(테마 변수) */ +/* 244-2 다이어그램 — 상자 · 선 · km 글(테마 변수) · 스크롤 없음(244-8 · 그림 = 틀 안 맞춤) */ .b08-milestone__diagram { - overflow: auto; - max-height: 48vh; + overflow: hidden; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface); diff --git a/B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts b/B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts index 6e5d74a6d..e22e9b69c 100644 --- a/B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts +++ b/B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts @@ -104,6 +104,8 @@ export function createDiagram( ): { root: HTMLElement; refresh: () => void } { const host = el("div", { className: "b08-milestone__diagram" }); host.dataset.b08Milestone = "diagram"; + // 틀 위 휠 = 줌만(244-8) — 그림 밖 여백 · 단추 줄 휠도 페이지 스크롤로 새지 않음 + host.addEventListener("wheel", (e) => e.preventDefault(), { passive: false }); let zoomState: ZoomPanState | undefined; // 다시 그려도 배율 그대로 const INFO: Record void> = { route: (id) => openRouteInfo(projectId, id), @@ -147,7 +149,8 @@ export function createDiagram( "aria-label": "거리이정표 다이어그램", }); // 틀 너비 = 아래 표 너비(244-6) · 높이는 비율대로(팬 · 줌 화면 px ↔ 그림 좌표가 어긋나지 않게) - root.style.width = "100%"; + // 높이 48vh 넘으면 그만큼 좁혀 틀 안에 다 들어감(244-8 · 스크롤 없음) + root.style.width = `min(100%, calc(48vh * ${vw / vh}))`; root.style.height = "auto"; const layer = svg("g", {}); const shift = svg("g", { transform: `translate(${-vx} ${-vy})` }); diff --git a/resources/tester/test_244_6_b08_milestone_pan_client.py b/resources/tester/test_244_6_b08_milestone_pan_client.py index bada838af..4d459a64f 100644 --- a/resources/tester/test_244_6_b08_milestone_pan_client.py +++ b/resources/tester/test_244_6_b08_milestone_pan_client.py @@ -107,7 +107,7 @@ def test_표_폭_다이어그램_폭_좌측_카드_없음(): assert "width: fit-content;" in block[: block.index("}")] # 틀 = 표 너비 assert ".b08-milestone > .b08-milestone__diagram {\n contain: inline-size;" in css ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts") - assert 'root.style.width = "100%";' in ui # 다이어그램 = 틀 너비 + assert "root.style.width = `min(100%, calc(48vh * ${vw / vh}))`;" in ui # 다이어그램 = 틀 너비 page = _read("B08_Quantity/B08_Quantity_UI_Milestone.ts") assert "children: [diagram.root, none, table.root]" in page # 좌측 패널 카드 없음 · 공사지 · 기준점은 모달 diff --git a/resources/tester/test_244_8_b08_milestone_no_scroll.py b/resources/tester/test_244_8_b08_milestone_no_scroll.py new file mode 100644 index 000000000..cbf8855aa --- /dev/null +++ b/resources/tester/test_244_8_b08_milestone_no_scroll.py @@ -0,0 +1,85 @@ +"""244-8 B08 거리이정표 다이어그램 영역 스크롤 없앰(사용자 135번). + +틀 = overflow hidden · 그림 = 틀 안 맞춤(높이 48vh 넘으면 좁힘) · 휠 = 줌만. +휠은 공용 `A00_Common/b_svg_zoom_pan.ts` 와 다이어그램 틀 듣개를 node 로 돌려 +진짜 wheel 사건(cancelable)을 보내 기본동작(스크롤)이 막히는지 · 배율이 바뀌는지 잼. +""" + +import json +import re +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE) +DIAGRAM = "B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts" + + +def _read(rel): + return (ROOT / rel).read_text(encoding="utf-8") + + +def _node(script): + src = IMPORTS.sub("", _read("A00_Common/b_svg_zoom_pan.ts")) + result = subprocess.run( + ["node", "--experimental-strip-types", "--no-warnings", + "--input-type=module-typescript", "-e", src + "\n" + script], + capture_output=True, text=True, encoding="utf-8", timeout=60, + ) # fmt: skip + assert result.returncode == 0, result.stderr + return json.loads(result.stdout) + + +def test_틀_스크롤_없음_그림_맞춤(): + css = _read("B08_Quantity/B08_Quantity_UI_Milestone.css") + block = css[css.index("\n.b08-milestone__diagram {") :] + block = block[: block.index("}")] + assert "overflow: hidden;" in block and "overflow: auto" not in block + assert "max-height" not in block # 높이는 그림이 맞춤 — 틀이 자르지 않음 + ui = _read(DIAGRAM) + # 너비 = 틀 · 높이 48vh 넘으면 비율대로 좁힘(height auto 라 화면 px ↔ 그림 좌표 그대로) + assert "root.style.width = `min(100%, calc(48vh * ${vw / vh}))`;" in ui + assert 'root.style.height = "auto";' in ui + + +def test_휠_진짜_사건_줌_스크롤_막음(): + ui = _read(DIAGRAM) + line = next(ln.strip() for ln in ui.splitlines() if 'host.addEventListener("wheel"' in ln) + got = _node( + """ +const W = 600, H = 400; +class Svg extends EventTarget { + classList = { add() {}, remove() {} }; + getBoundingClientRect() { return { left: 0, top: 0, width: W, height: H }; } +} +function wheel(target: EventTarget, deltaY: number) { + const ev = new Event("wheel", { cancelable: true, bubbles: true }); + for (const [k, v] of Object.entries({ deltaY, clientX: 300, clientY: 200 })) + Object.defineProperty(ev, k, { value: v }); + target.dispatchEvent(ev); + return ev.defaultPrevented; +} +const svg = new Svg(); +const layer = { t: "", setAttribute(_k: string, v: string) { this.t = v; } }; +attachSvgZoomPan({ svg: svg as any, layer: layer as any, widthPx: W, heightPx: H, + pad: { left: 0, right: 0, top: 0, bottom: 0 }, wheelZoom: true }); +const svgIn = wheel(svg, 100); +const zoomIn = layer.t; +const svgOut = wheel(svg, -100); +const zoomOut = layer.t; +// 틀 듣개(그림 밖 여백 · 단추 줄) — 소스 줄 그대로 돌림 · passive 면 preventDefault 무시됨 +const host = new EventTarget(); +let opts: unknown = null; +const add = host.addEventListener.bind(host); +host.addEventListener = (t: string, f: any, o: any) => { opts = o; add(t, f, o); }; +LINE +const hostBlocked = wheel(host, 100); +console.log(JSON.stringify({ svgIn, zoomIn, svgOut, zoomOut, hostBlocked, opts })); +""".replace("LINE", line) + ) + # 그림 위 휠 = 줌(커서 자리 붙잡음) · 기본동작(스크롤) 막힘 = 스크롤 위치 안 바뀜 + assert got["svgIn"] is True and got["zoomIn"].endswith(" scale(1.15)") + assert got["zoomIn"].startswith("translate(-45 -29.99") # 커서(300, 200) 제자리 + assert got["svgOut"] is True and got["zoomOut"] != got["zoomIn"] # 밀면 축소 + # 그림 밖 틀 위 휠도 페이지 스크롤로 안 샘 + assert got["hostBlocked"] is True and got["opts"] == {"passive": False}