feat: Refactor engine structure and add UI customization support
- Refactor to delegate logic to and - Add for manager classes - Implement dynamic styling for Toolbar (color config, CSS vars) - Implement dynamic styling for Dialog (options, CSS vars) - Add example - Add documentation for Toolbar and Dialog - Update demo to showcase new styling features
This commit is contained in:
31
demo.html
31
demo.html
@@ -1,31 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>BIM Engine SDK Demo</title>
|
|
||||||
<script src="./dist/bim-engine-sdk.umd.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="app" style="width: 1000px; height: 500px; border: 1px dashed #ccc;"></div>
|
|
||||||
<script>
|
|
||||||
// 等待 SDK 加载
|
|
||||||
window.onload = () => {
|
|
||||||
if (window.LyzBimEngineSDK) {
|
|
||||||
const Engine = window.LyzBimEngineSDK.BimEngine;
|
|
||||||
try {
|
|
||||||
const instance = new Engine(document.getElementById('app'));
|
|
||||||
console.log('Engine initialized:', instance);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Init failed:', err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('SDK not found');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
171
demo/index.html
Normal file
171
demo/index.html
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>BIM Engine SDK Demo</title>
|
||||||
|
<script src="../dist/bim-engine-sdk.umd.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app" style="width: 100%; height: 500px; border: 1px dashed #ccc;"></div>
|
||||||
|
|
||||||
|
<div style="margin-top: 20px; display: flex; gap: 10px; flex-wrap: wrap;">
|
||||||
|
<button onclick="toggleToolbar()">切换工具栏显隐</button>
|
||||||
|
<button onclick="toggleLabel()">切换文字标签</button>
|
||||||
|
<button onclick="addCustomGroup()">添加自定义组(最前)</button>
|
||||||
|
<button onclick="addCustomButton()">添加按钮到自定义组</button>
|
||||||
|
<button onclick="toggleLocationBtn()">切换"定位"按钮显隐</button>
|
||||||
|
<button onclick="changeToolbarColor()">修改工具栏颜色 (蓝色)</button>
|
||||||
|
<button onclick="changeToolbarStyleFull()">修改工具栏样式 (浅色主题)</button>
|
||||||
|
<button onclick="resetToolbarStyle()">重置工具栏样式</button>
|
||||||
|
<button onclick="openRedDialog()">打开红色弹窗 (样式定制)</button>
|
||||||
|
<button onclick="openCustomHeaderDialog()">打开自定义标题弹窗</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let engine = null;
|
||||||
|
let isToolbarVisible = true;
|
||||||
|
let isLabelVisible = true;
|
||||||
|
let isLocationVisible = true;
|
||||||
|
let customGroupAdded = false;
|
||||||
|
|
||||||
|
// 等 SDK <20><>载
|
||||||
|
window.onload = () => {
|
||||||
|
if (window.LyzBimEngineSDK) {
|
||||||
|
const Engine = window.LyzBimEngineSDK.BimEngine;
|
||||||
|
|
||||||
|
try {
|
||||||
|
engine = new Engine(document.getElementById('app'));
|
||||||
|
console.log('Engine initialized:', engine);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Init failed:', err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('SDK not found');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function toggleToolbar() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
isToolbarVisible = !isToolbarVisible;
|
||||||
|
engine.toolbar.setVisible(isToolbarVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLabel() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
isLabelVisible = !isLabelVisible;
|
||||||
|
engine.toolbar.setShowLabel(isLabelVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCustomGroup() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
if (customGroupAdded) {
|
||||||
|
alert('已添加过');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 添加到 group-1 之前
|
||||||
|
engine.toolbar.addGroup('custom-group', 'group-1');
|
||||||
|
customGroupAdded = true;
|
||||||
|
console.log('Added custom group');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCustomButton() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
if (!customGroupAdded) {
|
||||||
|
alert('请先添加自定义组');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const btnId = 'custom-btn-' + Date.now();
|
||||||
|
|
||||||
|
engine.toolbar.addButton({
|
||||||
|
id: btnId,
|
||||||
|
groupId: 'custom-group',
|
||||||
|
type: 'button',
|
||||||
|
label: '新按钮',
|
||||||
|
// 一个简单的笑脸 SVG
|
||||||
|
icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><path d="M8 14s1.5 2 4 2 4-2 4-2"></path><line x1="9" y1="9" x2="9.01" y2="9"></line><line x1="15" y1="9" x2="15.01" y2="9"></line></svg>',
|
||||||
|
onClick: (btn) => {
|
||||||
|
alert('你点击了动态添加的按钮: ' + btn.label);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLocationBtn() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
isLocationVisible = !isLocationVisible;
|
||||||
|
// location 按钮的 ID 是 'location'
|
||||||
|
engine.toolbar.setButtonVisibility('location', isLocationVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeToolbarColor() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
// 仅修改背景色
|
||||||
|
engine.toolbar.setBackgroundColor('rgba(0, 100, 200, 0.9)');
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeToolbarStyleFull() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
// 完整的浅色主题配置
|
||||||
|
engine.toolbar.setColors({
|
||||||
|
backgroundColor: '#f0f0f0',
|
||||||
|
btnBackgroundColor: '#ffffff',
|
||||||
|
btnHoverColor: '#e0e0e0',
|
||||||
|
btnActiveColor: '#d0d0d0',
|
||||||
|
iconColor: '#333333',
|
||||||
|
iconActiveColor: '#0078d4',
|
||||||
|
textColor: '#666666',
|
||||||
|
textActiveColor: '#000000'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetToolbarStyle() {
|
||||||
|
if (!engine || !engine.toolbar) return;
|
||||||
|
// 重置为默认深色主题
|
||||||
|
engine.toolbar.setColors({
|
||||||
|
backgroundColor: 'rgba(17, 17, 17, 0.88)',
|
||||||
|
btnBackgroundColor: 'transparent',
|
||||||
|
btnHoverColor: '#444',
|
||||||
|
btnActiveColor: 'rgba(255, 255, 255, 0.15)',
|
||||||
|
iconColor: '#ccc',
|
||||||
|
iconActiveColor: '#fff',
|
||||||
|
textColor: '#ccc',
|
||||||
|
textActiveColor: '#fff'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRedDialog() {
|
||||||
|
if (!engine || !engine.dialog) return;
|
||||||
|
engine.dialog.create({
|
||||||
|
title: '警报',
|
||||||
|
content: '<div style="color: #ffcccc;">这是一个高度定制的警告弹窗。<br>注意查看标题栏和边框颜色。</div>',
|
||||||
|
width: 300,
|
||||||
|
height: 150,
|
||||||
|
backgroundColor: 'rgba(100, 0, 0, 0.95)',
|
||||||
|
headerBackgroundColor: '#cc0000',
|
||||||
|
titleColor: '#ffffff',
|
||||||
|
borderColor: '#ff6666',
|
||||||
|
position: { x: 50, y: 50 } // 自定义坐标
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCustomHeaderDialog() {
|
||||||
|
if (!engine || !engine.dialog) return;
|
||||||
|
engine.dialog.create({
|
||||||
|
title: '自定义样式弹窗',
|
||||||
|
content: '观察标题栏背景和文字颜色。',
|
||||||
|
width: 300,
|
||||||
|
headerBackgroundColor: '#0078d4', // 蓝色标题栏
|
||||||
|
titleColor: '#ffffff',
|
||||||
|
backgroundColor: '#ffffff', // 白色内容背景
|
||||||
|
textColor: '#333333', // 深色文字
|
||||||
|
borderColor: '#0078d4', // 蓝色边框
|
||||||
|
position: 'center'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
649
dist/bim-engine-sdk.es.js
vendored
649
dist/bim-engine-sdk.es.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bim-engine-sdk.es.js.map
vendored
2
dist/bim-engine-sdk.es.js.map
vendored
File diff suppressed because one or more lines are too long
9
dist/bim-engine-sdk.umd.js
vendored
9
dist/bim-engine-sdk.umd.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bim-engine-sdk.umd.js.map
vendored
2
dist/bim-engine-sdk.umd.js.map
vendored
File diff suppressed because one or more lines are too long
363
dist/index.d.ts
vendored
363
dist/index.d.ts
vendored
@@ -1,9 +1,81 @@
|
|||||||
export declare class BimEngine {
|
/**
|
||||||
|
* 通用弹窗组件类
|
||||||
|
* 支持拖拽、缩放、自定义内容和位置。
|
||||||
|
*/
|
||||||
|
declare class BimDialog {
|
||||||
|
private element;
|
||||||
|
private options;
|
||||||
private container;
|
private container;
|
||||||
private optBtnGroups;
|
private header;
|
||||||
constructor(container: HTMLElement | string);
|
private contentArea;
|
||||||
|
private _isDestroyed;
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param options 弹窗配置选项
|
||||||
|
*/
|
||||||
|
constructor(options: DialogOptions);
|
||||||
|
/**
|
||||||
|
* 创建弹窗的 DOM 结构
|
||||||
|
*/
|
||||||
|
private createDom;
|
||||||
|
/**
|
||||||
|
* 设置元素尺寸
|
||||||
|
*/
|
||||||
|
private setSize;
|
||||||
|
/**
|
||||||
|
* 初始化组件功能
|
||||||
|
*/
|
||||||
private init;
|
private init;
|
||||||
private initOptBtnGroups;
|
/**
|
||||||
|
* 初始化弹窗位置
|
||||||
|
*/
|
||||||
|
private initPosition;
|
||||||
|
/**
|
||||||
|
* 初始化拖拽功能
|
||||||
|
*/
|
||||||
|
private initDrag;
|
||||||
|
/**
|
||||||
|
* 初始化缩放功能
|
||||||
|
*/
|
||||||
|
private initResize;
|
||||||
|
/**
|
||||||
|
* 动态设置内容
|
||||||
|
* @param content 内容元素或 HTML 字符串
|
||||||
|
*/
|
||||||
|
setContent(content: HTMLElement | string): void;
|
||||||
|
/**
|
||||||
|
* 关闭弹窗并销毁
|
||||||
|
*/
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BimEngine 主类
|
||||||
|
* 负责初始化整个应用界面,协调各个子模块(如工具栏、弹窗等)。
|
||||||
|
*/
|
||||||
|
export declare class BimEngine {
|
||||||
|
/** 主容器元素 */
|
||||||
|
private container;
|
||||||
|
/** 内部包装器元素,用于承载所有 UI 组件 */
|
||||||
|
private wrapper;
|
||||||
|
/** 工具栏管理器实例 */
|
||||||
|
toolbar: ToolbarManager | null;
|
||||||
|
/** 弹窗管理器实例 */
|
||||||
|
dialog: DialogManager | null;
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 容器元素或容器 ID
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement | string);
|
||||||
|
/**
|
||||||
|
* 初始化方法
|
||||||
|
* 创建 DOM 结构并初始化各子模块
|
||||||
|
*/
|
||||||
|
private init;
|
||||||
|
/**
|
||||||
|
* 销毁实例
|
||||||
|
* 清理所有资源和 DOM 元素
|
||||||
|
*/
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,15 +83,25 @@ export declare class BimEngine {
|
|||||||
* 按钮配置接口(用于外部定义按钮)
|
* 按钮配置接口(用于外部定义按钮)
|
||||||
*/
|
*/
|
||||||
declare interface ButtonConfig {
|
declare interface ButtonConfig {
|
||||||
|
/** 唯一标识 */
|
||||||
id: string;
|
id: string;
|
||||||
|
/** 按钮类型:普通按钮或菜单按钮 */
|
||||||
type: ButtonType;
|
type: ButtonType;
|
||||||
|
/** 按钮显示文字 */
|
||||||
label: string;
|
label: string;
|
||||||
|
/** SVG 图标(内联 SVG 字符串) */
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
/** 是否保持激活状态(默认 false) */
|
||||||
keepActive?: boolean;
|
keepActive?: boolean;
|
||||||
|
/** 是否禁用 */
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
/** 点击回调函数 */
|
||||||
onClick?: (button: OptButton) => void;
|
onClick?: (button: OptButton) => void;
|
||||||
|
/** 子按钮配置(可选,用于菜单按钮) */
|
||||||
children?: ButtonConfig[];
|
children?: ButtonConfig[];
|
||||||
|
/** 所属组ID */
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
|
/** 父按钮ID(如果是子按钮,则必填) */
|
||||||
parentId?: string;
|
parentId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +109,9 @@ declare interface ButtonConfig {
|
|||||||
* 按钮组接口
|
* 按钮组接口
|
||||||
*/
|
*/
|
||||||
export declare interface ButtonGroup {
|
export declare interface ButtonGroup {
|
||||||
|
/** 组 ID */
|
||||||
id: string;
|
id: string;
|
||||||
|
/** 组内按钮列表 */
|
||||||
buttons: OptButton[];
|
buttons: OptButton[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,57 +121,236 @@ declare type ButtonType = 'button' | 'menu';
|
|||||||
* 点击事件载荷
|
* 点击事件载荷
|
||||||
*/
|
*/
|
||||||
export declare interface ClickPayload {
|
export declare interface ClickPayload {
|
||||||
|
/** 被点击的按钮对象 */
|
||||||
button: OptButton;
|
button: OptButton;
|
||||||
|
/** 触发的动作类型 */
|
||||||
action: 'activate' | 'deactivate' | 'trigger';
|
action: 'activate' | 'deactivate' | 'trigger';
|
||||||
|
/** 当前激活状态 */
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class OptBtnGroups {
|
/**
|
||||||
|
* 弹窗颜色配置
|
||||||
|
*/
|
||||||
|
declare interface DialogColors {
|
||||||
|
/** 窗体背景颜色,默认 rgba(17, 17, 17, 0.95) */
|
||||||
|
backgroundColor?: string;
|
||||||
|
/** 标题栏背景颜色,默认 #2a2a2a */
|
||||||
|
headerBackgroundColor?: string;
|
||||||
|
/** 标题文字颜色,默认 #fff */
|
||||||
|
titleColor?: string;
|
||||||
|
/** 内容文字颜色,默认 #ccc */
|
||||||
|
textColor?: string;
|
||||||
|
/** 边框颜色,默认 #444 */
|
||||||
|
borderColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗管理器
|
||||||
|
* 负责创建和管理应用中的各类弹窗。
|
||||||
|
*/
|
||||||
|
declare class DialogManager {
|
||||||
|
/** 弹窗挂载的父容器 */
|
||||||
private container;
|
private container;
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 弹窗挂载的目标容器
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement);
|
||||||
|
/**
|
||||||
|
* 创建一个通用弹窗
|
||||||
|
* @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)
|
||||||
|
* @returns BimDialog 实例
|
||||||
|
*/
|
||||||
|
create(options: Omit<DialogOptions, 'container'>): BimDialog;
|
||||||
|
/**
|
||||||
|
* 显示二次封装的模型信息弹窗
|
||||||
|
* 演示如何调用特定的业务弹窗组件
|
||||||
|
*/
|
||||||
|
showInfoDialog(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗配置选项接口
|
||||||
|
*/
|
||||||
|
declare interface DialogOptions extends DialogColors {
|
||||||
|
/** 弹窗挂载的父容器 */
|
||||||
|
container: HTMLElement;
|
||||||
|
/** 弹窗标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 弹窗内容,支持 HTML 字符串或 HTMLElement */
|
||||||
|
content?: HTMLElement | string;
|
||||||
|
/** 弹窗宽度,数字(像素)或字符串(如 '50%') */
|
||||||
|
width?: number | string;
|
||||||
|
/** 弹窗高度 */
|
||||||
|
height?: number | string;
|
||||||
|
/** 弹窗位置 */
|
||||||
|
position?: DialogPosition;
|
||||||
|
/** 是否可拖拽 */
|
||||||
|
draggable?: boolean;
|
||||||
|
/** 是否可调整大小 */
|
||||||
|
resizable?: boolean;
|
||||||
|
/** 最小宽度限制 */
|
||||||
|
minWidth?: number;
|
||||||
|
/** 最小高度限制 */
|
||||||
|
minHeight?: number;
|
||||||
|
/** 关闭时的回调函数 */
|
||||||
|
onClose?: () => void;
|
||||||
|
/** 弹窗唯一标识 ID (可选) */
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗位置类型定义
|
||||||
|
* 可以是预设的字符串位置(如 'center', 'top-left' 等),
|
||||||
|
* 也可以是具体的坐标对象 { x, y }
|
||||||
|
*/
|
||||||
|
declare type DialogPosition = 'center' | 'top-left' | 'top-center' | 'top-right' | 'left-center' | 'right-center' | 'bottom-left' | 'bottom-center' | 'bottom-right' | {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 底部操作按钮组组件
|
||||||
|
* 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。
|
||||||
|
*/
|
||||||
|
export declare class OptBtnGroups {
|
||||||
|
/** 挂载容器 */
|
||||||
|
private container;
|
||||||
|
/** 组件配置选项 */
|
||||||
private options;
|
private options;
|
||||||
|
/** 按钮组列表,按顺序存储 */
|
||||||
private groups;
|
private groups;
|
||||||
|
/** 当前处于激活状态的按钮 ID 集合 */
|
||||||
private activeBtnIds;
|
private activeBtnIds;
|
||||||
|
/** 按钮 DOM 元素的引用映射,方便快速查找 */
|
||||||
private btnRefs;
|
private btnRefs;
|
||||||
|
/** 当<><E5BD93>显示的下拉菜单元素 */
|
||||||
private dropdownElement;
|
private dropdownElement;
|
||||||
|
/** 鼠标悬停计时器,用于处理菜单显示的防抖 */
|
||||||
private hoverTimeout;
|
private hoverTimeout;
|
||||||
|
/** 默认图标 SVG */
|
||||||
private readonly DEFAULT_ICON;
|
private readonly DEFAULT_ICON;
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param options 配置选项
|
||||||
|
*/
|
||||||
constructor(options: OptBtnGroupsOptions);
|
constructor(options: OptBtnGroupsOptions);
|
||||||
|
/**
|
||||||
|
* 初始化容器
|
||||||
|
*/
|
||||||
private initContainer;
|
private initContainer;
|
||||||
|
/**
|
||||||
|
* 应用样式配置到 CSS 变量
|
||||||
|
*/
|
||||||
|
private applyStyles;
|
||||||
|
/**
|
||||||
|
* 更新颜色配置
|
||||||
|
* @param colors 颜色配置对象
|
||||||
|
*/
|
||||||
|
setColors(colors: ToolbarColors): void;
|
||||||
/**
|
/**
|
||||||
* 添加按钮组
|
* 添加按钮组
|
||||||
* @param groupId 组ID
|
* @param groupId 组ID
|
||||||
* @param beforeGroupId 在哪个组之前插入(可选),不传则插入到最后
|
* @param beforeGroupId 在哪个组之前插入(可选<EFBFBD><EFBFBD><EFBFBD>,不传则插入到最后
|
||||||
*/
|
*/
|
||||||
addGroup(groupId: string, beforeGroupId?: string): void;
|
addGroup(groupId: string, beforeGroupId?: string): void;
|
||||||
/**
|
/**
|
||||||
* 添加按钮
|
* 添加按钮到指定组
|
||||||
* @param config 按钮配置(必须包含 groupId,可选包含 parentId)
|
* @param config 按钮配置(必须包含 groupId,可选包含 parentId)
|
||||||
*/
|
*/
|
||||||
addButton(config: ButtonConfig): void;
|
addButton(config: ButtonConfig): void;
|
||||||
|
/**
|
||||||
|
* 递归查找按钮
|
||||||
|
*/
|
||||||
private findButton;
|
private findButton;
|
||||||
|
/**
|
||||||
|
* 初始化组件,加载默认按钮配置
|
||||||
|
*/
|
||||||
init(): Promise<void>;
|
init(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* 渲染整个工具栏
|
||||||
|
*/
|
||||||
render(): void;
|
render(): void;
|
||||||
|
/**
|
||||||
|
* 渲染单个按钮组
|
||||||
|
*/
|
||||||
private renderGroup;
|
private renderGroup;
|
||||||
|
/**
|
||||||
|
* 渲染单个按钮
|
||||||
|
*/
|
||||||
private renderButton;
|
private renderButton;
|
||||||
|
/**
|
||||||
|
* 处理按钮点击事件
|
||||||
|
*/
|
||||||
private handleClick;
|
private handleClick;
|
||||||
|
/**
|
||||||
|
* 处理子菜单项点击事件
|
||||||
|
*/
|
||||||
private handleSubClick;
|
private handleSubClick;
|
||||||
|
/**
|
||||||
|
* 处理鼠标移入事件(显示菜单)
|
||||||
|
*/
|
||||||
private handleMouseEnter;
|
private handleMouseEnter;
|
||||||
|
/**
|
||||||
|
* 处理鼠标移出事件(隐藏菜单)
|
||||||
|
*/
|
||||||
private handleMouseLeave;
|
private handleMouseLeave;
|
||||||
|
/**
|
||||||
|
* 显示下拉菜单
|
||||||
|
*/
|
||||||
private showDropdown;
|
private showDropdown;
|
||||||
|
/**
|
||||||
|
* 渲染下拉菜单项
|
||||||
|
*/
|
||||||
private renderDropdownItem;
|
private renderDropdownItem;
|
||||||
|
/**
|
||||||
|
* 关闭所有下拉菜单
|
||||||
|
*/
|
||||||
private closeDropdown;
|
private closeDropdown;
|
||||||
|
/**
|
||||||
|
* 更新按钮的激活状态样式
|
||||||
|
*/
|
||||||
private updateButtonState;
|
private updateButtonState;
|
||||||
|
/**
|
||||||
|
* 获取图标 SVG 字符串
|
||||||
|
*/
|
||||||
private getIcon;
|
private getIcon;
|
||||||
|
/**
|
||||||
|
* 更新按钮可见性
|
||||||
|
* @param buttonId 按钮ID
|
||||||
|
* @param visible 是否可见
|
||||||
|
*/
|
||||||
|
updateButtonVisibility(buttonId: string, visible: boolean): void;
|
||||||
|
/**
|
||||||
|
* 设置是否显示标签
|
||||||
|
* @param show 是否显示
|
||||||
|
*/
|
||||||
|
setShowLabel(show: boolean): void;
|
||||||
|
/**
|
||||||
|
* 设置背景颜色 (兼容旧接口)
|
||||||
|
* @param color CSS 颜色值
|
||||||
|
*/
|
||||||
|
setBackgroundColor(color: string): void;
|
||||||
|
/**
|
||||||
|
* 检查按钮是否可见
|
||||||
|
*/
|
||||||
private isVisible;
|
private isVisible;
|
||||||
|
/**
|
||||||
|
* 销毁组件,清理资源
|
||||||
|
*/
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OptBtnGroups 配置选项
|
* OptBtnGroups 配置选项
|
||||||
*/
|
*/
|
||||||
export declare interface OptBtnGroupsOptions {
|
export declare interface OptBtnGroupsOptions extends ToolbarColors {
|
||||||
|
/** 容器元素或 ID */
|
||||||
container: HTMLElement | string;
|
container: HTMLElement | string;
|
||||||
|
/** 是否显示标签 */
|
||||||
showLabel?: boolean;
|
showLabel?: boolean;
|
||||||
|
/** 按钮可见性配置 Map */
|
||||||
visibility?: Record<string, boolean>;
|
visibility?: Record<string, boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +358,91 @@ export declare interface OptBtnGroupsOptions {
|
|||||||
* 操作按钮接口(内部使用,继承配置)
|
* 操作按钮接口(内部使用,继承配置)
|
||||||
*/
|
*/
|
||||||
export declare interface OptButton extends ButtonConfig {
|
export declare interface OptButton extends ButtonConfig {
|
||||||
|
/** 内部使用的子按钮列表 */
|
||||||
children?: OptButton[];
|
children?: OptButton[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具栏颜色配置接口
|
||||||
|
*/
|
||||||
|
declare interface ToolbarColors {
|
||||||
|
/** 工具栏背景颜色 */
|
||||||
|
backgroundColor?: string;
|
||||||
|
/** 按钮默认背景颜色 */
|
||||||
|
btnBackgroundColor?: string;
|
||||||
|
/** 按钮 Hover 背景颜色 */
|
||||||
|
btnHoverColor?: string;
|
||||||
|
/** 按钮激活状态背景颜色 */
|
||||||
|
btnActiveColor?: string;
|
||||||
|
/** 图标默认颜色 */
|
||||||
|
iconColor?: string;
|
||||||
|
/** 图标激活/Hover 颜色 */
|
||||||
|
iconActiveColor?: string;
|
||||||
|
/** 文字默认颜色 */
|
||||||
|
textColor?: string;
|
||||||
|
/** 文字激活/Hover 颜色 */
|
||||||
|
textActiveColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具栏管理器
|
||||||
|
* 负责管理底部操作栏的按钮组、按钮及其可见性等状态。
|
||||||
|
*/
|
||||||
|
declare class ToolbarManager {
|
||||||
|
/** 内部工具栏组件实例 */
|
||||||
|
private optBtnGroups;
|
||||||
|
/** 工具栏挂载的容器 */
|
||||||
|
private container;
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 工具栏挂载的容器元素
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement);
|
||||||
|
/**
|
||||||
|
* 初始化工具栏
|
||||||
|
*/
|
||||||
|
private init;
|
||||||
|
/**
|
||||||
|
* 添加一个工具栏按钮组
|
||||||
|
* @param groupId 新组的 ID
|
||||||
|
* @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后
|
||||||
|
*/
|
||||||
|
addGroup(groupId: string, beforeGroupId?: string): void;
|
||||||
|
/**
|
||||||
|
* 添加一个工具栏按钮
|
||||||
|
* @param config 按钮配置对象
|
||||||
|
*/
|
||||||
|
addButton(config: ButtonConfig): void;
|
||||||
|
/**
|
||||||
|
* 设置按钮的可见性
|
||||||
|
* @param buttonId 按钮 ID
|
||||||
|
* @param visible 是否可见
|
||||||
|
*/
|
||||||
|
setButtonVisibility(buttonId: string, visible: boolean): void;
|
||||||
|
/**
|
||||||
|
* 设置是否显示按钮下方的文字标签
|
||||||
|
* @param show 是否显示
|
||||||
|
*/
|
||||||
|
setShowLabel(show: boolean): void;
|
||||||
|
/**
|
||||||
|
* 设置整个工具栏的可见性
|
||||||
|
* @param visible 是否可见
|
||||||
|
*/
|
||||||
|
setVisible(visible: boolean): void;
|
||||||
|
/**
|
||||||
|
* 设置工具栏背景颜色
|
||||||
|
* @param color CSS 颜色值
|
||||||
|
*/
|
||||||
|
setBackgroundColor(color: string): void;
|
||||||
|
/**
|
||||||
|
* 设置工具栏详细颜色配置
|
||||||
|
* @param colors 颜色配置对象
|
||||||
|
*/
|
||||||
|
setColors(colors: ToolbarColors): void;
|
||||||
|
/**
|
||||||
|
* 销毁工具栏管理器
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
|
|
||||||
export { }
|
export { }
|
||||||
|
|||||||
95
docs/Dialog.md
Normal file
95
docs/Dialog.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# 弹窗组件 (Dialog)
|
||||||
|
|
||||||
|
BimEngine SDK 提供了可拖拽、可缩放的通用弹窗组件 `DialogManager` (底层基于 `BimDialog`),支持自定义内容和高度定制的样式。
|
||||||
|
|
||||||
|
## 1. 组件作用
|
||||||
|
|
||||||
|
* 提供浮动的交互窗口。
|
||||||
|
* 支持任意 HTML 内容挂载。
|
||||||
|
* 内置拖拽移动和拖拽缩放功能。
|
||||||
|
* 支持丰富的样式定制接口。
|
||||||
|
|
||||||
|
## 2. 初始化与使用
|
||||||
|
|
||||||
|
通过 `BimEngine` 实例访问:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const engine = new BimEngine('container-id');
|
||||||
|
// engine.dialog 即为 DialogManager 实例
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 配置项 (DialogOptions)
|
||||||
|
|
||||||
|
`engine.dialog.create(options)` 方法接受以下配置:
|
||||||
|
|
||||||
|
| 属性 | 类型 | 默认值 | 说明 |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| `title` | `string` | `'Dialog'` | 弹窗标题 |
|
||||||
|
| `content` | `string \| HTMLElement` | - | 弹窗内容 |
|
||||||
|
| `width` | `number \| string` | `300` | 宽度 |
|
||||||
|
| `height` | `number \| string` | `'auto'` | 高度 |
|
||||||
|
| `position` | `DialogPosition` | `'center'` | 初始位置 (支持 'center', 'top-left' 等或坐标对象) |
|
||||||
|
| `draggable` | `boolean` | `true` | 是否可拖拽 |
|
||||||
|
| `resizable` | `boolean` | `false` | 是否可调整大小 |
|
||||||
|
| `minWidth` | `number` | `200` | 最小宽度 |
|
||||||
|
| `minHeight` | `number` | `100` | 最小高度 |
|
||||||
|
| `onClose` | `Function` | - | 关闭时的回调 |
|
||||||
|
| `backgroundColor` | `string` | `rgba(17, 17, 17, 0.95)` | 窗体背景色 |
|
||||||
|
| `headerBackgroundColor` | `string` | `#2a2a2a` | 标题栏背景色 |
|
||||||
|
| `titleColor` | `string` | `#fff` | 标题文字颜色 |
|
||||||
|
| `textColor` | `string` | `#ccc` | 内容默认文字颜色 |
|
||||||
|
| `borderColor` | `string` | `#444` | 边框颜色 |
|
||||||
|
|
||||||
|
## 4. 使用案例
|
||||||
|
|
||||||
|
### 案例 1: 创建基本弹窗
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
engine.dialog.create({
|
||||||
|
title: '欢迎',
|
||||||
|
content: 'Hello World!',
|
||||||
|
width: 400
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案例 2: 创建包含复杂 DOM 的弹窗
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = '<button>Click Me</button>';
|
||||||
|
div.querySelector('button').onclick = () => alert('Clicked');
|
||||||
|
|
||||||
|
engine.dialog.create({
|
||||||
|
title: '交互组件',
|
||||||
|
content: div,
|
||||||
|
resizable: true
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案例 3: 定制样式 (红色主题)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
engine.dialog.create({
|
||||||
|
title: '警告',
|
||||||
|
content: '这是一个红色主题的警告弹窗。',
|
||||||
|
backgroundColor: 'rgba(100, 0, 0, 0.9)', // 深红背景
|
||||||
|
headerBackgroundColor: '#ff0000', // 鲜红标题栏
|
||||||
|
titleColor: '#ffffff',
|
||||||
|
borderColor: '#ff4444'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案例 4: 监听关闭事件
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const dlg = engine.dialog.create({
|
||||||
|
title: '任务',
|
||||||
|
content: '处理中...',
|
||||||
|
onClose: () => {
|
||||||
|
console.log('弹窗已关闭');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 手动关闭
|
||||||
|
// dlg.close();
|
||||||
|
```
|
||||||
130
docs/Toolbar.md
Normal file
130
docs/Toolbar.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# 工具栏组件 (Toolbar)
|
||||||
|
|
||||||
|
BimEngine SDK 提供了功能强大的工具栏组件 `ToolbarManager` (底层基于 `OptBtnGroups`),支持多级菜单、按钮分组、动态显隐和样式高度定制。
|
||||||
|
|
||||||
|
## 1. 组件作用
|
||||||
|
|
||||||
|
* 提供一个统一的底部操作栏。
|
||||||
|
* 支持按钮分组管理。
|
||||||
|
* 支持层级下拉菜单。
|
||||||
|
* 提供标准的交互反馈(Hover、Active、Disabled)。
|
||||||
|
* 支持完全的样式定制(背景色、图标色、文字色等)。
|
||||||
|
|
||||||
|
## 2. 初始化与使用
|
||||||
|
|
||||||
|
通常通过 `BimEngine` 实例访问:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const engine = new BimEngine('container-id');
|
||||||
|
// engine.toolbar 即为 ToolbarManager 实例
|
||||||
|
```
|
||||||
|
|
||||||
|
或者单独使用(不推荐,除非只需工具栏):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ToolbarManager } from 'bim-engine-sdk';
|
||||||
|
const toolbar = new ToolbarManager(document.getElementById('toolbar-container'));
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 配置项
|
||||||
|
|
||||||
|
`ToolbarManager` 没有直接的配置项,但它管理着底层的 `OptBtnGroups`。主要配置在于添加按钮时的 `ButtonConfig` 和样式设置。
|
||||||
|
|
||||||
|
### ButtonConfig (按钮配置)
|
||||||
|
|
||||||
|
| 属性 | 类型 | 说明 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `id` | `string` | 按钮唯一标识 |
|
||||||
|
| `type` | `'button' \| 'menu'` | 按钮类型 |
|
||||||
|
| `label` | `string` | 按钮显示文字 |
|
||||||
|
| `icon` | `string` | SVG 图标字符串 |
|
||||||
|
| `groupId` | `string` | 所属组 ID (必需) |
|
||||||
|
| `parentId` | `string` | 父按钮 ID (可选,用于子菜单) |
|
||||||
|
| `keepActive` | `boolean` | 是否保持激活状态 (Toggle 模式) |
|
||||||
|
| `disabled` | `boolean` | 是否禁用 |
|
||||||
|
| `onClick` | `Function` | 点击回调 |
|
||||||
|
| `children` | `ButtonConfig[]` | 子按钮数组 |
|
||||||
|
|
||||||
|
### ToolbarColors (颜色配置)
|
||||||
|
|
||||||
|
用于 `setColors` 方法。
|
||||||
|
|
||||||
|
| 属性 | 说明 | 默认值 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `backgroundColor` | 工具栏背景色 | `rgba(17, 17, 17, 0.88)` |
|
||||||
|
| `btnBackgroundColor` | 按钮默认背景 | `transparent` |
|
||||||
|
| `btnHoverColor` | 按钮 Hover 背景 | `#444` |
|
||||||
|
| `btnActiveColor` | 按钮激活背景 | `rgba(255, 255, 255, 0.15)` |
|
||||||
|
| `iconColor` | 图标默认颜色 | `#ccc` |
|
||||||
|
| `iconActiveColor` | 图标激活颜色 | `#fff` |
|
||||||
|
| `textColor` | 文字默认颜色 | `#ccc` |
|
||||||
|
| `textActiveColor` | 文字激活颜色 | `#fff` |
|
||||||
|
|
||||||
|
## 4. 使用案例
|
||||||
|
|
||||||
|
### 案例 1: 添加自定义按钮组和按钮
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 1. 添加一个新组
|
||||||
|
engine.toolbar.addGroup('my-group');
|
||||||
|
|
||||||
|
// 2. 在该组添加按钮
|
||||||
|
engine.toolbar.addButton({
|
||||||
|
id: 'my-btn',
|
||||||
|
groupId: 'my-group',
|
||||||
|
type: 'button',
|
||||||
|
label: '自定义功能',
|
||||||
|
icon: '<svg>...</svg>', // 填入 SVG 内容
|
||||||
|
onClick: (btn) => {
|
||||||
|
console.log('Clicked!', btn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案例 2: 添加带下拉菜单的按钮
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
engine.toolbar.addButton({
|
||||||
|
id: 'menu-btn',
|
||||||
|
groupId: 'my-group',
|
||||||
|
type: 'menu',
|
||||||
|
label: '更多选项',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'sub-1',
|
||||||
|
type: 'button',
|
||||||
|
label: '选项 A',
|
||||||
|
onClick: () => console.log('A')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'sub-2',
|
||||||
|
type: 'button',
|
||||||
|
label: '选项 B',
|
||||||
|
onClick: () => console.log('B')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案例 3: 修改工具栏样式
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
engine.toolbar.setColors({
|
||||||
|
backgroundColor: 'rgba(0, 122, 255, 0.9)', // 蓝色背景
|
||||||
|
iconColor: '#ffffff', // 白色图标
|
||||||
|
btnHoverColor: 'rgba(255, 255, 255, 0.2)'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案例 4: 控制显隐
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 隐藏整个工具栏
|
||||||
|
engine.toolbar.setVisible(false);
|
||||||
|
|
||||||
|
// 隐藏特定按钮
|
||||||
|
engine.toolbar.setButtonVisibility('my-btn', false);
|
||||||
|
|
||||||
|
// 隐藏文字标签
|
||||||
|
engine.toolbar.setShowLabel(false);
|
||||||
|
```
|
||||||
@@ -1,10 +1,26 @@
|
|||||||
import './bim-engine.css';
|
import './bim-engine.css';
|
||||||
import { OptBtnGroups } from './toolbar';
|
import { ToolbarManager } from './modules/toolbar-manager';
|
||||||
|
import { DialogManager } from './modules/dialog-manager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BimEngine 主类
|
||||||
|
* 负责初始化整个应用界面,协调各个子模块(如工具栏、弹窗等)。
|
||||||
|
*/
|
||||||
export class BimEngine {
|
export class BimEngine {
|
||||||
|
/** 主容器元素 */
|
||||||
private container: HTMLElement;
|
private container: HTMLElement;
|
||||||
private optBtnGroups: OptBtnGroups | null = null;
|
/** 内部包装器元素,用于承载所有 UI 组件 */
|
||||||
|
private wrapper: HTMLElement | null = null;
|
||||||
|
|
||||||
|
/** 工具栏管理器实例 */
|
||||||
|
public toolbar: ToolbarManager | null = null;
|
||||||
|
/** 弹窗管理器实例 */
|
||||||
|
public dialog: DialogManager | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 容器元素或容器 ID
|
||||||
|
*/
|
||||||
constructor(container: HTMLElement | string) {
|
constructor(container: HTMLElement | string) {
|
||||||
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
||||||
if (!el) throw new Error('Container not found');
|
if (!el) throw new Error('Container not found');
|
||||||
@@ -12,20 +28,24 @@ export class BimEngine {
|
|||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化方法
|
||||||
|
* 创建 DOM 结构并初始化各子模块
|
||||||
|
*/
|
||||||
private init() {
|
private init() {
|
||||||
// 1. 清空容器可能存在的旧内容
|
// 1. 清空容器可能存在的旧内容
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
|
|
||||||
// 2. 创建外层容器 div
|
// 2. 创建外层容器 div
|
||||||
const wrapper = document.createElement('div');
|
this.wrapper = document.createElement('div');
|
||||||
wrapper.className = 'bim-engine-wrapper';
|
this.wrapper.className = 'bim-engine-wrapper';
|
||||||
|
|
||||||
// 3. 创建标题 h1
|
// 3. 创建标题 h1
|
||||||
const title = document.createElement('h1');
|
const title = document.createElement('h1');
|
||||||
title.textContent = 'BimEngine';
|
title.textContent = 'BimEngine';
|
||||||
title.className = 'bim-engine-title';
|
title.className = 'bim-engine-title';
|
||||||
|
|
||||||
// 4. 创建段落 p
|
// 4. 创建描述段落 p
|
||||||
const desc = document.createElement('p');
|
const desc = document.createElement('p');
|
||||||
desc.textContent = '这是一个使用BIM-ENGINE。';
|
desc.textContent = '这是一个使用BIM-ENGINE。';
|
||||||
desc.className = 'bim-engine-desc';
|
desc.className = 'bim-engine-desc';
|
||||||
@@ -36,34 +56,58 @@ export class BimEngine {
|
|||||||
btnGroupContainer.className = 'bim-engine-opt-btn-container';
|
btnGroupContainer.className = 'bim-engine-opt-btn-container';
|
||||||
|
|
||||||
// 7. 组装元素
|
// 7. 组装元素
|
||||||
wrapper.appendChild(title);
|
this.wrapper.appendChild(title);
|
||||||
wrapper.appendChild(desc);
|
this.wrapper.appendChild(desc);
|
||||||
wrapper.appendChild(btnGroupContainer); // 将按钮组放入 wrapper 中
|
|
||||||
|
// 初始化管理器
|
||||||
|
this.dialog = new DialogManager(this.wrapper);
|
||||||
|
this.toolbar = new ToolbarManager(btnGroupContainer);
|
||||||
|
|
||||||
// 8. 挂载到主容器
|
// 5. 测试按钮(更新为使用管理器)
|
||||||
this.container.appendChild(wrapper);
|
// 5.1 创建普通测试弹窗按钮
|
||||||
|
const dialogBtn = document.createElement('button');
|
||||||
|
dialogBtn.textContent = '打开测试弹窗';
|
||||||
|
dialogBtn.className = 'bim-engine-btn';
|
||||||
|
dialogBtn.onclick = () => {
|
||||||
|
this.dialog?.create({
|
||||||
|
title: '测试弹窗',
|
||||||
|
content: '<div style="padding: 10px;">这是一个 <b>可拖拽</b> 且 <b>可缩放</b> 的弹窗。<br><br>你可以尝试拖动标题栏,或者拖动右下角改变大小。</div>',
|
||||||
|
width: 300,
|
||||||
|
height: 400,
|
||||||
|
position: 'top-left',
|
||||||
|
draggable: true,
|
||||||
|
resizable: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 9. 初始化操作按钮组
|
// 5.2 创建二次封装信息弹窗按钮
|
||||||
this.initOptBtnGroups(btnGroupContainer);
|
const infoDialogBtn = document.createElement('button');
|
||||||
}
|
infoDialogBtn.textContent = '打开信息弹窗 (封装版)';
|
||||||
|
infoDialogBtn.className = 'bim-engine-btn';
|
||||||
private initOptBtnGroups(container: HTMLElement) {
|
infoDialogBtn.style.marginLeft = '10px';
|
||||||
this.optBtnGroups = new OptBtnGroups({
|
infoDialogBtn.onclick = () => {
|
||||||
container,
|
this.dialog?.showInfoDialog();
|
||||||
showLabel: true
|
};
|
||||||
});
|
|
||||||
|
// 将按钮和工具栏容器添加到包装器中
|
||||||
// 初始化并加载默认按钮
|
this.wrapper.appendChild(dialogBtn);
|
||||||
this.optBtnGroups.init().catch(err => {
|
this.wrapper.appendChild(infoDialogBtn);
|
||||||
console.error('Failed to initialize OptBtnGroups:', err);
|
this.wrapper.appendChild(btnGroupContainer);
|
||||||
});
|
|
||||||
|
// 8. 将包装器挂载到主容器
|
||||||
|
this.container.appendChild(this.wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁实例
|
||||||
|
* 清理所有资源和 DOM 元素
|
||||||
|
*/
|
||||||
public destroy() {
|
public destroy() {
|
||||||
if (this.optBtnGroups) {
|
if (this.toolbar) {
|
||||||
this.optBtnGroups.destroy();
|
this.toolbar.destroy();
|
||||||
this.optBtnGroups = null;
|
this.toolbar = null;
|
||||||
}
|
}
|
||||||
|
this.dialog = null;
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
30
src/dialog/bimInfoDialog/index.css
Normal file
30
src/dialog/bimInfoDialog/index.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
.bim-info-dialog-content {
|
||||||
|
padding: 16px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-info-dialog-content h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
color: #0078d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-info-dialog-content ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-info-dialog-content li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-info-dialog-content li strong {
|
||||||
|
width: 80px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
61
src/dialog/bimInfoDialog/index.ts
Normal file
61
src/dialog/bimInfoDialog/index.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import './index.css';
|
||||||
|
import { BimDialog } from '../index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BimInfoDialog (二次封装示例)
|
||||||
|
* 这是一个展示项目信息的业务弹窗组件,内部封装了 BimDialog。
|
||||||
|
*/
|
||||||
|
export class BimInfoDialog {
|
||||||
|
private dialog: BimDialog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 父容器
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement) {
|
||||||
|
// 创建自定义的 DOM 内容
|
||||||
|
const contentEl = document.createElement('div');
|
||||||
|
contentEl.className = 'bim-info-dialog-content';
|
||||||
|
|
||||||
|
const infoTitle = document.createElement('h3');
|
||||||
|
infoTitle.textContent = 'Model Information';
|
||||||
|
|
||||||
|
const infoList = document.createElement('ul');
|
||||||
|
infoList.innerHTML = `
|
||||||
|
<li><strong>Name:</strong> Sample Project</li>
|
||||||
|
<li><strong>Version:</strong> 1.0.0</li>
|
||||||
|
<li><strong>Date:</strong> ${new Date().toLocaleDateString()}</li>
|
||||||
|
<li><strong>Status:</strong> <span style="color: green;">Active</span></li>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const actionBtn = document.createElement('button');
|
||||||
|
actionBtn.textContent = 'Update Status';
|
||||||
|
actionBtn.style.marginTop = '10px';
|
||||||
|
actionBtn.onclick = () => {
|
||||||
|
alert('Status updated!');
|
||||||
|
};
|
||||||
|
|
||||||
|
contentEl.appendChild(infoTitle);
|
||||||
|
contentEl.appendChild(infoList);
|
||||||
|
contentEl.appendChild(actionBtn);
|
||||||
|
|
||||||
|
// 初始化 BimDialog,直接传入构建好的 HTMLElement
|
||||||
|
this.dialog = new BimDialog({
|
||||||
|
container: container,
|
||||||
|
title: 'Project Info (Wrapped)',
|
||||||
|
content: contentEl,
|
||||||
|
width: 320,
|
||||||
|
height: 'auto',
|
||||||
|
position: 'center',
|
||||||
|
resizable: true,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭弹窗
|
||||||
|
*/
|
||||||
|
public close() {
|
||||||
|
this.dialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/dialog/index.css
Normal file
95
src/dialog/index.css
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
:root {
|
||||||
|
--bim-dialog-bg: rgba(17, 17, 17, 0.95);
|
||||||
|
--bim-dialog-header-bg: #2a2a2a;
|
||||||
|
--bim-dialog-title-color: #fff;
|
||||||
|
--bim-dialog-text-color: #ccc;
|
||||||
|
--bim-dialog-border-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog {
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--bim-dialog-bg);
|
||||||
|
border: 1px solid var(--bim-dialog-border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 1000;
|
||||||
|
color: var(--bim-dialog-title-color);
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 200px;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog-header {
|
||||||
|
height: 32px;
|
||||||
|
background-color: var(--bim-dialog-header-bg);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 10px;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
border-bottom: 1px solid var(--bim-dialog-border-color);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog-header.draggable {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: var(--bim-dialog-title-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog-close {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog-close:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--bim-dialog-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 缩放句柄 */
|
||||||
|
.bim-dialog-resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
cursor: se-resize;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右下角装饰,类似斜线 */
|
||||||
|
.bim-dialog-resize-handle::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 3px;
|
||||||
|
right: 3px;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-right: 2px solid #666;
|
||||||
|
border-bottom: 2px solid #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-dialog-resize-handle:hover::after {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
294
src/dialog/index.ts
Normal file
294
src/dialog/index.ts
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
import './index.css';
|
||||||
|
import type { DialogOptions } from './index.type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用弹窗组件类
|
||||||
|
* 支持拖拽、缩放、自定义内容和位置。
|
||||||
|
*/
|
||||||
|
export class BimDialog {
|
||||||
|
private element: HTMLElement;
|
||||||
|
private options: DialogOptions;
|
||||||
|
private container: HTMLElement;
|
||||||
|
private header: HTMLElement;
|
||||||
|
private contentArea: HTMLElement;
|
||||||
|
private _isDestroyed = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param options 弹窗配置选项
|
||||||
|
*/
|
||||||
|
constructor(options: DialogOptions) {
|
||||||
|
// 合并默认配置
|
||||||
|
this.options = {
|
||||||
|
title: 'Dialog',
|
||||||
|
width: 300,
|
||||||
|
height: 'auto',
|
||||||
|
position: 'center',
|
||||||
|
draggable: true,
|
||||||
|
resizable: false,
|
||||||
|
minWidth: 200,
|
||||||
|
minHeight: 100,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
this.container = options.container;
|
||||||
|
|
||||||
|
// 创建 DOM 结构
|
||||||
|
this.element = this.createDom();
|
||||||
|
this.header = this.element.querySelector('.bim-dialog-header') as HTMLElement;
|
||||||
|
this.contentArea = this.element.querySelector('.bim-dialog-content') as HTMLElement;
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建弹窗的 DOM 结构
|
||||||
|
*/
|
||||||
|
private createDom(): HTMLElement {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.className = 'bim-dialog';
|
||||||
|
|
||||||
|
if (this.options.id) el.id = this.options.id;
|
||||||
|
|
||||||
|
// 应用颜色配置到 CSS 变量 (局部作用域)
|
||||||
|
const style = el.style;
|
||||||
|
if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor);
|
||||||
|
if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor);
|
||||||
|
if (this.options.titleColor) style.setProperty('--bim-dialog-title-color', this.options.titleColor);
|
||||||
|
if (this.options.textColor) style.setProperty('--bim-dialog-text-color', this.options.textColor);
|
||||||
|
if (this.options.borderColor) style.setProperty('--bim-dialog-border-color', this.options.borderColor);
|
||||||
|
|
||||||
|
// 设置初始尺寸
|
||||||
|
this.setSize(el, this.options.width, this.options.height);
|
||||||
|
|
||||||
|
// 创建标题栏 (Header)
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'bim-dialog-header';
|
||||||
|
if (this.options.draggable) header.classList.add('draggable');
|
||||||
|
|
||||||
|
const title = document.createElement('span');
|
||||||
|
title.className = 'bim-dialog-title';
|
||||||
|
title.textContent = this.options.title || '';
|
||||||
|
|
||||||
|
const closeBtn = document.createElement('span');
|
||||||
|
closeBtn.className = 'bim-dialog-close';
|
||||||
|
closeBtn.innerHTML = '×';
|
||||||
|
closeBtn.onclick = () => this.close();
|
||||||
|
|
||||||
|
header.appendChild(title);
|
||||||
|
header.appendChild(closeBtn);
|
||||||
|
|
||||||
|
// 创建内容区域 (Content)
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.className = 'bim-dialog-content';
|
||||||
|
if (typeof this.options.content === 'string') {
|
||||||
|
content.innerHTML = this.options.content;
|
||||||
|
} else if (this.options.content instanceof HTMLElement) {
|
||||||
|
content.appendChild(this.options.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.appendChild(header);
|
||||||
|
el.appendChild(content);
|
||||||
|
|
||||||
|
// 如果允许缩放,创建缩放手柄
|
||||||
|
if (this.options.resizable) {
|
||||||
|
const resizeHandle = document.createElement('div');
|
||||||
|
resizeHandle.className = 'bim-dialog-resize-handle';
|
||||||
|
el.appendChild(resizeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置元素尺寸
|
||||||
|
*/
|
||||||
|
private setSize(el: HTMLElement, width?: number | string, height?: number | string) {
|
||||||
|
if (width !== undefined) {
|
||||||
|
el.style.width = typeof width === 'number' ? `${width}px` : width;
|
||||||
|
}
|
||||||
|
if (height !== undefined) {
|
||||||
|
el.style.height = typeof height === 'number' ? `${height}px` : height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化组件功能
|
||||||
|
*/
|
||||||
|
private init() {
|
||||||
|
this.container.appendChild(this.element);
|
||||||
|
|
||||||
|
// 必须先挂载才能计算尺寸进行定位
|
||||||
|
this.initPosition();
|
||||||
|
|
||||||
|
if (this.options.draggable) {
|
||||||
|
this.initDrag();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.resizable) {
|
||||||
|
this.initResize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化弹窗位置
|
||||||
|
*/
|
||||||
|
private initPosition() {
|
||||||
|
const pos = this.options.position;
|
||||||
|
|
||||||
|
const elRect = this.element.getBoundingClientRect();
|
||||||
|
|
||||||
|
// 计算相对父容器的定位
|
||||||
|
let left = 0;
|
||||||
|
let top = 0;
|
||||||
|
|
||||||
|
const pW = this.container.clientWidth;
|
||||||
|
const pH = this.container.clientHeight;
|
||||||
|
const elW = elRect.width;
|
||||||
|
const elH = elRect.height;
|
||||||
|
|
||||||
|
if (typeof pos === 'object' && 'x' in pos) {
|
||||||
|
left = pos.x;
|
||||||
|
top = pos.y;
|
||||||
|
} else {
|
||||||
|
switch (pos) {
|
||||||
|
case 'center':
|
||||||
|
left = (pW - elW) / 2;
|
||||||
|
top = (pH - elH) / 2;
|
||||||
|
break;
|
||||||
|
case 'top-left': left = 0; top = 0; break;
|
||||||
|
case 'top-center': left = (pW - elW) / 2; top = 0; break;
|
||||||
|
case 'top-right': left = pW - elW; top = 0; break;
|
||||||
|
case 'left-center': left = 0; top = (pH - elH) / 2; break;
|
||||||
|
case 'right-center': left = pW - elW; top = (pH - elH) / 2; break;
|
||||||
|
case 'bottom-left': left = 0; top = pH - elH; break;
|
||||||
|
case 'bottom-center': left = (pW - elW) / 2; top = pH - elH; break;
|
||||||
|
case 'bottom-right': left = pW - elW; top = pH - elH; break;
|
||||||
|
default:
|
||||||
|
left = (pW - elW) / 2;
|
||||||
|
top = (pH - elH) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的边界检查,防止初始位置溢出
|
||||||
|
left = Math.max(0, Math.min(left, pW - elW));
|
||||||
|
top = Math.max(0, Math.min(top, pH - elH));
|
||||||
|
|
||||||
|
this.element.style.left = `${left}px`;
|
||||||
|
this.element.style.top = `${top}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化拖拽功能
|
||||||
|
*/
|
||||||
|
private initDrag() {
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let startLeft = 0;
|
||||||
|
let startTop = 0;
|
||||||
|
|
||||||
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
startLeft = this.element.offsetLeft;
|
||||||
|
startTop = this.element.offsetTop;
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
|
||||||
|
let newLeft = startLeft + dx;
|
||||||
|
let newTop = startTop + dy;
|
||||||
|
|
||||||
|
// 边界限制,防止拖出容器
|
||||||
|
const maxLeft = this.container.clientWidth - this.element.offsetWidth;
|
||||||
|
const maxTop = this.container.clientHeight - this.element.offsetHeight;
|
||||||
|
|
||||||
|
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
|
||||||
|
newTop = Math.max(0, Math.min(newTop, maxTop));
|
||||||
|
|
||||||
|
this.element.style.left = `${newLeft}px`;
|
||||||
|
this.element.style.top = `${newTop}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.header.addEventListener('mousedown', onMouseDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化缩放功能
|
||||||
|
*/
|
||||||
|
private initResize() {
|
||||||
|
const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;
|
||||||
|
if (!handle) return;
|
||||||
|
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let startW = 0;
|
||||||
|
let startH = 0;
|
||||||
|
|
||||||
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
startW = this.element.offsetWidth;
|
||||||
|
startH = this.element.offsetHeight;
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
|
||||||
|
const newW = Math.max(this.options.minWidth || 100, startW + dx);
|
||||||
|
const newH = Math.max(this.options.minHeight || 50, startH + dy);
|
||||||
|
|
||||||
|
this.element.style.width = `${newW}px`;
|
||||||
|
this.element.style.height = `${newH}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
handle.addEventListener('mousedown', onMouseDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态设置内容
|
||||||
|
* @param content 内容元素或 HTML 字符串
|
||||||
|
*/
|
||||||
|
public setContent(content: HTMLElement | string) {
|
||||||
|
this.contentArea.innerHTML = '';
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
this.contentArea.innerHTML = content;
|
||||||
|
} else {
|
||||||
|
this.contentArea.appendChild(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭弹窗并销毁
|
||||||
|
*/
|
||||||
|
public close() {
|
||||||
|
if (this._isDestroyed) return;
|
||||||
|
this.element.remove();
|
||||||
|
this._isDestroyed = true;
|
||||||
|
if (this.options.onClose) {
|
||||||
|
this.options.onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/dialog/index.type.ts
Normal file
57
src/dialog/index.type.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* 弹窗位置类型定义
|
||||||
|
* 可以是预设的字符串位置(如 'center', 'top-left' 等),
|
||||||
|
* 也可以是具体的坐标对象 { x, y }
|
||||||
|
*/
|
||||||
|
export type DialogPosition =
|
||||||
|
| 'center'
|
||||||
|
| 'top-left' | 'top-center' | 'top-right'
|
||||||
|
| 'left-center' | 'right-center'
|
||||||
|
| 'bottom-left' | 'bottom-center' | 'bottom-right'
|
||||||
|
| { x: number; y: number };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗颜色配置
|
||||||
|
*/
|
||||||
|
export interface DialogColors {
|
||||||
|
/** 窗体背景颜色,默认 rgba(17, 17, 17, 0.95) */
|
||||||
|
backgroundColor?: string;
|
||||||
|
/** 标题栏背景颜色,默认 #2a2a2a */
|
||||||
|
headerBackgroundColor?: string;
|
||||||
|
/** 标题文字颜色,默认 #fff */
|
||||||
|
titleColor?: string;
|
||||||
|
/** 内容文字颜色,默认 #ccc */
|
||||||
|
textColor?: string;
|
||||||
|
/** 边框颜色,默认 #444 */
|
||||||
|
borderColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗配置选项接口
|
||||||
|
*/
|
||||||
|
export interface DialogOptions extends DialogColors {
|
||||||
|
/** 弹窗挂载的父容器 */
|
||||||
|
container: HTMLElement;
|
||||||
|
/** 弹窗标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 弹窗内容,支持 HTML 字符串或 HTMLElement */
|
||||||
|
content?: HTMLElement | string;
|
||||||
|
/** 弹窗宽度,数字(像素)或字符串(如 '50%') */
|
||||||
|
width?: number | string;
|
||||||
|
/** 弹窗高度 */
|
||||||
|
height?: number | string;
|
||||||
|
/** 弹窗位置 */
|
||||||
|
position?: DialogPosition;
|
||||||
|
/** 是否可拖拽 */
|
||||||
|
draggable?: boolean;
|
||||||
|
/** 是否可调整大小 */
|
||||||
|
resizable?: boolean;
|
||||||
|
/** 最小宽度限制 */
|
||||||
|
minWidth?: number;
|
||||||
|
/** 最小高度限制 */
|
||||||
|
minHeight?: number;
|
||||||
|
/** 关闭时的回调函数 */
|
||||||
|
onClose?: () => void;
|
||||||
|
/** 弹窗唯一标识 ID (可选) */
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
import { BimEngine } from './bim-engine';
|
import { BimEngine } from './bim-engine';
|
||||||
|
|
||||||
|
// 导出 OptBtnGroups 组件,用于工具栏操作
|
||||||
export { OptBtnGroups } from './toolbar';
|
export { OptBtnGroups } from './toolbar';
|
||||||
|
|
||||||
|
// 导出相关类型定义
|
||||||
export type { OptButton, ButtonGroup, OptBtnGroupsOptions, ClickPayload } from './toolbar/index.type';
|
export type { OptButton, ButtonGroup, OptBtnGroupsOptions, ClickPayload } from './toolbar/index.type';
|
||||||
export { BimEngine };
|
|
||||||
|
// 导出主引擎类
|
||||||
|
export { BimEngine };
|
||||||
40
src/modules/dialog-manager.ts
Normal file
40
src/modules/dialog-manager.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { BimDialog } from '../dialog';
|
||||||
|
import { BimInfoDialog } from '../dialog/bimInfoDialog';
|
||||||
|
import type { DialogOptions } from '../dialog/index.type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗管理器
|
||||||
|
* 负责创建和管理应用中的各类弹窗。
|
||||||
|
*/
|
||||||
|
export class DialogManager {
|
||||||
|
/** 弹窗挂载的父容器 */
|
||||||
|
private container: HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 弹窗挂载的目标容器
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement) {
|
||||||
|
this.container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个通用弹窗
|
||||||
|
* @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)
|
||||||
|
* @returns BimDialog 实例
|
||||||
|
*/
|
||||||
|
public create(options: Omit<DialogOptions, 'container'>): BimDialog {
|
||||||
|
return new BimDialog({
|
||||||
|
container: this.container,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示二次封装的模型信息弹窗
|
||||||
|
* 演示如何调用特定的业务弹窗组件
|
||||||
|
*/
|
||||||
|
public showInfoDialog() {
|
||||||
|
new BimInfoDialog(this.container);
|
||||||
|
}
|
||||||
|
}
|
||||||
132
src/modules/toolbar-manager.ts
Normal file
132
src/modules/toolbar-manager.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import type { ToolbarColors } from '../toolbar/index.type';
|
||||||
|
import { OptBtnGroups } from '../toolbar';
|
||||||
|
import type { ButtonConfig } from '../toolbar/index.type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具栏管理器
|
||||||
|
* 负责管理底部操作栏的按钮组、按钮及其可见性等状态。
|
||||||
|
*/
|
||||||
|
export class ToolbarManager {
|
||||||
|
/** 内部工具栏组件实例 */
|
||||||
|
private optBtnGroups: OptBtnGroups | null = null;
|
||||||
|
/** 工具栏挂载的容器 */
|
||||||
|
private container: HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 工具栏挂载的容器元素
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement) {
|
||||||
|
this.container = container;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化工具栏
|
||||||
|
*/
|
||||||
|
private init() {
|
||||||
|
this.optBtnGroups = new OptBtnGroups({
|
||||||
|
container: this.container,
|
||||||
|
showLabel: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化并加载默认按钮配置
|
||||||
|
this.optBtnGroups.init().catch(err => {
|
||||||
|
console.error('Failed to initialize OptBtnGroups:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个工具栏按钮组
|
||||||
|
* @param groupId 新组的 ID
|
||||||
|
* @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后
|
||||||
|
*/
|
||||||
|
public addGroup(groupId: string, beforeGroupId?: string) {
|
||||||
|
if (this.optBtnGroups) {
|
||||||
|
this.optBtnGroups.addGroup(groupId, beforeGroupId);
|
||||||
|
this.optBtnGroups.render(); // 重新渲染以更新 UI
|
||||||
|
} else {
|
||||||
|
console.warn('Toolbar not initialized yet.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个工具栏按钮
|
||||||
|
* @param config 按钮配置对象
|
||||||
|
*/
|
||||||
|
public addButton(config: ButtonConfig) {
|
||||||
|
if (this.optBtnGroups) {
|
||||||
|
this.optBtnGroups.addButton(config);
|
||||||
|
this.optBtnGroups.render(); // 重新渲染以更新 UI
|
||||||
|
} else {
|
||||||
|
console.warn('Toolbar not initialized yet.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置按钮的可见性
|
||||||
|
* @param buttonId 按钮 ID
|
||||||
|
* @param visible 是否可见
|
||||||
|
*/
|
||||||
|
public setButtonVisibility(buttonId: string, visible: boolean) {
|
||||||
|
if (this.optBtnGroups) {
|
||||||
|
this.optBtnGroups.updateButtonVisibility(buttonId, visible);
|
||||||
|
} else {
|
||||||
|
console.warn('Toolbar not initialized yet.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否显示按钮下方的文字标签
|
||||||
|
* @param show 是否显示
|
||||||
|
*/
|
||||||
|
public setShowLabel(show: boolean) {
|
||||||
|
if (this.optBtnGroups) {
|
||||||
|
this.optBtnGroups.setShowLabel(show);
|
||||||
|
} else {
|
||||||
|
console.warn('Toolbar not initialized yet.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置整个工具栏的可见性
|
||||||
|
* @param visible 是否可见
|
||||||
|
*/
|
||||||
|
public setVisible(visible: boolean) {
|
||||||
|
this.container.style.display = visible ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置工具栏背景颜色
|
||||||
|
* @param color CSS 颜色值
|
||||||
|
*/
|
||||||
|
public setBackgroundColor(color: string) {
|
||||||
|
if (this.optBtnGroups) {
|
||||||
|
this.optBtnGroups.setBackgroundColor(color);
|
||||||
|
} else {
|
||||||
|
console.warn('Toolbar not initialized yet.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置工具栏详细颜色配置
|
||||||
|
* @param colors 颜色配置对象
|
||||||
|
*/
|
||||||
|
public setColors(colors: ToolbarColors) {
|
||||||
|
if (this.optBtnGroups) {
|
||||||
|
this.optBtnGroups.setColors(colors);
|
||||||
|
} else {
|
||||||
|
console.warn('Toolbar not initialized yet.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁工具栏管理器
|
||||||
|
*/
|
||||||
|
public destroy() {
|
||||||
|
if (this.optBtnGroups) {
|
||||||
|
this.optBtnGroups.destroy();
|
||||||
|
this.optBtnGroups = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,26 @@
|
|||||||
|
:root {
|
||||||
|
--bim-toolbar-bg: rgba(17, 17, 17, 0.88);
|
||||||
|
--bim-btn-bg: transparent;
|
||||||
|
--bim-btn-hover-bg: #444;
|
||||||
|
--bim-btn-active-bg: rgba(255, 255, 255, 0.15);
|
||||||
|
--bim-icon-color: #ccc;
|
||||||
|
--bim-icon-active-color: #fff;
|
||||||
|
--bim-btn-text-color: #ccc;
|
||||||
|
--bim-btn-text-active-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
/* 容器样式 */
|
/* 容器样式 */
|
||||||
.toolbar-container {
|
.toolbar-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* 容器本身不需要背景 */
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
/* Firefox 隐藏滚动条 */
|
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
/* IE 10+ 隐藏滚动条 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-container::-webkit-scrollbar {
|
.toolbar-container::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
/* Chrome/Safari 隐藏滚动条 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 按钮组样式 */
|
/* 按钮组样式 */
|
||||||
@@ -23,15 +30,13 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background-color: rgba(17, 17, 17, 0.88);
|
background-color: var(--bim-toolbar-bg);
|
||||||
/* 每个组独立的背景 */
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-divider {
|
.has-divider {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
/* 增加右边距来分隔组 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 按钮包装器 */
|
/* 按钮包装器 */
|
||||||
@@ -49,23 +54,21 @@
|
|||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #ccc;
|
background-color: var(--bim-btn-bg);
|
||||||
|
color: var(--bim-icon-color);
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
/* 默认透明底边 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.opt-btn:hover {
|
.opt-btn:hover {
|
||||||
background-color: #444;
|
background-color: var(--bim-btn-hover-bg);
|
||||||
color: #fff;
|
color: var(--bim-icon-active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.opt-btn.active {
|
.opt-btn.active {
|
||||||
background-color: rgba(255, 255, 255, 0.15);
|
background-color: var(--bim-btn-active-bg);
|
||||||
/* 白色半透明背景 */
|
color: var(--bim-icon-active-color);
|
||||||
color: #fff;
|
|
||||||
border-bottom: 2px solid #fff;
|
border-bottom: 2px solid #fff;
|
||||||
/* 纯白色底部横条 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.opt-btn.disabled {
|
.opt-btn.disabled {
|
||||||
@@ -81,7 +84,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
/* 防止图标被压缩 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.opt-btn-icon svg {
|
.opt-btn-icon svg {
|
||||||
@@ -93,7 +95,13 @@
|
|||||||
.opt-btn-label {
|
.opt-btn-label {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
color: var(--bim-btn-text-color);
|
||||||
}
|
}
|
||||||
|
.opt-btn:hover .opt-btn-label,
|
||||||
|
.opt-btn.active .opt-btn-label {
|
||||||
|
color: var(--bim-btn-text-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 下拉箭头样式 */
|
/* 下拉箭头样式 */
|
||||||
.opt-btn-arrow {
|
.opt-btn-arrow {
|
||||||
@@ -105,12 +113,10 @@
|
|||||||
transition: transform 0.2s ease;
|
transition: transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 箭头旋转 (菜单展开时) */
|
|
||||||
.opt-btn-arrow.rotated {
|
.opt-btn-arrow.rotated {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 无标签模式调整 */
|
|
||||||
.opt-btn.no-label .opt-btn-arrow {
|
.opt-btn.no-label .opt-btn-arrow {
|
||||||
top: 2px;
|
top: 2px;
|
||||||
right: 2px;
|
right: 2px;
|
||||||
@@ -119,16 +125,13 @@
|
|||||||
/* 下拉菜单样式 */
|
/* 下拉菜单样式 */
|
||||||
.opt-btn-dropdown {
|
.opt-btn-dropdown {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
/* 固定定位 */
|
|
||||||
transform: translate(-50%, -100%);
|
transform: translate(-50%, -100%);
|
||||||
/* 水平居中并向上移动 */
|
background-color: var(--bim-toolbar-bg);
|
||||||
background-color: rgba(17, 17, 17, 0.88);
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
/* 高层级 */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@@ -137,45 +140,40 @@
|
|||||||
.opt-btn-dropdown-item {
|
.opt-btn-dropdown-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
/* 垂直布局 */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #b3b4b4;
|
color: var(--bim-icon-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
background-color: var(--bim-btn-bg);
|
||||||
|
|
||||||
.opt-btn-dropdown-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
/* 移除最后一项的分隔线 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.opt-btn-dropdown-item:hover {
|
.opt-btn-dropdown-item:hover {
|
||||||
background-color: #444;
|
background-color: var(--bim-btn-hover-bg);
|
||||||
color: #fff;
|
color: var(--bim-icon-active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.opt-btn-dropdown-item .opt-btn-icon.small {
|
.opt-btn-dropdown-item .opt-btn-icon.small {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
/* 与主按钮图标大小一致 */
|
|
||||||
height: 30px;
|
height: 30px;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
/* 移除右边距 */
|
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
/* 添加下边距 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.opt-btn-dropdown-item span {
|
.opt-btn-dropdown-item span {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
/* 较小的文字 */
|
color: var(--bim-btn-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.opt-btn-dropdown-item:hover span {
|
||||||
|
color: var(--bim-btn-text-active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 无标签模式调整 */
|
|
||||||
.opt-btn.no-label .opt-btn-icon {
|
.opt-btn.no-label .opt-btn-icon {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,37 @@ import type {
|
|||||||
OptButton,
|
OptButton,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
OptBtnGroupsOptions,
|
OptBtnGroupsOptions,
|
||||||
ButtonConfig
|
ButtonConfig,
|
||||||
|
ToolbarColors
|
||||||
} from './index.type';
|
} from './index.type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 底部操作按钮组组件
|
||||||
|
* 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。
|
||||||
|
*/
|
||||||
export class OptBtnGroups {
|
export class OptBtnGroups {
|
||||||
|
/** 挂载容器 */
|
||||||
private container: HTMLElement;
|
private container: HTMLElement;
|
||||||
|
/** 组件配置选项 */
|
||||||
private options: OptBtnGroupsOptions;
|
private options: OptBtnGroupsOptions;
|
||||||
// 改用 Array 存储 Group,方便控制顺序
|
/** 按钮组列表,按顺序存储 */
|
||||||
private groups: ButtonGroup[] = [];
|
private groups: ButtonGroup[] = [];
|
||||||
|
/** 当前处于激活状态的按钮 ID 集合 */
|
||||||
private activeBtnIds: Set<string> = new Set();
|
private activeBtnIds: Set<string> = new Set();
|
||||||
|
/** 按钮 DOM 元素的引用映射,方便快速查找 */
|
||||||
private btnRefs: Map<string, HTMLElement> = new Map();
|
private btnRefs: Map<string, HTMLElement> = new Map();
|
||||||
|
/** 当<><E5BD93>显示的下拉菜单元素 */
|
||||||
private dropdownElement: HTMLElement | null = null;
|
private dropdownElement: HTMLElement | null = null;
|
||||||
|
/** 鼠标悬停计时器,用于处理菜单显示的防抖 */
|
||||||
private hoverTimeout: number | null = null;
|
private hoverTimeout: number | null = null;
|
||||||
|
|
||||||
|
/** 默认图标 SVG */
|
||||||
private readonly DEFAULT_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect></svg>';
|
private readonly DEFAULT_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect></svg>';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param options 配置选项
|
||||||
|
*/
|
||||||
constructor(options: OptBtnGroupsOptions) {
|
constructor(options: OptBtnGroupsOptions) {
|
||||||
const el = typeof options.container === 'string'
|
const el = typeof options.container === 'string'
|
||||||
? document.getElementById(options.container)
|
? document.getElementById(options.container)
|
||||||
@@ -33,16 +49,47 @@ export class OptBtnGroups {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.initContainer();
|
this.initContainer();
|
||||||
|
this.applyStyles(); // 应用初始样式配置
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化容器
|
||||||
|
*/
|
||||||
private initContainer(): void {
|
private initContainer(): void {
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
|
this.container.classList.add('toolbar-root'); // 添加一个根类方便定位样式
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用样式配置到 CSS 变量
|
||||||
|
*/
|
||||||
|
private applyStyles(): void {
|
||||||
|
const style = this.container.style;
|
||||||
|
if (this.options.backgroundColor) style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);
|
||||||
|
if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);
|
||||||
|
if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);
|
||||||
|
if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);
|
||||||
|
if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);
|
||||||
|
if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);
|
||||||
|
if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);
|
||||||
|
if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新颜色配置
|
||||||
|
* @param colors 颜色配置对象
|
||||||
|
*/
|
||||||
|
public setColors(colors: ToolbarColors): void {
|
||||||
|
// 更新 options
|
||||||
|
this.options = { ...this.options, ...colors };
|
||||||
|
// 应用到 CSS 变量
|
||||||
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加按钮组
|
* 添加按钮组
|
||||||
* @param groupId 组ID
|
* @param groupId 组ID
|
||||||
* @param beforeGroupId 在哪个组之前插入(可选),不传则插入到最后
|
* @param beforeGroupId 在哪个组之前插入(可选<EFBFBD><EFBFBD><EFBFBD>,不传则插入到最后
|
||||||
*/
|
*/
|
||||||
public addGroup(groupId: string, beforeGroupId?: string): void {
|
public addGroup(groupId: string, beforeGroupId?: string): void {
|
||||||
if (this.groups.some(g => g.id === groupId)) {
|
if (this.groups.some(g => g.id === groupId)) {
|
||||||
@@ -66,7 +113,7 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加按钮
|
* 添加按钮到指定组
|
||||||
* @param config 按钮配置(必须包含 groupId,可选包含 parentId)
|
* @param config 按钮配置(必须包含 groupId,可选包含 parentId)
|
||||||
*/
|
*/
|
||||||
public addButton(config: ButtonConfig): void {
|
public addButton(config: ButtonConfig): void {
|
||||||
@@ -87,7 +134,7 @@ export class OptBtnGroups {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
// Add as sub-button
|
// 添加为子按钮(菜单项)
|
||||||
const parentBtn = this.findButton(group.buttons, parentId);
|
const parentBtn = this.findButton(group.buttons, parentId);
|
||||||
if (!parentBtn) {
|
if (!parentBtn) {
|
||||||
throw new Error(`Parent button ${parentId} not found in group ${groupId}`);
|
throw new Error(`Parent button ${parentId} not found in group ${groupId}`);
|
||||||
@@ -97,11 +144,14 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
parentBtn.children.push(button);
|
parentBtn.children.push(button);
|
||||||
} else {
|
} else {
|
||||||
// Add as main button
|
// 添加为主按钮
|
||||||
group.buttons.push(button);
|
group.buttons.push(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归查找按钮
|
||||||
|
*/
|
||||||
private findButton(buttons: OptButton[], id: string): OptButton | undefined {
|
private findButton(buttons: OptButton[], id: string): OptButton | undefined {
|
||||||
for (const btn of buttons) {
|
for (const btn of buttons) {
|
||||||
if (btn.id === id) return btn;
|
if (btn.id === id) return btn;
|
||||||
@@ -113,7 +163,11 @@ export class OptBtnGroups {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化组件,加载默认按钮配置
|
||||||
|
*/
|
||||||
public async init(): Promise<void> {
|
public async init(): Promise<void> {
|
||||||
|
// 动态导入默认按钮配置
|
||||||
const { homeButton } = await import('./buttons/home');
|
const { homeButton } = await import('./buttons/home');
|
||||||
const { locationButton } = await import('./buttons/location');
|
const { locationButton } = await import('./buttons/location');
|
||||||
const { walkMenuButton } = await import('./buttons/walk/walk-menu');
|
const { walkMenuButton } = await import('./buttons/walk/walk-menu');
|
||||||
@@ -122,7 +176,7 @@ export class OptBtnGroups {
|
|||||||
const { settingButton } = await import('./buttons/setting');
|
const { settingButton } = await import('./buttons/setting');
|
||||||
const { infoButton } = await import('./buttons/info');
|
const { infoButton } = await import('./buttons/info');
|
||||||
|
|
||||||
// 添加组1
|
// 配置默认组和按钮
|
||||||
this.addGroup('group-1');
|
this.addGroup('group-1');
|
||||||
this.addButton(homeButton);
|
this.addButton(homeButton);
|
||||||
this.addButton(walkMenuButton);
|
this.addButton(walkMenuButton);
|
||||||
@@ -135,6 +189,9 @@ export class OptBtnGroups {
|
|||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染整个工具栏
|
||||||
|
*/
|
||||||
public render(): void {
|
public render(): void {
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
this.btnRefs.clear();
|
this.btnRefs.clear();
|
||||||
@@ -142,7 +199,7 @@ export class OptBtnGroups {
|
|||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
wrapper.className = 'toolbar-container';
|
wrapper.className = 'toolbar-container';
|
||||||
|
|
||||||
// 直接遍历数组,顺序由 addGroup 控制
|
// 渲染所有组
|
||||||
this.groups.forEach((group, index) => {
|
this.groups.forEach((group, index) => {
|
||||||
const groupElement = this.renderGroup(group, index, this.groups.length);
|
const groupElement = this.renderGroup(group, index, this.groups.length);
|
||||||
wrapper.appendChild(groupElement);
|
wrapper.appendChild(groupElement);
|
||||||
@@ -151,6 +208,9 @@ export class OptBtnGroups {
|
|||||||
this.container.appendChild(wrapper);
|
this.container.appendChild(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染单个按钮组
|
||||||
|
*/
|
||||||
private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {
|
private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {
|
||||||
const groupEl = document.createElement('div');
|
const groupEl = document.createElement('div');
|
||||||
groupEl.className = 'opt-btn-group';
|
groupEl.className = 'opt-btn-group';
|
||||||
@@ -169,6 +229,9 @@ export class OptBtnGroups {
|
|||||||
return groupEl;
|
return groupEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染单个按钮
|
||||||
|
*/
|
||||||
private renderButton(button: OptButton): HTMLElement {
|
private renderButton(button: OptButton): HTMLElement {
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
wrapper.className = 'opt-btn-wrapper';
|
wrapper.className = 'opt-btn-wrapper';
|
||||||
@@ -176,6 +239,7 @@ export class OptBtnGroups {
|
|||||||
const btnEl = document.createElement('div');
|
const btnEl = document.createElement('div');
|
||||||
btnEl.className = 'opt-btn';
|
btnEl.className = 'opt-btn';
|
||||||
|
|
||||||
|
// 设置激活状态
|
||||||
if (this.activeBtnIds.has(button.id)) {
|
if (this.activeBtnIds.has(button.id)) {
|
||||||
btnEl.classList.add('active');
|
btnEl.classList.add('active');
|
||||||
}
|
}
|
||||||
@@ -191,11 +255,13 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 渲染图标
|
||||||
const icon = document.createElement('div');
|
const icon = document.createElement('div');
|
||||||
icon.className = 'opt-btn-icon';
|
icon.className = 'opt-btn-icon';
|
||||||
icon.innerHTML = this.getIcon(button.icon);
|
icon.innerHTML = this.getIcon(button.icon);
|
||||||
btnEl.appendChild(icon);
|
btnEl.appendChild(icon);
|
||||||
|
|
||||||
|
// 渲染标签
|
||||||
if (this.options.showLabel && button.label) {
|
if (this.options.showLabel && button.label) {
|
||||||
const label = document.createElement('span');
|
const label = document.createElement('span');
|
||||||
label.className = 'opt-btn-label';
|
label.className = 'opt-btn-label';
|
||||||
@@ -203,6 +269,7 @@ export class OptBtnGroups {
|
|||||||
btnEl.appendChild(label);
|
btnEl.appendChild(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有子菜单,渲染箭头
|
||||||
if (button.children && button.children.length > 0) {
|
if (button.children && button.children.length > 0) {
|
||||||
const arrow = document.createElement('span');
|
const arrow = document.createElement('span');
|
||||||
arrow.className = 'opt-btn-arrow';
|
arrow.className = 'opt-btn-arrow';
|
||||||
@@ -210,6 +277,7 @@ export class OptBtnGroups {
|
|||||||
btnEl.appendChild(arrow);
|
btnEl.appendChild(arrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 绑定事件
|
||||||
btnEl.addEventListener('click', () => this.handleClick(button));
|
btnEl.addEventListener('click', () => this.handleClick(button));
|
||||||
btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));
|
btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));
|
||||||
btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());
|
btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());
|
||||||
@@ -220,9 +288,13 @@ export class OptBtnGroups {
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理按钮点击事件
|
||||||
|
*/
|
||||||
private handleClick(button: OptButton): void {
|
private handleClick(button: OptButton): void {
|
||||||
if (button.disabled) return;
|
if (button.disabled) return;
|
||||||
|
|
||||||
|
// 如果没有子菜单,直接触发
|
||||||
if (!button.children || button.children.length === 0) {
|
if (!button.children || button.children.length === 0) {
|
||||||
if (button.keepActive) {
|
if (button.keepActive) {
|
||||||
const wasActive = this.activeBtnIds.has(button.id);
|
const wasActive = this.activeBtnIds.has(button.id);
|
||||||
@@ -242,6 +314,9 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理子菜单项点击事件
|
||||||
|
*/
|
||||||
private handleSubClick(button: OptButton): void {
|
private handleSubClick(button: OptButton): void {
|
||||||
if (button.keepActive) {
|
if (button.keepActive) {
|
||||||
const wasActive = this.activeBtnIds.has(button.id);
|
const wasActive = this.activeBtnIds.has(button.id);
|
||||||
@@ -260,6 +335,9 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理鼠标移入事件(显示菜单)
|
||||||
|
*/
|
||||||
private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {
|
private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {
|
||||||
if (this.hoverTimeout) {
|
if (this.hoverTimeout) {
|
||||||
clearTimeout(this.hoverTimeout);
|
clearTimeout(this.hoverTimeout);
|
||||||
@@ -278,12 +356,18 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理鼠标移出事件(隐藏菜单)
|
||||||
|
*/
|
||||||
private handleMouseLeave(): void {
|
private handleMouseLeave(): void {
|
||||||
this.hoverTimeout = window.setTimeout(() => {
|
this.hoverTimeout = window.setTimeout(() => {
|
||||||
this.closeDropdown();
|
this.closeDropdown();
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示下拉菜单
|
||||||
|
*/
|
||||||
private showDropdown(button: OptButton, btnEl: HTMLElement): void {
|
private showDropdown(button: OptButton, btnEl: HTMLElement): void {
|
||||||
this.closeDropdown();
|
this.closeDropdown();
|
||||||
|
|
||||||
@@ -291,6 +375,22 @@ export class OptBtnGroups {
|
|||||||
|
|
||||||
const dropdown = document.createElement('div');
|
const dropdown = document.createElement('div');
|
||||||
dropdown.className = 'opt-btn-dropdown';
|
dropdown.className = 'opt-btn-dropdown';
|
||||||
|
|
||||||
|
// 下拉菜单也应用当前的 CSS 变量样式,因为它们通常挂载在 body 上,所以需要单独设置或者确保能继承
|
||||||
|
// 简单起见,我们可以直接将容器上的 CSS 变量复制过来,或者设置内联样式
|
||||||
|
// 更好的是:如果我们在 this.container 上设置 CSS 变量,
|
||||||
|
// 而 dropdown 挂载在 body 上,它无法继承。
|
||||||
|
// 所以我们需要将 CSS 变量也应用到 dropdown 上。
|
||||||
|
|
||||||
|
const style = dropdown.style;
|
||||||
|
if (this.options.backgroundColor) style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);
|
||||||
|
if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);
|
||||||
|
if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);
|
||||||
|
if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);
|
||||||
|
if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);
|
||||||
|
if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);
|
||||||
|
if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);
|
||||||
|
if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);
|
||||||
|
|
||||||
const rect = btnEl.getBoundingClientRect();
|
const rect = btnEl.getBoundingClientRect();
|
||||||
const centerX = rect.left + rect.width / 2;
|
const centerX = rect.left + rect.width / 2;
|
||||||
@@ -305,6 +405,7 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 保持菜单显<E58D95><E698BE><EFBFBD>
|
||||||
dropdown.addEventListener('mouseenter', () => {
|
dropdown.addEventListener('mouseenter', () => {
|
||||||
if (this.hoverTimeout) {
|
if (this.hoverTimeout) {
|
||||||
clearTimeout(this.hoverTimeout);
|
clearTimeout(this.hoverTimeout);
|
||||||
@@ -318,6 +419,9 @@ export class OptBtnGroups {
|
|||||||
this.dropdownElement = dropdown;
|
this.dropdownElement = dropdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染下拉菜单项
|
||||||
|
*/
|
||||||
private renderDropdownItem(button: OptButton): HTMLElement {
|
private renderDropdownItem(button: OptButton): HTMLElement {
|
||||||
const item = document.createElement('div');
|
const item = document.createElement('div');
|
||||||
item.className = 'opt-btn-dropdown-item';
|
item.className = 'opt-btn-dropdown-item';
|
||||||
@@ -341,6 +445,9 @@ export class OptBtnGroups {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭所有下拉菜单
|
||||||
|
*/
|
||||||
private closeDropdown(): void {
|
private closeDropdown(): void {
|
||||||
if (this.dropdownElement) {
|
if (this.dropdownElement) {
|
||||||
this.dropdownElement.remove();
|
this.dropdownElement.remove();
|
||||||
@@ -355,6 +462,9 @@ export class OptBtnGroups {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新按钮的激活状态样式
|
||||||
|
*/
|
||||||
private updateButtonState(buttonId: string): void {
|
private updateButtonState(buttonId: string): void {
|
||||||
const btnEl = this.btnRefs.get(buttonId);
|
const btnEl = this.btnRefs.get(buttonId);
|
||||||
if (btnEl) {
|
if (btnEl) {
|
||||||
@@ -366,14 +476,53 @@ export class OptBtnGroups {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图标 SVG 字符串
|
||||||
|
*/
|
||||||
private getIcon(icon?: string): string {
|
private getIcon(icon?: string): string {
|
||||||
return icon || this.DEFAULT_ICON;
|
return icon || this.DEFAULT_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新按钮可见性
|
||||||
|
* @param buttonId 按钮ID
|
||||||
|
* @param visible 是否可见
|
||||||
|
*/
|
||||||
|
public updateButtonVisibility(buttonId: string, visible: boolean): void {
|
||||||
|
if (!this.options.visibility) {
|
||||||
|
this.options.visibility = {};
|
||||||
|
}
|
||||||
|
this.options.visibility[buttonId] = visible;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否显示标签
|
||||||
|
* @param show 是否显示
|
||||||
|
*/
|
||||||
|
public setShowLabel(show: boolean): void {
|
||||||
|
this.options.showLabel = show;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置背景颜色 (兼容旧接口)
|
||||||
|
* @param color CSS 颜色值
|
||||||
|
*/
|
||||||
|
public setBackgroundColor(color: string): void {
|
||||||
|
this.setColors({ backgroundColor: color });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查按钮是否可见
|
||||||
|
*/
|
||||||
private isVisible(id: string): boolean {
|
private isVisible(id: string): boolean {
|
||||||
return this.options.visibility?.[id] !== false;
|
return this.options.visibility?.[id] !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁组件,清理资源
|
||||||
|
*/
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
this.closeDropdown();
|
this.closeDropdown();
|
||||||
if (this.hoverTimeout) {
|
if (this.hoverTimeout) {
|
||||||
@@ -382,6 +531,6 @@ export class OptBtnGroups {
|
|||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
this.btnRefs.clear();
|
this.btnRefs.clear();
|
||||||
this.activeBtnIds.clear();
|
this.activeBtnIds.clear();
|
||||||
this.groups = []; // 清空数组
|
this.groups = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,40 +4,78 @@ export type ButtonType = 'button' | 'menu';
|
|||||||
* 按钮配置接口(用于外部定义按钮)
|
* 按钮配置接口(用于外部定义按钮)
|
||||||
*/
|
*/
|
||||||
export interface ButtonConfig {
|
export interface ButtonConfig {
|
||||||
id: string; // 唯一标识
|
/** 唯一标识 */
|
||||||
type: ButtonType; // 按钮类型
|
id: string;
|
||||||
label: string; // 按钮文字
|
/** 按钮类型:普通按钮或菜单按钮 */
|
||||||
icon?: string; // SVG 图标(内联 SVG 字符串)
|
type: ButtonType;
|
||||||
keepActive?: boolean; // 是否保持激活状态(默认 false)
|
/** 按钮显示文字 */
|
||||||
disabled?: boolean; // 是否禁用
|
label: string;
|
||||||
onClick?: (button: OptButton) => void; // 点击回调
|
/** SVG 图标(内联 SVG 字符串) */
|
||||||
children?: ButtonConfig[]; // 子按钮配置(可选,用于菜单按钮)
|
icon?: string;
|
||||||
|
/** 是否保持激活状态(默认 false) */
|
||||||
|
keepActive?: boolean;
|
||||||
|
/** 是否禁用 */
|
||||||
|
disabled?: boolean;
|
||||||
|
/** 点击回调函数 */
|
||||||
|
onClick?: (button: OptButton) => void;
|
||||||
|
/** 子按钮配置(可选,用于菜单按钮) */
|
||||||
|
children?: ButtonConfig[];
|
||||||
|
|
||||||
groupId?: string; // 所属组ID
|
/** 所属组ID */
|
||||||
parentId?: string; // 父按钮ID(如果是子按钮)
|
groupId?: string;
|
||||||
|
/** 父按钮ID(如果是子按钮,则必填) */
|
||||||
|
parentId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作按钮接口(内部使用,继承配置)
|
* 操作按钮接口(内部使用,继承配置)
|
||||||
*/
|
*/
|
||||||
export interface OptButton extends ButtonConfig {
|
export interface OptButton extends ButtonConfig {
|
||||||
children?: OptButton[]; // 内部使用的子按钮列表
|
/** 内部使用的子按钮列表 */
|
||||||
|
children?: OptButton[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按钮组接口
|
* 按钮组接口
|
||||||
*/
|
*/
|
||||||
export interface ButtonGroup {
|
export interface ButtonGroup {
|
||||||
|
/** 组 ID */
|
||||||
id: string;
|
id: string;
|
||||||
|
/** 组内按钮列表 */
|
||||||
buttons: OptButton[];
|
buttons: OptButton[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具栏颜色配置接口
|
||||||
|
*/
|
||||||
|
export interface ToolbarColors {
|
||||||
|
/** 工具栏背景颜色 */
|
||||||
|
backgroundColor?: string;
|
||||||
|
/** 按钮默认背景颜色 */
|
||||||
|
btnBackgroundColor?: string;
|
||||||
|
/** 按钮 Hover 背景颜色 */
|
||||||
|
btnHoverColor?: string;
|
||||||
|
/** 按钮激活状态背景颜色 */
|
||||||
|
btnActiveColor?: string;
|
||||||
|
/** 图标默认颜色 */
|
||||||
|
iconColor?: string;
|
||||||
|
/** 图标激活/Hover 颜色 */
|
||||||
|
iconActiveColor?: string;
|
||||||
|
/** 文字默认颜色 */
|
||||||
|
textColor?: string;
|
||||||
|
/** 文字激活/Hover 颜色 */
|
||||||
|
textActiveColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OptBtnGroups 配置选项
|
* OptBtnGroups 配置选项
|
||||||
*/
|
*/
|
||||||
export interface OptBtnGroupsOptions {
|
export interface OptBtnGroupsOptions extends ToolbarColors {
|
||||||
|
/** 容器元素或 ID */
|
||||||
container: HTMLElement | string;
|
container: HTMLElement | string;
|
||||||
|
/** 是否显示标签 */
|
||||||
showLabel?: boolean;
|
showLabel?: boolean;
|
||||||
|
/** 按钮可见性配置 Map */
|
||||||
visibility?: Record<string, boolean>;
|
visibility?: Record<string, boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +83,10 @@ export interface OptBtnGroupsOptions {
|
|||||||
* 点击事件载荷
|
* 点击事件载荷
|
||||||
*/
|
*/
|
||||||
export interface ClickPayload {
|
export interface ClickPayload {
|
||||||
|
/** 被点击的按钮对象 */
|
||||||
button: OptButton;
|
button: OptButton;
|
||||||
|
/** 触发的动作类型 */
|
||||||
action: 'activate' | 'deactivate' | 'trigger';
|
action: 'activate' | 'deactivate' | 'trigger';
|
||||||
|
/** 当前激活状态 */
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user