diff --git a/B05_Profile/B05_Profile_UI_Style_RouteEdit.css b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css index 7c6680997..7c35b874b 100644 --- a/B05_Profile/B05_Profile_UI_Style_RouteEdit.css +++ b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css @@ -14,7 +14,7 @@ min-height: 0; } -/* 도구줄 — 지도 왼쪽 위(공용 `ui-map-tools` · 넘치면 줄바꿈 · 빈 자리는 지도 조작 통과) · [?] 안내 판은 그 아래. */ +/* 도구줄 — 지도 왼쪽 위(공용 `ui-map-tools` · 넘치면 가로 밀기 · 빈 자리는 지도 조작 통과) · [?] 안내 판은 그 아래. */ .b05-routeedit__tools { position: absolute; z-index: 2; diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css index 1df8f3989..77484c73f 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css @@ -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 바탕 겹 단추 — 왼쪽 위 · 급경사 범례 — 왼쪽 아래 */ diff --git a/resources/tester/test_568_1_map_tools.py b/resources/tester/test_568_1_map_tools.py index 2017121ee..9cfd9653f 100644 --- a/resources/tester/test_568_1_map_tools.py +++ b/resources/tester/test_568_1_map_tools.py @@ -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 # 휠 = 가로 밀기 diff --git a/ui_template/ui_template_map_tools.css b/ui_template/ui_template_map_tools.css index d12e6fbb0..debd97581 100644 --- a/ui_template/ui_template_map_tools.css +++ b/ui_template/ui_template_map_tools.css @@ -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열 · 글판은 클릭 통과 */ diff --git a/ui_template/ui_template_map_tools.ts b/ui_template/ui_template_map_tools.ts index 524b4deb8..47c7e28cd 100644 --- a/ui_template/ui_template_map_tools.ts +++ b/ui_template/ui_template_map_tools.ts @@ -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,