diff --git a/A00_Common/design/design_page.css b/A00_Common/design/design_page.css index 81881acbe..c2944578a 100644 --- a/A00_Common/design/design_page.css +++ b/A00_Common/design/design_page.css @@ -55,16 +55,9 @@ display: none; } -.b05-route-profile__tools > .b05-route-profile__popout { - padding: var(--btn-bar-padding); - font-size: var(--btn-bar-font-size); -} - -/* 유토곡선 · 배수유역 판 [창 분할](계획서 325-3) — 종단 [창 분할]과 같은 양식 · 유토는 판 좌상단에 띄움. */ +/* 유토곡선 · 배수유역 판 [창 분할](계획서 325-3) — 종단 [창 분할]과 같은 양식(부품 단추 단 18 · 553-2) · 유토는 판 좌상단에 띄움. */ .b05-subpanel__popout { flex: none; - padding: var(--btn-bar-padding); - font-size: var(--btn-bar-font-size); } .b05-profile__masshaul-overlay > .b05-subpanel__popout { diff --git a/A00_Common/design/design_profile_popout.ts b/A00_Common/design/design_profile_popout.ts index ccd46475f..d36372f5b 100644 --- a/A00_Common/design/design_profile_popout.ts +++ b/A00_Common/design/design_profile_popout.ts @@ -123,6 +123,7 @@ export function attachProfilePopout( button: HTMLButtonElement, ): void { button.className = "ui-btn ui-btn--ghost b05-route-profile__popout"; + button.setAttribute("data-size", "18"); // 부품 단추 단 18(553-2) — 옆 도구 단추와 같은 키 button.title = "종단 하단 패널을 별도 창으로 분할 · 다시 누르면 결합"; attachPopout(profile, { piece: profile.root, @@ -139,6 +140,7 @@ export function attachSubPanelPopouts(profile: DesignProfile): void { const subButton = (title: string): HTMLButtonElement => { const button = document.createElement("button"); button.className = "ui-btn ui-btn--ghost b05-subpanel__popout"; + button.setAttribute("data-size", "18"); button.title = title; return button; }; diff --git a/A00_Common/design/design_structure_form.ts b/A00_Common/design/design_structure_form.ts index fac7319a8..bcaa84741 100644 --- a/A00_Common/design/design_structure_form.ts +++ b/A00_Common/design/design_structure_form.ts @@ -283,7 +283,7 @@ export function createSpecForm(links: SpecLinks | null, hooks: SpecFormHooks = { if (!c.required && hooks.count?.(c) === undefined) { const remove = document.createElement("button"); remove.type = "button"; - remove.className = "b05-route__irregular-btn"; + remove.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn"; remove.textContent = "삭제"; remove.title = `${c.title} 상자 삭제`; remove.dataset.specRemove = c.id; @@ -333,7 +333,7 @@ export function createSpecForm(links: SpecLinks | null, hooks: SpecFormHooks = { if (c.required || (c.max && list.length >= c.max) || hooks.count?.(c) !== undefined) continue; const add = document.createElement("button"); add.type = "button"; - add.className = "b05-route__irregular-btn b05-structure__spec-add"; + add.className = "ui-btn ui-btn--ghost b05-route__irregular-btn b05-structure__spec-add"; add.textContent = `+ ${containerTitle(c, names, groupNames[c.link])}`; add.title = "추가"; add.dataset.specAdd = c.id; diff --git a/B05_Profile/B05_Profile_UI_Drainage_Chrome.ts b/B05_Profile/B05_Profile_UI_Drainage_Chrome.ts index 0aa64a118..3b4dbbd66 100644 --- a/B05_Profile/B05_Profile_UI_Drainage_Chrome.ts +++ b/B05_Profile/B05_Profile_UI_Drainage_Chrome.ts @@ -72,11 +72,15 @@ export function createDrainageChrome( const updateButton = document.createElement("button"); updateButton.type = "button"; - updateButton.className = "b05-drainage__analyze b05-drainage__update"; + // 머리 줄 단추 = 부품 「단추」 보조 · 단 22(553-2) + updateButton.className = "ui-btn ui-btn--ghost b05-drainage__analyze b05-drainage__update"; + updateButton.setAttribute("data-size", "22"); updateButton.textContent = "업데이트"; const proposeButton = document.createElement("button"); proposeButton.type = "button"; - proposeButton.className = "b05-drainage__analyze b05-drainage__tool b05-drainage__propose"; + proposeButton.className = + "ui-btn ui-btn--ghost b05-drainage__analyze b05-drainage__tool b05-drainage__propose"; + proposeButton.setAttribute("data-size", "22"); proposeButton.textContent = ui_locales.B05_Drainage_Btn_Propose[currentLanguageIndex]; proposeButton.title = ui_locales.B05_Drainage_Btn_Propose_Tip[currentLanguageIndex]; header.append(updateButton, proposeButton); diff --git a/B05_Profile/B05_Profile_UI_Drainage_Facility_Fields.ts b/B05_Profile/B05_Profile_UI_Drainage_Facility_Fields.ts index 76f3b12a8..d614f25f1 100644 --- a/B05_Profile/B05_Profile_UI_Drainage_Facility_Fields.ts +++ b/B05_Profile/B05_Profile_UI_Drainage_Facility_Fields.ts @@ -121,9 +121,12 @@ export function stepper(input: HTMLInputElement, stepM: number, decimals = 1): H const button = (label: string, delta: number): HTMLButtonElement => { const element = document.createElement("button"); element.type = "button"; - element.className = "b05-structure__step"; + // 부품 「아이콘 단추」 단 24(553-2) · 폭은 덩어리 몫(28) + element.className = "ui-icon-btn ui-icon-btn--ghost b05-structure__step"; + element.setAttribute("data-size", "24"); element.textContent = label; element.title = `${stepM}m ${delta > 0 ? "늘리기" : "줄이기"}`; + element.setAttribute("aria-label", element.title); element.addEventListener("click", nudge(delta)); return element; }; diff --git a/B05_Profile/B05_Profile_UI_Drainage_Parts.ts b/B05_Profile/B05_Profile_UI_Drainage_Parts.ts index 5bef30fc6..1c74d1464 100644 --- a/B05_Profile/B05_Profile_UI_Drainage_Parts.ts +++ b/B05_Profile/B05_Profile_UI_Drainage_Parts.ts @@ -10,6 +10,8 @@ import { stateKey } from "../A00_Common/b_page_state"; import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { themeColor } from "@ui/ui_template_palette"; import { showToast } from "@ui/ui_template_elements"; +import { createToggle } from "@ui/ui_template_toggle"; +import { setListRowActive } from "@ui/ui_template_list_row"; import { DRAINAGE_SHEET_LAYERS, fetchCachedSheetLayer, @@ -92,7 +94,7 @@ export function formatArea(areaM2: number): string { return areaM2 >= 10000 ? `${(areaM2 / 10000).toFixed(2)}ha` : `${Math.round(areaM2)}㎡`; } -/** 제목 우측 표시 토글 — 등고선·세류·흐름 화살표·상류 세류가 모두 같은 양식을 쓴다. */ +/** 제목 우측 표시 토글 — 등고선·세류·흐름 화살표·상류 세류가 모두 같은 양식(부품 「켜기」 칩 · 단 20 · 색 점 = 층 색 · 553-2). */ export function addLayerToggle( container: HTMLElement, label: string, @@ -101,20 +103,20 @@ export function addLayerToggle( onToggle: (next: boolean) => void, title?: string, ): HTMLButtonElement { - const button = document.createElement("button"); - button.type = "button"; - button.className = "b05-drainage__layer-button" + (initial ? " is-active" : ""); - button.textContent = label; - button.style.setProperty("--b05-layer-color", color); - button.setAttribute("aria-pressed", String(initial)); + const button = createToggle({ + kind: "chip", + label, + checked: initial, + dot: color, + size: 20, + onChange: (active) => { + button.classList.toggle("is-active", active); + onToggle(active); + }, + }).root; + button.classList.add("b05-drainage__layer-button"); + button.classList.toggle("is-active", initial); if (title) button.title = title; - let active = initial; - button.addEventListener("click", () => { - active = !active; - button.classList.toggle("is-active", active); - button.setAttribute("aria-pressed", String(active)); - onToggle(active); - }); container.append(button); return button; } @@ -140,7 +142,12 @@ export function renderBasinRows( basins.forEach((basin) => { const row = document.createElement("button"); row.type = "button"; - row.className = "b05-drainage__basin" + (selected === basin.index ? " is-selected" : ""); + // 목록 줄 = 부품 「목록 줄」 단 32(553-2) · 옛 이름(is-selected)은 자리 몫 + const picked = selected === basin.index; + row.className = + "ui-list-row ui-sidebar-row b05-drainage__basin" + (picked ? " is-selected" : ""); + row.setAttribute("data-size", "32"); + setListRowActive(row, picked); const badge = document.createElement("span"); badge.className = "b05-drainage__basin-index"; badge.textContent = String(basin.index); diff --git a/B05_Profile/B05_Profile_UI_Drainage_Propose.ts b/B05_Profile/B05_Profile_UI_Drainage_Propose.ts index a97bac2b7..947350dd9 100644 --- a/B05_Profile/B05_Profile_UI_Drainage_Propose.ts +++ b/B05_Profile/B05_Profile_UI_Drainage_Propose.ts @@ -58,12 +58,14 @@ export function createDrainagePropose(deps: { const title = document.createElement("span"); const add = document.createElement("button"); add.type = "button"; - add.className = "b05-drainage__analyze"; + add.className = "ui-btn ui-btn--filled b05-drainage__analyze"; + add.setAttribute("data-size", "22"); add.textContent = "넣기"; add.title = "고른 후보만 관으로 넣습니다 — 초안이라 [저장] 때 남습니다."; const shut = document.createElement("button"); shut.type = "button"; - shut.className = "b05-drainage__analyze"; + shut.className = "ui-btn ui-btn--ghost b05-drainage__analyze"; + shut.setAttribute("data-size", "22"); shut.textContent = "닫기"; head.append(title, add, shut); const list = document.createElement("ul"); diff --git a/B05_Profile/B05_Profile_UI_Drainage_Suggest.ts b/B05_Profile/B05_Profile_UI_Drainage_Suggest.ts index 6ba8725a4..0e75b43e2 100644 --- a/B05_Profile/B05_Profile_UI_Drainage_Suggest.ts +++ b/B05_Profile/B05_Profile_UI_Drainage_Suggest.ts @@ -51,7 +51,8 @@ export function createDrainageSuggest( const title = document.createElement("span"); const apply = document.createElement("button"); apply.type = "button"; - apply.className = "b05-drainage__analyze"; + apply.className = "ui-btn ui-btn--filled b05-drainage__analyze"; + apply.setAttribute("data-size", "22"); apply.textContent = "적용"; const applyTitle = "관경 · 시설 제안을 관 초안에 한 번에 얹습니다 — [저장] 때 남습니다(관 자리는 [자동 제안])."; diff --git a/B05_Profile/B05_Profile_UI_IrregularStations.ts b/B05_Profile/B05_Profile_UI_IrregularStations.ts index 1286d9d34..ba28b9afb 100644 --- a/B05_Profile/B05_Profile_UI_IrregularStations.ts +++ b/B05_Profile/B05_Profile_UI_IrregularStations.ts @@ -233,14 +233,14 @@ export function createIrregularStationsSection( const primary = document.createElement("button"); primary.type = "button"; - primary.className = "b05-route__irregular-btn is-primary"; + primary.className = "ui-btn ui-btn--filled b05-route__irregular-btn is-primary"; const remove = document.createElement("button"); remove.type = "button"; - remove.className = "b05-route__irregular-btn is-danger"; + remove.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn is-danger"; remove.textContent = "삭제"; const reset = document.createElement("button"); reset.type = "button"; - reset.className = "b05-route__irregular-btn"; + reset.className = "ui-btn ui-btn--ghost b05-route__irregular-btn"; reset.textContent = "리셋"; const actions = document.createElement("div"); actions.className = "b05-route__irregular-actions"; diff --git a/B05_Profile/B05_Profile_UI_Panel_View.ts b/B05_Profile/B05_Profile_UI_Panel_View.ts index 1997915b0..2d7a36b3d 100644 --- a/B05_Profile/B05_Profile_UI_Panel_View.ts +++ b/B05_Profile/B05_Profile_UI_Panel_View.ts @@ -109,6 +109,10 @@ export function createViewControls(callbacks: ViewControlCallbacks): { separator3, corridorRefresh, ); + // 보기 줄 단추 = 부품 단추 단 32(553-2) + viewControls + .querySelectorAll(".ui-btn") + .forEach((b) => b.setAttribute("data-size", "32")); return { viewControls, diff --git a/B05_Profile/B05_Profile_UI_Profile_Edit.ts b/B05_Profile/B05_Profile_UI_Profile_Edit.ts index d9f92806e..1e3b515f9 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Edit.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Edit.ts @@ -23,7 +23,7 @@ import { viewOf } from "../common_util/common_util_view_window"; /** 버튼 폭(21px)에 여유를 더한 값 — 이보다 가까우면 겹친 것으로 본다. */ const BUTTON_CLEARANCE_PX = 23; -const BUTTON_HALF_PX = 10; +const BUTTON_HALF_PX = 9; // 부품 아이콘 단추 단 18 의 반(553-2) /** 길게 누르기: 이만큼 유지하면 반복이 시작되고, 그 뒤 초당 10회(0.1m씩)로 이어진다. */ export const HOLD_DELAY_MS = 500; export const HOLD_INTERVAL_MS = 100; @@ -286,8 +286,11 @@ function overlayButton( ): HTMLButtonElement { const button = document.createElement("button"); button.type = "button"; - button.className = `b05-profile-edit__btn ${className}`; + // 부품 「아이콘 단추」 선 꼴 · 단 18(553-2) · 글자 기호 그대로 + button.className = `ui-icon-btn ui-icon-btn--outline b05-profile-edit__btn ${className}`; + button.setAttribute("data-size", "18"); button.textContent = glyph; + button.setAttribute("aria-label", title); button.title = `${title}\n(길게 누르면 0.5초 뒤부터 연속 조정)`; // 포인터로 누르면 즉시 1회 반응하고, 이어지는 click은 중복이므로 삼킨다. let swallowClick = false; diff --git a/B05_Profile/B05_Profile_UI_Profile_Tools.ts b/B05_Profile/B05_Profile_UI_Profile_Tools.ts index d6a51f530..0bbb72a9b 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Tools.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Tools.ts @@ -73,10 +73,12 @@ export interface ProfileTools { mountOverlay: (geom: FreeChartGeom) => void; } +/** 도구 단추 = 부품 「단추」 유리 · 단 18(553-2 · 클래스로 — 시험이 별칭 없이 이 파일을 돌림) */ function toolButton(label: string, title: string, onClick: () => void): HTMLButtonElement { const button = document.createElement("button"); button.type = "button"; - button.className = "b05-route-profile__tool"; + button.className = "ui-btn ui-btn--glass b05-route-profile__tool"; + button.setAttribute("data-size", "18"); button.textContent = label; button.title = title; button.addEventListener("click", (event) => { diff --git a/B05_Profile/B05_Profile_UI_Profile_Zoom.ts b/B05_Profile/B05_Profile_UI_Profile_Zoom.ts index fb29af27f..7d5c687cc 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Zoom.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Zoom.ts @@ -45,9 +45,12 @@ export function createProfileZoom(onChange: () => void): ProfileZoom { function add(label: string, title: string, action: () => void): HTMLButtonElement { const button = document.createElement("button"); button.type = "button"; - button.className = "b05-profile__zoom-btn"; + // 부품 「아이콘 단추」 단 22(553-2) · 글자 기호 그대로 + button.className = "ui-icon-btn ui-icon-btn--ghost b05-profile__zoom-btn"; + button.setAttribute("data-size", "22"); button.textContent = label; button.title = title; + button.setAttribute("aria-label", title); button.addEventListener("click", (event) => { // 카드·측점 선택으로 번지면 그래프를 다시 그리며 방금 맞춘 배율이 날아간다. event.stopPropagation(); diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts index 00e895126..f79b34212 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts @@ -1,4 +1,4 @@ -import { createButton } from "@ui/ui_template_elements"; +import { createIconButton } from "@ui/ui_template_elements"; import { createSidePanels } from "@ui/ui_template_side_panels"; import { bindFootprintToggle } from "./B05_Profile_UI_RouteEdit_Footprint"; import { bindMarkToggle } from "./B05_Profile_UI_RouteEdit_Marks"; @@ -80,38 +80,38 @@ export function createRouteEditChrome(
- - - - - - - - - - -
@@ -148,7 +148,10 @@ export function createRouteEditChrome( `; parent.append(overlay); // 부른 쪽 단추([3D])도 같은 유리 단추로 — 지도 위에 떠 있는 도구줄. - tools.forEach((tool) => tool.classList.replace("ui-btn--ghost", "ui-btn--glass")); + tools.forEach((tool) => { + tool.classList.replace("ui-btn--ghost", "ui-btn--glass"); + tool.setAttribute("data-size", "32"); // 도구줄 단 32(553-2) + }); overlay.querySelector('[data-slot="tools"]')!.replaceWith(...tools); // 횡단 미리보기 = 패널 양식(382-1 · 손잡이 · 머리 · 폭 끌기 · 열림 · 폭 기억) · 자리는 지도 위 덮개 그대로 · @@ -223,10 +226,8 @@ export function createRouteEditChrome( lift.observe(info); lift.observe(overlay.querySelector(".b05-routeedit__side-wrap")!); - const measureCloseButton = createButton({ label: "✕", variant: "ghost" }); + const measureCloseButton = createIconButton({ glyph: "✕", title: "닫기", size: 24 }); measureCloseButton.classList.add("b05-routeedit__measure-close"); - measureCloseButton.setAttribute("aria-label", "닫기"); - measureCloseButton.title = "닫기"; overlay.querySelector('[data-slot="measure-close"]')!.replaceWith(measureCloseButton); const canvas = overlay.querySelector(".b05-routeedit__canvas")!; diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts index b215188ce..49138e2f5 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts @@ -31,9 +31,9 @@ const FIT_MARGIN_M = 30; export const RECOMMEND_MAP_HTML = `
- -
`; diff --git a/B05_Profile/B05_Profile_UI_Structures_Form.ts b/B05_Profile/B05_Profile_UI_Structures_Form.ts index 6dd1af3be..7c23f88de 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Form.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Form.ts @@ -123,14 +123,14 @@ export function buildStructuresForm(options: { const primary = document.createElement("button"); primary.type = "button"; - primary.className = "b05-route__irregular-btn is-primary"; + primary.className = "ui-btn ui-btn--filled b05-route__irregular-btn is-primary"; const removeButton = document.createElement("button"); removeButton.type = "button"; - removeButton.className = "b05-route__irregular-btn is-danger"; + removeButton.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn is-danger"; removeButton.textContent = "삭제"; const resetButton = document.createElement("button"); resetButton.type = "button"; - resetButton.className = "b05-route__irregular-btn"; + resetButton.className = "ui-btn ui-btn--ghost b05-route__irregular-btn"; resetButton.textContent = "리셋"; const actions = document.createElement("div"); actions.className = "b05-route__irregular-actions"; diff --git a/B05_Profile/B05_Profile_UI_Structures_List.ts b/B05_Profile/B05_Profile_UI_Structures_List.ts index 9d30940a8..2570cf353 100644 --- a/B05_Profile/B05_Profile_UI_Structures_List.ts +++ b/B05_Profile/B05_Profile_UI_Structures_List.ts @@ -249,7 +249,7 @@ export function renderStructureList(params: StructureListParams): void { clear.className = "b05-route__irregular-clear"; const button = document.createElement("button"); button.type = "button"; - button.className = "b05-route__irregular-btn is-danger"; + button.className = "ui-btn ui-btn--danger-ghost b05-route__irregular-btn is-danger"; button.textContent = "구조물 전체 삭제"; button.addEventListener("click", params.onClearAll); clear.append(button); diff --git a/B05_Profile/B05_Profile_UI_Structures_Summary_Row.ts b/B05_Profile/B05_Profile_UI_Structures_Summary_Row.ts index 1f7b9dda4..5d73409d2 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Summary_Row.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Summary_Row.ts @@ -40,7 +40,7 @@ export function buildSummaryRow(): SummaryRow { name.dataset.b05Summary = "pick"; const clear = document.createElement("button"); clear.type = "button"; - clear.className = "b05-route__irregular-btn"; + clear.className = "ui-btn ui-btn--ghost b05-route__irregular-btn"; clear.textContent = "해제"; clear.dataset.b05Summary = "clear"; const line = document.createElement("div"); diff --git a/B05_Profile/B05_Profile_UI_Structures_Widening.ts b/B05_Profile/B05_Profile_UI_Structures_Widening.ts index ee2c7cf48..523e8b40e 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Widening.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Widening.ts @@ -69,7 +69,7 @@ export function createWideningBox(hooks: { const on = !isRunOff(run, off); const button = document.createElement("button"); button.type = "button"; - button.className = `b05-route__irregular-btn${on ? " is-on" : ""}`; + button.className = `ui-btn ui-btn--ghost b05-route__irregular-btn${on ? " is-on" : ""}`; button.setAttribute("aria-pressed", String(on)); button.dataset.wideningRun = `${run.start_m}-${run.end_m}`; const from = stationLabel(run.start_m, step); diff --git a/B05_Profile/B05_Profile_UI_Style.css b/B05_Profile/B05_Profile_UI_Style.css index 565299592..ae4585097 100644 --- a/B05_Profile/B05_Profile_UI_Style.css +++ b/B05_Profile/B05_Profile_UI_Style.css @@ -197,10 +197,10 @@ html.is-bm-route .b05-route__view-controls { } /* 방위 나침반을 놓을 자리 — ISO 버튼 바로 아래(2026-09-03 사용자 지시). - 버튼 줄 높이 34px + 위 여백 16px 아래에 붙인다. 위젯 모양·색은 공용 + 버튼 줄 높이 32px(단추 단 32) + 위 여백 16px 아래에 붙인다. 위젯 모양·색은 공용 (`ui_template/ui_template_compass.css`)이고 여기서는 위치만 준다. */ .b05-route__compass { - top: calc(var(--spacing-16) + 34px + var(--spacing-8)); + top: calc(var(--spacing-16) + 32px + var(--spacing-8)); left: var(--spacing-16); } @@ -217,9 +217,9 @@ html.is-bm-route .b05-route__view-controls { white-space: nowrap; } -.b05-route__view-controls .ui-btn { - height: 34px; - padding: 0 var(--spacing-8); +/* 보기 줄 단추 = 부품 단추 단 32(553-2) · 줄 폭이 지도 폭을 넘지 않게 안 여백은 8 그대로 */ +.b05-route__view-controls .ui-btn[data-size] { + padding-inline: var(--spacing-8); } .b05-route__view-separator { @@ -231,7 +231,7 @@ html.is-bm-route .b05-route__view-controls { /* [3D 업데이트] 대기 표시 — 계획선 편집이 3D에 아직 안 들어갔음을 점으로 알린다. */ .b05-route__view-controls .ui-btn.is-pending { position: relative; - border-color: var(--color-warning, #d98a00); + border-color: var(--color-warning); } .b05-route__view-controls .ui-btn.is-pending::after { @@ -242,7 +242,7 @@ html.is-bm-route .b05-route__view-controls { width: 6px; height: 6px; border-radius: 50%; - background: var(--color-warning, #d98a00); + background: var(--color-warning); } .b05-route__panel { @@ -356,30 +356,14 @@ html.is-bm-route .b05-route__view-controls { gap: var(--spacing-8); } +/* [추가] · [삭제] · [리셋] = 부품 「단추」(553-2 · 주 filled · 위험 고스트 · 보조 ghost) — 크기 = 좌측 패널 양식 */ .b05-route__irregular-btn { flex: 1 1 0; - padding: var(--spacing-8); - border: 1px solid var(--color-border); - border-radius: var(--radius-inputs); - background: var(--color-surface); - color: var(--color-text-body); - font-size: var(--text-caption); - cursor: pointer; } -.b05-route__irregular-btn.is-primary { - border-color: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent); - color: var(--color-text); -} - -.b05-route__irregular-btn.is-danger { - border-color: color-mix(in srgb, var(--color-danger) 50%, transparent); - color: var(--color-danger); -} - -.b05-route__irregular-btn:disabled { - opacity: 0.45; - cursor: default; +/* 안 여백 8 그대로 — 옆 칸 폭 보호(구조물 조합 [해제]) */ +.b05-route__irregular-btn.ui-btn { + padding-inline: var(--spacing-8); } .b05-route__irregular-list { @@ -590,27 +574,7 @@ html.is-bm-route .b05-route__view-controls { margin-right: var(--spacing-8); } -.b05-route-profile__tool { - padding: var(--btn-bar-padding); - border: 1px solid color-mix(in srgb, var(--color-border) 75%, transparent); - border-radius: 3px; - background: color-mix(in srgb, var(--color-surface-raised) 80%, transparent); - color: var(--color-text); - font-family: var(--font-body); - font-size: var(--btn-bar-font-size); - line-height: 1; - cursor: pointer; -} - -.b05-route-profile__tool:disabled { - opacity: 0.35; - cursor: default; -} - -.b05-route-profile__tool.is-active { - border-color: var(--color-primary); - background: color-mix(in srgb, var(--color-primary) 22%, transparent); -} +/* 종단 도구 단추 = 부품 「단추」 유리 · 단 18(553-2 · `_Profile_Tools.ts`) */ .b05-route-profile__tool-hint { color: var(--color-text-muted); diff --git a/B05_Profile/B05_Profile_UI_Style_Drainage.css b/B05_Profile/B05_Profile_UI_Style_Drainage.css index 1783176c1..ddffdc834 100644 --- a/B05_Profile/B05_Profile_UI_Style_Drainage.css +++ b/B05_Profile/B05_Profile_UI_Style_Drainage.css @@ -87,28 +87,9 @@ pointer-events: auto; } -/* 표시 토글 버튼 — B04 지도 레이어 버튼(.b04-map__layer-button--gis)과 같은 양식을 쓴다. - 크기(패딩·글자)만 이 패널 기준을 유지한다(2026-08-01 사용자 지시). */ +/* 표시 토글 = 부품 「켜기」 칩 단 20(553-2 · 색 점 = 층 선 색) — 한 줄 유지 · 좁아져도 안 쭈그러듦. */ .b05-drainage__layer-button { - /* 한 줄 유지 — 좁아져도 글자가 접히거나 쭈그러들지 않게 크기를 고정한다. */ flex: 0 0 auto; - white-space: nowrap; - padding: 2px var(--spacing-8); - border: 1px solid var(--color-border); - border-radius: var(--radius-inputs); - background: var(--color-surface); - color: var(--color-text-secondary); - font-size: var(--text-caption); - opacity: 0.55; - cursor: pointer; -} - -/* 켜진 레이어는 그 레이어의 선 색을 테두리·글자·안쪽 링에 그대로 쓴다(지도와 바로 대조). */ -.b05-drainage__layer-button.is-active { - border-color: var(--b05-layer-color, var(--color-border)); - box-shadow: inset 0 0 0 1px var(--b05-layer-color, transparent); - color: var(--b05-layer-color, var(--color-text-body)); - opacity: 1; } /* 배관 우클릭 메뉴 — 뷰포트 기준 절대 위치(B04 지도와 같은 조작) · 모양은 공용 `.ui-ctx-menu`. */ @@ -156,21 +137,9 @@ pointer-events: none; } +/* 머리 줄 단추 = 부품 「단추」 단 22(553-2) — 자리만 */ .b05-drainage__analyze { margin-left: auto; - padding: 2px var(--spacing-8); - border: 1px solid - color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent); - border-radius: var(--radius-inputs); - background: var(--color-surface); - color: var(--color-text-body); - font-size: var(--text-caption); - cursor: pointer; -} - -.b05-drainage__analyze:disabled { - opacity: 0.45; - cursor: default; } /* 머리 줄 둘째 단추([자동 제안]) — [업데이트] 우측에 나란히(auto 마진 해제). */ @@ -178,21 +147,11 @@ margin-left: var(--spacing-4, 4px); } -/* 배관 편집 토글 활성 상태. */ -.b05-drainage__analyze.is-active { - background: color-mix( - in srgb, - var(--color-royal-amethyst, rgb(109 40 217)) 18%, - var(--color-surface) - ); - border-color: var(--color-royal-amethyst, rgb(109 40 217)); -} - /* 유역 제원 목록 — 면적·유역표고·유하거리·관경(수식 확정 전까지 "미정"). */ /* 유역 목록은 3행까지만 보이고 나머지는 스크롤한다(2026-08-01 사용자 지시). 높이를 비율로 잡으면 유역 수와 무관하게 잘려 몇 개인지 가늠이 안 된다. */ .b05-drainage__basins { - --b05-basin-row: 34px; + --b05-basin-row: 32px; display: flex; max-height: calc(3 * var(--b05-basin-row) + 2 * 2px + 2 * var(--spacing-8)); @@ -217,25 +176,11 @@ display: none; } -.b05-drainage__basin { - display: flex; - min-height: var(--b05-basin-row); - align-items: center; - gap: var(--spacing-8); - padding: var(--spacing-4) var(--spacing-8); - border: 1px solid transparent; - border-radius: var(--radius-inputs); - background: none; - color: var(--color-text-body); +/* 유역 줄 = 부품 「목록 줄」 단 32(553-2) — 번호 원 · 제원 왼쪽 붙임 · 글 12(제원 한 줄) */ +.b05-drainage__basin.ui-list-row { + flex: none; + justify-content: flex-start; font-size: var(--text-caption); - text-align: left; - cursor: pointer; -} - -.b05-drainage__basin:hover, -.b05-drainage__basin.is-selected { - border-color: var(--color-border); - background: var(--color-surface); } /* 지도 위 서클 번호와 같은 파스텔 색을 써서 목록 항목과 유역을 눈으로 잇는다. */ @@ -270,7 +215,7 @@ /* 배수유역 낡음(326-6) — 옛 그림 그대로 · [업데이트] 단추에 점([3D 업데이트] 꼴) · 구석 「계산 중」. */ .b05-drainage__update.is-pending { position: relative; - border-color: var(--color-warning, #d98a00); + border-color: var(--color-warning); } .b05-drainage__update.is-pending::after { @@ -281,7 +226,7 @@ width: 6px; height: 6px; border-radius: 50%; - background: var(--color-warning, #d98a00); + background: var(--color-warning); } .b05-drainage__busy { diff --git a/B05_Profile/B05_Profile_UI_Style_RouteEdit.css b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css index 986c779ab..f0b92666f 100644 --- a/B05_Profile/B05_Profile_UI_Style_RouteEdit.css +++ b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css @@ -63,7 +63,7 @@ /* 조작 설명 — 도구줄 아래 왼쪽 · 기본 접힘 · [?] 로 펼침(345-5 덧 3 · 352-1) · 펼치면 **2열**(사용자 지시 ⑩). */ .b05-routeedit__hint { - top: calc(var(--spacing-16) + 34px + var(--spacing-8)); + top: calc(var(--spacing-16) + 32px + var(--spacing-8)); left: var(--spacing-16); display: flex; align-items: flex-start; @@ -72,11 +72,10 @@ pointer-events: none; } -.b05-routeedit__hint-toggle { +.b05-routeedit__hint-toggle.ui-btn[data-size] { flex: none; - padding: var(--spacing-4, 4px) var(--spacing-8); + padding-inline: var(--spacing-8); font-weight: 600; - line-height: 1; pointer-events: auto; } @@ -132,22 +131,12 @@ display: block; } +/* 접기 = 부품 「아이콘 단추」 단 18(553-2) */ .b05-routeedit__info-toggle { grid-column: -2; - padding: 0 2px; - border: 0; - background: none; - color: inherit; - font: inherit; - line-height: inherit; - cursor: pointer; pointer-events: auto; } -.b05-routeedit__info-toggle:hover { - color: var(--color-text-body); -} - /* 회전 단추 — 지도 오른쪽 위(사용자 지시 ⑯) · 횡단 판 왼쪽에 앉음(352-1). 여기만 클릭을 받는다. */ .b05-routeedit__spin { position: absolute; @@ -158,12 +147,6 @@ gap: var(--spacing-8); } -.b05-routeedit__spin .ui-btn { - padding: var(--spacing-8) var(--spacing-12); - font-size: var(--text-body); - line-height: 1; -} - .b05-routeedit__legend { display: flex; flex-direction: column; @@ -192,7 +175,7 @@ .b05-routeedit__side-wrap { position: absolute; z-index: 2; - top: calc(var(--spacing-16) + 34px + var(--spacing-8)); /* 도구줄 · 회전 단추 줄 아래 */ + top: calc(var(--spacing-16) + 32px + var(--spacing-8)); /* 도구줄(단 32) · 회전 단추 줄 아래 */ right: var(--spacing-12); bottom: calc(var(--spacing-12) + var(--b05-profile-cover, 0px)); display: flex; @@ -377,7 +360,7 @@ display: inline-flex; align-items: center; gap: var(--spacing-4, 4px); - height: 34px; + height: 32px; padding: 0 var(--spacing-8); border: 1px solid color-mix(in srgb, var(--color-border) 65%, transparent); border-radius: var(--radius-8, 6px); diff --git a/B05_Profile/B05_Profile_UI_Style_Structures.css b/B05_Profile/B05_Profile_UI_Style_Structures.css index 640125d02..28c3d3d71 100644 --- a/B05_Profile/B05_Profile_UI_Style_Structures.css +++ b/B05_Profile/B05_Profile_UI_Style_Structures.css @@ -284,15 +284,15 @@ align-items: stretch; margin: 0; padding: 0; - border: 1px solid var(--color-border, #3a3f4a); - border-radius: var(--radius-inputs, 4px); + border: 1px solid var(--color-border); + border-radius: var(--radius-inputs); background: var(--color-surface); gap: 0; overflow: hidden; } .b05-structure__stepper:focus-within { - border-color: var(--color-royal-amethyst, rgb(109 40 217)); + border-color: var(--color-accent); } /* 가운데 숫자칸은 자기 테두리·배경을 버리고 덩어리 안에 녹는다. */ @@ -309,32 +309,22 @@ outline: none; } -/* 좌우 끝 버튼 — 덩어리 안에서는 가르는 선 하나씩만 갖는다. */ -.b05-structure__step { - flex: none; +/* 좌우 끝 버튼 = 부품 「아이콘 단추」(553-2) — 덩어리 안에서는 가르는 선 하나씩만 갖는다. */ +.b05-structure__stepper > .b05-structure__step { /* 손가락·마우스로 집기 쉽게 넓힌다(2026-08-29 사용자 지시). 숫자칸이 남는 폭을 차지하므로 이 값이 곧 버튼 폭이다. */ width: 28px; - padding: 0; + height: 100%; border: 0; border-radius: 0; - background: transparent; - color: var(--color-text-body); - font-size: 12px; - line-height: 1; - cursor: pointer; } -.b05-structure__step:first-child { - border-right: 1px solid var(--color-border, #3a3f4a); +.b05-structure__stepper > .b05-structure__step:first-child { + border-right: 1px solid var(--color-border); } -.b05-structure__step:last-child { - border-left: 1px solid var(--color-border, #3a3f4a); -} - -.b05-structure__step:hover { - background: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 14%, transparent); +.b05-structure__stepper > .b05-structure__step:last-child { + border-left: 1px solid var(--color-border); } /* 구조물 배치 폼의 숫자 칸 — 브라우저 기본 증감 화살표(스피너)를 없앤다 @@ -389,10 +379,6 @@ height: var(--b05-control-h); } -.b05-structure__step { - height: 100%; -} - /* 조정창에서 옮겨 온 행(단 수·옵션) — 같은 변수로 맞춘다. */ .b05-structure__adjust-slot .b06-structure-panel__controls { height: var(--b05-control-h); @@ -634,10 +620,10 @@ font-size: var(--text-caption); } -/* 켬 = 확폭 포함 — 테두리색 + 표식(`.is-primary` 는 [추가] 단추 몫이라 안 씀) */ -.b05-structure__widening .b05-route__irregular-btn.is-on { - border-color: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent); - color: var(--color-text); +/* 켬 = 확폭 포함 — 부품 보조 단추 꼴 + 표식 · 끔 = 흐린 선 · 글(`.is-primary` 는 [추가] 단추 몫이라 안 씀) */ +.b05-structure__widening .b05-route__irregular-btn[aria-pressed="false"] { + border-color: var(--color-border); + color: var(--color-text-secondary); } .b05-structure__widening [data-widening-run][aria-pressed="true"]::before { diff --git a/B05_Profile/B05_Profile_UI_Style_Table.css b/B05_Profile/B05_Profile_UI_Style_Table.css index 48c37ede7..6dd46cdc8 100644 --- a/B05_Profile/B05_Profile_UI_Style_Table.css +++ b/B05_Profile/B05_Profile_UI_Style_Table.css @@ -413,34 +413,15 @@ pointer-events: none; } -/* 상시 보이게 둔다(예전엔 패널 hover 시에만 노출). 크기는 ▲▼·↺·⬆⬇ 전부 21×17로 - 키웠다(2026-08-04 사용자 지시 — 18×15는 누르기도 읽기도 작았다). */ +/* 상시 보이게 둔다(예전엔 패널 hover 시에만 노출). ▲▼·↺·⬆⬇ = 부품 「아이콘 단추」 선 꼴 단 18(553-2 · 18×18). */ .b05-profile-edit__btn { position: absolute; - width: 21px; - height: 17px; - padding: 0; - font-size: 12px; - line-height: 1; - border: 1px solid color-mix(in srgb, var(--color-border) 75%, transparent); - border-radius: 3px; - background: color-mix(in srgb, var(--color-surface-raised) 80%, transparent); - color: var(--color-text); - font-size: 9px; - line-height: 1; - cursor: pointer; opacity: 0.9; pointer-events: auto; - transition: - opacity var(--transition-fast), - background var(--transition-fast); } .b05-route-profile .b05-profile-edit__btn:hover, .b05-route-profile .b05-profile-edit__btn:focus-visible { - border-color: var(--color-border); - background: var(--color-surface-raised); - color: var(--color-text); opacity: 1; } @@ -470,8 +451,8 @@ } .b05-profile-edit__btn.is-down { - border-color: color-mix(in srgb, var(--color-chart-0, #5b8def) 65%, transparent); - color: var(--color-chart-0, #5b8def); + border-color: color-mix(in srgb, var(--color-chart-0) 65%, transparent); + color: var(--color-chart-0); } .b05-route-profile .b05-profile-edit__btn.is-up:hover, @@ -483,16 +464,15 @@ .b05-route-profile .b05-profile-edit__btn.is-down:hover, .b05-route-profile .b05-profile-edit__btn.is-down:focus-visible { - border-color: var(--color-chart-0, #5b8def); - background: color-mix(in srgb, var(--color-chart-0, #5b8def) 14%, var(--color-surface-raised)); - color: var(--color-chart-0, #5b8def); + border-color: var(--color-chart-0); + background: color-mix(in srgb, var(--color-chart-0) 14%, var(--color-surface-raised)); + color: var(--color-chart-0); } .b05-profile-edit__btn.is-reset { top: 22px; - border-color: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 55%, transparent); - background: var(--color-surface-raised); - color: color-mix(in srgb, var(--color-royal-amethyst, rgb(139 92 246)) 85%, var(--color-text)); + border-color: color-mix(in srgb, var(--color-accent) 55%, transparent); + color: var(--color-accent); opacity: 0.95; } .b05-profile-table__segment.is-structure { @@ -528,30 +508,12 @@ margin-left: auto; } -.b05-profile__zoom-btn { - width: 22px; - height: 22px; - padding: 0; - border: none; - border-left: 1px solid var(--color-border); - background: none; - color: var(--color-text-secondary); - font-size: 0.8rem; - line-height: 1; - cursor: pointer; +/* 묶음 안 단추 = 부품 「아이콘 단추」 단 22(553-2) — 붙은 묶음이라 둥글기 없이 가르는 선 하나씩 · + * 배율 한계(축소는 폭맞춤, 확대는 8배)에 닿은 단추는 부품 막힘 꼴(흐림). */ +.b05-profile__zoom > .b05-profile__zoom-btn { + border-radius: 0; } -.b05-profile__zoom-btn:first-child { - border-left: none; -} - -.b05-profile__zoom-btn:hover:not(:disabled) { - color: var(--color-text); - background: var(--color-surface); -} - -/* 배율 한계(축소는 폭맞춤, 확대는 8배)에 닿은 버튼 — 눌러도 변화가 없으니 흐리게 죽인다. */ -.b05-profile__zoom-btn:disabled { - opacity: 0.35; - cursor: default; +.b05-profile__zoom-btn + .b05-profile__zoom-btn { + border-left-color: var(--color-border); } diff --git a/resources/tester/helper_224_1c_subpanel_popout.cjs b/resources/tester/helper_224_1c_subpanel_popout.cjs index c3bf297be..58f068ecd 100644 --- a/resources/tester/helper_224_1c_subpanel_popout.cjs +++ b/resources/tester/helper_224_1c_subpanel_popout.cjs @@ -25,6 +25,11 @@ function el(tag, className = "") { parent: null, listeners: {}, style: { height: "", cssText: "" }, + attrs: {}, + setAttribute(k, v) { + node.attrs[k] = String(v); + }, + getAttribute: (k) => node.attrs[k] ?? null, }; node.classList = { list: () => node.className.split(/\s+/).filter(Boolean), diff --git a/resources/tester/test_215_1_free_profile.py b/resources/tester/test_215_1_free_profile.py index 947c9cbdf..42c88571a 100644 --- a/resources/tester/test_215_1_free_profile.py +++ b/resources/tester/test_215_1_free_profile.py @@ -151,7 +151,9 @@ def test_도구줄_직선화_오른쪽_자유지정(): tools = _read("B05_Profile/B05_Profile_UI_Profile_Tools.ts") assert '"자유지정"' in tools assert "wrap.append(reset, undo, redo, straighten, free, shift, up, down)" in tools - assert 'className = "b05-route-profile__tool"' in tools # 같은 단추 양식 + assert ( + 'className = "ui-btn ui-btn--glass b05-route-profile__tool"' in tools + ) # 같은 단추 양식(부품 단추 · 553-2) def test_적용은_직선화와_같은_길_키_배선(): diff --git a/resources/tester/test_224_1c_subpanel_popout.py b/resources/tester/test_224_1c_subpanel_popout.py index df926a112..9d3f3d7fe 100644 --- a/resources/tester/test_224_1c_subpanel_popout.py +++ b/resources/tester/test_224_1c_subpanel_popout.py @@ -102,6 +102,9 @@ def test_배선과_양식(): assert "attachSubPanelPopouts(profile);" in shared css = (ROOT / "A00_Common/design/design_page.css").read_text(encoding="utf-8") block = re.search(r"\n\.b05-subpanel__popout \{([^}]*)\}", css).group(1) - assert "var(--btn-bar-padding)" in block and "var(--btn-bar-font-size)" in block + # 크기 = 부품 단추 단 18(553-2) — 페이지 CSS 여백 덮어쓰기 없음 + assert "flex: none;" in block and "padding" not in block + popout = (ROOT / "A00_Common/design/design_profile_popout.ts").read_text(encoding="utf-8") + assert popout.count('button.setAttribute("data-size", "18")') == 2 assert ".b05-profile__masshaul-overlay.is-popout," in css assert ".b05-drainage.is-popout {" in css diff --git a/resources/tester/test_345_5c_info_hint_collapse.py b/resources/tester/test_345_5c_info_hint_collapse.py index 7497d6665..dec4d6269 100644 --- a/resources/tester/test_345_5c_info_hint_collapse.py +++ b/resources/tester/test_345_5c_info_hint_collapse.py @@ -35,7 +35,7 @@ def test_도움말_기본_접힘_펼침_기억() -> None: # 접힘 = 몸통 없음 · 펼침 = 2열 · 단추만 클릭 받음 assert "display: none;" in _rule(".b05-routeedit__hint-body") assert "display: grid;" in _rule(".b05-routeedit__hint.is-open > .b05-routeedit__hint-body") - assert "pointer-events: auto;" in _rule(".b05-routeedit__hint-toggle") + assert "pointer-events: auto;" in _rule(".b05-routeedit__hint-toggle.ui-btn[data-size]") assert "pointer-events: none;" in _rule(".b05-routeedit__hint")