feat(부품): 634-1 여럿 고르기 메뉴 · 한 행 두 영역 · 막힘 글 · 새 아이콘(요구 546)

- createPickMenu multi — menuitemcheckbox · 눌러도 안 닫힘 · values() · 줄 icon · disabled · title · setItem · 단추 ▾ + 켠 수 배지(0 = 없음) · 하나라도 켜면 눌림 · 밀기 · 창 크기 바뀌면 닫힘 · offLabel 없으면 끔 줄 · 눌림 없음
- createMapToolRow — 그룹1 왼쪽(행 반까지) · 그룹2 오른쪽 · 영역마다 min-width 0 + 자기 가로 밀기 · [?] 판은 행 아래
- mapToolTip — 평소 제목만 · 막힘 때만 「제목 — 까닭」 · MAP_TOOLS 글 = 표 호버제목
- 아이콘 — 묶음 넷(streams · signpost · parcelHouse · ban) · 겹친 셋 대신(contours · cutFill · lineament) · 스냅 간격 spacing
- 배지 count · accent 덧 · 부품 모음 견본 「고르기 메뉴(하나 · 여럿)」 · 지도 도구줄 한 행 견본
- 시험 568_1 고침 — 예정노선 둘 줄이 한 줄 꼴이 되어 목록에 잡힘 · 새 지킴 634_1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 11:26:52 +09:00
co-authored by Claude Opus 5.5
parent bbe3a05201
commit 2265ac4f7c
10 changed files with 339 additions and 57 deletions
+38 -23
View File
@@ -10,6 +10,7 @@
* hint: { lines: ["휠 = 확대"] } }) → { root, bar, hint, button(act) }
* 페이지 문구가 다르면 `mapToolButton("measure", { title })` 를 묶음에 그대로 끼움 · 고르기 · 칩도 요소로 끼움
* 누름 동작은 페이지가 붙임(`onClick` 또는 `[data-act]` 로 찾아 붙임) · 자리는 페이지 CSS(root)
* 한 행 두 영역 = `createMapToolRow(겹 줄 root, 도구줄 root)`(634-1) · 막힘 글 = `mapToolTip(단추, 까닭)`
* 대신하는 옛 변종 = B05 `b05-route__view-controls` 글자 유리 단추 줄 · `b05-routeedit__hint` 안내 판 · D03 `tool()` 지역 함수
* ========================================================================== */
@@ -20,29 +21,19 @@ import "./ui_template_map_tools.css";
/** 단추 목록 — 차례 = 도구줄 차례 · toggle = 켬끔 단추(aria-pressed) */
export const MAP_TOOLS = {
undo: { icon: "undo", title: "되돌리기 (Ctrl+Z)" },
redo: { icon: "redo", title: "다시하기 (Ctrl+Y)" },
view3d: { icon: "cube", title: "3D 보기" },
"rotate-ccw": { icon: "rotateCcw", title: "반시계로 돌리기" },
"rotate-cw": { icon: "rotateCw", title: "시계로 돌리기" },
"contour-minor": { icon: "ripple", title: "1m 등고선", toggle: true },
undo: { icon: "undo", title: "되돌리기" },
redo: { icon: "redo", title: "다시하기" },
view3d: { icon: "cube", title: "3D" },
"rotate-ccw": { icon: "rotateCcw", title: "좌 회전" },
"rotate-cw": { icon: "rotateCw", title: "우 회전" },
"contour-minor": { icon: "contours", title: "등고선 1m", toggle: true },
stations: { icon: "tallymarks", title: "측점", toggle: true },
slopes: { icon: "percentage", title: "기울기", toggle: true },
footprint: { icon: "road", title: "절/성토선", toggle: true },
measure: { icon: "ruler", title: "거리 재기", toggle: true },
"station-snap": { icon: "magnet", title: "측점 스냅", toggle: true },
"expected-draw": {
icon: "pencil",
title:
"예정노선 변경 — 켜면 예정노선이 맨 위 · 예정노선 페이지와 같은 긋기 · 고르기 · 고치기 → 평면선을 예정노선 따라 통째 다시 추적 (켠 동안 계획선 · 구조물은 안 집힘)",
toggle: true,
},
"expected-original": {
icon: "lineDashed",
title:
"예정노선(원본) — 파일로 올린 예정노선을 옅은 점선으로 보이기 · 숨기기 (고친 예정노선과 다를 때만 보임)",
toggle: true,
},
slopes: { icon: "percentage", title: "길이·기울기", toggle: true },
footprint: { icon: "cutFill", title: "절성토선", toggle: true },
measure: { icon: "ruler", title: "길이측정", toggle: true },
"station-snap": { icon: "magnet", title: "스냅", toggle: true },
"expected-draw": { icon: "pencil", title: "예정노선 변경", toggle: true },
"expected-original": { icon: "lineDashed", title: "예정노선 원본", toggle: true },
} satisfies Record<string, { icon: IconName; title: string; toggle?: boolean }>;
export type MapToolAct = keyof typeof MAP_TOOLS;
@@ -68,9 +59,18 @@ export function mapToolButton(act: MapToolAct, opts: MapToolButtonOptions = {}):
onClick: opts.onClick,
});
button.dataset.act = act;
button.dataset.tip = opts.title ?? tool.title;
return button;
}
/** 마우스 글(요구 546) — 평소 제목만 · 막힘이면 「제목 — 까닭」 · 제목 = `data-tip`(없으면 지금 글). */
export function mapToolTip(el: HTMLElement, why?: string | null): void {
el.dataset.tip ??= el.title;
const text = why ? `${el.dataset.tip} — ${why}` : el.dataset.tip;
el.title = text;
el.setAttribute("aria-label", text);
}
export interface MapToolsOptions {
/** 묶음들 — 사이에 나눔선 · act 이름 또는 페이지가 만든 요소 */
groups: (MapToolAct | HTMLElement)[][];
@@ -119,7 +119,7 @@ export function createMapTools(opts: MapToolsOptions): MapTools {
let hint: HTMLElement | null = null;
if (opts.hint) {
const { lines, onToggle } = opts.hint;
const toggle = createIconButton({ icon: "help", title: "조작 도움말 펼치기 · 접기", size: 32 });
const toggle = createIconButton({ icon: "help", title: "도움말", size: 32 });
toggle.dataset.act = "hint-toggle";
const panel = document.createElement("div");
panel.className = "ui-map-tools__hint";
@@ -154,3 +154,18 @@ export function createMapTools(opts: MapToolsOptions): MapTools {
button: (act) => root.querySelector<HTMLButtonElement>(`[data-act="${act}"]`),
};
}
/** 한 행 두 영역(634-1 · 요구 546) — 왼쪽(지도 겹) · 오른쪽(도구) · 영역마다 자기 가로 밀기 · 모자라면 오른쪽 먼저 밀림 ·
* [?] 안내 판은 행 아래로 옮김 · 자리(위 · 왼쪽 · 오른쪽)는 페이지 CSS(root). */
export function createMapToolRow(left: HTMLElement, right: HTMLElement): HTMLElement {
const root = document.createElement("div");
root.className = "ui-map-row";
const line = document.createElement("div");
line.className = "ui-map-row__line";
line.append(left, right);
const hints = [left, right].flatMap((area) =>
Array.from(area.querySelectorAll<HTMLElement>(":scope > .ui-map-tools__hint")),
);
root.append(line, ...hints);
return root;
}