/* ============================================================================= * B05_Profile_UI_Profile_Zoom.ts * 종단면도 줌 조작구 — 버튼 셋(줌인·줌아웃·초기화), 2026-09-04 사용자 확정. * * 공사 범위가 넓으면 종단 그래프가 눌려 읽히지 않는다. 사람이 맞출 것은 **가로 하나**다. * * X (가로) — **폭 배수**다. SVG transform 으로 늘리면 그래프만 커지고 측점 테이블· * 계획고 편집 버튼층·구조물 알약 레인이 어긋난다(넷이 같은 `chainageMapper` * 를 쓴다). 캔버스 폭 자체를 키우고 가로 스크롤로 훑는다. * Y (세로) — **프로그램이 자동으로 맞춘다**. 보이는 구간의 지반·계획선 범위에 맞춰 * 잡으므로(`windowElevationRange`) 세로 배율·창 이동 버튼이 필요 없어졌다. * 옛 `⇕+`·`⇕−`·`▲`·`▼` 네 버튼은 그래서 없앴다. * * **배율 1 = 기본값이자 축소 한계**(사용자 확정) — 폭맞춤보다 더 줄이면 측점이 겹쳐 * 읽을 수 없다. 한계에 닿은 버튼은 흐리게 죽인다. * * 배율은 페이지가 들고 있다 — 편집·재계산으로 다시 그려도 유지된다(B06 `cardZoomStates` 규칙). * ========================================================================== */ /** 한 번 누를 때 배율 배수. 횡단도 줌(1/0.85)보다 성글게 — 폭 배수라 한 칸이 크게 느껴진다. */ const ZOOM_STEP = 1.25; /** 가로 폭 배수 상한 — 이 이상은 캔버스가 수만 px이 되어 브라우저가 버겁다. */ const MAX_X = 8; export interface ProfileZoomState { /** 가로 폭 배수(1 = 현행 폭맞춤 = 기본값·축소 한계). */ x: number; } export interface ProfileZoom { /** 절·성토 요약줄 오른쪽 끝에 붙는 버튼 묶음(한 번 만들어 계속 쓴다). */ bar: HTMLElement; state: () => ProfileZoomState; } const clamp = (value: number, min: number, max: number): number => Math.min(max, Math.max(min, value)); export function createProfileZoom(onChange: () => void): ProfileZoom { const state: ProfileZoomState = { x: 1 }; const bar = document.createElement("div"); bar.className = "b05-profile__zoom"; function add(label: string, title: string, action: () => void): HTMLButtonElement { const button = document.createElement("button"); button.type = "button"; button.className = "b05-profile__zoom-btn"; button.textContent = label; button.title = title; button.addEventListener("click", (event) => { // 카드·측점 선택으로 번지면 그래프를 다시 그리며 방금 맞춘 배율이 날아간다. event.stopPropagation(); action(); syncDisabled(); onChange(); }); bar.append(button); return button; } const zoomIn = add("+", "가로 확대 — 측점 간격을 넓혀 폅니다 (세로는 자동으로 맞춥니다)", () => { state.x = clamp(state.x * ZOOM_STEP, 1, MAX_X); }); const zoomOut = add("−", "가로 축소 — 기본 폭(화면 맞춤)까지만 줄어듭니다", () => { state.x = clamp(state.x / ZOOM_STEP, 1, MAX_X); }); add("⤢", "기본 상태로 — 가로 폭맞춤, 세로 자동", () => { state.x = 1; }); /** 한계에 닿은 버튼은 눌러도 변화가 없다 — 흐리게 죽여 그 사실을 보인다. */ function syncDisabled(): void { zoomOut.disabled = state.x <= 1 + 1e-9; zoomIn.disabled = state.x >= MAX_X - 1e-9; } syncDisabled(); return { bar, state: () => ({ ...state }) }; }