Files
bim_engine/docs/components/按钮组组件-ButtonGroup.md
yuding 8d027419e4 docs: 文档中文化重命名并添加规范文档
- 将所有英文文档重命名为中文格式
- 新增 AI代码规范模板.md(通用开发规范)
- 新增 国际化实现指南.md(i18n 规范)
- 重复文档移至 .recycle 目录
2026-01-21 10:59:23 +08:00

32 KiB
Raw Blame History

ButtonGroup 组件详细文档

本文档详细描述 ButtonGroup 组件的实现细节,包括 API、UI 结构、逻辑流程等,供 AI 根据文档重现组件。


1. 组件概述

1.1 基本信息

  • 组件名称: BimButtonGroup
  • 文件路径: src/components/button-group/index.ts
  • 类型定义: src/components/button-group/index.type.ts
  • 样式文件: src/components/button-group/index.css
  • 实现接口: IBimComponent
  • 用途: 提供通用的按钮组组件,支持按钮、菜单、分组、嵌套菜单等功能

1.2 在 SDK 中的位置

  • ButtonGroup 组件是独立的 UI 组件
  • 必须通过 ButtonGroupManagerToolbarManager 使用,不允许直接使用
  • ButtonGroupManager 位于 src/managers/button-group-manager.ts
  • ToolbarManager 位于 src/managers/toolbar-manager.ts

2. 组件类 API 文档

2.1 构造函数

constructor(options: ButtonGroupOptions)

参数:

  • options: ButtonGroupOptions - 按钮组配置选项(详见类型定义)

默认配置:

{
    showLabel: true,
    visibility: {},
    direction: 'row',      // 默认横向排列
    position: 'static',    // 默认静态定位
    align: 'vertical',     // 默认图标在上
    expand: 'down'         // 默认向下展开
}

行为:

  • 解析容器元素(支持字符串 ID 或 HTMLElement
  • 合并用户配置和默认配置
  • 记录用户自定义的颜色属性
  • 初始化容器
  • 应用样式

2.2 公共方法

setEngine(engine: BimEngine): void

设置引擎实例

参数:

  • engine: BimEngine - 引擎实例

功能:

  • 保存引擎引用,用于发送事件

init(): Promise<void>

初始化组件功能(实现 IBimComponent 接口)

功能:

  • 调用 render() 渲染按钮组
  • 订阅语言变更:localeManager.subscribe()
  • 订阅主题变更:themeManager.subscribe()

返回: Promise异步方法但当前实现是同步的

setTheme(theme: ThemeConfig): void

设置主题(实现 IBimComponent 接口)

参数:

  • theme: ThemeConfig - 全局主题配置

功能:

  • 将主题颜色映射到按钮组颜色
  • 只应用没有被用户自定义的颜色属性
  • 如果用户已自定义颜色,保持用户自定义

主题颜色映射:

  • theme.panelBackgroundbackgroundColor
  • theme.componentBackgroundbtnBackgroundColor
  • theme.componentHoverbtnHoverColor
  • theme.componentActivebtnActiveColor
  • theme.iconiconColor
  • theme.iconActiveiconActiveColor
  • theme.textSecondarytextColor
  • theme.textPrimarytextActiveColor

setLocales(): void

设置语言(实现 IBimComponent 接口)

功能:

  • 调用 render() 重新渲染,更新所有按钮的标签文本

setColors(colors: ButtonGroupColors): void

直接设置颜色(强制覆盖)

参数:

  • colors: ButtonGroupColors - 颜色配置对象

功能:

  • 更新配置选项
  • 标记这些颜色为自定义,后续的 setTheme() 不会覆盖它们
  • 应用样式

setBackgroundColor(color: string): void

设置背景颜色

参数:

  • color: string - 背景颜色值

功能:

  • 调用 setColors({ backgroundColor: color })

addGroup(groupId: string, beforeGroupId?: string): void

添加按钮组

参数:

  • groupId: string - 组 ID必须唯一
  • beforeGroupId: string (可选) - 在此组之前插入

功能:

  • 创建新的按钮组
  • 如果组已存在,忽略
  • 如果指定了 beforeGroupId,在该组之前插入;否则追加到末尾

addButton(config: ButtonConfig): void

添加按钮

参数:

  • config: ButtonConfig - 按钮配置

功能:

  • 将按钮添加到指定组
  • 如果指定了 parentId,将按钮添加为父按钮的子项(嵌套菜单)
  • 如果未指定 parentId,将按钮添加到组的根级别

按钮类型:

  • type: 'button' - 普通按钮
  • type: 'menu' - 菜单按钮(有子项)

render(): void

渲染按钮组

功能:

  • 清空容器
  • 清空按钮引用映射
  • 遍历所有组,渲染每个组
  • 每个组渲染为一个 .bim-btn-group-section 元素

updateButtonVisibility(id: string, visible: boolean): void

更新按钮可见性

参数:

  • id: string - 按钮 ID
  • visible: boolean - 是否可见

功能:

  • 更新 options.visibility 对象
  • 调用 render() 重新渲染

setShowLabel(show: boolean): void

设置是否显示标签

参数:

  • show: boolean - 是否显示标签

功能:

  • 更新 options.showLabel
  • 调用 updateLabelsVisibility() 更新所有按钮的标签显示状态

destroy(): void

销毁组件(实现 IBimComponent 接口)

功能:

  • 取消语言和主题订阅
  • 关闭下拉菜单(如果打开)
  • 清空容器
  • 清空按钮引用映射

2.3 受保护方法

emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]): void

