diff --git a/A00_Common/design/design_panel.ts b/A00_Common/design/design_panel.ts index 47e3a136d..0c3418605 100644 --- a/A00_Common/design/design_panel.ts +++ b/A00_Common/design/design_panel.ts @@ -82,15 +82,15 @@ export function createSectionFrame( heading.textContent = title; if (view === "profile") { // ui-sidebar-section: 사이드 컨테이너 공통 외곽선(진하게, 2026-08-05 사용자 지시). + // ui-panel-form: 좌측 패널 컨테이너 공용 양식(269 · 「구조물 배치」 기준 칸 · 라벨 · 단추) — 두 보기 모두. // 접힘 초기값만 여기서 준다 — 펼치기·접기 토글은 ui_template_collapsible 전역 몫. - root.className = `b05-route__panel-section ui-collapsible ui-sidebar-section${collapsed ? " is-collapsed" : ""}`; + root.className = `b05-route__panel-section ui-collapsible ui-sidebar-section ui-panel-form${collapsed ? " is-collapsed" : ""}`; heading.className = "ui-collapsible__title"; const body = document.createElement("div"); body.className = "b05-route__panel-body"; root.append(heading, body); return { root, body }; } - // ui-panel-form: 좌측 패널 컨테이너 공용 양식(269 · 「구조물 배치」 기준 칸 · 라벨 · 단추). root.className = `b06-profile__group ui-collapsible ui-sidebar-section ui-panel-form${collapsed ? " is-collapsed" : ""}`; heading.className = "b06-profile__group-legend ui-collapsible__title"; root.append(heading); diff --git a/B08_Quantity/B08_Quantity_UI_Options.ts b/B08_Quantity/B08_Quantity_UI_Options.ts index 62d0a3147..9255a5cf5 100644 --- a/B08_Quantity/B08_Quantity_UI_Options.ts +++ b/B08_Quantity/B08_Quantity_UI_Options.ts @@ -21,7 +21,7 @@ import { showLoadingOverlay, showToast, } from "@ui/ui_template_elements"; -import { attachCollapsible } from "@ui/ui_template_collapsible"; +import { attachCollapsible, createPanelCard } from "@ui/ui_template_collapsible"; import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; import { clearState, readState, writeState } from "../A00_Common/b_page_state"; import { request } from "./B08_Quantity_UI_Hydro"; @@ -85,14 +85,11 @@ export function onOptionsChange(host: HTMLElement, fn: () => void): void { const pctText = (value: number): string => String(Math.round(value * 1e6) / 1e6); +/** 카드 = 좌측 패널 공용 카드(269 · 「구조물 배치」 양식). */ function card(id: string, title: keyof typeof ui_locales, ...body: HTMLElement[]): HTMLElement { - const section = document.createElement("section"); - section.className = "ui-collapsible ui-sidebar-section ui-sidebar-card"; + const section = createPanelCard(L(title)); section.dataset.b08Options = id; - const heading = document.createElement("h3"); - heading.className = "ui-collapsible__title"; - heading.textContent = L(title); - section.append(heading, ...body); + section.append(...body); return section; } diff --git a/B08_Quantity/B08_Quantity_UI_Page.css b/B08_Quantity/B08_Quantity_UI_Page.css index e07c12821..c443aea19 100644 --- a/B08_Quantity/B08_Quantity_UI_Page.css +++ b/B08_Quantity/B08_Quantity_UI_Page.css @@ -82,8 +82,9 @@ grid-template-columns: repeat(3, minmax(0, 1fr)); } +/* 칸과의 틈 = 공용 카드 틈(269) */ .b08-options__hint { - margin: var(--spacing-4) 0 0; + margin: 0; color: var(--color-text-secondary); font-size: var(--text-caption); } diff --git a/B09_Estimation/B09_Estimation_UI_Page.css b/B09_Estimation/B09_Estimation_UI_Page.css index b82d9cb06..81137921b 100644 --- a/B09_Estimation/B09_Estimation_UI_Page.css +++ b/B09_Estimation/B09_Estimation_UI_Page.css @@ -179,9 +179,9 @@ background: var(--color-warning); } -/* 122 데이터 점검 [업데이트] — 제목 아래 틈 = 카드 왼쪽 여유와 같게 */ +/* 122 데이터 점검 [업데이트] — 제목 아래 틈 = 카드 왼쪽 여유와 같게(공용 카드 틈 8 + 4) */ .b09-options [data-b09-refresh] { - margin-top: calc(var(--spacing-8) + var(--spacing-4)); + margin-top: var(--spacing-4); } /* 연결 점검 목록 — 이름 · 탭(작게) · 알림 글 */ diff --git a/B09_Estimation/B09_Estimation_UI_Shell.ts b/B09_Estimation/B09_Estimation_UI_Shell.ts index 474e7bcf1..725235b36 100644 --- a/B09_Estimation/B09_Estimation_UI_Shell.ts +++ b/B09_Estimation/B09_Estimation_UI_Shell.ts @@ -19,7 +19,7 @@ import { showLoadingOverlay, showToast, } from "@ui/ui_template_elements"; -import { attachCollapsible } from "@ui/ui_template_collapsible"; +import { attachCollapsible, createPanelCard } from "@ui/ui_template_collapsible"; import { cellColumn, createDataTable, type DataTable } from "@ui/ui_template_data_table"; import { attachHoverInfo } from "@ui/ui_template_hover_info"; import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; @@ -359,12 +359,9 @@ export function renderB09Estimation(root: HTMLElement): void { }), }); reset.dataset.b09BillReset = ""; - const section = document.createElement("section"); - section.className = "ui-collapsible ui-sidebar-section ui-sidebar-card"; - const heading = document.createElement("h3"); - heading.className = "ui-collapsible__title"; - heading.textContent = "데이터 점검"; - section.append(heading, refresh, check.list); + // 카드 = 좌측 패널 공용 카드(269 · 「구조물 배치」 양식) + const section = createPanelCard("데이터 점검"); + section.append(refresh, check.list); const actions = document.createElement("div"); actions.className = "ui-sidebar-actions"; actions.append(save, reset); diff --git a/resources/tester/test_122_b08_options.py b/resources/tester/test_122_b08_options.py index 3e87c8bfd..6db3d80ed 100644 --- a/resources/tester/test_122_b08_options.py +++ b/resources/tester/test_122_b08_options.py @@ -156,7 +156,7 @@ def test_좌측_패널_컨테이너_셋과_저장_줄(): ) assert 'card("summary", "B08_Quantity_Tab_StructureSummary", one.root),' in panel assert 'card("material", "B08_Quantity_Tab_Materials", ...surchargeBlock),' in panel - assert "ui-collapsible ui-sidebar-section ui-sidebar-card" in panel + assert "createPanelCard(L(title))" in panel # 269 좌측 패널 공용 카드 assert ( 'actions.className = "ui-sidebar-actions"' in panel and "attachCollapsible(panel)" in panel ) diff --git a/resources/tester/test_203_1_b09_data_check.py b/resources/tester/test_203_1_b09_data_check.py index 2beecbcb5..804896861 100644 --- a/resources/tester/test_203_1_b09_data_check.py +++ b/resources/tester/test_203_1_b09_data_check.py @@ -21,11 +21,11 @@ SHELL = (ROOT / "B09_Estimation/B09_Estimation_UI_Shell.ts").read_text("utf-8") def test_묶음_이름_단추_글_설명_없음(): - assert 'heading.textContent = "데이터 점검";' in SHELL and "원가 단가" not in SHELL + assert 'createPanelCard("데이터 점검")' in SHELL and "원가 단가" not in SHELL # 269 공용 카드 assert 'label: "업데이트",' in SHELL and "단가 갱신" not in SHELL # 옛 굳힌 시각 안내 글(made) 없음 · 묶음 = 제목 · [업데이트] · 연결 점검 assert "made.textContent" not in SHELL and "b09-estimation__hint" not in SHELL - assert "section.append(heading, refresh, check.list);" in SHELL + assert "section.append(refresh, check.list);" in SHELL # 제목 = 공용 카드(269) assert "check.draw(payload, sheets);" in SHELL diff --git a/resources/tester/test_240_1_b09_bill_folder_font_drag.py b/resources/tester/test_240_1_b09_bill_folder_font_drag.py index eda6e364f..ec2b4564f 100644 --- a/resources/tester/test_240_1_b09_bill_folder_font_drag.py +++ b/resources/tester/test_240_1_b09_bill_folder_font_drag.py @@ -48,7 +48,12 @@ def test_끌기_미리_보기_어디_안_몇_레벨_폴더_테두리_끄는_줄_ def test_데이터_점검_업데이트_위_틈_카드_왼쪽_여유와_같음(): css = _read(B09 / "B09_Estimation_UI_Page.css") m = re.search(r"\.b09-options \[data-b09-refresh\] \{\s*margin-top: ([^;]+);", css) - assert m and m.group(1) == "calc(var(--spacing-8) + var(--spacing-4))" + # 269 — 공용 카드 틈(8) + 4 = 카드 왼쪽 여유(12) + assert m and m.group(1) == "var(--spacing-4)" + overlay = _read(ROOT / "ui_template" / "ui_template_overlay.css") + assert re.search( + r":where\(\.ui-sidebar-card\.ui-panel-form\) \{[^}]*gap: var\(--spacing-8\);", overlay + ) card = _read(ROOT / "ui_template" / "ui_template_overlay.css") assert re.search( r"\.ui-sidebar-card \{\s*padding: calc\(var\(--spacing-8\) \+ var\(--spacing-4\)\);", card diff --git a/resources/tester/test_269_panel_form.py b/resources/tester/test_269_panel_form.py index 124de92bc..33ff60bd2 100644 --- a/resources/tester/test_269_panel_form.py +++ b/resources/tester/test_269_panel_form.py @@ -122,3 +122,53 @@ def test_B06_컨테이너_공용_양식(): rule = _block(B06_CSS, line[:-2]) for prop in ("height", "padding", "font-size"): assert not re.search(r"(?:^|\s)" + prop + r"\s*:", rule), (line, prop) + + +B08_OPTIONS = _read("B08_Quantity/B08_Quantity_UI_Options.ts") +B09_SHELL = _read("B09_Estimation/B09_Estimation_UI_Shell.ts") +# 기준(「구조물 배치」 폼 자신) · 부르는 곳 없는 옛 함수는 점검 밖 +PANEL_DIRS = ( + "A00_Common/design", + "B05_Profile", + "B06_Section", + "B07_DesignDetail", + "B08_Quantity", + "B09_Estimation", +) +REFERENCE = { + "B05_Profile/B05_Profile_UI_Structures_Form.ts", + "B05_Profile/B05_Profile_UI_IrregularStations.ts", +} + + +def test_B05_칸_틀_두_보기_모두_공용_양식(): + frame = DESIGN_PANEL[DESIGN_PANEL.index("export function createSectionFrame(") :] + frame = frame[: frame.index("\n}\n")] + assert frame.count("ui-sidebar-section ui-panel-form") == 2 + + +def test_B08_B09_카드_공용_카드(): + assert "createPanelCard(L(title))" in B08_OPTIONS + assert 'createPanelCard("데이터 점검")' in B09_SHELL + for src in (B08_OPTIONS, B09_SHELL): + assert "ui-sidebar-card" not in src + + +def test_점검_B05_이상_좌측_컨테이너는_모두_공용_양식(): + """손으로 짠 컨테이너(ui-sidebar-section)에 공용 양식이 빠진 곳 = 0.""" + misses, users = [], [] + for page in PANEL_DIRS: + for path in (ROOT / page).glob("*.ts"): + rel = path.relative_to(ROOT).as_posix() + text = path.read_text(encoding="utf-8") + if "createIrregularStationsSection(" in text: + users.append(path.name) + if rel in REFERENCE: + continue + for n, line in enumerate(text.splitlines(), 1): + code = line.split("//")[0] + if "ui-sidebar-section" in code and "ui-panel-form" not in code: + misses.append(f"{rel}:{n}") + assert misses == [] + # 점검 밖 옛 함수 = 정말 부르는 곳 없음(살아나면 점검에 넣음) + assert users == ["B05_Profile_UI_IrregularStations.ts"]