import { BimComponent } from '../core/component'; import { BimEngine } from '../bim-engine'; import { BimRightKey } from '../components/right-key'; import { BimMenu } from '../components/menu'; import { MenuItemConfig } from '../components/menu/item'; /** * 右键菜单管理器 (RightKeyManager) * 负责协调右键交互流程: * 1. 监听 Canvas/容器的 contextmenu 事件 * 2. 通过注册的处理器 (Handler) 获取需要显示的菜单项 * 3. 实例化 Menu 组件并装载到 RightKey 容器中显示 */ export class RightKeyManager extends BimComponent { private container: HTMLElement; private rightKeyPanel: BimRightKey; // 存储注册的上下文处理器 // 每个处理器接收鼠标事件,返回一组菜单项(如果没有对应菜单则返回 null) private contextHandlers: Array<(e: MouseEvent) => MenuItemConfig[] | null> = []; constructor(engine: BimEngine, container: HTMLElement) { super(engine); this.container = container; // 初始化右键容器,设置极高的层级以覆盖所有 UI // 将事件监听和触发逻辑下放给 BimRightKey 组件 this.rightKeyPanel = new BimRightKey({ zIndex: 9000, container: this.container, onContext: this.handleContextMenu }); this.rightKeyPanel.init(); } public destroy(): void { this.rightKeyPanel.destroy(); } /** * 注册上下文菜单处理器 * @param handler 处理函数,接收鼠标事件,返回菜单项数组 */ public registerHandler(handler: (e: MouseEvent) => MenuItemConfig[] | null): void { this.contextHandlers.push(handler); } /** * 手动显示菜单 * 允许外部直接调用以显示特定的菜单,不一定依赖右键事件 * @param x 屏幕 X 坐标 * @param y 屏幕 Y 坐标 * @param items 菜单项列表 * @param groupOrder 可选的分组顺序 */ public showMenu(x: number, y: number, items: MenuItemConfig[], groupOrder?: string[]): void { if (!items || items.length === 0) return; // 1. 创建菜单内容组件 const menu = new BimMenu({ items, groupOrder }); menu.init(); // 必须初始化以生成 DOM // 2. 将菜单挂载到右键容器 this.rightKeyPanel.mount(menu); // 3. 显示容器 this.rightKeyPanel.show(x, y); } /** * 隐藏右键菜单 */ public hide(): void { this.rightKeyPanel.hide(); } /** * 处理右键点击事件 * 由 BimRightKey 组件在检测到有效右键点击时调用 */ private handleContextMenu = (e: MouseEvent): void => { // 1. 确定上下文项 // 遍历所有注册的处理器,找到第一个返回非空结果的处理器 // 这种责任链模式允许插件优先处理特定对象的右键 let items: MenuItemConfig[] | null = null; for (const handler of this.contextHandlers) { const result = handler(e); if (result && result.length > 0) { if (!items) items = []; items = items.concat(result); } } // 2. 如果有菜单项,则显示 if (items && items.length > 0) { this.showMenu(e.clientX, e.clientY, items); } else { // 如果没有任何内容,则关闭可能存在的菜单 this.hide(); } }; }