// DOM 小工具 + 单例浮层菜单。 import { escapeHtml } from "./format.js"; export const $ = (id) => document.getElementById(id); // ───── floating dropdown menu (single instance) ───── // 用 position: fixed 单例避免被 pane overflow 裁剪;按位置算出右上角对齐 let _menuItems = null; export function showMenu(triggerEl, items) { _menuItems = items; const menu = $("floating-menu"); menu.innerHTML = items.map((it) => { const cls = "dd-item " + (it.cls || ""); const dis = it.disabled ? " disabled" : ""; return ``; }).join(""); menu.querySelectorAll(".dd-item").forEach((btn) => { btn.onclick = (e) => { e.stopPropagation(); const act = btn.dataset.act; const item = _menuItems && _menuItems.find((i) => i.act === act); hideMenu(); if (item && item.onclick) item.onclick(); }; }); // 默认右下展开;若空间不足则改向上 const rect = triggerEl.getBoundingClientRect(); menu.style.visibility = "hidden"; menu.classList.add("show"); const mh = menu.offsetHeight || 120; menu.style.right = Math.max(4, window.innerWidth - rect.right) + "px"; menu.style.left = "auto"; if (rect.bottom + mh + 8 > window.innerHeight) { menu.style.top = Math.max(4, rect.top - mh - 4) + "px"; } else { menu.style.top = (rect.bottom + 4) + "px"; } menu.style.visibility = ""; } export function hideMenu() { _menuItems = null; $("floating-menu").classList.remove("show"); } document.addEventListener("click", (e) => { if (e.target.closest(".dd-toggle")) return; if (e.target.closest("#floating-menu")) return; hideMenu(); }, true); window.addEventListener("resize", hideMenu); // 滚动 pane 时菜单位置失效,直接关 document.addEventListener("scroll", hideMenu, true);