Files
yuding 4a09d52283 feat(clipping): implement hide/recover toggle for all section dialogs
Update all three section dialogs to support hide/show toggle:

SectionAxisDialogManager:
- onHideToggle now calls hideSection()/recoverSection()

SectionBoxDialogManager:
- onHideToggle now calls hideSection()/recoverSection()

SectionPlanePanel:
- Add isHidden state tracking
- Change onHide to onHideToggle(isHidden)
- Add setHiddenState/getHiddenState methods
- Update button to toggle active state

SectionPlaneDialogManager:
- Switch to onHideToggle callback
- Call hideSection()/recoverSection() based on toggle state

Behavior: Click hide button to hide section, click again to recover.
2026-02-02 16:36:17 +08:00

288 lines
8.0 KiB
Markdown
Raw Permalink 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.

# Architectural Decisions: 构件详情右键菜单功能
## 2026-01-28
### Decision 1: 选中状态存储在 Engine 而非 EngineManager
**Context**:
- 需要存储当前选中的构件信息url + id
- Engine 组件监听底层 click 事件
- EngineManager 是 Engine 的代理层
**Options Considered**:
1. 存储在 Engine 组件中
2. 存储在 EngineManager 中
3. 存储在独立的 SelectionManager 中
**Decision**: 选择 Option 1 - 存储在 Engine 组件中
**Rationale**:
- Engine 直接监听底层事件,数据流最短
- 避免事件转发的复杂性
- EngineManager 作为代理,只需暴露访问方法
- 保持单一职责原则Engine 管理状态EngineManager 提供接口
**Consequences**:
- ✅ 数据流清晰:底层事件 → Engine 状态 → EngineManager 访问
- ✅ 性能更好:无需事件转发
- ⚠️ Engine 组件职责略有增加(但合理)
---
### Decision 2: 使用动态菜单生成而非事件驱动更新
**Context**:
- 右键菜单内容需要根据选中状态变化
- 选中状态频繁变化
**Options Considered**:
1. 注册静态菜单 + 监听选中事件动态更新
2. 注册处理器函数,每次右键时动态生成菜单
**Decision**: 选择 Option 2 - 动态生成菜单
**Rationale**:
- 右键菜单不常使用,生成成本低
- 避免维护菜单状态的复杂性
- 无需监听选中事件单独更新菜单
- 代码更简洁,逻辑集中
**Implementation**:
```typescript
rightKey.registerHandler((_e) => {
const selected = this.getSelectedComponent();
const items: MenuItemConfig[] = [];
if (selected) {
items.push({ id: 'componentDetail', ... });
}
items.push({ id: 'showAll', ... });
return items;
});
```
**Consequences**:
- ✅ 代码简洁,逻辑集中
- ✅ 无需手动同步菜单状态
- ✅ 易于扩展(添加更多条件判断)
- ⚠️ 每次右键都重新生成(但开销可忽略)
---
### Decision 3: ComponentDetailManager 不注册为 BaseDialogManager
**Context**:
- ComponentDetailManager 需要创建对话框
- BaseDialogManager 提供了对话框生命周期管理
- 现有的 MeasureDialogManager、SectionPlaneDialogManager 等都继承 BaseDialogManager
**Options Considered**:
1. 继承 BaseDialogManager与现有 Manager 一致)
2. 不继承,直接使用 DialogManager API
**Decision**: 选择 Option 2 - 不继承 BaseDialogManager
**Rationale**:
- ComponentDetailManager 的对话框逻辑简单,无需复杂生命周期管理
- 无需 Panel 组件(直接使用 Collapse + Description
- 避免继承带来的不必要复杂性
- 直接调用 DialogManager.create() 更灵活
**Implementation**:
```typescript
export class ComponentDetailManager {
private dialog: BimDialog | null = null;
public show(modelUrl: string, componentId: string): void {
this.createDialog();
// ...
}
private createDialog(): void {
const registry = ManagerRegistry.getInstance();
this.dialog = registry.dialog?.create({ ... });
}
}
```
**Consequences**:
- ✅ 代码更简洁119 行 vs 预计 200+ 行)
- ✅ 职责明确Manager 只负责数据获取和展示
- ⚠️ 与现有 Manager 不一致(但合理,因需求不同)
---
### Decision 4: 属性数据在 Manager 层转换而非 UI 组件层
**Context**:
- 底层 API 返回格式: `{ properties: [{ name, children: [...] }] }`
- BimCollapse 需要格式: `{ items: [{ categoryName, items: [...] }] }`
**Options Considered**:
1. ComponentDetailManager 转换数据后传给 UI
2. 直接传原始数据UI 组件自己转换
3. 创建 Adapter 类专门处理转换
**Decision**: 选择 Option 1 - Manager 层转换
**Rationale**:
- Manager 职责包括数据适配
- UI 组件保持纯粹(只负责展示)
- 转换逻辑集中,易于维护
- 无需额外的 Adapter 类(简单转换)
**Implementation**:
```typescript
private renderProperties(data: any): void {
const categories = data.properties.map((cat: any) => ({
categoryName: cat.name,
items: cat.children.map((child: any) => ({
key: child.name,
value: child.value
}))
}));
const collapse = new BimCollapse({ items: categories, ... });
}
```
**Consequences**:
- ✅ UI 组件可复用性更强
- ✅ 数据转换逻辑集中
- ⚠️ Manager 职责略有增加(但合理)
---
### Decision 5: "显示全部"功能暂时只打印日志
**Context**:
- 右键菜单需要"显示全部"选项
- 底层 API 尚未明确(可能是 showAllComponents、resetVisibility 等)
**Options Considered**:
1. 实现完整功能(调用底层 API
2. 暂时只打印日志,等 API 明确后实现
3. 跳过此功能
**Decision**: 选择 Option 2 - 暂时只打印日志
**Rationale**:
- 不阻塞主功能(构件详情)
- 底层 API 不明确,避免错误实现
- 菜单结构已就位,后续补充实现即可
- 符合 MVP 原则
**Implementation**:
```typescript
public showAllComponents(): void {
console.log('[EngineManager] 显示全部');
// TODO: 调用底层 API 显示所有构件
}
```
**Consequences**:
- ✅ 不阻塞主功能开发
- ✅ 菜单结构完整
- ⚠️ 用户点击后无实际效果(需后续补充)
---
### Decision 6: 文档重构为多章节而非单独创建新文档
**Context**:
- 现有 TOOLBAR_API_CALLCHAIN.md 只记录 Toolbar 调用链
- 新增右键菜单和构件交互功能
- 未来可能还有更多功能模块
**Options Considered**:
1. 创建独立文档 RIGHTKEY_API_CALLCHAIN.md
2. 重构现有文档为多章节结构
3. 合并到 README 或其他文档
**Decision**: 选择 Option 2 - 重构为多章节 API_CALLCHAIN.md
**Rationale**:
- 统一的调用链文档便于查阅
- 支持未来扩展(第四章、第五章...
- 避免文档碎片化
- 保持现有内容(第一章),降低风险
**Structure**:
```
# BIM Engine SDK - API 调用链文档
## 第一章:工具栏 (Toolbar)
- 首页、框选放大、测量、剖切...
## 第二章:右键菜单 (Context Menu)
- 构件详情、显示全部、信息、首页
## 第三章:构件交互 (Component Interaction)
- 构件选中、取消选中
```
**Consequences**:
- ✅ 文档结构更清晰
- ✅ 易于扩展
- ✅ 避免重复内容Info、Home 在多处复用)
- ⚠️ 文件变大734 → 1232 行)
---
### Decision 7: BimEngine 不自动初始化 ComponentDetailManager
**Context**:
- 现有 ManagerMeasure、SectionPlane 等)都有独立的 init 方法
- ComponentDetailManager 是新增功能
- 需要决定初始化时机
**Options Considered**:
1. 在 BimEngine.init() 中自动初始化
2. 提供 initComponentDetail() 方法,由用户选择是否初始化
3. 延迟初始化(首次使用时)
**Decision**: 选择 Option 1实际实现- 在 BimEngine.init() 中自动初始化
**Rationale**:
- 构件详情是核心功能,大部分项目都需要
- 与现有 Manager 初始化方式保持一致
- 避免用户忘记初始化导致功能不可用
- 初始化成本低(只是实例化)
**Implementation**:
```typescript
// src/bim-engine.ts
private init() {
// ...
this.componentDetail = new ComponentDetailManager();
this.registry.componentDetail = this.componentDetail;
}
```
**Consequences**:
- ✅ 开箱即用
- ✅ 与现有 Manager 一致
- ⚠️ 即使不使用也会初始化(但开销可忽略)
**Note**: 实际实现中选择了自动初始化,与 Plan 中的"提供 initComponentDetail()"不同,但更符合现有架构。
---
## Summary
**Core Decisions**:
1. ✅ 状态存储在 Engine数据源头
2. ✅ 动态菜单生成(简洁高效)
3. ✅ 不继承 BaseDialogManager需求简单
4. ✅ Manager 层转换数据(职责明确)
5. ✅ "显示全部"暂时占位(不阻塞)
6. ✅ 文档多章节结构(统一管理)
7. ✅ 自动初始化(开箱即用)
**Guiding Principles**:
- **KISS**: Keep It Simple, Stupid
- **YAGNI**: You Aren't Gonna Need It
- **Single Responsibility**: 每个组件职责明确
- **Consistency**: 与现有代码保持一致(除非有充分理由)