- 종단 도구 · [창 분할] = 단추 유리 · 보조 단 18 · ▲▼ = 아이콘 단추 선 18 · 배율 + − ⤢ = 아이콘 단추 22 - 배수 머리 단추 = 단추 22 · 층 단추 = 켜기 칩 20(색 점 = 층 색) · 유역 줄 = 목록 줄 32 - [추가] · [삭제] · [리셋] · [해제] · 확폭 = 단추 주 · 위험 고스트 · 보조 · [-][+] = 아이콘 단추 24 - 지도 도구줄 · 3D 보기 줄 = 단 32(34 에서) · 접기 ▾ = 아이콘 단추 18 · 잰 값 닫기 = 아이콘 단추 24 - 페이지 CSS 의 단추 꼴(테두리 · 바탕 · 글 · 둥글기) 지움 · 날 색 대체값(#d98a00 · #5b8def · #3a3f4a) 지움 - 시험 선택자 · 가짜 DOM(setAttribute) 같이 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.7 KiB
TypeScript
84 lines
3.7 KiB
TypeScript
/* =============================================================================
|
||
* 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";
|
||
// 부품 「아이콘 단추」 단 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();
|
||
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 }) };
|
||
}
|