- _RouteEdit_View.fitView(처음 열기 · [맞춤] 한 벌) · _RouteEdit_Cover.onCoverChange(덮인 높이 바뀜 알림 · 떠난 편집기는 뗌) - 시험: test_345_5b_plan_visible_area 3 · 관련 81 통과 · typecheck 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
62 lines
2.6 KiB
TypeScript
62 lines
2.6 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_UI_RouteEdit_Cover.ts
|
|
* 노선 편집 평면 아래를 **뜬 종단 판**이 덮은 높이를 잰다(345-5 덧).
|
|
*
|
|
* 칸 · 판 크기가 바뀔 때마다(창 · 판 높이 끌기 · 접기 · 새 창 떼기) 다시 재서
|
|
* 칸에 `--b05-routeedit-cover`(px)를 적는다 — 상태 · 범례 · 잰 값 글판이 판 바로 위로 오름 ·
|
|
* 곡선 바는 `onCoverChange` 로 다시 자리 잡음.
|
|
* 맞춤(`_View.fitView`)은 `coveredPx()` 를 읽는다. 셈은 `logic/_RouteEdit_Visible`.
|
|
* ========================================================================== */
|
|
|
|
import { coveredBottomPx } from "./logic/B05_Profile_RouteEdit_Visible";
|
|
|
|
// ponytail: 편집기는 페이지에 하나 — 덮인 높이 한 값 · 둘이 되면 칸마다 값으로.
|
|
let covered = 0;
|
|
const watching = new WeakSet<HTMLElement>();
|
|
const listeners = new Set<() => boolean>();
|
|
|
|
/** 지금 평면 아래 덮인 높이(px). */
|
|
export function coveredPx(): number {
|
|
return covered;
|
|
}
|
|
|
|
/** 덮인 높이가 바뀌면 부름(곡선 바 자리 · 345-5 덧 2) — `false` 를 돌려주면 뗌(떠난 편집기). */
|
|
export function onCoverChange(listener: () => boolean): void {
|
|
listeners.add(listener);
|
|
}
|
|
|
|
/** 같은 가운데 칸의 종단 판 — 새 창으로 떼면 없음. */
|
|
function panelOf(wrap: HTMLElement): HTMLElement | null {
|
|
return (
|
|
wrap.closest(".design-page__main")?.querySelector<HTMLElement>(".b05-route-profile") ?? null
|
|
);
|
|
}
|
|
|
|
function span(element: HTMLElement): { top: number; bottom: number } {
|
|
const rect = element.getBoundingClientRect();
|
|
return { top: rect.top, bottom: rect.bottom };
|
|
}
|
|
|
|
function measure(wrap: HTMLElement, panel: HTMLElement | null): void {
|
|
const before = covered;
|
|
covered = coveredBottomPx(span(wrap), panel ? span(panel) : null);
|
|
wrap.style.setProperty("--b05-routeedit-cover", `${covered}px`);
|
|
if (covered !== before)
|
|
for (const listener of listeners) if (!listener()) listeners.delete(listener);
|
|
}
|
|
|
|
/** 칸을 지켜본다(여러 번 불러도 한 번만 붙음) · 부를 때마다 한 번 잰다. */
|
|
export function watchCover(wrap: HTMLElement): void {
|
|
if (watching.has(wrap)) return measure(wrap, panelOf(wrap));
|
|
watching.add(wrap);
|
|
let observed: HTMLElement | null = null;
|
|
const watch = new ResizeObserver(() => {
|
|
if (!wrap.isConnected) return watch.disconnect(); // 떠남
|
|
const panel = panelOf(wrap);
|
|
if (panel && panel !== observed) watch.observe((observed = panel));
|
|
measure(wrap, panel);
|
|
});
|
|
watch.observe(wrap);
|
|
measure(wrap, panelOf(wrap));
|
|
}
|