From ef79b5b3709f3ca2a1115e2e1443e9b121a920f2 Mon Sep 17 00:00:00 2001 From: yuding <1023798085@qq.com> Date: Wed, 10 Dec 2025 10:10:09 +0800 Subject: [PATCH] refactor(right-key): move interaction logic to component and trigger on mouseup --- src/components/right-key/index.ts | 52 +++++++++++++++++++++++++++++-- src/components/right-key/types.ts | 4 +++ src/managers/right-key-manager.ts | 20 +++++------- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/components/right-key/index.ts b/src/components/right-key/index.ts index 200047c..ffb16bd 100644 --- a/src/components/right-key/index.ts +++ b/src/components/right-key/index.ts @@ -13,8 +13,13 @@ export class BimRightKey implements IBimComponent { private content: IRightKeyContent | null = null; private isVisible: boolean = false; private onCloseCallback?: () => void; + private options?: RightKeyOptions; + + private mouseDownTime: number = 0; + private readonly CLICK_THRESHOLD: number = 200; // ms constructor(options?: RightKeyOptions) { + this.options = options; this.element = document.createElement('div'); this.element.className = `bim-right-key ${options?.className || ''}`; @@ -36,6 +41,13 @@ export class BimRightKey implements IBimComponent { e.preventDefault(); e.stopPropagation(); }); + + // 绑定容器交互事件 + if (this.options?.container) { + this.options.container.addEventListener('mousedown', this.handleContainerMouseDown); + this.options.container.addEventListener('mouseup', this.handleContainerMouseUp); + this.options.container.addEventListener('contextmenu', this.handleContainerContextMenu); + } } public setTheme(_theme: ThemeConfig): void { @@ -51,10 +63,46 @@ export class BimRightKey implements IBimComponent { public destroy(): void { document.removeEventListener('mousedown', this.handleGlobalClick); + + if (this.options?.container) { + this.options.container.removeEventListener('mousedown', this.handleContainerMouseDown); + this.options.container.removeEventListener('mouseup', this.handleContainerMouseUp); + this.options.container.removeEventListener('contextmenu', this.handleContainerContextMenu); + } + this.unmountContent(); this.element.remove(); } + private handleContainerMouseDown = (e: MouseEvent): void => { + // 记录右键按下时间 (button 2 是右键) + if (e.button === 2) { + this.mouseDownTime = Date.now(); + } + }; + + private handleContainerMouseUp = (e: MouseEvent): void => { + // 只处理右键 (button 2) + if (e.button !== 2) return; + + // 检查点击时长,如果是长按或拖拽(时间过长),则不触发回调 + const pressDuration = Date.now() - this.mouseDownTime; + if (pressDuration > this.CLICK_THRESHOLD) { + return; + } + + // 触发有效右键回调 + if (this.options?.onContext) { + this.options.onContext(e); + } + }; + + private handleContainerContextMenu = (e: MouseEvent): void => { + // 阻止浏览器默认的右键菜单 + // 真正的菜单触发逻辑已移至 mouseup,这里只负责拦截默认行为 + e.preventDefault(); + }; + /** * 设置关闭时的回调函数 * 通常用于通知 Manager 状态变更 @@ -80,7 +128,7 @@ export class BimRightKey implements IBimComponent { */ public unmountContent(): void { if (this.content) { - this.content.destroy(); // 重要:调用组件销毁方法清理资源 + this.content.destroy(); // 重要:调用组件销毁方法清理���源 this.element.innerHTML = ''; this.content = null; } @@ -100,7 +148,7 @@ export class BimRightKey implements IBimComponent { this.element.style.left = `${x}px`; this.element.style.top = `${y}px`; - // 2. 获取容器���寸和视口尺寸 + // 2. 获取容器尺寸和视口尺寸 const rect = this.element.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; diff --git a/src/components/right-key/types.ts b/src/components/right-key/types.ts index d251a62..b69f59e 100644 --- a/src/components/right-key/types.ts +++ b/src/components/right-key/types.ts @@ -15,4 +15,8 @@ export interface RightKeyOptions { className?: string; /** 层级 (z-index) */ zIndex?: number; + /** 监听事件的容器 */ + container?: HTMLElement; + /** 有效右键点击时的回调 */ + onContext?: (e: MouseEvent) => void; } diff --git a/src/managers/right-key-manager.ts b/src/managers/right-key-manager.ts index c4884ec..ba590c1 100644 --- a/src/managers/right-key-manager.ts +++ b/src/managers/right-key-manager.ts @@ -16,7 +16,7 @@ export class RightKeyManager extends BimComponent { private rightKeyPanel: BimRightKey; // 存储注册的上下文处理器 - // 每个处理��接收鼠标事件,返回一组菜单项(如果没有对应菜单则返回 null) + // 每个处理器接收鼠标事件,返回一组菜单项(如果没有对应菜单则返回 null) private contextHandlers: Array<(e: MouseEvent) => MenuItemConfig[] | null> = []; constructor(engine: BimEngine, container: HTMLElement) { @@ -24,18 +24,16 @@ export class RightKeyManager extends BimComponent { this.container = container; // 初始化右键容器,设置极高的层级以覆盖所有 UI - this.rightKeyPanel = new BimRightKey({ zIndex: 9000 }); + // 将事件监听和触发逻辑下放给 BimRightKey 组件 + this.rightKeyPanel = new BimRightKey({ + zIndex: 9000, + container: this.container, + onContext: this.handleContextMenu + }); this.rightKeyPanel.init(); - - this.initEventListeners(); - } - - private initEventListeners(): void { - this.container.addEventListener('contextmenu', this.handleContextMenu); } public destroy(): void { - this.container.removeEventListener('contextmenu', this.handleContextMenu); this.rightKeyPanel.destroy(); } @@ -78,11 +76,9 @@ export class RightKeyManager extends BimComponent { /** * 处理右键点击事件 + * 由 BimRightKey 组件在检测到有效右键点击时调用 */ private handleContextMenu = (e: MouseEvent): void => { - // 阻止浏览器默认的右键菜单 - e.preventDefault(); - // 1. 确定上下文项 // 遍历所有注册的处理器,找到第一个返回非空结果的处理器 // 这种责任链模式允许插件优先处理特定对象的右键