From 473e3532e887b31e6a19b0e6bedb25f0775d8fee Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 10 Oct 2026 15:32:00 +0900 Subject: [PATCH] =?UTF-8?q?style(b07):=20=ED=9A=A1=EB=8B=A8=EB=8F=84=20?= =?UTF-8?q?=EB=B0=B0=EC=B9=98=20=EC=B9=B8=20=EB=8B=A8=EC=B6=94=20=C2=B7=20?= =?UTF-8?q?=EC=88=AB=EC=9E=90=20=EC=B9=B8=20=ED=95=9C=20=EA=B7=9C=EA=B2=A9?= =?UTF-8?q?=20=C2=B7=20=ED=95=9C=20=EC=A4=84=EC=97=90=20=ED=95=9C=20?= =?UTF-8?q?=EC=9D=BC=20=C2=B7=20=EA=B2=80=EC=9D=80=20=EC=84=A0=20=3D=20?= =?UTF-8?q?=EC=9D=B8=EC=87=84=20=EC=B9=B8=EC=84=A0=20=ED=8C=90=EC=A0=95(21?= =?UTF-8?q?2-3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 줄 = 배치 열 · 행 / [이 장] [모든 장] / 표 배율 [배율 적용] / [안내선 숨기기] · 한 줄 안 같은 너비 - 단추 · 숫자 칸 · 이름 = 줄 위 작은 단추 토큰(--btn-bar-* · 높이 16) · 옛 한 줄 감기 · 48px 고정 지움 - 안내선 숨김 뒤 남는 검은 선 = 인쇄 칸선(도각 층 · #8ea0b5 · 밝은 바탕에서 짙게) — 지우지 않음 - 새 시험 test_212_3 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL --- .../B07_DesignDetail_UI_Panels.ts | 22 ++++-- .../B07_DesignDetail_UI_Style.css | 37 +++++++-- .../test_212_3_b07_cross_layout_buttons.py | 79 +++++++++++++++++++ 3 files changed, 123 insertions(+), 15 deletions(-) create mode 100644 resources/tester/test_212_3_b07_cross_layout_buttons.py diff --git a/B07_DesignDetail/B07_DesignDetail_UI_Panels.ts b/B07_DesignDetail/B07_DesignDetail_UI_Panels.ts index 0f214f7c5..68426a2fa 100644 --- a/B07_DesignDetail/B07_DesignDetail_UI_Panels.ts +++ b/B07_DesignDetail/B07_DesignDetail_UI_Panels.ts @@ -185,8 +185,9 @@ export interface CrossLayoutExtras { } /** - * 횡단 장 배치 칸(140-3 · 153-1) — 「배치 열 [4] 행 [4] [이 장] [모든 장]」 + 「표 배율 [1] [적용]」. - * `onApply` 가 없으면(장이 아닌 도면) [이 장]을 끔. + * 횡단 장 배치 칸(140-3 · 153-1) — 줄마다 한 일(212-3): + * 「배치 열 [4] 행 [4]」 · 「[이 장] [모든 장]」 · 「표 배율 [1] [배율 적용]」 · (부르는 쪽이 붙이는 [안내선 숨기기]). + * 단추 · 숫자 칸 = 줄 위 작은 단추 한 규격(`--btn-bar-*`) · `onApply` 가 없으면(장이 아닌 도면) [이 장]을 끔. */ export function buildCrossLayoutControls( grid: [number, number], @@ -196,6 +197,12 @@ export function buildCrossLayoutControls( ): HTMLElement { const box = document.createElement("div"); box.className = "b07-cross-layout"; + const row = (...items: HTMLElement[]): HTMLElement => { + const line = document.createElement("div"); + line.className = "b07-cross-layout__row"; + line.append(...items); + return line; + }; const number = (value: number, label: string) => createInputField({ label, @@ -227,10 +234,11 @@ export function buildCrossLayoutControls( return button; }; box.append( - cols.root, - rows.root, - action("이 장", "이 장만 이 배치로", onApply), - action("모든 장", "모든 장을 이 배치로(장마다 값 지움)", onAllDefault), + row(cols.root, rows.root), + row( + action("이 장", "이 장만 이 배치로", onApply), + action("모든 장", "모든 장을 이 배치로(장마다 값 지움)", onAllDefault), + ), ); if (extras.scale) { const { onApply: applyScale } = extras.scale; @@ -255,7 +263,7 @@ export function buildCrossLayoutControls( }, }); button.title = "모든 장의 표 · 표 위 글 크기(기본 1)"; - box.append(scale.root, button); + box.append(row(scale.root, button)); } return box; } diff --git a/B07_DesignDetail/B07_DesignDetail_UI_Style.css b/B07_DesignDetail/B07_DesignDetail_UI_Style.css index e99f64b41..71c501fb3 100644 --- a/B07_DesignDetail/B07_DesignDetail_UI_Style.css +++ b/B07_DesignDetail/B07_DesignDetail_UI_Style.css @@ -225,8 +225,9 @@ color: var(--color-text-muted); } -/* 횡단 장 배치(153-1) — 횡단도 묶음 맨 아래 전체 폭: 배치 열 [4] 행 [4] [이 장] [모든 장]. - 좌측이 182px 까지 줄어드니 줄바꿈 허용 · 공용 칸의 오류 자리는 접음. */ +/* 횡단 장 배치(153-1 · 212-3) — 횡단도 묶음 맨 아래 전체 폭 · 한 줄에 한 일: + 배치 열 · 행 / [이 장] [모든 장] / 표 배율 [배율 적용] / [안내선 숨기기]. + 단추 · 숫자 칸 = 줄 위 작은 단추 한 규격(`--btn-bar-*` · 높이 16) · 한 줄 안 같은 너비 · 오류 자리는 접음. */ .b07-cross-layout-slot { grid-column: 1 / -1; } @@ -237,25 +238,45 @@ .b07-cross-layout { display: flex; - flex-wrap: wrap; - align-items: center; - gap: var(--spacing-4) var(--spacing-8); + flex-direction: column; + gap: var(--spacing-4); } -.b07-cross-layout > .ui-field { +.b07-cross-layout__row { + display: flex; + align-items: center; + gap: var(--spacing-8); +} + +.b07-cross-layout__row > * { + flex: 1 1 0; + min-width: 0; +} + +.b07-cross-layout .ui-field { display: flex; flex-direction: row; align-items: center; gap: var(--spacing-4); } +.b07-cross-layout .ui-btn, +.b07-cross-layout .ui-field .ui-input { + height: auto; + padding: var(--btn-bar-padding); + font-size: var(--btn-bar-font-size); + line-height: 1; +} + .b07-cross-layout .ui-field__label { margin: 0; + font-size: var(--btn-bar-font-size); white-space: nowrap; } -.b07-cross-layout .ui-input { - width: 48px; +.b07-cross-layout .ui-field .ui-input { + width: 100%; + min-width: 0; } .b07-cross-layout .ui-field__error { diff --git a/resources/tester/test_212_3_b07_cross_layout_buttons.py b/resources/tester/test_212_3_b07_cross_layout_buttons.py new file mode 100644 index 000000000..3b51c0f18 --- /dev/null +++ b/resources/tester/test_212_3_b07_cross_layout_buttons.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +"""212-3 B07 좌측 「횡단도」 배치 칸 — 단추 · 숫자 칸 한 규격 · 한 줄에 한 일 · 남는 검은 선 판정. + +못박는 것 + - 줄 = 「배치 열 · 행」 · 「[이 장] [모든 장]」 · 「표 배율 [배율 적용]」 + · [안내선 숨기기](부르는 쪽). + - 단추 · 숫자 칸 · 이름 = 줄 위 작은 단추 토큰(187 `--btn-bar-*` · 높이 16) · 한 줄 안 같은 너비. + - 옛 한 줄 감기(flex-wrap) · 숫자 칸 48px 고정 없음. + - [안내선 숨기기] 뒤 남는 검은 선 = 인쇄 칸선(도각 층 도면 개체 · 회청 `#8ea0b5` + · 밝은 바탕에서 짙게 그려짐) — 안내선(겹판)과 다른 것이라 지우지 않음 · 단추는 겹판만 끔. +""" + +import re +from pathlib import Path + +from B07_DesignDetail.B07_DesignDetail_Engine_Cad import FRAME_LAYER_ID, TABLE_LINE_COLOR +from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import _grid_lines +from B07_DesignDetail.B07_DesignDetail_Engine_Template import use_project_templates + +ROOT = Path(__file__).resolve().parents[2] +CSS = (ROOT / "B07_DesignDetail/B07_DesignDetail_UI_Style.css").read_text(encoding="utf-8") +PANELS = (ROOT / "B07_DesignDetail/B07_DesignDetail_UI_Panels.ts").read_text(encoding="utf-8") +PAGE = (ROOT / "B07_DesignDetail/B07_DesignDetail_UI_Page.ts").read_text(encoding="utf-8") +GUIDE = (ROOT / "B07_DesignDetail/openwebcad/src/integration/guide-area.ts").read_text( + encoding="utf-8" +) +THEME = (ROOT / "ui_template/ui_template_theme.css").read_text(encoding="utf-8") + + +def _block(selector: str) -> str: + m = re.search(r"(?:^|\n)" + re.escape(selector) + r"\s*\{([^}]*)\}", CSS) + assert m, selector + return m.group(1) + + +def _decl(block: str, prop: str) -> str: + m = re.search(r"(?:^|;|\s)" + re.escape(prop) + r"\s*:\s*([^;]+);", block) + assert m, prop + return m.group(1).strip() + + +def test_단추_숫자_칸_한_규격_토큰(): + shared = _block(".b07-cross-layout .ui-btn,\n.b07-cross-layout .ui-field .ui-input") + assert _decl(shared, "padding") == "var(--btn-bar-padding)" + assert _decl(shared, "font-size") == "var(--btn-bar-font-size)" + assert _decl(shared, "line-height") == "1" + assert _decl(shared, "height") == "auto" # 공용 작은 칸 30px 를 풀어 단추 높이에 맞춤 + label = _block(".b07-cross-layout .ui-field__label") + assert _decl(label, "font-size") == "var(--btn-bar-font-size)" + # 토큰은 theme 한 곳(187) + assert "--btn-bar-padding: 1px var(--spacing-8);" in THEME + assert "--btn-bar-font-size: var(--text-caption);" in THEME + + +def test_한_줄에_한_일_같은_너비(): + box = _block(".b07-cross-layout") + assert _decl(box, "flex-direction") == "column" + assert "flex-wrap" not in box + assert _decl(_block(".b07-cross-layout__row > *"), "flex") == "1 1 0" + assert "width: 48px" not in CSS + assert 'line.className = "b07-cross-layout__row"' in PANELS + assert "row(cols.root, rows.root)" in PANELS + assert re.search(r"row\(\s*action\(\"이 장\"[^;]*action\(\"모든 장\"", PANELS, re.S) + assert "box.append(row(scale.root, button));" in PANELS + # 안내선 단추 = 배치 칸 맨 아래 한 줄(전체 폭) + assert "controls.append(guideToggle);" in PAGE + + +def test_남는_검은_선은_인쇄_칸선_지우지_않음(): + use_project_templates(None) + lines = _grid_lines("cross_s00000m", 4, 4) + assert len(lines) == 10 # 세로 5 · 가로 5 그대로 + assert all(e["layerId"] == FRAME_LAYER_ID for e in lines) + assert all(e["lineColor"] == TABLE_LINE_COLOR == "#8ea0b5" for e in lines) + # 안내선 단추는 겹판(점선 영역 + 붉은 틀)만 — 도면 개체를 안 건드림 + draw = GUIDE[GUIDE.index("export function drawGuide") :] + draw = draw[: draw.index("\n}\n")] + assert "if (!guide || !guideVisible) return;" in draw + assert "getEntities" not in draw and "setEntities" not in draw