Files
bim_engine/docs/components/menu.md

318 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 构造函数
```typescript
constructor(options: MenuOptions)
```
**参数**:
- `options`: `MenuOptions` - 菜单配置选项
**MenuOptions 定义**:
```typescript
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` 接口)
**功能**:
- 取消语言和主题订阅
- 关闭并销毁所有打开的子菜<E5AD90><E88F9C>
- 从 DOM 中移除自身元素
#### `getElement(): HTMLElement`
获取组件根元素(实现 `IRightKeyContent` 接口)
**返回**: `HTMLElement` (根 `<ul>` 元素)
**功能**:
- 允许父容器(如 `BimRightKey`)获取并挂载此菜单
---
## 3. 分化组件说明
**BimMenu 没有子类或分化组件**。它通过递归自身来实现多级菜单。
---
## 4. Manager API 文档
参见 [RightKey 组件文档](./right-key.md) 中的 `RightKeyManager``BimMenu` 本身是一个纯 UI 组件,通常不直接通过 Manager 管理,而是被 `RightKeyManager` 动态创建和销毁。
---
## 5. UI 详细描述
### 5.1 DOM 结构
```html
<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: column`
- `background`: `var(--bim-ui_bg_color)`
- `border-radius: 4px`
- `padding: 4px 0`, `margin: 0`, `list-style: none` (关键:移除列表默认样式)
- `min-width: 160px`
- `box-shadow`: `0 4px 12px rgba(0,0,0,0.2)`
- `user-select: none`
#### `.bim-menu-item` (菜单项)
- `display: flex`, `align-items: center`
- `padding: 6px 12px`
- `cursor: pointer`
- `position: relative`
- `color`: `var(--bim-ui_text_primary)`
#### `.bim-menu-item:hover`
- `background-color`: `var(--bim-ui_bg_hover)`
#### `.bim-menu-item.disabled`
- `opacity: 0.5`
- `cursor: not-allowed`
- `pointer-events: none`
#### `.bim-menu-divider` (分割线)
- `height: 1px`
- `background-color`: `var(--bim-ui_border_color)`
- `margin: 4px 0`
---
## 6. 逻辑流程详细描述
### 6.1 渲染流程 (`render`)
1. **数据分桶**: 遍历 `items`,根据 `item.group` (默认为 'default') 将菜单项放入不同的数组中。
2. **确定顺序**:
- 如果提供了 `groupOrder`,优先按其顺序排列组。
- 未指定的组按遍历顺序追加。
3. **渲染分组**:
- 遍历排序后的组键。
- 如果不是第一组,先插入一个 `.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 数据结构示例
```typescript
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
```typescript
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
```typescript
interface MenuItemConfig {
id: string;
label: string;
onClick?: () => void;
icon?: string; // SVG 字符串
group?: string; // 分组标识
order?: number; // 排序权重
children?: MenuItemConfig[];
disabled?: boolean;
visible?: boolean;
}
```
### 11.2 MenuOptions
```typescript
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 |