- 겹 표(map_layer_list) = 그룹1 22 줄 표 차례 · title = 호버제목 · group(수계망도 · 도로 · 토지·건물 · 제한·재해) · pages · 숨김 표 걷음 · 도로300m 한 줄(「도로」 묶음 · 기능 남김) - 겹 줄(map_layer_bar) = 단독 넷 | 묶음 넷(여럿 고르기 메뉴 · 켠 수 배지 · 안 닫힘) | 그래픽 카드 · sync 가 단추 · 메뉴 줄 막힘 · 글을 맞춤(why = 페이지 까닭) - 도구 표(map_toolbar) = 그룹2 17 줄 title · mapToolGroups 가 글을 표로 맞춤 · 기본 단추 생성 · 지형 칩 막힘 「수치지형도/라이다 — 까닭」 - 띠 = 한 자료 · 겹 둘(GL fillEdge 거름 · 옛 캔버스 · 범례 줄 따로) · B05 띠 받기(D03 과 같은 서버 길 · bandCollection 공용) - D03 = 겹 줄 · 도구줄 한 행(createMapToolRow) · 222px 기댐 · 줄바꿈 CSS 걷음 · 측점 · 기울기 호버 덮어쓰기 걷음 · LAS 올리기 호버 걷음 - B05 = 세로 두 줄 → 한 행 · 호버 덮어쓰기 걷음(도구줄 · 1m 등고선 · 3D · 추천) · 겹 막힘 querySelector 덮어쓰기 → 공용 sync · 임도(업로드) 보임 + 「받은 임도 자료 없음」 막힘 · 스냅 간격 날 select → 고르기 메뉴(하나) - 켬끔 기억 열쇠 · 측점 · 기울기 처음 값 · 그래픽 카드 취향 열쇠 둘 그대로 - 낡은 시험 고침(568_1 · 622_2 · 624_2 · 624_3 · 553_4 · 625_draw · b05_routeedit_initial · 626_3 · 390_4 · 346_2 · 353_1 · 581_2 · 584_1 · 584_3) — 표 꼴 row() · 호버 글 = 표 · 한 행 · 고르기 메뉴로 바뀐 글을 잡음 · 새 지킴 634_2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
134 lines
5.0 KiB
TypeScript
134 lines
5.0 KiB
TypeScript
/* =============================================================================
|
|
* map_layer_bar.ts
|
|
* 공용 지도 겹 줄(PLAN 569-2 · 586-2 · 624-2 · 634-2 · 요구 530 · 546) — 겹 단추 한 줄(공용 지도 도구줄 `createMapTools`):
|
|
* 단독 단추(표 묶음 없음) | 묶음 단추(여럿 고르기 메뉴 · 켠 수 배지 · 골라도 안 닫힘) | 그래픽 카드 단추(GL ↔ 옛 캔버스).
|
|
* 보일 겹 · 묶음 · 호버제목 · 처음 켬 · 취향 열쇠 = 표(`map_layer_list`) · 누르면 켬끔 뒤집고 취향에 적고 `onToggle`.
|
|
* 막힘 = 지형을 아직 안 받음(`ready: false`) · 조각만 쓰는 겹은 옛 캔버스에서 · 페이지 까닭(`why`) — 「제목 — 까닭」.
|
|
* 받기 · 그리기 · 범례는 페이지 몫 — 켬끔 값(`on`)은 같은 객체를 같이 씀.
|
|
* 쓰는 법 = `createMapLayerBar({ page: "d03", glOn, onToggle, onGl })` → `{ root, bar, on, toggle, save, sync }`.
|
|
* ========================================================================== */
|
|
|
|
import { createIconButton } from "@ui/ui_template_button";
|
|
import { createPickMenu, type PickMenu } from "@ui/ui_template_context_menu";
|
|
import { createMapTools, mapToolTip } from "@ui/ui_template_map_tools";
|
|
import { writeByKey } from "../b_page_state";
|
|
import { tileOnly } from "./map_forest_layers";
|
|
import {
|
|
LAYER_GROUPS,
|
|
MAP_LAYER_PAGES,
|
|
pageLayers,
|
|
readLayerPref,
|
|
type LayerGroup,
|
|
type MapLayerKey,
|
|
type MapPage,
|
|
} from "./map_layer_list";
|
|
|
|
/** 그래픽 카드 단추 호버(표 밖 · 그룹1 끝 단독). */
|
|
const GL_TITLE = "그래픽 카드";
|
|
|
|
export interface MapLayerBar {
|
|
/** 도구줄 감싼 것(자리는 페이지 CSS · 한 행 = `createMapToolRow`). */
|
|
root: HTMLElement;
|
|
/** 도구줄 자체(폭 재기). */
|
|
bar: HTMLElement;
|
|
/** 켬끔 — 페이지가 바꾸면 `save` · 받기 실패로 끄는 것은 안 적음. */
|
|
on: Record<MapLayerKey, boolean>;
|
|
/** 뒤집고 적고 `onToggle`. */
|
|
toggle: (key: MapLayerKey) => void;
|
|
save: () => void;
|
|
/** 단추 · 메뉴 줄의 눌림 · 막힘 · 글을 켬끔 값에 맞춤 · `why` = 페이지 막힘 까닭(겹마다). */
|
|
sync: (state: {
|
|
ready: boolean;
|
|
glOn: boolean;
|
|
why?: Partial<Record<MapLayerKey, string>>;
|
|
}) => void;
|
|
}
|
|
|
|
export function createMapLayerBar(opts: {
|
|
page: MapPage;
|
|
glOn: boolean;
|
|
onToggle: (key: MapLayerKey) => void;
|
|
/** 그래픽 카드 단추 누름. */
|
|
onGl: () => void;
|
|
}): MapLayerBar {
|
|
const { page } = opts;
|
|
const on = readLayerPref(page);
|
|
const save = (): void => writeByKey(MAP_LAYER_PAGES[page].pref, JSON.stringify(on));
|
|
const toggle = (key: MapLayerKey): void => {
|
|
on[key] = !on[key];
|
|
save();
|
|
opts.onToggle(key);
|
|
};
|
|
const defs = pageLayers(page);
|
|
const buttons = new Map<MapLayerKey, HTMLButtonElement>();
|
|
const menus = new Map<LayerGroup, PickMenu>();
|
|
const singles: HTMLElement[] = [];
|
|
for (const def of defs) {
|
|
if (!def.group) {
|
|
const button = createIconButton({
|
|
icon: def.icon,
|
|
title: def.title,
|
|
size: 32,
|
|
pressed: on[def.key],
|
|
onClick: () => toggle(def.key),
|
|
});
|
|
button.dataset.layer = def.key;
|
|
button.dataset.tip = def.title;
|
|
buttons.set(def.key, button);
|
|
singles.push(button);
|
|
continue;
|
|
}
|
|
if (menus.has(def.group)) continue;
|
|
const group = def.group;
|
|
const menu = createPickMenu({
|
|
multi: true,
|
|
icon: LAYER_GROUPS[group].icon,
|
|
title: LAYER_GROUPS[group].name,
|
|
items: defs
|
|
.filter((one) => one.group === group)
|
|
.map((one) => ({ value: one.key, label: one.title, icon: one.icon })),
|
|
onPick: (key) => key && toggle(key as MapLayerKey),
|
|
});
|
|
menu.root.dataset.layerGroup = group;
|
|
menus.set(group, menu);
|
|
}
|
|
for (const def of defs)
|
|
if (def.group) menus.get(def.group)!.setItem(def.key, { checked: on[def.key] });
|
|
const glButton = createIconButton({
|
|
icon: "gauge",
|
|
title: GL_TITLE,
|
|
size: 32,
|
|
pressed: opts.glOn,
|
|
onClick: opts.onGl,
|
|
});
|
|
glButton.dataset.layer = "gl";
|
|
const tools = createMapTools({
|
|
label: "지도 겹",
|
|
groups: [singles, [...menus.values()].map((menu) => menu.root), [glButton]],
|
|
});
|
|
|
|
function sync({ ready, glOn, why = {} }: Parameters<MapLayerBar["sync"]>[0]): void {
|
|
for (const def of defs) {
|
|
const reason = !ready
|
|
? "지형 받기 뒤 보임"
|
|
: (why[def.key] ??
|
|
(tileOnly(def.key) && !glOn ? "옛 그림(그래픽 카드 끔)엔 안 보임" : null));
|
|
const button = buttons.get(def.key);
|
|
if (button) {
|
|
button.setAttribute("aria-pressed", String(on[def.key]));
|
|
button.disabled = !!reason;
|
|
mapToolTip(button, reason);
|
|
continue;
|
|
}
|
|
menus.get(def.group!)!.setItem(def.key, {
|
|
checked: on[def.key],
|
|
disabled: !!reason,
|
|
title: reason ? `${def.title} — ${reason}` : def.title,
|
|
});
|
|
}
|
|
glButton.setAttribute("aria-pressed", String(glOn));
|
|
}
|
|
|
|
return { root: tools.root, bar: tools.bar, on, toggle, save, sync };
|
|
}
|