fix(B05 노선 편집 평면 아래 글판): 횡단 판 넓을 때 상태 글판 · 잰 값 창 안 겹침 (408-4)
- 횡단 판 왼쪽 빈 폭을 두 글판이 반씩(--b05-bottom-half) · 좁으면 줄바꿈 - 반 폭 200 px 아래면 잰 값 창을 상태 글판 위로 쌓음(is-narrow-bottom · --b05-info-h · logic bottomRowNarrow) - Chrome 관찰에 상태 글판 · 횡단 판 크기 더함 - 시험 test_408_4_bottom_row 새로 · 345_5c 폭 규칙 고쳐 씀 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
This commit is contained in:
@@ -2,7 +2,7 @@ import { createButton } from "@ui/ui_template_elements";
|
||||
import { createSidePanels, type SidePanels } from "@ui/ui_template_side_panels";
|
||||
import { bindFootprintToggle } from "./B05_Profile_UI_RouteEdit_Footprint";
|
||||
import { bindMarkToggle } from "./B05_Profile_UI_RouteEdit_Marks";
|
||||
import { infoLiftPx } from "./logic/B05_Profile_RouteEdit_InfoLift";
|
||||
import { bottomRowNarrow, infoLiftPx } from "./logic/B05_Profile_RouteEdit_InfoLift";
|
||||
|
||||
/* =============================================================================
|
||||
* B05_Profile_UI_RouteEdit_Chrome.ts
|
||||
@@ -207,7 +207,8 @@ export function createRouteEditChrome(
|
||||
infoToggle.addEventListener("click", () => setInfo(!infoCollapsed));
|
||||
|
||||
// 뜬 종단 판(이웃 칸)이 지도 아래를 덮으면 상태 · 범례를 그 위로(408-2) — 판 높이 끌기 · 접기 ·
|
||||
// 창 분할(판이 다른 창으로 감 = 못 찾음)을 판 크기 관찰로 따라감.
|
||||
// 창 분할(판이 다른 창으로 감 = 못 찾음)을 판 크기 관찰로 따라감. 횡단 판이 넓어 아래 줄이 좁으면 잰 값 창을
|
||||
// 상태 글판 위로 쌓음(408-4 · 횡단 판 · 글판 크기도 관찰).
|
||||
let watchedProfile: Element | null = null;
|
||||
const liftInfo = (): void => {
|
||||
if (!overlay.isConnected) return; // 붙기 전 첫 알림 · 걷힌 뒤 — 판 관찰은 판과 함께 사라짐
|
||||
@@ -220,9 +221,17 @@ export function createRouteEditChrome(
|
||||
"--b05-profile-cover",
|
||||
`${infoLiftPx(map.bottom, map.height, top)}px`,
|
||||
);
|
||||
const sideOpen = parseFloat(overlay.style.getPropertyValue("--b05-side-open")) || 0;
|
||||
overlay.classList.toggle("is-narrow-bottom", bottomRowNarrow(map.width, sideOpen));
|
||||
overlay.style.setProperty(
|
||||
"--b05-info-h",
|
||||
`${Math.round(info.getBoundingClientRect().height)}px`,
|
||||
);
|
||||
};
|
||||
const lift = new ResizeObserver(liftInfo);
|
||||
lift.observe(overlay);
|
||||
lift.observe(info);
|
||||
lift.observe(overlay.querySelector(".b05-routeedit__side-wrap")!);
|
||||
|
||||
const measureCloseButton = createButton({ label: "✕", variant: "ghost" });
|
||||
measureCloseButton.classList.add("b05-routeedit__measure-close");
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
(`.b05-route__view-controls` · ☰ 비킴 `--wf-burger-pad`) · 횡단 판은 오른쪽을 덮는 판. */
|
||||
.b05-routeedit {
|
||||
--b05-side-open: 0px; /* 횡단 판이 지도를 덮는 폭(손잡이 포함 · `_Chrome` 이 적음) */
|
||||
/* 아래 줄 반 폭(408-4) — 횡단 판 왼쪽 빈 폭을 상태 글판 · 잰 값 창이 반씩(안 겹침 · 좁으면 줄바꿈) ·
|
||||
% 는 쓰는 자리(지도 칸) 기준 · 반이 모자라면(`is-narrow-bottom` · `_Chrome`) 빈 폭 전부 · 위아래로 쌓음. */
|
||||
--b05-bottom-half: max(0px, calc((100% - 3 * var(--spacing-12) - var(--b05-side-open)) / 2));
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
@@ -97,7 +100,7 @@
|
||||
grid-template-columns: minmax(0, 1fr) auto auto;
|
||||
align-items: start;
|
||||
gap: 0 var(--spacing-12);
|
||||
max-width: min(460px, 46%);
|
||||
max-width: min(460px, 46%, var(--b05-bottom-half));
|
||||
padding: var(--spacing-4, 4px) var(--spacing-8);
|
||||
line-height: 1.45;
|
||||
}
|
||||
@@ -374,7 +377,7 @@
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-8);
|
||||
max-width: 52%;
|
||||
max-width: var(--b05-bottom-half);
|
||||
padding: var(--spacing-8) var(--spacing-12);
|
||||
border: 1px solid color-mix(in srgb, #22c55e 60%, transparent);
|
||||
border-radius: var(--radius-8, 6px);
|
||||
@@ -385,6 +388,17 @@
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
/* 아래 줄 좁음(408-4) — 잰 값 창은 상태 글판 위에 · 둘 다 횡단 판 왼쪽 빈 폭 전부. */
|
||||
.b05-routeedit.is-narrow-bottom {
|
||||
--b05-bottom-half: max(0px, calc(100% - 2 * var(--spacing-12) - var(--b05-side-open)));
|
||||
}
|
||||
|
||||
.b05-routeedit.is-narrow-bottom .b05-routeedit__measure {
|
||||
bottom: calc(
|
||||
var(--spacing-12) + var(--b05-profile-cover, 0px) + var(--b05-info-h, 0px) + var(--spacing-8)
|
||||
);
|
||||
}
|
||||
|
||||
/* 글이 길면 접힌다 — flex 자식은 기본으로 안 줄어들어 왼쪽으로 넘쳐 잘렸다(2026-09-12). */
|
||||
.b05-routeedit__measure-text {
|
||||
min-width: 0;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile/logic/B05_Profile_RouteEdit_InfoLift.ts
|
||||
* 평면 상태 · 범례 올림(408-2) — 뜬 종단 판이 지도 아래를 덮은 높이만큼 위로.
|
||||
* 아래 줄 좁음(408-4) — 횡단 판 왼쪽 빈 폭의 반이 모자라면 잰 값 창을 상태 글판 위로 쌓음.
|
||||
* 파이썬 짝 아님. 순수 계산 — 화면 · fetch · sessionStorage 없음. 부르는 쪽은 `_UI_RouteEdit_Chrome`.
|
||||
* ========================================================================== */
|
||||
|
||||
@@ -17,3 +18,13 @@ export function infoLiftPx(
|
||||
const cover = Math.max(0, mapBottom - profileTop);
|
||||
return Math.round(Math.min(cover, Math.max(0, mapHeight - KEEP_PX)));
|
||||
}
|
||||
|
||||
/** 아래 줄 반 폭이 이보다 좁으면 나란히 두지 않고 쌓음(px). */
|
||||
const HALF_MIN_PX = 200;
|
||||
/** 지도 칸 가장자리 · 두 글판 사이 · 횡단 판 앞 간격(px) — CSS `--spacing-12`. */
|
||||
const GAP_PX = 12;
|
||||
|
||||
/** 상태 글판 · 잰 값 창을 나란히 못 두나 — `mapWidth` = 지도 칸 폭 · `sideOpen` = 횡단 판이 덮는 폭. */
|
||||
export function bottomRowNarrow(mapWidth: number, sideOpen: number): boolean {
|
||||
return (mapWidth - 3 * GAP_PX - sideOpen) / 2 < HALF_MIN_PX;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
||||
}).outputText;
|
||||
const box = { exports: {} };
|
||||
new Function("exports", "module", "require", js)(box.exports, box, require);
|
||||
const { infoLiftPx } = box.exports;
|
||||
const { infoLiftPx, bottomRowNarrow } = box.exports;
|
||||
|
||||
// 화면 1078 px · 지도 칸 0~1078
|
||||
process.stdout.write(
|
||||
@@ -29,5 +29,7 @@ process.stdout.write(
|
||||
popout: infoLiftPx(1078, 1078, null), // 창 분할 = 판 없음
|
||||
below: infoLiftPx(600, 600, 700), // 횡단 보기 = 판이 지도 아래 흐름
|
||||
tall: infoLiftPx(1078, 1078, 20), // 판을 끝까지 올림 = 지도 안에 남김
|
||||
// 408-4 지도 칸 1172 px · 횡단 판 접힘(손잡이 32) · 460 열림 · 891 넓힘 — 반 폭 200 아래면 쌓음
|
||||
narrow: [32, 492, 891, 736, 737].map((side) => bottomRowNarrow(1172, side)),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -58,7 +58,8 @@ def test_상태_글판_작게_반투명_자리() -> None:
|
||||
info = _rule(".b05-routeedit__info")
|
||||
# 왼쪽 아래 제자리 — 뜬 판이 덮으면 그 위로(408-2) · 폭 줄임
|
||||
assert "bottom: calc(var(--spacing-12) + var(--b05-profile-cover, 0px));" in info
|
||||
assert "max-width: min(460px, 46%);" in info and "grid-template-columns:" in info
|
||||
assert "max-width: min(460px, 46%, var(--b05-bottom-half));" in info # 408-4 반 폭
|
||||
assert "grid-template-columns:" in info
|
||||
# 상태 글 두 줄에서 자름 · 범례는 옆 칸 두 줄
|
||||
assert "-webkit-line-clamp: 2;" in _rule(".b05-routeedit__status")
|
||||
assert "flex-direction: column;" in _rule(".b05-routeedit__legend")
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
"""408-4 횡단 판이 넓을 때 상태 글판 · 잰 값 창 겹침 — 횡단 판 왼쪽 빈 폭을 반씩 나눠 쓰고, 반이 200 px
|
||||
아래면(`bottomRowNarrow`) 잰 값 창을 상태 글판 위로 쌓음(둘 다 빈 폭 전부 · 좁으면 줄바꿈).
|
||||
|
||||
TS 셈은 `helper_408_2_status_lift.cjs`(Node · `logic/B05_Profile_RouteEdit_InfoLift.ts`).
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_408_4_bottom_row.py -q
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
B05 = Path(__file__).resolve().parents[2] / "B05_Profile"
|
||||
CSS = B05 / "B05_Profile_UI_Style_RouteEdit.css"
|
||||
HELPER = Path(__file__).with_name("helper_408_2_status_lift.cjs")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def got() -> dict:
|
||||
run = subprocess.run( # noqa: S603 — 고정 실행 파일
|
||||
["node", str(HELPER)], capture_output=True, text=True, encoding="utf-8", timeout=60
|
||||
)
|
||||
assert run.returncode == 0, run.stderr
|
||||
return json.loads(run.stdout)
|
||||
|
||||
|
||||
def _rules(selector: str) -> list[str]:
|
||||
css = CSS.read_text(encoding="utf-8")
|
||||
return [part[: part.index("}")] for part in css.split(f"{selector} {{")[1:]]
|
||||
|
||||
|
||||
def test_반_폭이_모자라면_쌓음(got) -> None:
|
||||
# (1172 - 36 - 736) / 2 = 200 → 나란히 · 737 → 쌓음
|
||||
assert got["narrow"] == [False, False, True, False, True]
|
||||
|
||||
|
||||
def test_두_글판이_반_폭까지만() -> None:
|
||||
assert any(
|
||||
"max-width: min(460px, 46%, var(--b05-bottom-half));" in r
|
||||
for r in _rules(".b05-routeedit__info")
|
||||
)
|
||||
assert any("max-width: var(--b05-bottom-half);" in r for r in _rules(".b05-routeedit__measure"))
|
||||
half = _rules(".b05-routeedit")[0]
|
||||
assert (
|
||||
"--b05-bottom-half: max(0px, calc((100% - 3 * var(--spacing-12) - var(--b05-side-open)) / 2));"
|
||||
in half
|
||||
)
|
||||
|
||||
|
||||
def test_좁으면_잰_값_창이_상태_글판_위로() -> None:
|
||||
narrow = _rules(".b05-routeedit.is-narrow-bottom")[0]
|
||||
assert "calc(100% - 2 * var(--spacing-12) - var(--b05-side-open))" in narrow
|
||||
measure = _rules(".b05-routeedit.is-narrow-bottom .b05-routeedit__measure")[0]
|
||||
assert "var(--b05-info-h, 0px)" in measure and "var(--b05-profile-cover, 0px)" in measure
|
||||
|
||||
|
||||
def test_편집기가_횡단_판_글판_크기를_관찰() -> None:
|
||||
chrome = (B05 / "B05_Profile_UI_RouteEdit_Chrome.ts").read_text(encoding="utf-8")
|
||||
assert (
|
||||
"lift.observe(info)" in chrome
|
||||
and 'lift.observe(overlay.querySelector(".b05-routeedit__side-wrap")!)' in chrome
|
||||
)
|
||||
assert '"is-narrow-bottom"' in chrome and '"--b05-info-h"' in chrome
|
||||
Reference in New Issue
Block a user