发送事件

功能:

  • 如果引擎已设置,通过引擎发送事件
  • 如果引擎未设置,输出警告

2.4 私有方法(供理解实现细节)

initContainer(): void

初始化容器

功能:

  • 清空容器内容
  • 添加 bim-btn-group-root
  • 根据 direction 添加 dir-rowdir-column
  • 如果提供了 className,添加自定义类
  • 调用 updatePosition() 更新位置

updatePosition(): void

更新容器位置

功能:

  • 根据 position 选项设置容器位置
  • 支持预设位置(如 'center', 'top-left' 等)或坐标对象 { x, y }
  • 如果 position 是 'static',使用相对定位

位置计算逻辑:

  • static: 使用相对定位,不设置绝对定位属性
  • { x, y }: 直接使用坐标值
  • 预设位置: 使用 margin: 20pxtransform 实现定位

applyStyles(): void

应用样式到容器

功能:

  • 将配置的颜色值设置到 CSS 变量
  • CSS 变量映射:
    • backgroundColor--bim-btn-group-section-bg
    • btnBackgroundColor--bim-btn-bg
    • btnHoverColor--bim-btn-hover-bg
    • btnActiveColor--bim-btn-active-bg
    • iconColor--bim-icon-color
    • iconActiveColor--bim-icon-active-color
    • textColor--bim-btn-text-color
    • textActiveColor--bim-btn-text-active-color

renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement

渲染按钮组

参数:

  • group: ButtonGroup - 按钮组对象
  • index: number - 组索引
  • total: number - 总组数

返回: 组元素

功能:

  • 创建 .bim-btn-group-section 元素
  • 如果不是最后一组,添加 has-divider 类(用于显示分隔线)
  • 遍历组内的按钮,渲染每个可见的按钮

renderButton(button: OptButton): HTMLElement

渲染单个按钮

参数:

  • button: OptButton - 按钮配置

返回: 按钮包装元素

