/* ============================================================================= * 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; /** 뒤집고 적고 `onToggle`. */ toggle: (key: MapLayerKey) => void; save: () => void; /** 단추 · 메뉴 줄의 눌림 · 막힘 · 글을 켬끔 값에 맞춤 · `why` = 페이지 막힘 까닭(겹마다). */ sync: (state: { ready: boolean; glOn: boolean; why?: Partial>; }) => 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(); const menus = new Map(); 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[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 }; }