feat(지도 도구줄): 좁으면 한 줄 가로 밀기 · 넘친 쪽 끝 흐림 · 휠 가로 밀기 · D03 도구줄이 겹 단추줄을 안 덮음 (PLAN 571-1 · 요구 399)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 12:07:52 +09:00
co-authored by Claude Sonnet 5.5
parent 70b0e06df4
commit f4c43db01b
5 changed files with 72 additions and 1 deletions
@@ -14,7 +14,7 @@
min-height: 0;
}
/* 도구줄 — 지도 왼쪽 위(공용 `ui-map-tools` · 넘치면 줄바꿈 · 빈 자리는 지도 조작 통과) · [?] 안내 판은 그 아래. */
/* 도구줄 — 지도 왼쪽 위(공용 `ui-map-tools` · 넘치면 가로 밀기 · 빈 자리는 지도 조작 통과) · [?] 안내 판은 그 아래. */
.b05-routeedit__tools {
position: absolute;
z-index: 2;
@@ -25,10 +25,13 @@
touch-action: none;
}
/* 571-1 — 왼쪽은 겹 단추줄(222px) 오른쪽까지 · 그 사이가 모자라면 도구줄이 가로 밀림 · 오른쪽 붙임 그대로 */
.d03-plan__tools {
position: absolute;
top: var(--spacing-8);
right: var(--spacing-8);
left: calc(2 * var(--spacing-8) + 222px);
align-items: flex-end;
}
/* 569-2 바탕 겹 단추 — 왼쪽 위 · 급경사 범례 — 왼쪽 아래 */
+13
View File
@@ -79,3 +79,16 @@ def test_켬끔_안내_판():
assert "display: none;" in hint[: hint.index("}")]
assert "display: grid;" in CSS[CSS.index(".ui-map-tools__hint.is-open {") :]
assert "pointer-events: none;" in CSS # 빈 자리 · 글판은 지도 조작 통과
def test_571_1_한_줄_가로_밀기():
bar = CSS[CSS.index(".ui-map-tools > .ui-toolbar {") :]
bar = bar[: bar.index("}")]
assert "flex-wrap: nowrap;" in bar # 줄바꿈 없음
assert "overflow-x: auto;" in bar # 밀기 칸
assert "scrollbar-width: none;" in bar
assert "mask-image" in bar # 넘친 끝 흐림
assert ".ui-toolbar.is-more-end" in CSS and ".ui-toolbar.is-more-start" in CSS
assert "flex-shrink: 0;" in CSS # 단추가 찌그러지지 않음
assert "watchOverflow(bar);" in TS
assert '"wheel"' in TS and "passive: false" in TS # 휠 = 가로 밀기
+30
View File
@@ -10,6 +10,36 @@
.ui-map-tools > .ui-toolbar {
max-width: 100%;
pointer-events: auto;
/* 571-1 — 한 줄 고정 · 모자라면 가로 밀기(막대 숨김) · 넘친 쪽 끝만 흐림 */
flex-wrap: nowrap;
overflow-x: auto;
overscroll-behavior-x: contain;
scrollbar-width: none;
--map-tools-fade-start: 0px;
--map-tools-fade-end: 0px;
mask-image: linear-gradient(
to right,
transparent,
var(--color-text-body) var(--map-tools-fade-start),
var(--color-text-body) calc(100% - var(--map-tools-fade-end)),
transparent
);
}
.ui-map-tools > .ui-toolbar::-webkit-scrollbar {
display: none;
}
.ui-map-tools > .ui-toolbar > * {
flex-shrink: 0;
}
.ui-map-tools > .ui-toolbar.is-more-start {
--map-tools-fade-start: var(--spacing-24);
}
.ui-map-tools > .ui-toolbar.is-more-end {
--map-tools-fade-end: var(--spacing-24);
}
/* 안내 판 — 기본 접힘 · 2열 · 글판은 클릭 통과 */
+25
View File
@@ -4,6 +4,7 @@
* 꼴 = 유리 도구줄(`createToolbar` glass · 32) 한 줄 · 묶음 사이 나눔선 · 단추는 모두 아이콘(마우스 올리면 이름)
* [?] 안내 판 = 도구줄 아래 2열 글판(기본 접힘 · 클릭은 지도로 통과)
* 크기 = 단 32 하나(지도 위 도구)
* 좁을 때 = 줄바꿈 없이 가로 밀기(터치 · 휠) · 넘친 쪽 끝만 흐림(`is-more-start` · `is-more-end`)
* 상태 = 켬끔 단추는 `aria-pressed`(눌림 꼴) · 막힘 `disabled` · [?] 는 `aria-expanded`
* 쓰는 법 = createMapTools({ label: "평면 도구", groups: [["undo", "redo"], ["rotate-ccw", "rotate-cw", "measure"]],
* hint: { lines: ["휠 = 확대"] } }) → { root, bar, hint, button(act) }
@@ -74,6 +75,29 @@ export interface MapTools {
button(act: MapToolAct): HTMLButtonElement | null;
}
/** 넘침 표시 + 휠을 가로 밀기로 — 도구줄이 넘칠 때만 */
function watchOverflow(bar: HTMLElement): void {
const mark = (): void => {
const max = bar.scrollWidth - bar.clientWidth;
bar.classList.toggle("is-more-start", bar.scrollLeft > 1);
bar.classList.toggle("is-more-end", max - bar.scrollLeft > 1);
};
bar.addEventListener("scroll", mark, { passive: true });
bar.addEventListener(
"wheel",
(ev) => {
if (bar.scrollWidth <= bar.clientWidth || Math.abs(ev.deltaX) > Math.abs(ev.deltaY)) return;
ev.preventDefault();
bar.scrollLeft += ev.deltaY;
},
{ passive: false },
);
if (typeof ResizeObserver === "undefined") return;
const observer = new ResizeObserver(mark);
observer.observe(bar);
for (const child of Array.from(bar.children)) observer.observe(child);
}
export function createMapTools(opts: MapToolsOptions): MapTools {
const root = document.createElement("div");
root.className = "ui-map-tools";
@@ -110,6 +134,7 @@ export function createMapTools(opts: MapToolsOptions): MapTools {
});
root.append(bar);
if (hint) root.append(hint);
watchOverflow(bar);
return {
root,
bar,