Files
Aislo/B06_Section/B06_Section_UI_Cross_View_Zoom.ts
T
eomsangdonandClaude Opus 5.5 15092ee2b8 feat(B06 횡단): 카드 그래프 Ctrl+휠 = 마우스 자리 확대 · 축소 — 맨 휠은 목록 스크롤 그대로 (397)
공용 줌 부품 wheelZoom 에 "ctrl" 갈래 · 트랙패드 핀치 같은 확대 · 그래프 위 브라우저 확대 막음.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
2026-10-17 19:41:49 +09:00

102 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* B06_Section_UI_Cross_View_Zoom.ts
* 횡단 카드 그래프의 **확대·축소·팬** 조작구.
*
* ⚠ 줌·팬 **본문은 공용 조각**(`A00_Common/b_svg_zoom_pan.ts`)으로 옮겼다
* (2026-09-12 사용자 지시 「공용화해줘」) — 계획노선 편집 횡단이 같은 코드를 쓴다.
* 여기에는 **B06 것**만 남는다: 카드 여백(`CROSS_PAD`)을 씌운 감싸개, 측점별 배율 기억,
* 우측 상단 버튼 묶음. 맨 휠은 줌이 아님(Ctrl+휠만 줌 · PLAN 397) — 카드가 수십 장 깔리는
* 화면에서 휠을 줌에 묶으면 목록을 훑을 수가 없다(2026-08-02 사용자 확정).
* ========================================================================== */
import {
attachSvgZoomPan,
type ContentBounds,
type ZoomPanHandle,
type ZoomPanState,
} from "../A00_Common/b_svg_zoom_pan";
import { CROSS_PAD, L } from "./B06_Section_UI_Section_Common";
export type { ContentBounds, ZoomPanHandle, ZoomPanState };
/**
* 카드 표시 반폭 조작(2026-08-23 사용자 지시). 카드 하단 ◀/▶/↺을 없애고 우측 상단
* 줌 버튼에 합쳤다 — 원배율에서 −는 폭 넓히기, ⤢는 보기값 되돌림이다.
*/
export interface CrossWidthActions {
/** 표시 반폭을 deltaM(m)만큼 조절한다. 계산 반폭을 넘으면 페이지가 재생성까지 처리. */
step: (deltaM: number) => void;
/**
* 초기화 — 표시 반폭을 전역 보기값으로 되돌린다(PLAN 79-2 · 사면 교차점은 팬으로 봄).
*/
fit: () => void;
}
/** 측점별 줌·팬 상태 — 카드를 다시 만들어도 배율이 살아남는다(2026-08-22 사용자 ③). */
export const cardZoomStates = new Map<string, ZoomPanState>();
/** 카드 SVG 에 줌·팬을 붙인다 — 여백만 씌워 공용 조각에 넘긴다. 부르는 꼴은 그대로다. */
export function attachZoomPan(
svg: SVGSVGElement,
plotLayer: SVGGElement,
widthPx: number,
heightPx: number,
/** 이전 카드의 줌·팬 상태 — 조정창 조작으로 카드가 다시 그려져도 배율 유지. */
initial?: ZoomPanState,
/** 상태가 바뀔 때마다 부른다 — 카드 밖(뷰)이 담아 뒀다가 다음 렌더에 되돌린다. */
onChange?: (state: ZoomPanState) => void,
/** 도형이 실제로 그려진 범위(캐시 보유 폭). 확대 상태에서 팬 한계가 여기까지 늘어난다. */
content?: ContentBounds,
): ZoomPanHandle {
return attachSvgZoomPan({
svg,
layer: plotLayer,
widthPx,
heightPx,
pad: CROSS_PAD,
initial,
onChange,
content,
// 맨 휠은 카드 목록을 훑는 몫 · Ctrl+휠만 마우스 자리 줌(요구 245 · PLAN 397).
wheelZoom: "ctrl",
});
}
/**
* 그래프 우측 상단 줌 버튼(확대/축소/핏). 휠을 대신하는 조작구다.
*
* `width`를 넘기면 **원배율에서만** 버튼이 표시 반폭을 조절한다(2026-08-23 사용자 지시):
* + : 배율 확대(항상)
* − : 배율>1이면 축소, 원배율이면 표시 반폭 +1m(계산 반폭 20m를 넘으면 백엔드 재계산)
* ⤢ : 배율 원복 + 표시 반폭 초기화(전역 보기값)
*/
export function buildZoomControls(handle: ZoomPanHandle, width?: CrossWidthActions): HTMLElement {
const bar = document.createElement("div");
bar.className = "b06-cross-card__zoom";
const add = (label: string, title: string, action: () => void): void => {
const button = document.createElement("button");
button.type = "button";
button.className = "b06-cross-card__zoom-btn";
button.textContent = label;
button.title = title;
button.addEventListener("click", (event) => {
// 카드 선택으로 번지면 그래프가 다시 그려져 방금 맞춘 배율이 날아간다.
event.stopPropagation();
action();
});
bar.append(button);
};
// +는 언제나 배율 확대다 — 원배율에서 폭 좁히기로 바꿔 놨더니 배율이 1에서 못 올라가
// 가운데 버튼 팬(배율>1에서만 열림)까지 막혔다(2026-08-23 사용자 보고).
add("+", L("B06_Profile_View_ZoomIn"), () => handle.zoom(1 / 0.85));
add("−", L("B06_Profile_View_ZoomOut"), () => {
if (!width || handle.scale() > 1 + 1e-6) handle.zoom(0.85);
else width.step(1);
});
add("⤢", L("B06_Profile_View_ZoomFit"), () => {
handle.reset();
width?.fit();
});
return bar;
}