Files
bim_engine/.sisyphus/notepads/component-detail-rightclick/decisions.md
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

8.0 KiB
Raw Blame History

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:

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:

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:

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:

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:

// 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: 与现有代码保持一致(除非有充分理由)