9.2 KiB
9.2 KiB
Menu 组件详细文档
本文档详细描述 Menu 组件的实现细节,包括 API、UI 结构、逻辑流程等,供 AI 根据文档重现组件。
1. 组件概述
1.1 基本信息
- 组件名称:
BimMenu - 文件路径:
src/components/menu/index.ts - 类型定义:
src/components/menu/item.ts(配置),src/components/menu/types.ts(选项) - 样式文件:
src/components/menu/index.css - 实现接口:
IBimComponent,IRightKeyContent - 用途: 渲染一组菜单项,支持分组、排序、图标、快捷键提示和无限级递归子菜单。
- 特点: 纯数据驱动(基于
MenuItemConfig),内置国际化支持。
1.2 在 SDK 中的位置
- Menu 组件主要由
RightKeyManager使用,作为右键菜单的内容载体。 - 也可以单独实例化并挂载到任何容器中。
RightKeyManager位于src/managers/right-key-manager.ts。
2. 组件类 API 文档
2.1 构造函数
constructor(options: MenuOptions)
参数:
options:MenuOptions- 菜单配置选项
MenuOptions 定义:
interface MenuOptions {
items: MenuItemConfig[]; // 菜单项配置列表
groupOrder?: string[]; // 分组显示顺序
}
行为:
- 创建根元素
<ul> - 保存配置选项
2.2 公共方法
init(): void
初始化组件(实现 IBimComponent 接口)
功能:
- 调用
render()渲染 DOM 结构 - 订阅语言变更:
localeManager.subscribe() - 订阅主题变更:
themeManager.subscribe()(虽然目前主要通过 CSS 变量,但保留订阅以备扩展)
setTheme(theme: ThemeConfig): void
设置主题(实现 IBimComponent 接口)
参数:
theme:ThemeConfig
功能:
- 将主题颜色映射到 CSS 变量
--bim-ui_bg_color←theme.panelBackground--bim-ui_text_primary←theme.textPrimary--bim-ui_border_color←theme.border--bim-ui_bg_hover←theme.componentHover
setLocales(): void
设置语言(实现 IBimComponent 接口)
功能:
- 清空当前 DOM
- 重新调用
render(),使t()函数获取最新的翻译文本
destroy(): void
销毁组件(实现 IBimComponent, IRightKeyContent 接口)
功能:
- 取消语言和主题订阅
- 关闭并销毁所有打开的子菜<EFBFBD><EFBFBD>
- 从 DOM 中移除自身元素
getElement(): HTMLElement
获取组件根元素(实现 IRightKeyContent 接口)
返回: HTMLElement (根 <ul> 元素)
功能:
- 允许父容器(如
BimRightKey)获取并挂载此菜单
3. 分化组件说明
BimMenu 没有子类或分化组件。它通过递归自身来实现多级菜单。
4. Manager API 文档
参见 RightKey 组件文档 中的 RightKeyManager。BimMenu 本身是一个纯 UI 组件,通常不直接通过 Manager 管理,而是被 RightKeyManager 动态创建和销毁。
5. UI 详细描述
5.1 DOM 结构
<ul class="bim-menu">
<!-- 分组 1 -->
<li class="bim-menu-item [disabled]">
<div class="bim-menu-item-icon">[SVG图标]</div>
<div class="bim-menu-item-label">菜单文本</div>
<!-- 如果有子菜单 -->
<div class="bim-menu-item-arrow">
<svg>...</svg> <!-- 右箭头 -->
</div>
</li>
<!-- 分组分割线 -->
<li class="bim-menu-divider"></li>
<!-- 分组 2 -->
<li class="bim-menu-item">...</li>
</ul>
5.2 CSS 类名和样式
.bim-menu (根容器)
display: flex,flex-direction: columnbackground:var(--bim-ui_bg_color)border-radius: 4pxpadding: 4px 0,margin: 0,list-style: none(关键:移除列表默认样式)min-width: 160pxbox-shadow:0 4px 12px rgba(0,0,0,0.2)user-select: none
.bim-menu-item (菜单项)
display: flex,align-items: centerpadding: 6px 12pxcursor: pointerposition: relativecolor:var(--bim-ui_text_primary)
.bim-menu-item:hover
background-color:var(--bim-ui_bg_hover)
.bim-menu-item.disabled
opacity: 0.5cursor: not-allowedpointer-events: none
.bim-menu-divider (分割线)
height: 1pxbackground-color:var(--bim-ui_border_color)margin: 4px 0
6. 逻辑流程详细描述
6.1 渲染流程 (render)
- 数据分桶: 遍历
items,根据item.group(默认为 'default') 将菜单项放入不同的数组中。 - 确定顺序:
- 如果提供了
groupOrder,优先按其顺序排列组。 - 未指定的组按遍历顺序追加。
- 如果提供了
- 渲染分组:
- 遍历排序后的组键。
- 如果不是第一组,先插入一个
.bim-menu-divider。 - 获取组内项,根据
item.order升序排序。 - 遍历组内项,如果
item.visible !== false,调用createItemElement创建 DOM。
6.2 交互流程
点击事件
- 绑定: 在
.bim-menu-item上绑定click。 - 逻辑:
e.stopPropagation(): 阻止冒泡。- 检查
!hasChildren: 只有叶子节点才触发点击。 - 调用
item.onClick?.()。 - 注意: 点击后,通常期望菜单关闭。这依赖于
RightKeyManager或外部逻辑(通过onClick内部调用关闭,或者点击触发全局关闭)。
子菜单展开 (mouseenter)
- 触发: 鼠标移入带有子项的菜单项。
- 逻辑:
- 调用
closeSubMenu()关闭当前可能已打开的其他子菜单。 - 计算位置:通常位于父项右侧 (
rect.right),顶部对齐 (rect.top)。 - 创建子菜单容器
div(fixed 定位, z-index 10001)。 - 关键修复: 在容器上绑定
mousedown并stopPropagation,防止触发BimRightKey的全局关闭逻辑。 - 实例化新的
BimMenu(递归) 并init()。 - 将新菜单挂载到容器,容器挂载到
document.body。 - 保存引用到
this.activeSubMenu。 - 边界检测: 如果右侧超出屏幕,改为向左展开。
- 调用
子菜单关闭
- 触发: 鼠标移入不带子项的菜单项。
- 逻辑: 调用
closeSubMenu(),销毁实例并移除 DOM。
7. 国际化支持
7.1 实现方式
- 使用
src/services/locale.ts中的t()函数。 - 在
createItemElement中,直接使用t(item.label)设置文本。 - 组件初始化时订阅语言变更,变更发生时清空并重绘 (
setLocales->render)。
8. 主题支持
8.1 变量映射
- 在
setTheme中设置内联样式变量:--bim-ui_bg_color: 面板背景--bim-ui_text_primary: 主要文字--bim-ui_border_color: 边框/分割线--bim-ui_bg_hover: 悬停背景
9. 使用示例
9.1 数据结构示例
const menuItems: MenuItemConfig[] = [
{
id: 'view',
label: 'menu.view', // 翻译键
group: 'view',
children: [
{
id: 'home',
label: 'menu.home',
onClick: () => console.log('Go Home')
}
]
},
{
id: 'delete',
label: 'menu.delete',
group: 'edit',
icon: '<svg>...</svg>',
onClick: () => console.log('Delete')
}
];
9.2 手动创建(通常不直接使用,而是通过 RightKeyManager)
const menu = new BimMenu({ items: menuItems });
menu.init();
document.body.appendChild(menu.getElement());
10. 实现细节(供 AI 重现)
10.1 递归设计
组件自身不包含递归渲染 DOM 的逻辑,而是通过“交互触发”来递归实例化。即:子菜单不是一开始就渲染在 DOM 树中的,而是当用户鼠标悬停时,动态创建一个新的 BimMenu 实例并挂载到 body 上。这种设计避免了深层嵌套 DOM 带来的样式问题(如 overflow: hidden 截断子菜单)。
10.2 事件冲突处理
由于子菜单挂载在 body 上,点击子菜单在 DOM 树看来是“点击了主菜单外部”。这会触发 BimRightKey 的“点击外部关闭”逻辑。
解决方案: 子菜单容器监听 mousedown 并 stopPropagation(),欺骗 BimRightKey 认为点击没有发生(或者发生在内部)。
11. 类型定义
11.1 MenuItemConfig
interface MenuItemConfig {
id: string;
label: string;
onClick?: () => void;
icon?: string; // SVG 字符串
group?: string; // 分组标识
order?: number; // 排序权重
children?: MenuItemConfig[];
disabled?: boolean;
visible?: boolean;
}
11.2 MenuOptions
interface MenuOptions {
items: MenuItemConfig[];
groupOrder?: string[];
}
12. 文件清单
src/components/menu/index.ts: 组件核心逻辑src/components/menu/index.css: 组件样式src/components/menu/item.ts: 仅包含类型定义 (MenuItemConfig)src/components/menu/types.ts: 组件选项接口
13. 更新记录
| 日期 | 修改内容 | 修改人 |
|---|---|---|
| 2024-XX-XX | 重构为纯配置对象驱动,移除 BimMenuItem 类 | AI Assistant |