功能:

  • 创建 .opt-btn-wrapper.opt-btn 元素
  • 根据按钮的 align 或全局 align 设置布局方向
  • 如果按钮是激活状态,添加 active
  • 如果按钮是禁用状态,添加 disabled
  • 如果按钮没有标签,添加 no-label
  • 创建图标元素(.opt-btn-icon
  • 创建文字包装器(.opt-btn-text-wrapper),包含标签和箭头(如果有子项)
  • 绑定点击、鼠标进入、鼠标离开事件
  • 保存按钮引用到 btnRefs Map

renderDropdownItem(button: OptButton): HTMLElement

渲染下拉菜单项

参数:

  • button: OptButton - 按钮配置

返回: 菜单项元素

功能:

  • 创建 .opt-btn-dropdown-item 元素
  • 根据按钮的 align 设置布局方向(默认 'horizontal'
  • 创建图标和标签
  • 绑定点击事件

handleClick(button: OptButton): void

处理按钮点击

功能:

  • 如果按钮是禁用状态,忽略
  • 如果按钮没有子项:
    • 如果 keepActive 为 true切换激活状态
    • 关闭下拉菜单
    • 调用 onClick 回调(如果存在)
  • 如果按钮有子项,不处理(由鼠标悬停处理)

handleMouseEnter(button: OptButton, btnEl: HTMLElement): void

处理鼠标进入

功能:

  • 清除悬停超时
  • 如果按钮有子项,显示下拉菜单
  • 如果按钮没有子项,关闭下拉菜单

handleMouseLeave(): void

处理鼠标离开

功能:

  • 设置 200ms 延迟后关闭下拉菜单(防止鼠标移动到菜单时关闭)

showDropdown(button: OptButton, btnEl: HTMLElement): void

显示下拉菜单

功能:

  • 关闭当前打开的下拉菜单
  • 创建 .opt-btn-dropdown 元素
  • 根据主按钮组的方向设置菜单的布局方向:
    • 如果主组是横向(direction: 'row'),菜单纵向排列
    • 如果主组是纵向(direction: 'column'),菜单横向排列
  • 渲染所有可见的子按钮
  • 计算菜单位置(根据 expand 选项):
    • up: 向上展开,与按钮水平居中对齐
    • down: 向下展开,与按钮水平居中对齐
    • right: 向右展开,与按钮垂直居中对齐
    • left: 向左展开,与按钮垂直居中对齐
  • 将菜单添加到 document.body(绝对定位)
  • 绑定鼠标进入和离开事件(保持菜单打开)

closeDropdown(): void

关闭下拉菜单

功能:

  • 移除下拉菜单元素
  • 移除所有按钮箭头元素的 rotated

updateButtonState(buttonId: string): void

更新按钮状态

功能:

  • 根据 activeBtnIds Set 更新按钮的 active

findButton(buttons: OptButton[], id: string): OptButton | undefined

查找按钮(递归)

功能:

  • 在按钮数组中查找指定 ID 的按钮
  • 递归查找子按钮

findButtonById(id: string): OptButton | undefined

根据 ID 查找按钮

功能:

  • 在所有组中查找指定 ID 的按钮

isVisible(id: string): boolean

检查按钮是否可见

功能:

  • 检查 options.visibility 对象
  • 如果未设置或为 true返回 true
  • 如果设置为 false返回 false

getIcon(icon?: string): string

获取图标 HTML

功能:

  • 如果提供了图标,返回图标 HTML
  • 否则返回默认图标

updateLabelsVisibility(): void

更新标签可见性

功能:

  • 遍历所有按钮引用
  • 根据 showLabel 选项和按钮是否有标签,更新 no-label

3. 分化组件说明

3.1 Toolbar

文件路径: src/components/button-group/toolbar/index.ts

继承关系: Toolbar extends BimButtonGroup

特殊功能:

  • 预定义了底部工具栏的配置
  • 自动加载默认按钮(首页、漫游、定位、设置、信息等)
  • 使用固定的配置(底部居中、横向排列、图标在上、向上展开)

实现方式:

  • 重写 init() 方法
  • 先调用 super.init()
  • 动态导入所有默认按钮配置
  • 创建两个组('group-1' 和 'group-2'
  • 添加所有默认按钮
  • 调用 render() 渲染

默认按钮列表:

  • group-1:
    • home - 首页按钮(通过工厂函数创建,注入 engine
    • walk-menu - 漫游菜单
    • walk-person - 人视
    • walk-bird - 鸟瞰
    • location - 定位
  • group-2:
    • setting - 设置
    • info - 信息

与父类的差异:

  • 固定了配置选项(position: 'bottom-center', direction: 'row', align: 'vertical', expand: 'up'
  • 自动加载默认按钮
  • 其他功能完全继承自 BimButtonGroup

4. Manager API 文档

4.1 ButtonGroupManager 类

文件路径: src/managers/button-group-manager.ts

继承关系: ButtonGroupManager extends BimComponent

构造函数

constructor(engine: BimEngine, container: HTMLElement)

参数:

  • engine: BimEngine - 引擎实例
  • container: HTMLElement - 按钮组挂载的目标容器

公共方法

create(id: string, options: Omit<ButtonGroupOptions, 'container'>): BimButtonGroup

创建按钮组

参数:

  • id: string - 按钮组唯一标识
  • options: Omit<ButtonGroupOptions, 'container'> - 配置选项(不需要传 container

返回: BimButtonGroup 实例

功能:

  • 自动使用 Manager 绑定的容器
  • 创建 BimButtonGroup 实例
  • 注入引擎实例
  • 调用 init() 初始化
  • 将按钮组保存到 Map 中
get(id: string): BimButtonGroup | undefined

获取按钮组

参数:

  • id: string - 按钮组 ID

返回: BimButtonGroup 实例或 undefined

updateTheme(theme: ThemeConfig): void

更新所有按钮组的主题

参数:

  • theme: ThemeConfig - 主题配置
destroy(): void

销毁管理器

功能:

  • 销毁所有按钮组
  • 清空 Map

4.2 ToolbarManager 类

文件路径: src/managers/toolbar-manager.ts

继承关系: ToolbarManager extends BimComponent

构造函数

constructor(engine: BimEngine, container: HTMLElement)

参数:

  • engine: BimEngine - 引擎实例
  • container: HTMLElement - 工具栏挂载的目标容器

行为:

  • 自动调用 init() 创建工具栏

公共方法

updateTheme(theme: ThemeConfig): void

更新工具栏主题

refresh(): void

刷新工具栏

功能:

  • 调用工具栏的 render() 方法重新渲染
addGroup(groupId: string, beforeGroupId?: string): void

添加按钮组

功能:

  • 转发到工具栏的 addGroup() 方法
  • 自动调用 render() 重新渲染
addButton(config: ButtonConfig): void

添加按钮

功能:

  • 转发到工具栏的 addButton() 方法
  • 自动调用 render() 重新渲染
setButtonVisibility(id: string, v: boolean): void

设置按钮可见性

功能:

  • 转发到工具栏的 updateButtonVisibility() 方法
setShowLabel(show: boolean): void

设置是否显示标签

功能:

  • 转发到工具栏的 setShowLabel() 方法
setVisible(visible: boolean): void

设置工具栏可见性

功能:

  • 通过设置容器的 visibility CSS 属性控制显示/隐藏
setBackgroundColor(color: string): void

设置背景颜色

功能:

  • 转发到工具栏的 setBackgroundColor() 方法
setColors(colors: ButtonGroupColors): void

设置颜色

功能:

  • 转发到工具栏的 setColors() 方法
destroy(): void

销毁管理器

功能:

  • 销毁工具栏实例

5. UI 详细描述

5.1 DOM 结构

完整 HTML 结构:

<div class="bim-btn-group-root [dir-row|dir-column] [static] [className]">
    <!-- 按钮组 1 -->
    <div class="bim-btn-group-section [has-divider]">
        <!-- 按钮包装器 -->
        <div class="opt-btn-wrapper">
            <div class="opt-btn [align-vertical|align-horizontal] [active] [disabled] [no-label]">
                <div class="opt-btn-icon">[图标 SVG]</div>
                <div class="opt-btn-text-wrapper">
                    <span class="opt-btn-label">标签文本</span>
                    [<span class="opt-btn-arrow [rotated]"></span>] <!-- 如果有子项 -->
                </div>
            </div>
        </div>
        <!-- 更多按钮... -->
    </div>
    
    <!-- 按钮组 2 -->
    <div class="bim-btn-group-section">
        <!-- 更多按钮... -->
    </div>
</div>

<!-- 下拉菜单(动态创建,添加到 document.body -->
<div class="opt-btn-dropdown" style="[位置样式]">
    <div class="opt-btn-dropdown-item [align-horizontal|align-vertical]">
        <div class="opt-btn-icon">[图标 SVG]</div>
        <span class="opt-btn-dropdown-label">标签文本</span>
    </div>
    <!-- 更多菜单项... -->
</div>

5.2 CSS 类名和样式

.bim-btn-group-root (根容器)

  • display: flex - Flex 布局
  • gap: 8px - 组之间的间距
  • z-index: 1000 - 层级
  • position: absolute - 绝对定位(除非是 static
  • pointer-events: auto - 确保可接收事件

.bim-btn-group-root.static (静态定位)

  • position: relative - 相对定位
  • 清除所有绝对定位属性

.bim-btn-group-root.dir-row (横向排列)

  • flex-direction: row - 横向排列
  • align-items: center - 垂直居中

.bim-btn-group-root.dir-column (纵向排列)

  • flex-direction: column - 纵向排列
  • align-items: stretch - 拉伸对齐

.bim-btn-group-section (按钮组)

  • display: flex - Flex 布局
  • gap: 4px - 按钮之间的间距
  • background-color: var(--bim-btn-group-section-bg) - 背景色
  • border-radius: 6px - 圆角
  • padding: 4px - 内边距
  • box-shadow - 阴影效果

.opt-btn-wrapper (按钮包装器)

  • position: relative - 相对定位(为下拉菜单提供定位参考)

.opt-btn (按钮本体)

  • display: flex - Flex 布局
  • cursor: pointer - 指针光标
  • border-radius: 4px - 圆角
  • transition: background-color 0.2s, color 0.2s - 过渡动画
  • padding: 6px - 内边距
  • align-items: center - 垂直居中
  • justify-content: center - 水平居中

.opt-btn:hover (按钮悬停)

  • background-color: var(--bim-btn-hover-bg) - 悬停背景色

.opt-btn.active (按钮激活)

  • background-color: var(--bim-btn-active-bg) - 激活背景色
  • color: var(--bim-btn-text-active-color) - 激活文字颜色

.opt-btn.disabled (按钮禁用)

  • opacity: 0.5 - 半透明
  • cursor: not-allowed - 禁止光标

.opt-btn-icon (图标)

  • width/height: var(--bim-icon-size) - 图标尺寸
  • display: flex - Flex 布局
  • align-items: center - 垂直居中
  • justify-content: center - 水平居中
  • color: var(--bim-icon-color) - 图标颜色
  • flex-shrink: 0 - 不收缩

.opt-btn-text-wrapper (文字包装器)

  • display: flex - Flex 布局
  • align-items: center - 垂直居中
  • justify-content: center - 水平居中
  • pointer-events: none - 不接收鼠标事件

.opt-btn-label (标签)

  • display: inline - 内联显示

.opt-btn.no-label .opt-btn-label (无标签时隐藏)

  • display: none - 隐藏

.opt-btn.align-vertical (垂直布局 - 图标在上)

  • flex-direction: column - 纵向排列
  • text-align: center - 文字居中

.opt-btn.align-horizontal (水平布局 - 图标在左)

  • flex-direction: row - 横向排列

.opt-btn-arrow (箭头)

  • font-size: 10px - 字体大小
  • opacity: 0.6 - 半透明
  • transition: transform 0.2s - 旋转过渡
  • margin-left: 4px - 左边距

.opt-btn-arrow.rotated (箭头旋转)

  • transform: rotate(180deg) - 旋转 180 度

.opt-btn-dropdown (下拉菜单)

  • position: absolute - 绝对定位
  • background-color: var(--bim-toolbar-bg) - 背景色
  • border-radius: 4px - 圆角
  • padding: 4px - 内边距
  • box-shadow - 阴影效果
  • z-index: 1001 - 层级
  • display: flex - Flex 布局
  • flex-direction: column - 纵向排列(默认)
  • border: 1px solid rgba(255, 255, 255, 0.1) - 边框
  • animation: dropdown-fade-in 0.2s - 淡入动画

.opt-btn-dropdown-item (菜单项)

  • display: flex - Flex 布局
  • align-items: center - 垂直居中
  • padding: 8px 12px - 内边距
  • cursor: pointer - 指针光标
  • border-radius: 4px - 圆角
  • color: var(--bim-btn-text-color) - 文字颜色
  • transition: background 0.2s - 背景过渡

.opt-btn-dropdown-item:hover (菜单项悬停)

  • background-color: var(--bim-btn-hover-bg) - 悬停背景色
  • color: #fff - 白色文字

5.3 CSS 变量

定义位置: 容器元素的内联样式

变量列表:

  • --bim-btn-group-section-bg: 按钮组背景颜色
  • --bim-btn-bg: 按钮背景颜色
  • --bim-btn-hover-bg: 按钮悬停背景颜色
  • --bim-btn-active-bg: 按钮激活背景颜色
  • --bim-icon-color: 图标颜色
  • --bim-icon-active-color: 图标激活颜色
  • --bim-btn-text-color: 文字颜色
  • --bim-btn-text-active-color: 文字激活颜色
  • --bim-toolbar-bg: 工具栏背景颜色(用于下拉菜单)

5.4 交互行为

按钮点击

  • 触发条件: 点击按钮元素
  • 行为:
    • 如果按钮是禁用状态,忽略
    • 如果按钮没有子项:
      • 如果 keepActive 为 true切换激活状态
      • 关闭下拉菜单
      • 调用 onClick 回调
    • 如果按钮有子项,不处理(由鼠标悬停处理)

鼠标悬停

  • 触发条件: 鼠标进入按钮
  • 行为:
    • 如果按钮有子项,显示下拉菜单
    • 如果按钮没有子项,关闭下拉菜单

鼠标离开

  • 触发条件: 鼠标离开按钮或下拉菜单
  • 行为:
    • 延迟 200ms 后关闭下拉菜单(防止鼠标移动到菜单时关闭)

下拉菜单显示

  • 触发条件: 鼠标悬停在有子项的按钮上
  • 行为:
    • 创建下拉菜单元素
    • 计算位置(根据 expand 选项)
    • 添加到 document.body
    • 显示淡入动画

下拉菜单关闭

  • 触发条件:
    • 鼠标离开按钮和菜单区域
    • 点击按钮(非菜单按钮)
  • 行为:
    • 移除下拉菜单元素
    • 移除箭头旋转状态

5.5 响应式行为

  • 按钮组位置会根据容器尺寸自动调整(如果使用绝对定位)
  • 下拉菜单位置会根据按钮位置和窗口尺寸自动计算
  • 标签文本支持多语言切换
  • 按钮可见性可以动态控制

6. 逻辑流程详细描述

6.1 初始化流程

  1. 构造函数调用:

    • 解析容器元素
    • 合并配置选项
    • 记录用户自定义的颜色属性
    • 调用 initContainer() 初始化容器
    • 调用 applyStyles() 应用样式
  2. init() 方法执行:

    • 调用 render() 渲染按钮组
    • 订阅语言变更:localeManager.subscribe()
    • 订阅主题变更:themeManager.subscribe()

6.2 渲染流程

  1. render() 方法:

    • 清空容器
    • 清空按钮引用映射
    • 遍历所有组,调用 renderGroup() 渲染每个组
  2. renderGroup() 方法:

    • 创建组元素
    • 如果不是最后一组,添加分隔线类
    • 遍历组内按钮,调用 renderButton() 渲染每个可见按钮
  3. renderButton() 方法:

    • 创建按钮包装器和按钮元素
    • 设置布局方向(根据按钮或全局配置)
    • 设置激活/禁用状态
    • 创建图标元素
    • 创建文字包装器(包含标签和箭头)
    • 绑定事件监听器
    • 保存按钮引用

6.3 事件处理流程

按钮点击流程

  1. 用户点击按钮 → click 事件
  2. handleClick() 被调用
  3. 检查按钮是否禁用
  4. 如果按钮没有子项:
    • 如果 keepActive 为 true切换激活状态
    • 更新按钮的 active
    • 关闭下拉菜单
    • 调用 onClick 回调

下拉菜单显示流程

  1. 鼠标进入按钮 → mouseenter 事件
  2. handleMouseEnter() 被调用
  3. 清除悬停超时
  4. 如果按钮有子项,调用 showDropdown()
  5. showDropdown() 执行:
    • 关闭当前打开的下拉菜单
    • 创建下拉菜单元素
    • 设置菜单布局方向(根据主组方向)
    • 渲染所有可见的子按钮
    • 计算菜单位置(根据 expand 选项)
    • 添加到 document.body
    • 绑定鼠标事件(保持菜单打开)

下拉菜单关闭流程

  1. 鼠标离开按钮或菜单 → mouseleave 事件
  2. handleMouseLeave() 被调用
  3. 设置 200ms 延迟
  4. 延迟后调用 closeDropdown()
  5. closeDropdown() 执行:
    • 移除下拉菜单元素
    • 移除箭头旋转状态

6.4 状态管理

内部状态

  • groups: 按钮组数组
  • activeBtnIds: 激活按钮 ID 的 Set
  • btnRefs: 按钮元素引用的 Map
  • dropdownElement: 当前打开的下拉菜单元素
  • hoverTimeout: 悬停超时的 ID
  • customColors: 用户自定义颜色属性的 Set

订阅管理

  • unsubscribeLocale: 语言订阅取消函数
  • unsubscribeTheme: 主题订阅取消函数

6.5 与其他组件的交互

  • 与 ButtonGroupManager/ToolbarManager: 通过 Manager 创建和管理
  • 与 ThemeManager: 订阅主题变更
  • 与 LocaleManager: 订阅语言变更
  • 与 BimEngine: 通过引擎发送事件(如果设置了引擎)

7. 国际化支持

7.1 使用的翻译键

  • button.label: 按钮标签(每个按钮的 label 属性)

7.2 语言变更处理

  • 组件订阅 localeManager.subscribe()
  • 语言变更时,setLocales() 方法被调用
  • 调用 render() 重新渲染,更新所有按钮的标签文本

7.3 实现细节

public setLocales(): void {
    this.render(); // 重新渲染,标签会通过 t() 函数获取最新翻译
}

// 在 renderButton() 中
label.textContent = t(button.label);

8. 主题支持

8.1 使用的主题变量

  • theme.panelBackgroundbackgroundColor
  • theme.componentBackgroundbtnBackgroundColor
  • theme.componentHoverbtnHoverColor
  • theme.componentActivebtnActiveColor
  • theme.iconiconColor
  • theme.iconActiveiconActiveColor
  • theme.textSecondarytextColor
  • theme.textPrimarytextActiveColor

8.2 主题变更处理

  • 组件订阅 themeManager.subscribe()
  • 主题变更时,setTheme() 方法被调用
  • 只应用没有被用户自定义的颜色属性
  • 如果用户已自定义颜色,保持用户自定义

8.3 自定义颜色保护机制

  • 构造函数中记录用户自定义的颜色属性
  • setTheme() 时只更新未自定义的颜色
  • setColors() 时标记颜色为自定义

9. 使用示例

9.1 基本使用(通过 ButtonGroupManager

import { BimEngine } from 'bim-engine-sdk';

const engine = new BimEngine('container');

// 创建按钮组
const buttonGroup = engine.buttonGroup.create('my-group', {
    position: 'top-right',
    direction: 'row',
    showLabel: true
});

// 添加组
buttonGroup.addGroup('group-1');

// 添加按钮
buttonGroup.addButton({
    id: 'btn-1',
    groupId: 'group-1',
    type: 'button',
    label: 'toolbar.home',
    icon: '<svg>...</svg>',
    onClick: (button) => {
        console.log('按钮被点击:', button.id);
    }
});

9.2 使用工具栏(通过 ToolbarManager

// 工具栏已自动创建,直接使用
engine.toolbar.addButton({
    id: 'custom-btn',
    groupId: 'group-1',
    type: 'button',
    label: 'toolbar.custom',
    icon: '<svg>...</svg>',
    onClick: (button) => {
        console.log('自定义按钮被点击');
    }
});

// 控制按钮可见性
engine.toolbar.setButtonVisibility('location', false);

// 控制标签显示
engine.toolbar.setShowLabel(false);

9.3 使用嵌套菜单

// 添加菜单按钮
buttonGroup.addButton({
    id: 'menu-btn',
    groupId: 'group-1',
    type: 'menu',
    label: 'toolbar.menu',
    icon: '<svg>...</svg>',
    children: [
        {
            id: 'sub-1',
            type: 'button',
            label: 'toolbar.sub1',
            onClick: () => console.log('子项 1')
        },
        {
            id: 'sub-2',
            type: 'button',
            label: 'toolbar.sub2',
            onClick: () => console.log('子项 2')
        }
    ]
});

10. 实现细节(供 AI 重现)

10.1 关键算法

位置计算算法

function calculatePosition(position, containerWidth, containerHeight) {
    const margin = 20;
    switch (position) {
        case 'top-left': return { top: margin, left: margin };
        case 'top-center': return { top: margin, left: '50%', transform: 'translateX(-50%)' };
        case 'top-right': return { top: margin, right: margin };
        // ... 其他位置
    }
}

下拉菜单位置计算算法

function calculateDropdownPosition(expand, buttonRect, dropdownRect) {
    switch (expand) {
        case 'up':
            return {
                bottom: window.innerHeight - buttonRect.top + 8,
                left: buttonRect.left + (buttonRect.width - dropdownRect.width) / 2
            };
        case 'down':
            return {
                top: buttonRect.bottom + 8,
                left: buttonRect.left + (buttonRect.width - dropdownRect.width) / 2
            };
        // ... 其他方向
    }
}

激活状态切换算法

function toggleActiveState(buttonId, activeBtnIds) {
    if (activeBtnIds.has(buttonId)) {
        activeBtnIds.delete(buttonId);
    } else {
        activeBtnIds.add(buttonId);
    }
}

10.2 性能优化点

  1. 按钮引用缓存:

    • 使用 btnRefs Map 缓存按钮元素引用
    • 避免重复查询 DOM
  2. 延迟关闭下拉菜单:

    • 使用 200ms 延迟,防止鼠标移动到菜单时关闭
    • 使用 setTimeoutclearTimeout 管理
  3. 条件渲染:

    • 只渲染可见的按钮(根据 visibility 选项)
    • 减少 DOM 操作
  4. CSS 变量:

    • 使用 CSS 变量应用主题和颜色
    • 主题变更时无需重新渲染 DOM

10.3 注意事项和边界情况

  1. 按钮 ID 唯一性:

    • 按钮 ID 必须唯一
    • 如果重复,可能导致状态管理错误
  2. 组 ID 唯一性:

    • 组 ID 必须唯一
    • addGroup() 会检查重复
  3. 嵌套菜单深度:

    • 当前实现只支持一级嵌套(按钮 → 子项)
    • 不支持多级嵌套
  4. 下拉菜单定位:

    • 下拉菜单添加到 document.body,使用绝对定位
    • 需要根据按钮位置和窗口尺寸计算位置
    • 可能需要处理边界情况(菜单超出窗口)
  5. 事件清理:

    • 组件销毁时必须清理所有事件监听
    • 必须取消主题和语言订阅
    • 必须关闭下拉菜单
  6. 翻译键处理:

    • 标签可能是翻译键或普通文本
    • 只有翻译键才需要通过 t() 函数处理
  7. 自定义颜色保护:

    • 用户自定义的颜色不会被主题覆盖
    • 需要正确记录和管理自定义颜色

11. 类型定义

11.1 ButtonGroupOptions

interface ButtonGroupOptions extends ButtonGroupColors {
    container: HTMLElement | string;    // 容器(必需)
    position?: GroupPosition;            // 位置(可选)
    direction?: GroupDirection;          // 排列方向(可选,默认 'row'
    align?: ButtonAlign;                 // 按钮内部布局(可选,默认 'vertical'
    expand?: ExpandDirection;            // 菜单展开方向(可选,默认 'down'
    showLabel?: boolean;                 // 是否显示标签(可选,默认 true
    visibility?: Record<string, boolean>; // 按钮可见性(可选)
    className?: string;                  // 自定义类名(可选)
}

11.2 ButtonConfig

interface ButtonConfig {
    id: string;                          // 按钮 ID必需必须唯一
    type: ButtonType;                    // 按钮类型(必需,'button' | 'menu'
    label: string;                       // 标签(必需,支持翻译键)
    icon?: string;                       // 图标 SVG可选
    keepActive?: boolean;                // 是否保持激活状态(可选)
    disabled?: boolean;                  // 是否禁用(可选)
    onClick?: (button: OptButton) => void; // 点击回调(可选)
    children?: ButtonConfig[];           // 子项(可选,用于菜单)
    groupId?: string;                    // 所属组 ID可选
    parentId?: string;                   // 父按钮 ID可选用于嵌套
    align?: ButtonAlign;                 // 按钮内部布局(可选)
    iconSize?: number;                   // 图标大小(可选,默认 32
    minWidth?: number;                   // 最小宽度(可选,默认 50
}

11.3 其他类型

type ButtonType = 'button' | 'menu';
type GroupPosition = 'center' | 'top-left' | 'top-center' | 'top-right' 
    | 'left-center' | 'right-center' 
    | 'bottom-left' | 'bottom-center' | 'bottom-right'
    | { x: number; y: number } | 'static';
type GroupDirection = 'row' | 'column';
type ButtonAlign = 'vertical' | 'horizontal';
type ExpandDirection = 'up' | 'down' | 'left' | 'right';

12. 文件清单

12.1 相关文件

  • src/components/button-group/index.ts - 主组件类
  • src/components/button-group/index.type.ts - 类型定义
  • src/components/button-group/index.css - 样式文件
  • src/components/button-group/toolbar/index.ts - 工具栏(分化组件)
  • src/managers/button-group-manager.ts - 通用按钮组管理器
  • src/managers/toolbar-manager.ts - 工具栏管理器

12.2 依赖文件

  • src/types/component.ts - IBimComponent 接口
  • src/themes/types.ts - ThemeConfig 类型
  • src/services/theme.ts - ThemeManager
  • src/services/locale.ts - LocaleManager
  • src/types/events.ts - EngineEvents 类型

13. 更新记录

日期 修改内容 修改人
2024-XX-XX 初始创建 AI Assistant

重要提醒: 本文档必须与组件代码保持同步。任何组件修改都必须更新本文档!