diff --git a/demo/index.html b/demo/index.html
index 5915251..d9aa954 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -6,23 +6,163 @@
BIM Engine SDK Demo
+
-
+
+
+
+
+
+
+
diff --git a/dist/bim-engine-sdk.es.js b/dist/bim-engine-sdk.es.js
index d6c1d92..001b129 100644
--- a/dist/bim-engine-sdk.es.js
+++ b/dist/bim-engine-sdk.es.js
@@ -1,365 +1,580 @@
-(function(){"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.appendChild(document.createTextNode('.bim-engine-wrapper{position:relative;width:100%;height:100%;font-family:sans-serif;color:#333;padding:20px;background-color:#e16969;border-radius:8px;border:1px solid #e0e0e0;box-sizing:border-box}.bim-engine-opt-btn-container{position:absolute;bottom:20px;left:50%;transform:translate(-50%);z-index:100}:root{--bim-toolbar-bg: rgba(17, 17, 17, .88);--bim-btn-bg: transparent;--bim-btn-hover-bg: #444;--bim-btn-active-bg: rgba(255, 255, 255, .15);--bim-icon-color: #ccc;--bim-icon-active-color: #fff;--bim-btn-text-color: #ccc;--bim-btn-text-active-color: #fff}.toolbar-container{display:flex;align-items:center;max-width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.toolbar-container::-webkit-scrollbar{display:none}.opt-btn-group{overflow:hidden;display:flex;align-items:center;flex-shrink:0;background-color:var(--bim-toolbar-bg);border-radius:4px;padding:4px 8px}.has-divider{margin-right:16px}.opt-btn-wrapper{position:relative}.opt-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;width:50px;min-height:50px;padding:4px;cursor:pointer;background-color:var(--bim-btn-bg);color:var(--bim-icon-color);transition:all .2s;border-bottom:2px solid transparent}.opt-btn:hover{background-color:var(--bim-btn-hover-bg);color:var(--bim-icon-active-color)}.opt-btn.active{background-color:var(--bim-btn-active-bg);color:var(--bim-icon-active-color);border-bottom:2px solid #fff}.opt-btn.disabled{opacity:.5;cursor:not-allowed}.opt-btn-icon{width:32px;height:32px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.opt-btn-icon svg{width:100%;height:100%}.opt-btn-label{font-size:10px;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{font-size:8px;position:absolute;top:2px;right:2px;opacity:.6;transition:transform .2s ease}.opt-btn-arrow.rotated{transform:rotate(180deg)}.opt-btn.no-label .opt-btn-arrow{top:2px;right:2px}.opt-btn-dropdown{position:fixed;transform:translate(-50%,-100%);background-color:var(--bim-toolbar-bg);border-radius:4px;overflow:hidden;box-shadow:0 4px 12px #0000004d;min-width:50px;z-index:9999;display:flex;flex-direction:column}.opt-btn-dropdown-item{display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--bim-icon-color);cursor:pointer;transition:background .2s;white-space:nowrap;min-width:50px;min-height:50px;padding:4px;background-color:var(--bim-btn-bg)}.opt-btn-dropdown-item:hover{background-color:var(--bim-btn-hover-bg);color:var(--bim-icon-active-color)}.opt-btn-dropdown-item .opt-btn-icon.small{width:30px;height:30px;margin-right:0;margin-bottom:4px}.opt-btn-dropdown-item span{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{width:32px;height:32px}:root{--bim-dialog-bg: rgba(17, 17, 17, .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 #0000004d;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;-webkit-user-select:none;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}.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}')),document.head.appendChild(o)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})();
-class f {
- /** 挂载容器 */
- container;
- /** 组件配置选项 */
- options;
- /** 按钮组列表,按顺序存储 */
- groups = [];
- /** 当前处于激活状态的按钮 ID 集合 */
- activeBtnIds = /* @__PURE__ */ new Set();
- /** 按钮 DOM 元素的引用映射,方便快速查找 */
- btnRefs = /* @__PURE__ */ new Map();
- /** 当��显示的下拉菜单元素 */
- dropdownElement = null;
- /** 鼠标悬停计时器,用于处理菜单显示的防抖 */
- hoverTimeout = null;
- /** 默认图标 SVG */
- DEFAULT_ICON = ' ';
+(function(){"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.appendChild(document.createTextNode('.bim-engine-wrapper{position:relative;width:100%;height:100%;font-family:sans-serif;color:#333;padding:20px;background-color:#e16969;border-radius:8px;border:1px solid #e0e0e0;box-sizing:border-box}.bim-engine-opt-btn-container{position:absolute;bottom:20px;left:50%;transform:translate(-50%);z-index:100}.bim-btn-group-root{display:flex;gap:8px;z-index:1000;position:absolute;pointer-events:auto}.bim-btn-group-root.static{position:relative;inset:auto;transform:none}.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;gap:4px;background-color:var(--bim-btn-group-section-bg, rgba(17, 17, 17, .88));border-radius:6px;padding:4px}.bim-btn-group-root.dir-row .bim-btn-group-section{flex-direction:row;align-items:center}.bim-btn-group-root.dir-column .bim-btn-group-section{flex-direction:column}.opt-btn-wrapper{position:relative}.opt-btn{display:flex;cursor:pointer;border-radius:4px;transition:background-color .2s,color .2s;color:var(--bim-btn-text-color, #ccc);background-color:var(--bim-btn-bg, transparent);padding:6px;align-items:center;position:relative;justify-content:center}.opt-btn:hover{background-color:var(--bim-btn-hover-bg, #444)}.opt-btn.active{background-color:var(--bim-btn-active-bg, rgba(255, 255, 255, .15));color:var(--bim-btn-text-active-color, #fff)}.opt-btn.active .opt-btn-icon{color:var(--bim-icon-active-color, #fff)}.opt-btn.disabled{opacity:.5;cursor:not-allowed}.opt-btn-icon{width:var(--bim-icon-size, 24px);height:var(--bim-icon-size, 24px);display:flex;align-items:center;justify-content:center;color:var(--bim-icon-color, #ccc);flex-shrink:0}.opt-btn-icon svg{width:100%;height:100%;fill:currentColor}.opt-btn-arrow{font-size:10px;opacity:.6;transition:transform .2s;display:inline-block;margin-left:4px}.opt-btn-arrow.rotated{transform:rotate(180deg)}.opt-btn-text-wrapper{display: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:not(.no-label){flex-direction:column;text-align:center}.opt-btn.align-vertical:not(.no-label) .opt-btn-text-wrapper{margin-top:4px}.opt-btn.align-vertical:not(.no-label) .opt-btn-label{font-size:12px;line-height:1.2}.opt-btn.align-horizontal:not(.no-label){flex-direction:row}.opt-btn.align-horizontal:not(.no-label) .opt-btn-text-wrapper{margin-left:8px}.opt-btn.align-horizontal:not(.no-label) .opt-btn-label{font-size:14px}.opt-btn.no-label .opt-btn-text-wrapper{width:0;height:0;margin:0;padding:0;overflow:visible;position:absolute;top:0;right:0}.opt-btn.no-label .opt-btn-arrow{position:absolute;top:2px;right:2px;margin:0;font-size:8px}.opt-btn-dropdown{position:absolute;background-color:var(--bim-toolbar-bg, rgba(17, 17, 17, .95));border-radius:4px;padding:4px;box-shadow:0 4px 12px #0003;z-index:1001;display:flex;flex-direction:column;border:1px solid rgba(255,255,255,.1);opacity:0;visibility:hidden;transform:translateY(-10px);transition:opacity .2s ease,transform .2s cubic-bezier(.2,0,.2,1),visibility .2s}@keyframes dropdown-fade-in{0%{opacity:0;transform:translateY(-8px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.opt-btn-dropdown{animation:dropdown-fade-in .2s cubic-bezier(.2,0,.2,1) forwards;opacity:1;visibility:visible;transform:none}.opt-btn-dropdown-item{display:flex;align-items:center;padding:8px 12px;cursor:pointer;border-radius:4px;color:var(--bim-btn-text-color, #ccc);transition:background .2s;box-sizing:border-box}.opt-btn-dropdown-item:hover{background-color:var(--bim-btn-hover-bg, #444);color:#fff}.opt-btn-dropdown-item.align-horizontal{flex-direction:row}.opt-btn-dropdown-item.align-horizontal .opt-btn-icon{width:18px;height:18px;margin-right:8px}.opt-btn-dropdown-item.align-vertical{flex-direction:column;text-align:center}.opt-btn-dropdown-item.align-vertical .opt-btn-icon{width:24px;height:24px;margin-bottom:4px}.opt-btn-dropdown-item.align-vertical .opt-btn-dropdown-label{font-size:12px}.bim-btn-group-root.is-bottom-toolbar .opt-btn-icon{width:32px;height:32px}.bim-btn-group-root.is-bottom-toolbar .opt-btn{padding:8px}:root{--bim-dialog-bg: rgba(17, 17, 17, .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 #0000004d;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;-webkit-user-select:none;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}.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}')),document.head.appendChild(o)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})();
+const w = {
+ common: {
+ title: "BimEngine",
+ description: "这是一个使用 BIM-ENGINE。",
+ openTestDialog: "打开测试弹窗",
+ openInfoDialog: "打开信息弹窗 (封装版)"
+ },
+ toolbar: {
+ home: "首页",
+ info: "信息",
+ location: "定位",
+ setting: "设置",
+ walk: "漫游",
+ walkPerson: "人视",
+ walkBird: "鸟瞰",
+ walkMenu: "菜单"
+ },
+ dialog: {
+ testTitle: "测试弹窗",
+ testContent: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
'
+ }
+}, L = {
+ common: {
+ title: "BimEngine",
+ description: "This is a BIM-ENGINE demo.",
+ openTestDialog: "Open Test Dialog",
+ openInfoDialog: "Open Info Dialog (Wrapped)"
+ },
+ toolbar: {
+ home: "Home",
+ info: "Info",
+ location: "Location",
+ setting: "Settings",
+ walk: "Walk",
+ walkPerson: "Person",
+ walkBird: "Bird Eye",
+ walkMenu: "Menu"
+ },
+ dialog: {
+ testTitle: "Test Dialog",
+ testContent: 'This is a draggable and resizable dialog. Try dragging the title bar or resizing from the bottom-right corner.
'
+ }
+};
+class T {
+ currentLocale = "zh-CN";
+ messages = {
+ "zh-CN": w,
+ "en-US": L
+ };
+ listeners = [];
+ constructor() {
+ }
/**
- * 构造函数
- * @param options 配置选项
+ * 获取当前语言
*/
+ getLocale() {
+ return this.currentLocale;
+ }
+ /**
+ * 切换语言
+ */
+ setLocale(t) {
+ this.currentLocale !== t && (this.currentLocale = t, this.notifyListeners());
+ }
+ /**
+ * 翻译核心方法
+ */
+ t(t) {
+ if (!t) return "";
+ const e = t.split(".");
+ let o = this.messages[this.currentLocale];
+ for (const i of e)
+ if (o && typeof o == "object" && i in o)
+ o = o[i];
+ else
+ return t;
+ return o;
+ }
+ /**
+ * 订阅变更
+ */
+ subscribe(t) {
+ return this.listeners.push(t), () => {
+ this.listeners = this.listeners.filter((e) => e !== t);
+ };
+ }
+ notifyListeners() {
+ this.listeners.forEach((t) => t(this.currentLocale));
+ }
+}
+const p = new T(), b = (c) => p.t(c), g = {
+ name: "dark",
+ primary: "#0078d4",
+ primaryHover: "#0063b1",
+ // 修改:背景色统一为浅灰,不再跟随深色模式变黑
+ background: "#f5f5f5",
+ panelBackground: "rgba(30, 30, 30, 0.9)",
+ // 注意:如果背景是浅色,主文字颜色通常需要是深色才能看清
+ // 但这里的 textPrimary 主要是用于 UI 组件内部的。
+ // 如果 BimEngine wrapper 上的文字直接显示在 background 上,
+ // 我们可能需要区分 "UI文字" 和 "页面文字"。
+ // 目前架构中:
+ // theme.textPrimary 会应用到 wrapper.style.color (BimEngine.ts)
+ // 以及 Toolbar/Dialog 的文字颜色。
+ // 如果背景是浅灰,而 wrapper 文字设置为白色 (#ffffff),那就看不清了。
+ // 这是一个语义冲突:
+ // 1. Panel (Toolbar/Dialog) 是黑底,需要白字。
+ // 2. Background (Wrapper) 是白底,需要黑字。
+ // 既然您要求背景统一浅灰,那么 Wrapper 上的“直接子文本”应该是深色。
+ // 但 Toolbar/Dialog 仍然是深色模式(黑底),它们需要白字。
+ // 妥协方案:
+ // 保持 textPrimary 为白色(为了适配黑���的 Toolbar/Dialog)。
+ // 但是在 BimEngine 中,如果背景强制改为浅色,Wrapper 的默认文字颜色可能需要单独处理,
+ // 或者我们可以认为 "Wrapper" 主要是承载 UI 组件的,直接写在 Wrapper 上的文字(标题/描述)
+ // 应该有自己的样式,而不是直接继承 theme.textPrimary。
+ // 在之前的 BimEngine.ts 中:
+ // this.wrapper.style.color = theme.textPrimary;
+ // 如果背景变浅灰,这里 textPrimary 还是白色的话,标题就看不见了。
+ // 所以,深色模式下:
+ // 背景:浅灰
+ // 组件:深黑
+ // 组件文字:白
+ // 页面文字:黑 (问题点)
+ // 让我们先按您的要求改背景。通常这种情况下,ThemeConfig 可能需要区分
+ // contentText (页面内容文字) 和 uiText (组件文字)。
+ // 但为了不破坏现有结构,我将假定 textPrimary 主要服务于 UI 组件。
+ // 为了让 Wrapper 上的标题可见,我们可能需要在 BimEngine 中移除对 wrapper.style.color 的强制设置,
+ // 或者在 presets 里把 textPrimary 改回来?不对,改回来 Toolbar 就看不清了。
+ // 方案:我将仅修改 background。
+ // 至于 Wrapper 上的标题(BimEngine 标题),由于在最新的 BimEngine.ts 中
+ // ���们已经移除了 titleEl 和 descEl(在之前的重构中),
+ // 所以现在 Wrapper 里主要是 Toolbar 和 Dialog,它们有自己的 panelBackground。
+ // 只要 Toolbar/Dialog 内部正常即可。
+ textPrimary: "#ffffff",
+ textSecondary: "#cccccc",
+ border: "#444444",
+ icon: "#cccccc",
+ iconActive: "#ffffff",
+ componentBackground: "transparent",
+ componentHover: "#333333",
+ componentActive: "rgba(255, 255, 255, 0.1)"
+}, x = {
+ name: "light",
+ primary: "#0078d4",
+ primaryHover: "#106ebe",
+ // 统一为浅灰
+ background: "#f5f5f5",
+ panelBackground: "#ffffff",
+ textPrimary: "#333333",
+ textSecondary: "#666666",
+ border: "#e0e0e0",
+ icon: "#555555",
+ iconActive: "#0078d4",
+ componentBackground: "transparent",
+ componentHover: "#f0f0f0",
+ componentActive: "#e0e0e0"
+};
+class E {
+ currentTheme = g;
+ listeners = [];
+ constructor() {
+ }
+ /**
+ * 获取当前主题配置
+ */
+ getTheme() {
+ return this.currentTheme;
+ }
+ /**
+ * 切换预设主题
+ * @param themeName 'dark' | 'light'
+ */
+ setTheme(t) {
+ t === "light" ? this.applyTheme(x) : this.applyTheme(g);
+ }
+ /**
+ * 应用自定义主题配置
+ * @param theme 配置对象
+ */
+ setCustomTheme(t) {
+ this.applyTheme(t);
+ }
+ /**
+ * 内部应用主题逻辑
+ */
+ applyTheme(t) {
+ this.currentTheme = t, this.notifyListeners();
+ }
+ /**
+ * 订阅主题变更
+ */
+ subscribe(t) {
+ return this.listeners.push(t), t(this.currentTheme), () => {
+ this.listeners = this.listeners.filter((e) => e !== t);
+ };
+ }
+ notifyListeners() {
+ this.listeners.forEach((t) => t(this.currentTheme));
+ }
+}
+const d = new E();
+class v {
+ container;
+ options;
+ groups = [];
+ activeBtnIds = /* @__PURE__ */ new Set();
+ btnRefs = /* @__PURE__ */ new Map();
+ dropdownElement = null;
+ hoverTimeout = null;
+ customColors = /* @__PURE__ */ new Set();
+ // 记录用户自定义的颜色属性
+ unsubscribeLocale = null;
+ unsubscribeTheme = null;
+ DEFAULT_ICON = ' ';
constructor(t) {
- const o = typeof t.container == "string" ? document.getElementById(t.container) : t.container;
- if (!o) throw new Error("Container not found");
- this.container = o, this.options = {
+ const e = typeof t.container == "string" ? document.getElementById(t.container) : t.container;
+ if (!e) throw new Error("Container not found");
+ this.container = e, this.options = {
showLabel: !0,
visibility: {},
+ direction: "row",
+ // 默认横向
+ position: "static",
+ // 默认静态定位
+ align: "vertical",
+ // 默认图标在上
+ expand: "down",
+ // 默认向下展开
...t
- }, this.initContainer(), this.applyStyles();
+ }, [
+ "backgroundColor",
+ "btnBackgroundColor",
+ "btnHoverColor",
+ "btnActiveColor",
+ "iconColor",
+ "iconActiveColor",
+ "textColor",
+ "textActiveColor"
+ ].forEach((i) => {
+ t[i] && this.customColors.add(i);
+ }), this.initContainer(), this.applyStyles();
}
- /**
- * 初始化容器
- */
initContainer() {
- this.container.innerHTML = "", this.container.classList.add("toolbar-root");
+ this.container.innerHTML = "", this.container.classList.add("bim-btn-group-root"), this.options.direction === "column" ? this.container.classList.add("dir-column") : this.container.classList.add("dir-row"), this.options.className && this.container.classList.add(this.options.className), this.updatePosition();
+ }
+ updatePosition() {
+ const t = this.options.position, e = this.container.style;
+ if (e.top = "", e.bottom = "", e.left = "", e.right = "", e.transform = "", t === "static") {
+ this.container.classList.add("static");
+ return;
+ }
+ if (this.container.classList.remove("static"), this.container.style.position = "absolute", typeof t == "object" && "x" in t)
+ e.left = `${t.x}px`, e.top = `${t.y}px`;
+ else {
+ const o = "20px";
+ switch (t) {
+ case "top-left":
+ e.top = o, e.left = o;
+ break;
+ case "top-center":
+ e.top = o, e.left = "50%", e.transform = "translateX(-50%)";
+ break;
+ case "top-right":
+ e.top = o, e.right = o;
+ break;
+ case "bottom-left":
+ e.bottom = o, e.left = o;
+ break;
+ case "bottom-center":
+ e.bottom = o, e.left = "50%", e.transform = "translateX(-50%)";
+ break;
+ case "bottom-right":
+ e.bottom = o, e.right = o;
+ break;
+ case "left-center":
+ e.left = o, e.top = "50%", e.transform = "translateY(-50%)";
+ break;
+ case "right-center":
+ e.right = o, e.top = "50%", e.transform = "translateY(-50%)";
+ break;
+ case "center":
+ e.top = "50%", e.left = "50%", e.transform = "translate(-50%, -50%)";
+ break;
+ }
+ }
}
/**
- * 应用样式配置到 CSS 变量
+ * 应用样式到容器
*/
applyStyles() {
const t = this.container.style;
- this.options.backgroundColor && t.setProperty("--bim-toolbar-bg", this.options.backgroundColor), this.options.btnBackgroundColor && t.setProperty("--bim-btn-bg", this.options.btnBackgroundColor), this.options.btnHoverColor && t.setProperty("--bim-btn-hover-bg", this.options.btnHoverColor), this.options.btnActiveColor && t.setProperty("--bim-btn-active-bg", this.options.btnActiveColor), this.options.iconColor && t.setProperty("--bim-icon-color", this.options.iconColor), this.options.iconActiveColor && t.setProperty("--bim-icon-active-color", this.options.iconActiveColor), this.options.textColor && t.setProperty("--bim-btn-text-color", this.options.textColor), this.options.textActiveColor && t.setProperty("--bim-btn-text-active-color", this.options.textActiveColor);
+ this.options.backgroundColor && t.setProperty("--bim-btn-group-section-bg", this.options.backgroundColor), this.options.btnBackgroundColor && t.setProperty("--bim-btn-bg", this.options.btnBackgroundColor), this.options.btnHoverColor && t.setProperty("--bim-btn-hover-bg", this.options.btnHoverColor), this.options.btnActiveColor && t.setProperty("--bim-btn-active-bg", this.options.btnActiveColor), this.options.iconColor && t.setProperty("--bim-icon-color", this.options.iconColor), this.options.iconActiveColor && t.setProperty("--bim-icon-active-color", this.options.iconActiveColor), this.options.textColor && t.setProperty("--bim-btn-text-color", this.options.textColor), this.options.textActiveColor && t.setProperty("--bim-btn-text-active-color", this.options.textActiveColor);
}
/**
- * 更新颜色配置
- * @param colors 颜色配置对象
+ * 设置主题颜色
+ * 只会应用到没有被用户自定义的颜色属性上
+ */
+ setTheme(t) {
+ const e = {
+ backgroundColor: t.panelBackground,
+ btnBackgroundColor: t.componentBackground,
+ btnHoverColor: t.componentHover,
+ btnActiveColor: t.componentActive,
+ iconColor: t.icon,
+ iconActiveColor: t.iconActive,
+ textColor: t.textSecondary,
+ textActiveColor: t.textPrimary
+ };
+ Object.entries(e).forEach(([o, i]) => {
+ const s = o;
+ this.customColors.has(s) || (this.options[s] = i);
+ }), this.applyStyles();
+ }
+ /**
+ * 直接设置颜色(强制覆盖)
+ * 设置的颜色会被标记为自定义,后续的 setTheme 不会覆盖它们
*/
setColors(t) {
- this.options = { ...this.options, ...t }, this.applyStyles();
+ this.options = { ...this.options, ...t }, Object.keys(t).forEach((e) => {
+ this.customColors.add(e);
+ }), this.applyStyles();
}
- /**
- * 添加按钮组
- * @param groupId 组ID
- * @param beforeGroupId 在哪个组之前插入(可选���,不传则插入到最后
- */
- addGroup(t, o) {
- if (this.groups.some((i) => i.id === t)) {
- console.warn("Group " + t + " already exists");
- return;
- }
- const e = { id: t, buttons: [] };
- if (o) {
- const i = this.groups.findIndex((n) => n.id === o);
- i !== -1 ? this.groups.splice(i, 0, e) : (console.warn(`Target group ${o} not found, appending ${t} to end.`), this.groups.push(e));
- } else
- this.groups.push(e);
+ async init() {
+ this.render(), this.unsubscribeLocale = p.subscribe(() => {
+ this.setLocales();
+ }), this.unsubscribeTheme = d.subscribe((t) => {
+ this.setTheme(t);
+ });
}
- /**
- * 添加按钮到指定组
- * @param config 按钮配置(必须包含 groupId,可选包含 parentId)
- */
- addButton(t) {
- const { groupId: o, parentId: e } = t;
- if (!o)
- throw new Error(`Button ${t.id} config must contain 'groupId'`);
- const i = this.groups.find((s) => s.id === o);
- if (!i)
- throw new Error(`Group ${o} not found. Please call addGroup first.`);
- const n = {
- ...t,
- children: t.children || []
- };
+ setLocales() {
+ this.render();
+ }
+ addGroup(t, e) {
+ if (this.groups.some((i) => i.id === t)) return;
+ const o = { id: t, buttons: [] };
if (e) {
- const s = this.findButton(i.buttons, e);
- if (!s)
- throw new Error(`Parent button ${e} not found in group ${o}`);
- s.children || (s.children = []), s.children.push(n);
+ const i = this.groups.findIndex((s) => s.id === e);
+ i !== -1 ? this.groups.splice(i, 0, o) : this.groups.push(o);
} else
- i.buttons.push(n);
+ this.groups.push(o);
}
- /**
- * 递归查找按钮
- */
- findButton(t, o) {
- for (const e of t) {
- if (e.id === o) return e;
- if (e.children) {
- const i = this.findButton(e.children, o);
+ addButton(t) {
+ const { groupId: e, parentId: o } = t, i = this.groups.find((n) => n.id === e);
+ if (!i) return;
+ const s = { ...t, children: t.children || [] };
+ if (o) {
+ const n = this.findButton(i.buttons, o);
+ n && (n.children || (n.children = []), n.children.push(s));
+ } else
+ i.buttons.push(s);
+ }
+ findButton(t, e) {
+ for (const o of t) {
+ if (o.id === e) return o;
+ if (o.children) {
+ const i = this.findButton(o.children, e);
if (i) return i;
}
}
}
- /**
- * 初始化组件,加载默认按钮配置
- */
- async init() {
- const { homeButton: t } = await import("./index-CAJWny5G.mjs"), { locationButton: o } = await import("./index-C12x1apF.mjs"), { walkMenuButton: e } = await import("./index-Wpi9Br9A.mjs"), { walkPersonButton: i } = await import("./index-BXbORK0j.mjs"), { walkBirdButton: n } = await import("./index-Djlk5GIH.mjs"), { settingButton: s } = await import("./index-DsRG5l_h.mjs"), { infoButton: r } = await import("./index-DvZ5eiUH.mjs");
- this.addGroup("group-1"), this.addButton(t), this.addButton(e), this.addButton(i), this.addButton(n), this.addButton(o), this.addGroup("group-2"), this.addButton(s), this.addButton(r), this.render();
- }
- /**
- * 渲染整个工具栏
- */
render() {
- this.container.innerHTML = "", this.btnRefs.clear();
- const t = document.createElement("div");
- t.className = "toolbar-container", this.groups.forEach((o, e) => {
- const i = this.renderGroup(o, e, this.groups.length);
- t.appendChild(i);
- }), this.container.appendChild(t);
+ this.container.innerHTML = "", this.btnRefs.clear(), this.groups.forEach((t, e) => {
+ const o = this.renderGroup(t, e, this.groups.length);
+ this.container.appendChild(o);
+ });
}
- /**
- * 渲染单个按钮组
- */
- renderGroup(t, o, e) {
+ renderGroup(t, e, o) {
const i = document.createElement("div");
- return i.className = "opt-btn-group", o < e - 1 && i.classList.add("has-divider"), t.buttons.forEach((n) => {
- if (this.isVisible(n.id)) {
- const s = this.renderButton(n);
- i.appendChild(s);
+ return i.className = "bim-btn-group-section", e < o - 1 && i.classList.add("has-divider"), t.buttons.forEach((s) => {
+ if (this.isVisible(s.id)) {
+ const n = this.renderButton(s);
+ i.appendChild(n);
}
}), i;
}
- /**
- * 渲染单个按钮
- */
renderButton(t) {
- const o = document.createElement("div");
- o.className = "opt-btn-wrapper";
const e = document.createElement("div");
- e.className = "opt-btn", this.activeBtnIds.has(t.id) && e.classList.add("active"), t.disabled && e.classList.add("disabled"), this.options.showLabel || (e.classList.add("no-label"), t.label && (e.title = t.label));
- const i = document.createElement("div");
- if (i.className = "opt-btn-icon", i.innerHTML = this.getIcon(t.icon), e.appendChild(i), this.options.showLabel && t.label) {
- const n = document.createElement("span");
- n.className = "opt-btn-label", n.textContent = t.label, e.appendChild(n);
+ e.className = "opt-btn-wrapper";
+ const o = document.createElement("div");
+ o.className = "opt-btn", (t.align || this.options.align || "vertical") === "horizontal" ? o.classList.add("align-horizontal") : o.classList.add("align-vertical"), this.activeBtnIds.has(t.id) && o.classList.add("active"), t.disabled && o.classList.add("disabled"), this.options.showLabel && t.label || o.classList.add("no-label");
+ const n = t.iconSize || 32, r = t.minWidth || 50;
+ o.style.minWidth = `${r}px`;
+ const a = document.createElement("div");
+ a.className = "opt-btn-icon", a.style.width = `${n}px`, a.style.height = `${n}px`, a.innerHTML = this.getIcon(t.icon), o.appendChild(a);
+ const l = document.createElement("div");
+ if (l.className = "opt-btn-text-wrapper", this.options.showLabel && t.label) {
+ const h = document.createElement("span");
+ h.className = "opt-btn-label", h.textContent = b(t.label), l.appendChild(h);
}
if (t.children && t.children.length > 0) {
- const n = document.createElement("span");
- n.className = "opt-btn-arrow", n.textContent = "▼", e.appendChild(n);
+ const h = document.createElement("span");
+ h.className = "opt-btn-arrow", h.textContent = "▼", l.appendChild(h);
}
- return e.addEventListener("click", () => this.handleClick(t)), e.addEventListener("mouseenter", () => this.handleMouseEnter(t, e)), e.addEventListener("mouseleave", () => this.handleMouseLeave()), this.btnRefs.set(t.id, e), o.appendChild(e), o;
+ return l.hasChildNodes() && o.appendChild(l), o.addEventListener("click", () => this.handleClick(t)), o.addEventListener("mouseenter", () => this.handleMouseEnter(t, o)), o.addEventListener("mouseleave", () => this.handleMouseLeave()), this.btnRefs.set(t.id, o), e.appendChild(o), e;
}
- /**
- * 处理按钮点击事件
- */
handleClick(t) {
t.disabled || (!t.children || t.children.length === 0) && (t.keepActive && (this.activeBtnIds.has(t.id) ? this.activeBtnIds.delete(t.id) : this.activeBtnIds.add(t.id), this.updateButtonState(t.id)), this.closeDropdown(), t.onClick && t.onClick(t));
}
- /**
- * 处理子菜单项点击事件
- */
- handleSubClick(t) {
- t.keepActive && (this.activeBtnIds.has(t.id) ? this.activeBtnIds.delete(t.id) : this.activeBtnIds.add(t.id), this.updateButtonState(t.id)), this.closeDropdown(), t.onClick && t.onClick(t);
+ handleMouseEnter(t, e) {
+ this.hoverTimeout && clearTimeout(this.hoverTimeout), t.children && t.children.length > 0 ? this.showDropdown(t, e) : this.closeDropdown();
}
- /**
- * 处理鼠标移入事件(显示菜单)
- */
- handleMouseEnter(t, o) {
- if (this.hoverTimeout && (clearTimeout(this.hoverTimeout), this.hoverTimeout = null), t.children && t.children.length > 0) {
- this.showDropdown(t, o);
- const e = o.querySelector(".opt-btn-arrow");
- e && e.classList.add("rotated");
- } else
- this.closeDropdown();
- }
- /**
- * 处理鼠标移出事件(隐藏菜单)
- */
handleMouseLeave() {
- this.hoverTimeout = window.setTimeout(() => {
- this.closeDropdown();
- }, 200);
+ this.hoverTimeout = window.setTimeout(() => this.closeDropdown(), 200);
}
- /**
- * 显示下拉菜单
- */
- showDropdown(t, o) {
+ showDropdown(t, e) {
if (this.closeDropdown(), !t.children) return;
- const e = document.createElement("div");
- e.className = "opt-btn-dropdown";
- const i = e.style;
- this.options.backgroundColor && i.setProperty("--bim-toolbar-bg", this.options.backgroundColor), this.options.btnBackgroundColor && i.setProperty("--bim-btn-bg", this.options.btnBackgroundColor), this.options.btnHoverColor && i.setProperty("--bim-btn-hover-bg", this.options.btnHoverColor), this.options.btnActiveColor && i.setProperty("--bim-btn-active-bg", this.options.btnActiveColor), this.options.iconColor && i.setProperty("--bim-icon-color", this.options.iconColor), this.options.iconActiveColor && i.setProperty("--bim-icon-active-color", this.options.iconActiveColor), this.options.textColor && i.setProperty("--bim-btn-text-color", this.options.textColor), this.options.textActiveColor && i.setProperty("--bim-btn-text-active-color", this.options.textActiveColor);
- const n = o.getBoundingClientRect(), s = n.left + n.width / 2;
- e.style.top = n.top - 8 + "px", e.style.left = s + "px", t.children.forEach((r) => {
+ const o = document.createElement("div");
+ o.className = "opt-btn-dropdown", this.options.backgroundColor && o.style.setProperty("--bim-toolbar-bg", this.options.backgroundColor);
+ const i = e.getBoundingClientRect(), s = this.options.expand || "down";
+ this.options.direction === "row" ? o.style.flexDirection = "column" : o.style.flexDirection = "row", document.body.appendChild(o), t.children.forEach((r) => {
if (this.isVisible(r.id)) {
const a = this.renderDropdownItem(r);
- e.appendChild(a);
+ o.appendChild(a);
}
- }), e.addEventListener("mouseenter", () => {
- this.hoverTimeout && (clearTimeout(this.hoverTimeout), this.hoverTimeout = null);
- }), e.addEventListener("mouseleave", () => this.handleMouseLeave()), document.body.appendChild(e), this.dropdownElement = e;
+ });
+ const n = o.getBoundingClientRect();
+ s === "up" ? (o.style.bottom = window.innerHeight - i.top + 8 + "px", o.style.left = i.left + (i.width - n.width) / 2 + "px") : s === "down" ? (o.style.top = i.bottom + 8 + "px", o.style.left = i.left + (i.width - n.width) / 2 + "px") : s === "right" ? (o.style.top = i.top + (i.height - n.height) / 2 + "px", o.style.left = i.right + 8 + "px") : s === "left" && (o.style.top = i.top + (i.height - n.height) / 2 + "px", o.style.right = window.innerWidth - i.left + 8 + "px"), o.addEventListener("mouseenter", () => {
+ this.hoverTimeout && clearTimeout(this.hoverTimeout);
+ }), o.addEventListener("mouseleave", () => this.handleMouseLeave()), this.dropdownElement = o;
}
- /**
- * 渲染下拉菜单项
- */
renderDropdownItem(t) {
- const o = document.createElement("div");
- o.className = "opt-btn-dropdown-item";
const e = document.createElement("div");
- if (e.className = "opt-btn-icon small", e.innerHTML = this.getIcon(t.icon), o.appendChild(e), this.options.showLabel) {
- const i = document.createElement("span");
- i.textContent = t.label, o.appendChild(i);
+ e.className = "opt-btn-dropdown-item", (t.align || "horizontal") === "horizontal" ? e.classList.add("align-horizontal") : e.classList.add("align-vertical");
+ const i = t.iconSize || 32, s = t.minWidth;
+ s && (e.style.minWidth = `${s}px`);
+ const n = document.createElement("div");
+ if (n.className = "opt-btn-icon", n.style.width = `${i}px`, n.style.height = `${i}px`, n.innerHTML = this.getIcon(t.icon), e.appendChild(n), this.options.showLabel && t.label) {
+ const r = document.createElement("span");
+ r.className = "opt-btn-dropdown-label", r.textContent = b(t.label), e.appendChild(r);
}
- return o.addEventListener("click", (i) => {
- i.stopPropagation(), this.handleSubClick(t);
- }), o;
+ return e.addEventListener("click", (r) => {
+ r.stopPropagation(), this.handleClick(t);
+ }), e;
}
- /**
- * 关闭所有下拉菜单
- */
closeDropdown() {
this.dropdownElement && (this.dropdownElement.remove(), this.dropdownElement = null), this.btnRefs.forEach((t) => {
- const o = t.querySelector(".opt-btn-arrow");
- o && o.classList.remove("rotated");
+ const e = t.querySelector(".opt-btn-arrow");
+ e && e.classList.remove("rotated");
});
}
- /**
- * 更新按钮的激活状态样式
- */
updateButtonState(t) {
- const o = this.btnRefs.get(t);
- o && (this.activeBtnIds.has(t) ? o.classList.add("active") : o.classList.remove("active"));
+ const e = this.btnRefs.get(t);
+ e && (this.activeBtnIds.has(t) ? e.classList.add("active") : e.classList.remove("active"));
}
- /**
- * 获取图标 SVG 字符串
- */
getIcon(t) {
return t || this.DEFAULT_ICON;
}
- /**
- * 更新按钮可见性
- * @param buttonId 按钮ID
- * @param visible 是否可见
- */
- updateButtonVisibility(t, o) {
- this.options.visibility || (this.options.visibility = {}), this.options.visibility[t] = o, this.render();
+ updateButtonVisibility(t, e) {
+ this.options.visibility || (this.options.visibility = {}), this.options.visibility[t] = e, this.render();
}
- /**
- * 设置是否显示标签
- * @param show 是否显示
- */
setShowLabel(t) {
- this.options.showLabel = t, this.render();
+ this.options.showLabel = t, this.updateLabelsVisibility();
+ }
+ updateLabelsVisibility() {
+ this.btnRefs.forEach((t, e) => {
+ const o = this.findButtonById(e);
+ if (!o) return;
+ this.options.showLabel && o.label ? t.classList.remove("no-label") : t.classList.add("no-label");
+ });
+ }
+ findButtonById(t) {
+ for (const e of this.groups) {
+ const o = this.findButton(e.buttons, t);
+ if (o) return o;
+ }
}
- /**
- * 设置背景颜色 (兼容旧接口)
- * @param color CSS 颜色值
- */
setBackgroundColor(t) {
this.setColors({ backgroundColor: t });
}
- /**
- * 检查按钮是否可见
- */
isVisible(t) {
return this.options.visibility?.[t] !== !1;
}
- /**
- * 销毁组件,清理资源
- */
destroy() {
- this.closeDropdown(), this.hoverTimeout && clearTimeout(this.hoverTimeout), this.container.innerHTML = "", this.btnRefs.clear(), this.activeBtnIds.clear(), this.groups = [];
+ this.unsubscribeLocale && (this.unsubscribeLocale(), this.unsubscribeLocale = null), this.unsubscribeTheme && (this.unsubscribeTheme(), this.unsubscribeTheme = null), this.closeDropdown(), this.container.innerHTML = "", this.btnRefs.clear();
}
}
-class g {
- /** 内部工具栏组件实例 */
- optBtnGroups = null;
- /** 工具栏挂载的容器 */
- container;
+class B extends v {
/**
- * 构造函数
- * @param container 工具栏挂载的容器元素
+ * 重写初始化,加载默认按钮
*/
+ async init() {
+ await super.init();
+ const { homeButton: t } = await import("./index-CAPOUzfO.mjs"), { locationButton: e } = await import("./index-Cadgm6mg.mjs"), { walkMenuButton: o } = await import("./index-BzDQeHxh.mjs"), { walkPersonButton: i } = await import("./index-CIgUZcJM.mjs"), { walkBirdButton: s } = await import("./index-psziCat8.mjs"), { settingButton: n } = await import("./index-DSz8VpYf.mjs"), { infoButton: r } = await import("./index-C4v-Lg_Y.mjs");
+ this.addGroup("group-1"), this.addButton(t), this.addButton(o), this.addButton(i), this.addButton(s), this.addButton(e), this.addGroup("group-2"), this.addButton(n), this.addButton(r), this.render();
+ }
+}
+class k {
+ toolbar = null;
+ toolbarContainer = null;
+ container;
constructor(t) {
this.container = t, this.init();
}
- /**
- * 初始化工具栏
- */
init() {
- this.optBtnGroups = new f({
- container: this.container,
- showLabel: !0
- }), this.optBtnGroups.init().catch((t) => {
- console.error("Failed to initialize OptBtnGroups:", t);
- });
+ this.toolbarContainer = document.createElement("div"), this.toolbarContainer.id = "opt-btn-groups", this.toolbarContainer.className = "bim-engine-opt-btn-container is-bottom-toolbar", this.container.appendChild(this.toolbarContainer), this.toolbar = new B({
+ container: this.toolbarContainer,
+ showLabel: !0,
+ direction: "row",
+ position: "bottom-center",
+ // 底部居中
+ align: "vertical",
+ // 图标在上
+ expand: "up"
+ // 向上展开
+ }), this.toolbar.init();
}
- /**
- * 添加一个工具栏按钮组
- * @param groupId 新组的 ID
- * @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后
- */
- addGroup(t, o) {
- this.optBtnGroups ? (this.optBtnGroups.addGroup(t, o), this.optBtnGroups.render()) : console.warn("Toolbar not initialized yet.");
+ updateTheme(t) {
+ this.toolbar?.setTheme(t);
}
- /**
- * 添加一个工具栏按钮
- * @param config 按钮配置对象
- */
- addButton(t) {
- this.optBtnGroups ? (this.optBtnGroups.addButton(t), this.optBtnGroups.render()) : console.warn("Toolbar not initialized yet.");
+ refresh() {
+ this.toolbar?.render();
}
- /**
- * 设置按钮的可见性
- * @param buttonId 按钮 ID
- * @param visible 是否可见
- */
- setButtonVisibility(t, o) {
- this.optBtnGroups ? this.optBtnGroups.updateButtonVisibility(t, o) : console.warn("Toolbar not initialized yet.");
- }
- /**
- * 设置是否显示按钮下方的文字标签
- * @param show 是否显示
- */
- setShowLabel(t) {
- this.optBtnGroups ? this.optBtnGroups.setShowLabel(t) : console.warn("Toolbar not initialized yet.");
- }
- /**
- * 设置整个工具栏的可见性
- * @param visible 是否可见
- */
- setVisible(t) {
- this.container.style.display = t ? "block" : "none";
- }
- /**
- * 设置工具栏背景颜色
- * @param color CSS 颜色值
- */
- setBackgroundColor(t) {
- this.optBtnGroups ? this.optBtnGroups.setBackgroundColor(t) : console.warn("Toolbar not initialized yet.");
- }
- /**
- * 设置工具栏详细颜色配置
- * @param colors 颜色配置对象
- */
- setColors(t) {
- this.optBtnGroups ? this.optBtnGroups.setColors(t) : console.warn("Toolbar not initialized yet.");
- }
- /**
- * 销毁工具栏管理器
- */
destroy() {
- this.optBtnGroups && (this.optBtnGroups.destroy(), this.optBtnGroups = null);
+ this.toolbar?.destroy(), this.toolbar = null;
+ }
+ // --- 转发 API ---
+ addGroup(t, e) {
+ this.toolbar?.addGroup(t, e), this.toolbar?.render();
+ }
+ addButton(t) {
+ this.toolbar?.addButton(t), this.toolbar?.render();
+ }
+ setButtonVisibility(t, e) {
+ this.toolbar?.updateButtonVisibility(t, e);
+ }
+ setShowLabel(t) {
+ this.toolbar?.setShowLabel(t);
+ }
+ setVisible(t) {
+ this.toolbarContainer && (this.toolbarContainer.style.visibility = t ? "visible" : "hidden");
+ }
+ setBackgroundColor(t) {
+ this.toolbar?.setBackgroundColor(t);
+ }
+ setColors(t) {
+ this.toolbar?.setColors(t);
}
}
-class u {
+class M {
+ activeGroups = [];
+ container;
+ constructor(t) {
+ this.container = t;
+ }
+ /**
+ * 创建一个新的按钮组
+ */
+ create(t) {
+ const e = document.createElement("div");
+ this.container.appendChild(e);
+ const o = new v({
+ container: e,
+ ...t
+ });
+ return o.init(), this.activeGroups.push(o), o;
+ }
+ updateTheme(t) {
+ this.activeGroups.forEach((e) => e.setTheme(t));
+ }
+ refresh() {
+ this.activeGroups.forEach((t) => t.render());
+ }
+ destroy() {
+ this.activeGroups.forEach((t) => t.destroy()), this.activeGroups = [];
+ }
+}
+class y {
element;
options;
container;
header;
contentArea;
_isDestroyed = !1;
+ _isInitialized = !1;
+ unsubscribeTheme = null;
+ unsubscribeLocale = null;
/**
* 构造函数
* @param options 弹窗配置选项
@@ -377,22 +592,46 @@ class u {
...t
}, this.container = t.container, this.element = this.createDom(), this.header = this.element.querySelector(".bim-dialog-header"), this.contentArea = this.element.querySelector(".bim-dialog-content"), this.init();
}
+ /**
+ * 设置主题
+ * @param theme 全局主题配置
+ */
+ setTheme(t) {
+ const e = this.element.style;
+ this.options.backgroundColor || e.setProperty("--bim-dialog-bg", t.panelBackground), this.options.headerBackgroundColor || e.setProperty("--bim-dialog-header-bg", t.componentHover), this.options.titleColor || e.setProperty("--bim-dialog-title-color", t.textPrimary), this.options.textColor || e.setProperty("--bim-dialog-text-color", t.textPrimary), this.options.borderColor || e.setProperty("--bim-dialog-border-color", t.border);
+ }
+ /**
+ * 初始化组件功能 (接口实现)
+ */
+ init() {
+ this._isInitialized || (this.container.appendChild(this.element), this.initPosition(), this.options.draggable && this.initDrag(), this.options.resizable && this.initResize(), this._isInitialized = !0, this.options.onOpen && this.options.onOpen(), this.unsubscribeTheme = d.subscribe((t) => {
+ this.setTheme(t);
+ }), this.unsubscribeLocale = p.subscribe(() => {
+ this.setLocales();
+ }));
+ }
+ setLocales() {
+ if (this.options.title) {
+ const t = this.header.querySelector(".bim-dialog-title");
+ t && (t.textContent = b(this.options.title));
+ }
+ }
/**
* 创建弹窗的 DOM 结构
*/
createDom() {
const t = document.createElement("div");
t.className = "bim-dialog", this.options.id && (t.id = this.options.id);
- const o = t.style;
- this.options.backgroundColor && o.setProperty("--bim-dialog-bg", this.options.backgroundColor), this.options.headerBackgroundColor && o.setProperty("--bim-dialog-header-bg", this.options.headerBackgroundColor), this.options.titleColor && o.setProperty("--bim-dialog-title-color", this.options.titleColor), this.options.textColor && o.setProperty("--bim-dialog-text-color", this.options.textColor), this.options.borderColor && o.setProperty("--bim-dialog-border-color", this.options.borderColor), this.setSize(t, this.options.width, this.options.height);
- const e = document.createElement("div");
- e.className = "bim-dialog-header", this.options.draggable && e.classList.add("draggable");
+ const e = t.style;
+ this.options.backgroundColor && e.setProperty("--bim-dialog-bg", this.options.backgroundColor), this.options.headerBackgroundColor && e.setProperty("--bim-dialog-header-bg", this.options.headerBackgroundColor), this.options.titleColor && e.setProperty("--bim-dialog-title-color", this.options.titleColor), this.options.textColor && e.setProperty("--bim-dialog-text-color", this.options.textColor), this.options.borderColor && e.setProperty("--bim-dialog-border-color", this.options.borderColor), this.setSize(t, this.options.width, this.options.height);
+ const o = document.createElement("div");
+ o.className = "bim-dialog-header", this.options.draggable && o.classList.add("draggable");
const i = document.createElement("span");
- i.className = "bim-dialog-title", i.textContent = this.options.title || "";
- const n = document.createElement("span");
- n.className = "bim-dialog-close", n.innerHTML = "×", n.onclick = () => this.close(), e.appendChild(i), e.appendChild(n);
- const s = document.createElement("div");
- if (s.className = "bim-dialog-content", typeof this.options.content == "string" ? s.innerHTML = this.options.content : this.options.content instanceof HTMLElement && s.appendChild(this.options.content), t.appendChild(e), t.appendChild(s), this.options.resizable) {
+ i.className = "bim-dialog-title", i.textContent = this.options.title ? b(this.options.title) : "";
+ const s = document.createElement("span");
+ s.className = "bim-dialog-close", s.innerHTML = "×", s.onclick = () => this.close(), o.appendChild(i), o.appendChild(s);
+ const n = document.createElement("div");
+ if (n.className = "bim-dialog-content", typeof this.options.content == "string" ? n.innerHTML = this.options.content : this.options.content instanceof HTMLElement && n.appendChild(this.options.content), t.appendChild(o), t.appendChild(n), this.options.resizable) {
const r = document.createElement("div");
r.className = "bim-dialog-resize-handle", t.appendChild(r);
}
@@ -401,74 +640,68 @@ class u {
/**
* 设置元素尺寸
*/
- setSize(t, o, e) {
- o !== void 0 && (t.style.width = typeof o == "number" ? `${o}px` : o), e !== void 0 && (t.style.height = typeof e == "number" ? `${e}px` : e);
- }
- /**
- * 初始化组件功能
- */
- init() {
- this.container.appendChild(this.element), this.initPosition(), this.options.draggable && this.initDrag(), this.options.resizable && this.initResize();
+ setSize(t, e, o) {
+ e !== void 0 && (t.style.width = typeof e == "number" ? `${e}px` : e), o !== void 0 && (t.style.height = typeof o == "number" ? `${o}px` : o);
}
/**
* 初始化弹窗位置
*/
initPosition() {
- const t = this.options.position, o = this.element.getBoundingClientRect();
- let e = 0, i = 0;
- const n = this.container.clientWidth, s = this.container.clientHeight, r = o.width, a = o.height;
+ const t = this.options.position, e = this.element.getBoundingClientRect();
+ let o = 0, i = 0;
+ const s = this.container.clientWidth, n = this.container.clientHeight, r = e.width, a = e.height;
if (typeof t == "object" && "x" in t)
- e = t.x, i = t.y;
+ o = t.x, i = t.y;
else
switch (t) {
case "center":
- e = (n - r) / 2, i = (s - a) / 2;
+ o = (s - r) / 2, i = (n - a) / 2;
break;
case "top-left":
- e = 0, i = 0;
+ o = 0, i = 0;
break;
case "top-center":
- e = (n - r) / 2, i = 0;
+ o = (s - r) / 2, i = 0;
break;
case "top-right":
- e = n - r, i = 0;
+ o = s - r, i = 0;
break;
case "left-center":
- e = 0, i = (s - a) / 2;
+ o = 0, i = (n - a) / 2;
break;
case "right-center":
- e = n - r, i = (s - a) / 2;
+ o = s - r, i = (n - a) / 2;
break;
case "bottom-left":
- e = 0, i = s - a;
+ o = 0, i = n - a;
break;
case "bottom-center":
- e = (n - r) / 2, i = s - a;
+ o = (s - r) / 2, i = n - a;
break;
case "bottom-right":
- e = n - r, i = s - a;
+ o = s - r, i = n - a;
break;
default:
- e = (n - r) / 2, i = (s - a) / 2;
+ o = (s - r) / 2, i = (n - a) / 2;
}
- e = Math.max(0, Math.min(e, n - r)), i = Math.max(0, Math.min(i, s - a)), this.element.style.left = `${e}px`, this.element.style.top = `${i}px`;
+ o = Math.max(0, Math.min(o, s - r)), i = Math.max(0, Math.min(i, n - a)), this.element.style.left = `${o}px`, this.element.style.top = `${i}px`;
}
/**
* 初始化拖拽功能
*/
initDrag() {
- let t = 0, o = 0, e = 0, i = 0;
- const n = (a) => {
- a.preventDefault(), t = a.clientX, o = a.clientY, e = this.element.offsetLeft, i = this.element.offsetTop, document.addEventListener("mousemove", s), document.addEventListener("mouseup", r);
- }, s = (a) => {
- const l = a.clientX - t, p = a.clientY - o;
- let d = e + l, h = i + p;
- const m = this.container.clientWidth - this.element.offsetWidth, b = this.container.clientHeight - this.element.offsetHeight;
- d = Math.max(0, Math.min(d, m)), h = Math.max(0, Math.min(h, b)), this.element.style.left = `${d}px`, this.element.style.top = `${h}px`;
+ let t = 0, e = 0, o = 0, i = 0;
+ const s = (a) => {
+ a.preventDefault(), t = a.clientX, e = a.clientY, o = this.element.offsetLeft, i = this.element.offsetTop, document.addEventListener("mousemove", n), document.addEventListener("mouseup", r);
+ }, n = (a) => {
+ const l = a.clientX - t, h = a.clientY - e;
+ let u = o + l, m = i + h;
+ const f = this.container.clientWidth - this.element.offsetWidth, C = this.container.clientHeight - this.element.offsetHeight;
+ u = Math.max(0, Math.min(u, f)), m = Math.max(0, Math.min(m, C)), this.element.style.left = `${u}px`, this.element.style.top = `${m}px`;
}, r = () => {
- document.removeEventListener("mousemove", s), document.removeEventListener("mouseup", r);
+ document.removeEventListener("mousemove", n), document.removeEventListener("mouseup", r);
};
- this.header.addEventListener("mousedown", n);
+ this.header.addEventListener("mousedown", s);
}
/**
* 初始化缩放功能
@@ -476,16 +709,16 @@ class u {
initResize() {
const t = this.element.querySelector(".bim-dialog-resize-handle");
if (!t) return;
- let o = 0, e = 0, i = 0, n = 0;
- const s = (l) => {
- l.preventDefault(), l.stopPropagation(), o = l.clientX, e = l.clientY, i = this.element.offsetWidth, n = this.element.offsetHeight, document.addEventListener("mousemove", r), document.addEventListener("mouseup", a);
+ let e = 0, o = 0, i = 0, s = 0;
+ const n = (l) => {
+ l.preventDefault(), l.stopPropagation(), e = l.clientX, o = l.clientY, i = this.element.offsetWidth, s = this.element.offsetHeight, document.addEventListener("mousemove", r), document.addEventListener("mouseup", a);
}, r = (l) => {
- const p = l.clientX - o, d = l.clientY - e, h = Math.max(this.options.minWidth || 100, i + p), m = Math.max(this.options.minHeight || 50, n + d);
- this.element.style.width = `${h}px`, this.element.style.height = `${m}px`;
+ const h = l.clientX - e, u = l.clientY - o, m = Math.max(this.options.minWidth || 100, i + h), f = Math.max(this.options.minHeight || 50, s + u);
+ this.element.style.width = `${m}px`, this.element.style.height = `${f}px`;
}, a = () => {
document.removeEventListener("mousemove", r), document.removeEventListener("mouseup", a);
};
- t.addEventListener("mousedown", s);
+ t.addEventListener("mousedown", n);
}
/**
* 动态设置内容
@@ -498,20 +731,25 @@ class u {
* 关闭弹窗并销毁
*/
close() {
- this._isDestroyed || (this.element.remove(), this._isDestroyed = !0, this.options.onClose && this.options.onClose());
+ this._isDestroyed || (this.unsubscribeTheme && (this.unsubscribeTheme(), this.unsubscribeTheme = null), this.unsubscribeLocale && (this.unsubscribeLocale(), this.unsubscribeLocale = null), this.element.remove(), this._isDestroyed = !0, this.options.onClose && this.options.onClose());
+ }
+ /**
+ * 销毁组件 (接口实现)
+ */
+ destroy() {
+ this.close();
}
}
-class v {
- dialog;
+class D extends y {
/**
* 构造函数
* @param container 父容器
*/
constructor(t) {
- const o = document.createElement("div");
- o.className = "bim-info-dialog-content";
- const e = document.createElement("h3");
- e.textContent = "Model Information";
+ const e = document.createElement("div");
+ e.className = "bim-info-dialog-content";
+ const o = document.createElement("h3");
+ o.textContent = "Model Information";
const i = document.createElement("ul");
i.innerHTML = `
Name: Sample Project
@@ -519,30 +757,35 @@ class v {
Date: ${(/* @__PURE__ */ new Date()).toLocaleDateString()}
Status: Active
`;
- const n = document.createElement("button");
- n.textContent = "Update Status", n.style.marginTop = "10px", n.onclick = () => {
+ const s = document.createElement("button");
+ s.textContent = "Update Status", s.style.marginTop = "10px", s.onclick = () => {
alert("Status updated!");
- }, o.appendChild(e), o.appendChild(i), o.appendChild(n), this.dialog = new u({
+ }, e.appendChild(o), e.appendChild(i), e.appendChild(s), super({
container: t,
- title: "Project Info (Wrapped)",
- content: o,
+ title: "dialog.testTitle",
+ content: e,
width: 320,
height: "auto",
position: "center",
resizable: !0,
- draggable: !0
+ draggable: !0,
+ // 可以在这里添加特定的 onClose 逻辑
+ onClose: () => {
+ console.log("Info dialog closed");
+ },
+ onOpen: () => {
+ console.log("Info dialog opened");
+ }
});
}
- /**
- * 关闭弹窗
- */
- close() {
- this.dialog.close();
- }
+ // 不需要再手动实现 setTheme, destroy, close, init
+ // 它们都已从 BimDialog 继承
}
-class w {
+class H {
/** 弹窗挂载的父容器 */
container;
+ /** 活跃的弹窗实例列表 */
+ activeDialogs = [];
/**
* 构造函数
* @param container 弹窗挂载的目标容器
@@ -556,76 +799,102 @@ class w {
* @returns BimDialog 实例
*/
create(t) {
- return new u({
+ const e = new y({
container: this.container,
- ...t
+ ...t,
+ onClose: () => {
+ this.activeDialogs = this.activeDialogs.filter((o) => o !== e), t.onClose && t.onClose();
+ }
});
+ return e.setTheme(d.getTheme()), this.activeDialogs.push(e), e;
}
/**
* 显示二次封装的模型信息弹窗
* 演示如何调用特定的业务弹窗组件
*/
showInfoDialog() {
- new v(this.container);
+ new D(this.container);
+ }
+ /**
+ * 响应全局主题变更
+ * @param theme 全局主题配置
+ */
+ updateTheme(t) {
+ this.activeDialogs.forEach((e) => {
+ e.setTheme && e.setTheme(t);
+ });
}
}
-class C {
- /** 主容器元素 */
+class P {
container;
- /** 内部包装器元素,用于承载所有 UI 组件 */
wrapper = null;
- /** 工具栏管理器实例 */
+ topLeftGroup = null;
+ // 保存左上角按钮组的引用
toolbar = null;
- /** 弹窗管理器实例 */
+ // 底部专用
+ buttonGroup = null;
+ // 通用
dialog = null;
- /**
- * 构造函数
- * @param container 容器元素或容器 ID
- */
- constructor(t) {
+ get localeManager() {
+ return p;
+ }
+ get themeManager() {
+ return d;
+ }
+ constructor(t, e) {
const o = typeof t == "string" ? document.getElementById(t) : t;
if (!o) throw new Error("Container not found");
- this.container = o, this.init();
+ this.container = o, e?.locale && p.setLocale(e.locale), e?.theme && (e.theme === "custom" ? console.warn("Custom theme should be set via setCustomTheme().") : d.setTheme(e.theme)), this.init();
+ }
+ setLocale(t) {
+ p.setLocale(t);
+ }
+ getLocale() {
+ return p.getLocale();
+ }
+ setTheme(t) {
+ d.setTheme(t);
+ }
+ setCustomTheme(t) {
+ d.setCustomTheme(t);
}
- /**
- * 初始化方法
- * 创建 DOM 结构并初始化各子模块
- */
init() {
- this.container.innerHTML = "", this.wrapper = document.createElement("div"), this.wrapper.className = "bim-engine-wrapper";
- const t = document.createElement("h1");
- t.textContent = "BimEngine", t.className = "bim-engine-title";
- const o = document.createElement("p");
- o.textContent = "这是一个使用BIM-ENGINE。", o.className = "bim-engine-desc";
- const e = document.createElement("div");
- e.id = "opt-btn-groups", e.className = "bim-engine-opt-btn-container", this.wrapper.appendChild(t), this.wrapper.appendChild(o), this.dialog = new w(this.wrapper), this.toolbar = new g(e);
- const i = document.createElement("button");
- i.textContent = "打开测试弹窗", i.className = "bim-engine-btn", i.onclick = () => {
- this.dialog?.create({
- title: "测试弹窗",
- content: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',
- width: 300,
- height: 400,
- position: "top-left",
- draggable: !0,
- resizable: !0
- });
- };
- const n = document.createElement("button");
- n.textContent = "打开信息弹窗 (封装版)", n.className = "bim-engine-btn", n.style.marginLeft = "10px", n.onclick = () => {
- this.dialog?.showInfoDialog();
- }, this.wrapper.appendChild(i), this.wrapper.appendChild(n), this.wrapper.appendChild(e), this.container.appendChild(this.wrapper);
+ this.container.innerHTML = "", this.wrapper = document.createElement("div"), this.wrapper.className = "bim-engine-wrapper", this.container.appendChild(this.wrapper), this.dialog = new H(this.wrapper), this.toolbar = new k(this.wrapper), this.buttonGroup = new M(this.wrapper), this.createTopLeftGroup(), this.updateTheme(d.getTheme()), this.topLeftGroup && this.topLeftGroup.setColors({
+ backgroundColor: "#ff00ff"
+ }), d.subscribe((t) => {
+ this.updateTheme(t);
+ });
+ }
+ createTopLeftGroup() {
+ this.buttonGroup && (this.topLeftGroup = this.buttonGroup.create({
+ position: "top-left",
+ direction: "column",
+ align: "vertical",
+ backgroundColor: "#ff00ff",
+ // 自定义背景色,不会被主题覆盖
+ showLabel: !1
+ }), this.topLeftGroup.addGroup("main"), this.topLeftGroup.addButton({
+ id: "menu-btn",
+ groupId: "main",
+ type: "button",
+ label: "Menu",
+ // 应该用 translation key
+ icon: ' ',
+ onClick: () => {
+ alert("点击按钮");
+ }
+ }), this.topLeftGroup.render());
+ }
+ updateTheme(t) {
+ this.wrapper && (this.wrapper.style.backgroundColor = t.background, this.wrapper.style.color = t.textPrimary);
}
- /**
- * 销毁实例
- * 清理所有资源和 DOM 元素
- */
destroy() {
- this.toolbar && (this.toolbar.destroy(), this.toolbar = null), this.dialog = null, this.container.innerHTML = "";
+ this.toolbar?.destroy(), this.buttonGroup?.destroy(), this.dialog = null, this.container.innerHTML = "";
}
}
export {
- C as BimEngine,
- f as OptBtnGroups
+ v as BimButtonGroup,
+ P as BimEngine,
+ B as Toolbar
};
//# sourceMappingURL=bim-engine-sdk.es.js.map
diff --git a/dist/bim-engine-sdk.es.js.map b/dist/bim-engine-sdk.es.js.map
index 1938650..8902926 100644
--- a/dist/bim-engine-sdk.es.js.map
+++ b/dist/bim-engine-sdk.es.js.map
@@ -1 +1 @@
-{"version":3,"file":"bim-engine-sdk.es.js","sources":["../src/toolbar/index.ts","../src/modules/toolbar-manager.ts","../src/dialog/index.ts","../src/dialog/bimInfoDialog/index.ts","../src/modules/dialog-manager.ts","../src/bim-engine.ts"],"sourcesContent":["import './index.css';\nimport type {\n OptButton,\n ButtonGroup,\n OptBtnGroupsOptions,\n ButtonConfig,\n ToolbarColors\n} from './index.type';\n\n/**\n * 底部操作按钮组组件\n * 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。\n */\nexport class OptBtnGroups {\n /** 挂载容器 */\n private container: HTMLElement;\n /** 组件配置选项 */\n private options: OptBtnGroupsOptions;\n /** 按钮组列表,按顺序存储 */\n private groups: ButtonGroup[] = [];\n /** 当前处于激活状态的按钮 ID 集合 */\n private activeBtnIds: Set = new Set();\n /** 按钮 DOM 元素的引用映射,方便快速查找 */\n private btnRefs: Map = new Map();\n /** 当��显示的下拉菜单元素 */\n private dropdownElement: HTMLElement | null = null;\n /** 鼠标悬停计时器,用于处理菜单显示的防抖 */\n private hoverTimeout: number | null = null;\n\n /** 默认图标 SVG */\n private readonly DEFAULT_ICON = ' ';\n\n /**\n * 构造函数\n * @param options 配置选项\n */\n constructor(options: OptBtnGroupsOptions) {\n const el = typeof options.container === 'string'\n ? document.getElementById(options.container)\n : options.container;\n\n if (!el) throw new Error('Container not found');\n\n this.container = el;\n this.options = {\n showLabel: true,\n visibility: {},\n ...options\n };\n\n this.initContainer();\n this.applyStyles(); // 应用初始样式配置\n }\n\n /**\n * 初始化容器\n */\n private initContainer(): void {\n this.container.innerHTML = '';\n this.container.classList.add('toolbar-root'); // 添加一个根类方便定位样式\n }\n\n /**\n * 应用样式配置到 CSS 变量\n */\n private applyStyles(): void {\n const style = this.container.style;\n if (this.options.backgroundColor) style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);\n if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);\n if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);\n if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);\n if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);\n if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);\n if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);\n if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);\n }\n\n /**\n * 更新颜色配置\n * @param colors 颜色配置对象\n */\n public setColors(colors: ToolbarColors): void {\n // 更新 options\n this.options = { ...this.options, ...colors };\n // 应用到 CSS 变量\n this.applyStyles();\n }\n\n /**\n * 添加按钮组\n * @param groupId 组ID\n * @param beforeGroupId 在哪个组之前插入(可选���,不传则插入到最后\n */\n public addGroup(groupId: string, beforeGroupId?: string): void {\n if (this.groups.some(g => g.id === groupId)) {\n console.warn('Group ' + groupId + ' already exists');\n return;\n }\n\n const newGroup: ButtonGroup = { id: groupId, buttons: [] };\n\n if (beforeGroupId) {\n const index = this.groups.findIndex(g => g.id === beforeGroupId);\n if (index !== -1) {\n this.groups.splice(index, 0, newGroup);\n } else {\n console.warn(`Target group ${beforeGroupId} not found, appending ${groupId} to end.`);\n this.groups.push(newGroup);\n }\n } else {\n this.groups.push(newGroup);\n }\n }\n\n /**\n * 添加按钮到指定组\n * @param config 按钮配置(必须包含 groupId,可选包含 parentId)\n */\n public addButton(config: ButtonConfig): void {\n const { groupId, parentId } = config;\n\n if (!groupId) {\n throw new Error(`Button ${config.id} config must contain 'groupId'`);\n }\n\n const group = this.groups.find(g => g.id === groupId);\n if (!group) {\n throw new Error(`Group ${groupId} not found. Please call addGroup first.`);\n }\n\n const button: OptButton = {\n ...config,\n children: config.children || []\n };\n\n if (parentId) {\n // 添加为子按钮(菜单项)\n const parentBtn = this.findButton(group.buttons, parentId);\n if (!parentBtn) {\n throw new Error(`Parent button ${parentId} not found in group ${groupId}`);\n }\n if (!parentBtn.children) {\n parentBtn.children = [];\n }\n parentBtn.children.push(button);\n } else {\n // 添加为主按钮\n group.buttons.push(button);\n }\n }\n\n /**\n * 递归查找按钮\n */\n private findButton(buttons: OptButton[], id: string): OptButton | undefined {\n for (const btn of buttons) {\n if (btn.id === id) return btn;\n if (btn.children) {\n const found = this.findButton(btn.children, id);\n if (found) return found;\n }\n }\n return undefined;\n }\n\n /**\n * 初始化组件,加载默认按钮配置\n */\n public async init(): Promise {\n // 动态导入默认按钮配置\n const { homeButton } = await import('./buttons/home');\n const { locationButton } = await import('./buttons/location');\n const { walkMenuButton } = await import('./buttons/walk/walk-menu');\n const { walkPersonButton } = await import('./buttons/walk/walk-person');\n const { walkBirdButton } = await import('./buttons/walk/walk-bird');\n const { settingButton } = await import('./buttons/setting');\n const { infoButton } = await import('./buttons/info');\n\n // 配置默认组和按钮\n this.addGroup('group-1');\n this.addButton(homeButton);\n this.addButton(walkMenuButton);\n this.addButton(walkPersonButton);\n this.addButton(walkBirdButton);\n this.addButton(locationButton);\n this.addGroup('group-2');\n this.addButton(settingButton);\n this.addButton(infoButton);\n this.render();\n }\n\n /**\n * 渲染整个工具栏\n */\n public render(): void {\n this.container.innerHTML = '';\n this.btnRefs.clear();\n\n const wrapper = document.createElement('div');\n wrapper.className = 'toolbar-container';\n\n // 渲染所有组\n this.groups.forEach((group, index) => {\n const groupElement = this.renderGroup(group, index, this.groups.length);\n wrapper.appendChild(groupElement);\n });\n\n this.container.appendChild(wrapper);\n }\n\n /**\n * 渲染单个按钮组\n */\n private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {\n const groupEl = document.createElement('div');\n groupEl.className = 'opt-btn-group';\n\n if (index < total - 1) {\n groupEl.classList.add('has-divider');\n }\n\n group.buttons.forEach(button => {\n if (this.isVisible(button.id)) {\n const btnWrapper = this.renderButton(button);\n groupEl.appendChild(btnWrapper);\n }\n });\n\n return groupEl;\n }\n\n /**\n * 渲染单个按钮\n */\n private renderButton(button: OptButton): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'opt-btn-wrapper';\n\n const btnEl = document.createElement('div');\n btnEl.className = 'opt-btn';\n\n // 设置激活状态\n if (this.activeBtnIds.has(button.id)) {\n btnEl.classList.add('active');\n }\n\n if (button.disabled) {\n btnEl.classList.add('disabled');\n }\n\n if (!this.options.showLabel) {\n btnEl.classList.add('no-label');\n if (button.label) {\n btnEl.title = button.label;\n }\n }\n\n // 渲染图标\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.innerHTML = this.getIcon(button.icon);\n btnEl.appendChild(icon);\n\n // 渲染标签\n if (this.options.showLabel && button.label) {\n const label = document.createElement('span');\n label.className = 'opt-btn-label';\n label.textContent = button.label;\n btnEl.appendChild(label);\n }\n\n // 如果有子菜单,渲染箭头\n if (button.children && button.children.length > 0) {\n const arrow = document.createElement('span');\n arrow.className = 'opt-btn-arrow';\n arrow.textContent = '▼';\n btnEl.appendChild(arrow);\n }\n\n // 绑定事件\n btnEl.addEventListener('click', () => this.handleClick(button));\n btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));\n btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());\n\n this.btnRefs.set(button.id, btnEl);\n\n wrapper.appendChild(btnEl);\n return wrapper;\n }\n\n /**\n * 处理按钮点击事件\n */\n private handleClick(button: OptButton): void {\n if (button.disabled) return;\n\n // 如果没有子菜单,直接触发\n if (!button.children || button.children.length === 0) {\n if (button.keepActive) {\n const wasActive = this.activeBtnIds.has(button.id);\n if (wasActive) {\n this.activeBtnIds.delete(button.id);\n } else {\n this.activeBtnIds.add(button.id);\n }\n this.updateButtonState(button.id);\n }\n\n this.closeDropdown();\n\n if (button.onClick) {\n button.onClick(button);\n }\n }\n }\n\n /**\n * 处理子菜单项点击事件\n */\n private handleSubClick(button: OptButton): void {\n if (button.keepActive) {\n const wasActive = this.activeBtnIds.has(button.id);\n if (wasActive) {\n this.activeBtnIds.delete(button.id);\n } else {\n this.activeBtnIds.add(button.id);\n }\n this.updateButtonState(button.id);\n }\n\n this.closeDropdown();\n\n if (button.onClick) {\n button.onClick(button);\n }\n }\n\n /**\n * 处理鼠标移入事件(显示菜单)\n */\n private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {\n if (this.hoverTimeout) {\n clearTimeout(this.hoverTimeout);\n this.hoverTimeout = null;\n }\n\n if (button.children && button.children.length > 0) {\n this.showDropdown(button, btnEl);\n\n const arrow = btnEl.querySelector('.opt-btn-arrow');\n if (arrow) {\n arrow.classList.add('rotated');\n }\n } else {\n this.closeDropdown();\n }\n }\n\n /**\n * 处理鼠标移出事件(隐藏菜单)\n */\n private handleMouseLeave(): void {\n this.hoverTimeout = window.setTimeout(() => {\n this.closeDropdown();\n }, 200);\n }\n\n /**\n * 显示下拉菜单\n */\n private showDropdown(button: OptButton, btnEl: HTMLElement): void {\n this.closeDropdown();\n\n if (!button.children) return;\n\n const dropdown = document.createElement('div');\n dropdown.className = 'opt-btn-dropdown';\n \n // 下拉菜单也应用当前的 CSS 变量样式,因为它们通常挂载在 body 上,所以需要单独设置或者确保能继承\n // 简单起见,我们可以直接将容器上的 CSS 变量复制过来,或者设置内联样式\n // 更好的是:如果我们在 this.container 上设置 CSS 变量,\n // 而 dropdown 挂载在 body 上,它无法继承。\n // 所以我们需要将 CSS 变量也应用到 dropdown 上。\n \n const style = dropdown.style;\n if (this.options.backgroundColor) style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);\n if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);\n if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);\n if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);\n if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);\n if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);\n if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);\n if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);\n\n const rect = btnEl.getBoundingClientRect();\n const centerX = rect.left + rect.width / 2;\n\n dropdown.style.top = rect.top - 8 + 'px';\n dropdown.style.left = centerX + 'px';\n\n button.children.forEach(subBtn => {\n if (this.isVisible(subBtn.id)) {\n const item = this.renderDropdownItem(subBtn);\n dropdown.appendChild(item);\n }\n });\n\n // 保持菜单显���\n dropdown.addEventListener('mouseenter', () => {\n if (this.hoverTimeout) {\n clearTimeout(this.hoverTimeout);\n this.hoverTimeout = null;\n }\n });\n\n dropdown.addEventListener('mouseleave', () => this.handleMouseLeave());\n\n document.body.appendChild(dropdown);\n this.dropdownElement = dropdown;\n }\n\n /**\n * 渲染下拉菜单项\n */\n private renderDropdownItem(button: OptButton): HTMLElement {\n const item = document.createElement('div');\n item.className = 'opt-btn-dropdown-item';\n\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon small';\n icon.innerHTML = this.getIcon(button.icon);\n item.appendChild(icon);\n\n if (this.options.showLabel) {\n const label = document.createElement('span');\n label.textContent = button.label;\n item.appendChild(label);\n }\n\n item.addEventListener('click', (e) => {\n e.stopPropagation();\n this.handleSubClick(button);\n });\n\n return item;\n }\n\n /**\n * 关闭所有下拉菜单\n */\n private closeDropdown(): void {\n if (this.dropdownElement) {\n this.dropdownElement.remove();\n this.dropdownElement = null;\n }\n\n this.btnRefs.forEach(btnEl => {\n const arrow = btnEl.querySelector('.opt-btn-arrow');\n if (arrow) {\n arrow.classList.remove('rotated');\n }\n });\n }\n\n /**\n * 更新按钮的激活状态样式\n */\n private updateButtonState(buttonId: string): void {\n const btnEl = this.btnRefs.get(buttonId);\n if (btnEl) {\n if (this.activeBtnIds.has(buttonId)) {\n btnEl.classList.add('active');\n } else {\n btnEl.classList.remove('active');\n }\n }\n }\n\n /**\n * 获取图标 SVG 字符串\n */\n private getIcon(icon?: string): string {\n return icon || this.DEFAULT_ICON;\n }\n\n /**\n * 更新按钮可见性\n * @param buttonId 按钮ID\n * @param visible 是否可见\n */\n public updateButtonVisibility(buttonId: string, visible: boolean): void {\n if (!this.options.visibility) {\n this.options.visibility = {};\n }\n this.options.visibility[buttonId] = visible;\n this.render();\n }\n\n /**\n * 设置是否显示标签\n * @param show 是否显示\n */\n public setShowLabel(show: boolean): void {\n this.options.showLabel = show;\n this.render();\n }\n\n /**\n * 设置背景颜色 (兼容旧接口)\n * @param color CSS 颜色值\n */\n public setBackgroundColor(color: string): void {\n this.setColors({ backgroundColor: color });\n }\n\n /**\n * 检查按钮是否可见\n */\n private isVisible(id: string): boolean {\n return this.options.visibility?.[id] !== false;\n }\n\n /**\n * 销毁组件,清理资源\n */\n public destroy(): void {\n this.closeDropdown();\n if (this.hoverTimeout) {\n clearTimeout(this.hoverTimeout);\n }\n this.container.innerHTML = '';\n this.btnRefs.clear();\n this.activeBtnIds.clear();\n this.groups = [];\n }\n}","import type { ToolbarColors } from '../toolbar/index.type';\nimport { OptBtnGroups } from '../toolbar';\nimport type { ButtonConfig } from '../toolbar/index.type';\n\n/**\n * 工具栏管理器\n * 负责管理底部操作栏的按钮组、按钮及其可见性等状态。\n */\nexport class ToolbarManager {\n /** 内部工具栏组件实例 */\n private optBtnGroups: OptBtnGroups | null = null;\n /** 工具栏挂载的容器 */\n private container: HTMLElement;\n\n /**\n * 构造函数\n * @param container 工具栏挂载的容器元素\n */\n constructor(container: HTMLElement) {\n this.container = container;\n this.init();\n }\n\n /**\n * 初始化工具栏\n */\n private init() {\n this.optBtnGroups = new OptBtnGroups({\n container: this.container,\n showLabel: true\n });\n\n // 初始化并加载默认按钮配置\n this.optBtnGroups.init().catch(err => {\n console.error('Failed to initialize OptBtnGroups:', err);\n });\n }\n\n /**\n * 添加一个工具栏按钮组\n * @param groupId 新组的 ID\n * @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后\n */\n public addGroup(groupId: string, beforeGroupId?: string) {\n if (this.optBtnGroups) {\n this.optBtnGroups.addGroup(groupId, beforeGroupId);\n this.optBtnGroups.render(); // 重新渲染以更新 UI\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 添加一个工具栏按钮\n * @param config 按钮配置对象\n */\n public addButton(config: ButtonConfig) {\n if (this.optBtnGroups) {\n this.optBtnGroups.addButton(config);\n this.optBtnGroups.render(); // 重新渲染以更新 UI\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置按钮的可见性\n * @param buttonId 按钮 ID\n * @param visible 是否可见\n */\n public setButtonVisibility(buttonId: string, visible: boolean) {\n if (this.optBtnGroups) {\n this.optBtnGroups.updateButtonVisibility(buttonId, visible);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置是否显示按钮下方的文字标签\n * @param show 是否显示\n */\n public setShowLabel(show: boolean) {\n if (this.optBtnGroups) {\n this.optBtnGroups.setShowLabel(show);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置整个工具栏的可见性\n * @param visible 是否可见\n */\n public setVisible(visible: boolean) {\n this.container.style.display = visible ? 'block' : 'none';\n }\n\n /**\n * 设置工具栏背景颜色\n * @param color CSS 颜色值\n */\n public setBackgroundColor(color: string) {\n if (this.optBtnGroups) {\n this.optBtnGroups.setBackgroundColor(color);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置工具栏详细颜色配置\n * @param colors 颜色配置对象\n */\n public setColors(colors: ToolbarColors) {\n if (this.optBtnGroups) {\n this.optBtnGroups.setColors(colors);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 销毁工具栏管理器\n */\n public destroy() {\n if (this.optBtnGroups) {\n this.optBtnGroups.destroy();\n this.optBtnGroups = null;\n }\n }\n}\n","import './index.css';\nimport type { DialogOptions } from './index.type';\n\n/**\n * 通用弹窗组件类\n * 支持拖拽、缩放、自定义内容和位置。\n */\nexport class BimDialog {\n private element: HTMLElement;\n private options: DialogOptions;\n private container: HTMLElement;\n private header: HTMLElement;\n private contentArea: HTMLElement;\n private _isDestroyed = false;\n\n /**\n * 构造函数\n * @param options 弹窗配置选项\n */\n constructor(options: DialogOptions) {\n // 合并默认配置\n this.options = {\n title: 'Dialog',\n width: 300,\n height: 'auto',\n position: 'center',\n draggable: true,\n resizable: false,\n minWidth: 200,\n minHeight: 100,\n ...options\n };\n this.container = options.container;\n\n // 创建 DOM 结构\n this.element = this.createDom();\n this.header = this.element.querySelector('.bim-dialog-header') as HTMLElement;\n this.contentArea = this.element.querySelector('.bim-dialog-content') as HTMLElement;\n\n // 初始化\n this.init();\n }\n\n /**\n * 创建弹窗的 DOM 结构\n */\n private createDom(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'bim-dialog';\n\n if (this.options.id) el.id = this.options.id;\n \n // 应用颜色配置到 CSS 变量 (局部作用域)\n const style = el.style;\n if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor);\n if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor);\n if (this.options.titleColor) style.setProperty('--bim-dialog-title-color', this.options.titleColor);\n if (this.options.textColor) style.setProperty('--bim-dialog-text-color', this.options.textColor);\n if (this.options.borderColor) style.setProperty('--bim-dialog-border-color', this.options.borderColor);\n\n // 设置初始尺寸\n this.setSize(el, this.options.width, this.options.height);\n\n // 创建标题栏 (Header)\n const header = document.createElement('div');\n header.className = 'bim-dialog-header';\n if (this.options.draggable) header.classList.add('draggable');\n\n const title = document.createElement('span');\n title.className = 'bim-dialog-title';\n title.textContent = this.options.title || '';\n\n const closeBtn = document.createElement('span');\n closeBtn.className = 'bim-dialog-close';\n closeBtn.innerHTML = '×';\n closeBtn.onclick = () => this.close();\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // 创建内容区域 (Content)\n const content = document.createElement('div');\n content.className = 'bim-dialog-content';\n if (typeof this.options.content === 'string') {\n content.innerHTML = this.options.content;\n } else if (this.options.content instanceof HTMLElement) {\n content.appendChild(this.options.content);\n }\n\n el.appendChild(header);\n el.appendChild(content);\n\n // 如果允许缩放,创建缩放手柄\n if (this.options.resizable) {\n const resizeHandle = document.createElement('div');\n resizeHandle.className = 'bim-dialog-resize-handle';\n el.appendChild(resizeHandle);\n }\n\n return el;\n }\n\n /**\n * 设置元素尺寸\n */\n private setSize(el: HTMLElement, width?: number | string, height?: number | string) {\n if (width !== undefined) {\n el.style.width = typeof width === 'number' ? `${width}px` : width;\n }\n if (height !== undefined) {\n el.style.height = typeof height === 'number' ? `${height}px` : height;\n }\n }\n\n /**\n * 初始化组件功能\n */\n private init() {\n this.container.appendChild(this.element);\n\n // 必须先挂载才能计算尺寸进行定位\n this.initPosition();\n\n if (this.options.draggable) {\n this.initDrag();\n }\n\n if (this.options.resizable) {\n this.initResize();\n }\n }\n\n /**\n * 初始化弹窗位置\n */\n private initPosition() {\n const pos = this.options.position;\n\n const elRect = this.element.getBoundingClientRect();\n \n // 计算相对父容器的定位\n let left = 0;\n let top = 0;\n\n const pW = this.container.clientWidth;\n const pH = this.container.clientHeight;\n const elW = elRect.width;\n const elH = elRect.height;\n\n if (typeof pos === 'object' && 'x' in pos) {\n left = pos.x;\n top = pos.y;\n } else {\n switch (pos) {\n case 'center':\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n break;\n case 'top-left': left = 0; top = 0; break;\n case 'top-center': left = (pW - elW) / 2; top = 0; break;\n case 'top-right': left = pW - elW; top = 0; break;\n case 'left-center': left = 0; top = (pH - elH) / 2; break;\n case 'right-center': left = pW - elW; top = (pH - elH) / 2; break;\n case 'bottom-left': left = 0; top = pH - elH; break;\n case 'bottom-center': left = (pW - elW) / 2; top = pH - elH; break;\n case 'bottom-right': left = pW - elW; top = pH - elH; break;\n default:\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n }\n }\n\n // 简单的边界检查,防止初始位置溢出\n left = Math.max(0, Math.min(left, pW - elW));\n top = Math.max(0, Math.min(top, pH - elH));\n\n this.element.style.left = `${left}px`;\n this.element.style.top = `${top}px`;\n }\n\n /**\n * 初始化拖拽功能\n */\n private initDrag() {\n let startX = 0;\n let startY = 0;\n let startLeft = 0;\n let startTop = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n startX = e.clientX;\n startY = e.clientY;\n startLeft = this.element.offsetLeft;\n startTop = this.element.offsetTop;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n let newLeft = startLeft + dx;\n let newTop = startTop + dy;\n\n // 边界限制,防止拖出容器\n const maxLeft = this.container.clientWidth - this.element.offsetWidth;\n const maxTop = this.container.clientHeight - this.element.offsetHeight;\n\n newLeft = Math.max(0, Math.min(newLeft, maxLeft));\n newTop = Math.max(0, Math.min(newTop, maxTop));\n\n this.element.style.left = `${newLeft}px`;\n this.element.style.top = `${newTop}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n this.header.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 初始化缩放功能\n */\n private initResize() {\n const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;\n if (!handle) return;\n\n let startX = 0;\n let startY = 0;\n let startW = 0;\n let startH = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n startX = e.clientX;\n startY = e.clientY;\n startW = this.element.offsetWidth;\n startH = this.element.offsetHeight;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n const newW = Math.max(this.options.minWidth || 100, startW + dx);\n const newH = Math.max(this.options.minHeight || 50, startH + dy);\n\n this.element.style.width = `${newW}px`;\n this.element.style.height = `${newH}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 动态设置内容\n * @param content 内容元素或 HTML 字符串\n */\n public setContent(content: HTMLElement | string) {\n this.contentArea.innerHTML = '';\n if (typeof content === 'string') {\n this.contentArea.innerHTML = content;\n } else {\n this.contentArea.appendChild(content);\n }\n }\n\n /**\n * 关闭弹窗并销毁\n */\n public close() {\n if (this._isDestroyed) return;\n this.element.remove();\n this._isDestroyed = true;\n if (this.options.onClose) {\n this.options.onClose();\n }\n }\n}\n","import './index.css';\nimport { BimDialog } from '../index';\n\n/**\n * BimInfoDialog (二次封装示例)\n * 这是一个展示项目信息的业务弹窗组件,内部封装了 BimDialog。\n */\nexport class BimInfoDialog {\n private dialog: BimDialog;\n\n /**\n * 构造函数\n * @param container 父容器\n */\n constructor(container: HTMLElement) {\n // 创建自定义的 DOM 内容\n const contentEl = document.createElement('div');\n contentEl.className = 'bim-info-dialog-content';\n\n const infoTitle = document.createElement('h3');\n infoTitle.textContent = 'Model Information';\n\n const infoList = document.createElement('ul');\n infoList.innerHTML = `\n Name: Sample Project \n Version: 1.0.0 \n Date: ${new Date().toLocaleDateString()} \n Status: Active \n `;\n\n const actionBtn = document.createElement('button');\n actionBtn.textContent = 'Update Status';\n actionBtn.style.marginTop = '10px';\n actionBtn.onclick = () => {\n alert('Status updated!');\n };\n\n contentEl.appendChild(infoTitle);\n contentEl.appendChild(infoList);\n contentEl.appendChild(actionBtn);\n\n // 初始化 BimDialog,直接传入构建好的 HTMLElement\n this.dialog = new BimDialog({\n container: container,\n title: 'Project Info (Wrapped)',\n content: contentEl, \n width: 320,\n height: 'auto',\n position: 'center',\n resizable: true,\n draggable: true\n });\n }\n\n /**\n * 关闭弹窗\n */\n public close() {\n this.dialog.close();\n }\n}","import { BimDialog } from '../dialog';\nimport { BimInfoDialog } from '../dialog/bimInfoDialog';\nimport type { DialogOptions } from '../dialog/index.type';\n\n/**\n * 弹窗管理器\n * 负责创建和管理应用中的各类弹窗。\n */\nexport class DialogManager {\n /** 弹窗挂载的父容器 */\n private container: HTMLElement;\n\n /**\n * 构造函数\n * @param container 弹窗挂载的目标容器\n */\n constructor(container: HTMLElement) {\n this.container = container;\n }\n\n /**\n * 创建一个通用弹窗\n * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)\n * @returns BimDialog 实例\n */\n public create(options: Omit): BimDialog {\n return new BimDialog({\n container: this.container,\n ...options\n });\n }\n\n /**\n * 显示二次封装的模型信息弹窗\n * 演示如何调用特定的业务弹窗组件\n */\n public showInfoDialog() {\n new BimInfoDialog(this.container);\n }\n}","import './bim-engine.css';\nimport { ToolbarManager } from './modules/toolbar-manager';\nimport { DialogManager } from './modules/dialog-manager';\n\n/**\n * BimEngine 主类\n * 负责初始化整个应用界面,协调各个子模块(如工具栏、弹窗等)。\n */\nexport class BimEngine {\n /** 主容器元素 */\n private container: HTMLElement;\n /** 内部包装器元素,用于承载所有 UI 组件 */\n private wrapper: HTMLElement | null = null;\n \n /** 工具栏管理器实例 */\n public toolbar: ToolbarManager | null = null;\n /** 弹窗管理器实例 */\n public dialog: DialogManager | null = null;\n\n /**\n * 构造函数\n * @param container 容器元素或容器 ID\n */\n constructor(container: HTMLElement | string) {\n const el = typeof container === 'string' ? document.getElementById(container) : container;\n if (!el) throw new Error('Container not found');\n this.container = el;\n this.init();\n }\n\n /**\n * 初始化方法\n * 创建 DOM 结构并初始化各子模块\n */\n private init() {\n // 1. 清空容器可能存在的旧内容\n this.container.innerHTML = '';\n\n // 2. 创建外层容器 div\n this.wrapper = document.createElement('div');\n this.wrapper.className = 'bim-engine-wrapper';\n\n // 3. 创建标题 h1\n const title = document.createElement('h1');\n title.textContent = 'BimEngine';\n title.className = 'bim-engine-title';\n\n // 4. 创建描述段落 p\n const desc = document.createElement('p');\n desc.textContent = '这是一个使用BIM-ENGINE。';\n desc.className = 'bim-engine-desc';\n\n // 6. 创建操作按钮组容器\n const btnGroupContainer = document.createElement('div');\n btnGroupContainer.id = 'opt-btn-groups';\n btnGroupContainer.className = 'bim-engine-opt-btn-container';\n\n // 7. 组装元素\n this.wrapper.appendChild(title);\n this.wrapper.appendChild(desc);\n \n // 初始化管理器\n this.dialog = new DialogManager(this.wrapper);\n this.toolbar = new ToolbarManager(btnGroupContainer);\n\n // 5. 测试按钮(更新为使用管理器)\n // 5.1 创建普通测试弹窗按钮\n const dialogBtn = document.createElement('button');\n dialogBtn.textContent = '打开测试弹窗';\n dialogBtn.className = 'bim-engine-btn';\n dialogBtn.onclick = () => {\n this.dialog?.create({\n title: '测试弹窗',\n content: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',\n width: 300,\n height: 400,\n position: 'top-left',\n draggable: true,\n resizable: true\n });\n };\n\n // 5.2 创建二次封装信息弹窗按钮\n const infoDialogBtn = document.createElement('button');\n infoDialogBtn.textContent = '打开信息弹窗 (封装版)';\n infoDialogBtn.className = 'bim-engine-btn';\n infoDialogBtn.style.marginLeft = '10px';\n infoDialogBtn.onclick = () => {\n this.dialog?.showInfoDialog();\n };\n\n // 将按钮和工具栏容器添加到包装器中\n this.wrapper.appendChild(dialogBtn);\n this.wrapper.appendChild(infoDialogBtn);\n this.wrapper.appendChild(btnGroupContainer);\n\n // 8. 将包装器挂载到主容器\n this.container.appendChild(this.wrapper);\n }\n\n /**\n * 销毁实例\n * 清理所有资源和 DOM 元素\n */\n public destroy() {\n if (this.toolbar) {\n this.toolbar.destroy();\n this.toolbar = null;\n }\n this.dialog = null;\n this.container.innerHTML = '';\n }\n}"],"names":["OptBtnGroups","options","el","style","colors","groupId","beforeGroupId","g","newGroup","index","config","parentId","group","button","parentBtn","buttons","id","btn","found","homeButton","locationButton","walkMenuButton","walkPersonButton","walkBirdButton","settingButton","infoButton","wrapper","groupElement","total","groupEl","btnWrapper","btnEl","icon","label","arrow","dropdown","rect","centerX","subBtn","item","e","buttonId","visible","show","color","ToolbarManager","container","err","BimDialog","header","title","closeBtn","content","resizeHandle","width","height","pos","elRect","left","top","pW","pH","elW","elH","startX","startY","startLeft","startTop","onMouseDown","onMouseMove","onMouseUp","dx","dy","newLeft","newTop","maxLeft","maxTop","handle","startW","startH","newW","newH","BimInfoDialog","contentEl","infoTitle","infoList","actionBtn","DialogManager","BimEngine","desc","btnGroupContainer","dialogBtn","infoDialogBtn"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"bim-engine-sdk.es.js","sources":["../src/locales/zh-CN.ts","../src/locales/en-US.ts","../src/services/locale.ts","../src/themes/presets.ts","../src/services/theme.ts","../src/components/button-group/index.ts","../src/components/button-group/toolbar/index.ts","../src/managers/toolbar-manager.ts","../src/managers/button-group-manager.ts","../src/components/dialog/index.ts","../src/components/dialog/bimInfoDialog/index.ts","../src/managers/dialog-manager.ts","../src/bim-engine.ts"],"sourcesContent":["import { TranslationDictionary } from './types';\n\nexport const zhCN: TranslationDictionary = {\n common: {\n title: 'BimEngine',\n description: '这是一个使用 BIM-ENGINE。',\n openTestDialog: '打开测试弹窗',\n openInfoDialog: '打开信息弹窗 (封装版)',\n },\n toolbar: {\n home: '首页',\n info: '信息',\n location: '定位',\n setting: '设置',\n walk: '漫游',\n walkPerson: '人视',\n walkBird: '鸟瞰',\n walkMenu: '菜单',\n },\n dialog: {\n testTitle: '测试弹窗',\n testContent: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',\n },\n};\n","import { TranslationDictionary } from './types';\n\nexport const enUS: TranslationDictionary = {\n common: {\n title: 'BimEngine',\n description: 'This is a BIM-ENGINE demo.',\n openTestDialog: 'Open Test Dialog',\n openInfoDialog: 'Open Info Dialog (Wrapped)',\n },\n toolbar: {\n home: 'Home',\n info: 'Info',\n location: 'Location',\n setting: 'Settings',\n walk: 'Walk',\n walkPerson: 'Person',\n walkBird: 'Bird Eye',\n walkMenu: 'Menu',\n },\n dialog: {\n testTitle: 'Test Dialog',\n testContent: 'This is a draggable and resizable dialog. Try dragging the title bar or resizing from the bottom-right corner.
',\n },\n};\n","import { LocaleType, TranslationDictionary } from '../locales/types';\nimport { zhCN } from '../locales/zh-CN';\nimport { enUS } from '../locales/en-US';\n\ntype LocaleChangeListener = (locale: LocaleType) => void;\n\n/**\n * 语言管理器类\n */\nexport class LocaleManager {\n private currentLocale: LocaleType = 'zh-CN';\n private messages: Record = {\n 'zh-CN': zhCN,\n 'en-US': enUS,\n };\n private listeners: LocaleChangeListener[] = [];\n\n constructor() {\n // 默认初始化\n }\n\n /**\n * 获取当前语言\n */\n public getLocale(): LocaleType {\n return this.currentLocale;\n }\n\n /**\n * 切换语言\n */\n public setLocale(locale: LocaleType) {\n if (this.currentLocale === locale) return;\n this.currentLocale = locale;\n this.notifyListeners();\n }\n\n /**\n * 翻译核心方法\n */\n public t(key: string): string {\n if (!key) return '';\n \n const keys = key.split('.');\n let value: any = this.messages[this.currentLocale];\n \n for (const k of keys) {\n if (value && typeof value === 'object' && k in value) {\n value = value[k];\n } else {\n return key;\n }\n }\n return value as string;\n }\n\n /**\n * 订阅变更\n */\n public subscribe(listener: LocaleChangeListener): () => void {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n private notifyListeners() {\n this.listeners.forEach(listener => listener(this.currentLocale));\n }\n}\n\n// --- 导出单例 ---\nexport const localeManager = new LocaleManager();\n\n// --- 导出便捷方法 ---\n/**\n * 全局翻译函数\n * @param key 键路径 (如 'toolbar.home')\n */\nexport const t = (key: string): string => localeManager.t(key);\n","import { ThemeConfig } from './types';\n\n/**\n * 深色主题 (默认)\n */\nexport const darkTheme: ThemeConfig = {\n name: 'dark',\n primary: '#0078d4',\n primaryHover: '#0063b1',\n \n // 修改:背景色统一为浅灰,不再跟随深色模式变黑\n background: '#f5f5f5', \n panelBackground: 'rgba(30, 30, 30, 0.9)',\n \n // 注意:如果背景是浅色,主文字颜色通常需要是深色才能看清\n // 但这里的 textPrimary 主要是用于 UI 组件内部的。\n // 如果 BimEngine wrapper 上的文字直接显示在 background 上,\n // 我们可能需要区分 \"UI文字\" 和 \"页面文字\"。\n // 目前架构中:\n // theme.textPrimary 会应用到 wrapper.style.color (BimEngine.ts)\n // 以及 Toolbar/Dialog 的文字颜色。\n \n // 如果背景是浅灰,而 wrapper 文字设置为白色 (#ffffff),那就看不清了。\n // 这是一个语义冲突:\n // 1. Panel (Toolbar/Dialog) 是黑底,需要白字。\n // 2. Background (Wrapper) 是白底,需要黑字。\n \n // 既然您要求背景统一浅灰,那么 Wrapper 上的“直接子文本”应该是深色。\n // 但 Toolbar/Dialog 仍然是深色模式(黑底),它们需要白字。\n \n // 妥协方案:\n // 保持 textPrimary 为白色(为了适配黑���的 Toolbar/Dialog)。\n // 但是在 BimEngine 中,如果背景强制改为浅色,Wrapper 的默认文字颜色可能需要单独处理,\n // 或者我们可以认为 \"Wrapper\" 主要是承载 UI 组件的,直接写在 Wrapper 上的文字(标题/描述)\n // 应该有自己的样式,而不是直接继承 theme.textPrimary。\n \n // 在之前的 BimEngine.ts 中:\n // this.wrapper.style.color = theme.textPrimary;\n \n // 如果背景变浅灰,这里 textPrimary 还是白色的话,标题就看不见了。\n // 所以,深色模式下:\n // 背景:浅灰\n // 组件:深黑\n // 组件文字:白\n // 页面文字:黑 (问题点)\n \n // 让我们先按您的要求改背景。通常这种情况下,ThemeConfig 可能需要区分 \n // contentText (页面内容文字) 和 uiText (组件文字)。\n // 但为了不破坏现有结构,我将假定 textPrimary 主要服务于 UI 组件。\n // 为了让 Wrapper 上的标题可见,我们可能需要在 BimEngine 中移除对 wrapper.style.color 的强制设置,\n // 或者在 presets 里把 textPrimary 改回来?不对,改回来 Toolbar 就看不清了。\n \n // 方案:我将仅修改 background。\n // 至于 Wrapper 上的标题(BimEngine 标题),由于在最新的 BimEngine.ts 中\n // ���们已经移除了 titleEl 和 descEl(在之前的重构中),\n // 所以现在 Wrapper 里主要是 Toolbar 和 Dialog,它们有自己的 panelBackground。\n // 只要 Toolbar/Dialog 内部正常即可。\n \n textPrimary: '#ffffff', \n textSecondary: '#cccccc',\n \n border: '#444444',\n \n icon: '#cccccc',\n iconActive: '#ffffff',\n \n componentBackground: 'transparent',\n componentHover: '#333333',\n componentActive: 'rgba(255, 255, 255, 0.1)'\n};\n\n/**\n * 浅色主题\n */\nexport const lightTheme: ThemeConfig = {\n name: 'light',\n primary: '#0078d4',\n primaryHover: '#106ebe',\n \n // 统一为浅灰\n background: '#f5f5f5',\n panelBackground: '#ffffff',\n \n textPrimary: '#333333',\n textSecondary: '#666666',\n \n border: '#e0e0e0',\n \n icon: '#555555',\n iconActive: '#0078d4',\n \n componentBackground: 'transparent',\n componentHover: '#f0f0f0',\n componentActive: '#e0e0e0'\n};","import { ThemeConfig } from '../themes/types';\nimport { darkTheme, lightTheme } from '../themes/presets';\n\ntype ThemeChangeListener = (theme: ThemeConfig) => void;\n\n/**\n * 主题管理器 (单例)\n */\nexport class ThemeManager {\n private currentTheme: ThemeConfig = darkTheme;\n private listeners: ThemeChangeListener[] = [];\n\n constructor() {\n // 默认初始化\n }\n\n /**\n * 获取当前主题配置\n */\n public getTheme(): ThemeConfig {\n return this.currentTheme;\n }\n\n /**\n * 切换预设主题\n * @param themeName 'dark' | 'light'\n */\n public setTheme(themeName: 'dark' | 'light') {\n if (themeName === 'light') {\n this.applyTheme(lightTheme);\n } else {\n this.applyTheme(darkTheme);\n }\n }\n\n /**\n * 应用自定义主题配置\n * @param theme 配置对象\n */\n public setCustomTheme(theme: ThemeConfig) {\n this.applyTheme(theme);\n }\n\n /**\n * 内部应用主题逻辑\n */\n private applyTheme(theme: ThemeConfig) {\n this.currentTheme = theme;\n this.notifyListeners();\n }\n\n /**\n * 订阅主题变更\n */\n public subscribe(listener: ThemeChangeListener): () => void {\n this.listeners.push(listener);\n // 立即回调一次当前状态\n listener(this.currentTheme);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n private notifyListeners() {\n this.listeners.forEach(listener => listener(this.currentTheme));\n }\n}\n\n// 导出单例\nexport const themeManager = new ThemeManager();","import './index.css';\nimport type {\n OptButton,\n ButtonGroup,\n ButtonGroupOptions,\n ButtonConfig,\n ButtonGroupColors\n} from './index.type';\nimport { t, localeManager } from '../../services/locale';\nimport { themeManager } from '../../services/theme';\nimport type { ThemeConfig } from '../../themes/types';\nimport { IBimComponent } from '../../types/component';\n\n/**\n * 通用按钮组组件 (BimButtonGroup)\n */\nexport class BimButtonGroup implements IBimComponent {\n private container: HTMLElement;\n private options: ButtonGroupOptions;\n private groups: ButtonGroup[] = [];\n private activeBtnIds: Set = new Set();\n private btnRefs: Map = new Map();\n private dropdownElement: HTMLElement | null = null;\n private hoverTimeout: number | null = null;\n private customColors: Set = new Set(); // 记录用户自定义的颜色属性\n private unsubscribeLocale: (() => void) | null = null;\n private unsubscribeTheme: (() => void) | null = null;\n\n private readonly DEFAULT_ICON = ' ';\n\n constructor(options: ButtonGroupOptions) {\n const el = typeof options.container === 'string'\n ? document.getElementById(options.container)\n : options.container;\n\n if (!el) throw new Error('Container not found');\n\n this.container = el;\n // 合并默认配置\n this.options = {\n showLabel: true,\n visibility: {},\n direction: 'row', // 默认横向\n position: 'static', // 默认静态定位\n align: 'vertical', // 默认图标在上\n expand: 'down', // 默认向下展开\n ...options\n };\n\n // 记录初始传入的自定义颜色\n const colorKeys: (keyof ButtonGroupColors)[] = [\n 'backgroundColor', 'btnBackgroundColor', 'btnHoverColor',\n 'btnActiveColor', 'iconColor', 'iconActiveColor',\n 'textColor', 'textActiveColor'\n ];\n colorKeys.forEach(key => {\n if (options[key]) {\n this.customColors.add(key);\n }\n });\n\n this.initContainer();\n this.applyStyles();\n }\n\n private initContainer(): void {\n this.container.innerHTML = '';\n this.container.classList.add('bim-btn-group-root');\n\n if (this.options.direction === 'column') {\n this.container.classList.add('dir-column');\n } else {\n this.container.classList.add('dir-row');\n }\n\n if (this.options.className) {\n this.container.classList.add(this.options.className);\n }\n\n this.updatePosition();\n }\n\n private updatePosition() {\n const pos = this.options.position;\n const style = this.container.style;\n\n style.top = ''; style.bottom = ''; style.left = ''; style.right = ''; style.transform = '';\n\n if (pos === 'static') {\n this.container.classList.add('static');\n return;\n }\n\n this.container.classList.remove('static');\n this.container.style.position = 'absolute';\n\n if (typeof pos === 'object' && 'x' in pos) {\n style.left = `${pos.x}px`;\n style.top = `${pos.y}px`;\n } else {\n const margin = '20px';\n switch (pos) {\n case 'top-left':\n style.top = margin; style.left = margin;\n break;\n case 'top-center':\n style.top = margin; style.left = '50%'; style.transform = 'translateX(-50%)';\n break;\n case 'top-right':\n style.top = margin; style.right = margin;\n break;\n case 'bottom-left':\n style.bottom = margin; style.left = margin;\n break;\n case 'bottom-center':\n style.bottom = margin; style.left = '50%'; style.transform = 'translateX(-50%)';\n break;\n case 'bottom-right':\n style.bottom = margin; style.right = margin;\n break;\n case 'left-center':\n style.left = margin; style.top = '50%'; style.transform = 'translateY(-50%)';\n break;\n case 'right-center':\n style.right = margin; style.top = '50%'; style.transform = 'translateY(-50%)';\n break;\n case 'center':\n style.top = '50%'; style.left = '50%'; style.transform = 'translate(-50%, -50%)';\n break;\n }\n }\n }\n\n /**\n * 应用样式到容器\n */\n private applyStyles(): void {\n const style = this.container.style;\n if (this.options.backgroundColor) style.setProperty('--bim-btn-group-section-bg', this.options.backgroundColor);\n if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);\n if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);\n if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);\n if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);\n if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);\n if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);\n if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);\n }\n\n /**\n * 设置主题颜色\n * 只会应用到没有被用户自定义的颜色属性上\n */\n public setTheme(theme: ThemeConfig): void {\n const themeColors: ButtonGroupColors = {\n backgroundColor: theme.panelBackground,\n btnBackgroundColor: theme.componentBackground,\n btnHoverColor: theme.componentHover,\n btnActiveColor: theme.componentActive,\n iconColor: theme.icon,\n iconActiveColor: theme.iconActive,\n textColor: theme.textSecondary,\n textActiveColor: theme.textPrimary\n };\n\n // 只应用没有被自定义的颜色\n Object.entries(themeColors).forEach(([key, value]) => {\n const colorKey = key as keyof ButtonGroupColors;\n if (!this.customColors.has(colorKey)) {\n this.options[colorKey] = value;\n }\n });\n\n this.applyStyles();\n }\n\n /**\n * 直接设置颜色(强制覆盖)\n * 设置的颜色会被标记为自定义,后续的 setTheme 不会覆盖它们\n */\n public setColors(colors: ButtonGroupColors): void {\n // 更新 options\n this.options = { ...this.options, ...colors };\n\n // 标记这些颜色为自定义\n Object.keys(colors).forEach(key => {\n this.customColors.add(key as keyof ButtonGroupColors);\n });\n\n this.applyStyles();\n }\n\n public async init(): Promise {\n this.render();\n\n // 自动订阅语言变更\n this.unsubscribeLocale = localeManager.subscribe(() => {\n this.setLocales();\n });\n\n // 自动订阅主题变更\n this.unsubscribeTheme = themeManager.subscribe((theme) => {\n this.setTheme(theme);\n });\n }\n\n public setLocales(): void {\n this.render();\n }\n\n public addGroup(groupId: string, beforeGroupId?: string): void {\n if (this.groups.some(g => g.id === groupId)) return;\n const newGroup: ButtonGroup = { id: groupId, buttons: [] };\n if (beforeGroupId) {\n const index = this.groups.findIndex(g => g.id === beforeGroupId);\n index !== -1 ? this.groups.splice(index, 0, newGroup) : this.groups.push(newGroup);\n } else {\n this.groups.push(newGroup);\n }\n }\n\n public addButton(config: ButtonConfig): void {\n const { groupId, parentId } = config;\n const group = this.groups.find(g => g.id === groupId);\n if (!group) return;\n\n const button: OptButton = { ...config, children: config.children || [] };\n if (parentId) {\n const parentBtn = this.findButton(group.buttons, parentId);\n if (parentBtn) {\n if (!parentBtn.children) parentBtn.children = [];\n parentBtn.children.push(button);\n }\n } else {\n group.buttons.push(button);\n }\n }\n\n private findButton(buttons: OptButton[], id: string): OptButton | undefined {\n for (const btn of buttons) {\n if (btn.id === id) return btn;\n if (btn.children) {\n const found = this.findButton(btn.children, id);\n if (found) return found;\n }\n }\n return undefined;\n }\n\n public render(): void {\n this.container.innerHTML = '';\n this.btnRefs.clear();\n\n this.groups.forEach((group, index) => {\n const groupElement = this.renderGroup(group, index, this.groups.length);\n this.container.appendChild(groupElement);\n });\n }\n\n private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {\n const groupEl = document.createElement('div');\n groupEl.className = 'bim-btn-group-section';\n\n if (index < total - 1) {\n groupEl.classList.add('has-divider');\n }\n\n group.buttons.forEach(button => {\n if (this.isVisible(button.id)) {\n const btnWrapper = this.renderButton(button);\n groupEl.appendChild(btnWrapper);\n }\n });\n return groupEl;\n }\n\n private renderButton(button: OptButton): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'opt-btn-wrapper';\n\n const btnEl = document.createElement('div');\n btnEl.className = 'opt-btn';\n\n // 按钮优先使用自己的 align,否则使用全局配置,默认为 vertical\n const align = button.align || this.options.align || 'vertical';\n if (align === 'horizontal') {\n btnEl.classList.add('align-horizontal');\n } else {\n btnEl.classList.add('align-vertical');\n }\n\n if (this.activeBtnIds.has(button.id)) btnEl.classList.add('active');\n if (button.disabled) btnEl.classList.add('disabled');\n\n // 判断是否显示 label\n const hasLabel = this.options.showLabel && button.label;\n if (!hasLabel) {\n btnEl.classList.add('no-label');\n }\n\n // 应用按钮的自定义样式\n const iconSize = button.iconSize || 32;\n const minWidth = button.minWidth || 50;\n btnEl.style.minWidth = `${minWidth}px`;\n\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.style.width = `${iconSize}px`;\n icon.style.height = `${iconSize}px`;\n icon.innerHTML = this.getIcon(button.icon);\n btnEl.appendChild(icon);\n\n // 创建文字和箭头的容器,确保它们始终在一起(无论主轴是横是竖)\n const textWrapper = document.createElement('div');\n textWrapper.className = 'opt-btn-text-wrapper';\n\n if (this.options.showLabel && button.label) {\n const label = document.createElement('span');\n label.className = 'opt-btn-label';\n label.textContent = t(button.label);\n textWrapper.appendChild(label);\n }\n\n if (button.children && button.children.length > 0) {\n const arrow = document.createElement('span');\n arrow.className = 'opt-btn-arrow';\n arrow.textContent = '▼';\n textWrapper.appendChild(arrow);\n }\n\n // 只有当有内容时才添加 wrapper\n if (textWrapper.hasChildNodes()) {\n btnEl.appendChild(textWrapper);\n }\n\n btnEl.addEventListener('click', () => this.handleClick(button));\n btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));\n btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());\n\n this.btnRefs.set(button.id, btnEl);\n wrapper.appendChild(btnEl);\n return wrapper;\n }\n\n private handleClick(button: OptButton): void {\n if (button.disabled) return;\n if (!button.children || button.children.length === 0) {\n if (button.keepActive) {\n const wasActive = this.activeBtnIds.has(button.id);\n if (wasActive) this.activeBtnIds.delete(button.id);\n else this.activeBtnIds.add(button.id);\n this.updateButtonState(button.id);\n }\n this.closeDropdown();\n if (button.onClick) button.onClick(button);\n }\n }\n\n private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {\n if (this.hoverTimeout) clearTimeout(this.hoverTimeout);\n if (button.children && button.children.length > 0) {\n this.showDropdown(button, btnEl);\n } else {\n this.closeDropdown();\n }\n }\n\n private handleMouseLeave(): void {\n this.hoverTimeout = window.setTimeout(() => this.closeDropdown(), 200);\n }\n\n private showDropdown(button: OptButton, btnEl: HTMLElement): void {\n this.closeDropdown();\n if (!button.children) return;\n\n const dropdown = document.createElement('div');\n dropdown.className = 'opt-btn-dropdown';\n if (this.options.backgroundColor) dropdown.style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);\n\n // 获取按钮的位置信息\n const btnRect = btnEl.getBoundingClientRect();\n const expand = this.options.expand || 'down';\n\n // 根据主按钮组的方向设置下拉菜单的布局方向\n if (this.options.direction === 'row') {\n dropdown.style.flexDirection = 'column'; // 横向按钮组,菜单纵向排列\n } else {\n dropdown.style.flexDirection = 'row'; // 纵向按钮组,菜单横向排列\n }\n\n // 先添加到 DOM 以便计算尺寸\n document.body.appendChild(dropdown);\n\n // 添加菜单项\n button.children.forEach(subBtn => {\n if (this.isVisible(subBtn.id)) {\n const item = this.renderDropdownItem(subBtn);\n dropdown.appendChild(item);\n }\n });\n\n // 获取下拉菜单的实际尺寸\n const dropdownRect = dropdown.getBoundingClientRect();\n\n if (expand === 'up') {\n // 向上展开,与按钮水平居中对齐\n dropdown.style.bottom = (window.innerHeight - btnRect.top + 8) + 'px';\n dropdown.style.left = (btnRect.left + (btnRect.width - dropdownRect.width) / 2) + 'px';\n } else if (expand === 'down') {\n // 向下展开,与按钮水平居中对齐\n dropdown.style.top = (btnRect.bottom + 8) + 'px';\n dropdown.style.left = (btnRect.left + (btnRect.width - dropdownRect.width) / 2) + 'px';\n } else if (expand === 'right') {\n // 向右展开,与按钮垂直居中对齐\n dropdown.style.top = (btnRect.top + (btnRect.height - dropdownRect.height) / 2) + 'px';\n dropdown.style.left = (btnRect.right + 8) + 'px';\n } else if (expand === 'left') {\n // 向左展开,与按钮垂直居中对齐\n dropdown.style.top = (btnRect.top + (btnRect.height - dropdownRect.height) / 2) + 'px';\n dropdown.style.right = (window.innerWidth - btnRect.left + 8) + 'px';\n }\n\n dropdown.addEventListener('mouseenter', () => { if (this.hoverTimeout) clearTimeout(this.hoverTimeout); });\n dropdown.addEventListener('mouseleave', () => this.handleMouseLeave());\n this.dropdownElement = dropdown;\n }\n\n private renderDropdownItem(button: OptButton): HTMLElement {\n const item = document.createElement('div');\n item.className = 'opt-btn-dropdown-item';\n\n // 应用按钮的 align 设置,默认为 horizontal(图标在左)\n const align = button.align || 'horizontal';\n if (align === 'horizontal') {\n item.classList.add('align-horizontal');\n } else {\n item.classList.add('align-vertical');\n }\n\n // 应用按钮的自定义样式\n const iconSize = button.iconSize || 32; // 二级菜单默认图标更小\n const minWidth = button.minWidth; // 不设置默认值,让下拉菜单项保持紧凑\n if (minWidth) {\n item.style.minWidth = `${minWidth}px`;\n }\n\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.style.width = `${iconSize}px`;\n icon.style.height = `${iconSize}px`;\n icon.innerHTML = this.getIcon(button.icon);\n item.appendChild(icon);\n\n // 只有在 showLabel 为 true 时才显示 label\n if (this.options.showLabel && button.label) {\n const label = document.createElement('span');\n label.className = 'opt-btn-dropdown-label';\n label.textContent = t(button.label);\n item.appendChild(label);\n }\n\n item.addEventListener('click', (e) => { e.stopPropagation(); this.handleClick(button); });\n return item;\n }\n\n private closeDropdown(): void {\n if (this.dropdownElement) {\n this.dropdownElement.remove();\n this.dropdownElement = null;\n }\n this.btnRefs.forEach(btnEl => {\n const arrow = btnEl.querySelector('.opt-btn-arrow');\n if (arrow) arrow.classList.remove('rotated');\n });\n }\n\n private updateButtonState(buttonId: string): void {\n const btnEl = this.btnRefs.get(buttonId);\n if (btnEl) {\n this.activeBtnIds.has(buttonId) ? btnEl.classList.add('active') : btnEl.classList.remove('active');\n }\n }\n\n private getIcon(icon?: string): string { return icon || this.DEFAULT_ICON; }\n public updateButtonVisibility(id: string, visible: boolean): void {\n if (!this.options.visibility) this.options.visibility = {};\n this.options.visibility[id] = visible;\n this.render();\n }\n public setShowLabel(show: boolean): void {\n this.options.showLabel = show;\n this.updateLabelsVisibility();\n }\n\n private updateLabelsVisibility(): void {\n this.btnRefs.forEach((btnEl, buttonId) => {\n // 查找按钮配置\n const button = this.findButtonById(buttonId);\n if (!button) return;\n\n const hasLabel = this.options.showLabel && button.label;\n\n // 只需要更新 no-label 类,CSS 会处理显示/隐藏\n if (hasLabel) {\n btnEl.classList.remove('no-label');\n } else {\n btnEl.classList.add('no-label');\n }\n });\n }\n\n private findButtonById(id: string): OptButton | undefined {\n for (const group of this.groups) {\n const found = this.findButton(group.buttons, id);\n if (found) return found;\n }\n return undefined;\n }\n public setBackgroundColor(color: string): void { this.setColors({ backgroundColor: color }); }\n private isVisible(id: string): boolean { return this.options.visibility?.[id] !== false; }\n public destroy(): void {\n if (this.unsubscribeLocale) {\n this.unsubscribeLocale();\n this.unsubscribeLocale = null;\n }\n if (this.unsubscribeTheme) {\n this.unsubscribeTheme();\n this.unsubscribeTheme = null;\n }\n this.closeDropdown();\n this.container.innerHTML = '';\n this.btnRefs.clear();\n }\n}\n","import { BimButtonGroup } from '../index';\n\n/**\n * 底部工具栏 (Toolbar)\n * BimButtonGroup 的子类,专门用于加载工具栏默认按钮。\n */\nexport class Toolbar extends BimButtonGroup {\n /**\n * 重写初始化,加载默认按钮\n */\n public async init(): Promise {\n await super.init();\n\n // 动态加载默认按钮配置\n const { homeButton } = await import('./buttons/home');\n const { locationButton } = await import('./buttons/location');\n const { walkMenuButton } = await import('./buttons/walk/walk-menu');\n const { walkPersonButton } = await import('./buttons/walk/walk-person');\n const { walkBirdButton } = await import('./buttons/walk/walk-bird');\n const { settingButton } = await import('./buttons/setting');\n const { infoButton } = await import('./buttons/info');\n\n this.addGroup('group-1');\n this.addButton(homeButton);\n this.addButton(walkMenuButton);\n this.addButton(walkPersonButton);\n this.addButton(walkBirdButton);\n this.addButton(locationButton);\n this.addGroup('group-2');\n this.addButton(settingButton);\n this.addButton(infoButton);\n\n this.render();\n }\n}\n","import type { ButtonGroupColors, ButtonConfig } from '../components/button-group/index.type';\nimport { Toolbar } from '../components/button-group/toolbar';\nimport type { ThemeConfig } from '../themes/types';\n\n/**\n * 底部工具栏管理器 (ToolbarManager)\n * 仅负责管理底部工具栏实例。\n */\nexport class ToolbarManager {\n private toolbar: Toolbar | null = null;\n private toolbarContainer: HTMLElement | null = null;\n private container: HTMLElement;\n\n constructor(container: HTMLElement) {\n this.container = container;\n this.init();\n }\n\n private init() {\n // 创建底部工具栏专用容器\n this.toolbarContainer = document.createElement('div');\n this.toolbarContainer.id = 'opt-btn-groups';\n this.toolbarContainer.className = 'bim-engine-opt-btn-container is-bottom-toolbar';\n this.container.appendChild(this.toolbarContainer);\n\n this.toolbar = new Toolbar({\n container: this.toolbarContainer,\n showLabel: true,\n direction: 'row',\n position: 'bottom-center', // 底部居中\n align: 'vertical', // 图标在上\n expand: 'up' // 向上展开\n });\n\n this.toolbar.init();\n }\n\n public updateTheme(theme: ThemeConfig) {\n this.toolbar?.setTheme(theme);\n }\n\n public refresh() {\n this.toolbar?.render();\n }\n\n public destroy() {\n this.toolbar?.destroy();\n this.toolbar = null;\n }\n\n // --- 转发 API ---\n public addGroup(groupId: string, beforeGroupId?: string) { this.toolbar?.addGroup(groupId, beforeGroupId); this.toolbar?.render(); }\n public addButton(config: ButtonConfig) { this.toolbar?.addButton(config); this.toolbar?.render(); }\n public setButtonVisibility(id: string, v: boolean) { this.toolbar?.updateButtonVisibility(id, v); }\n public setShowLabel(show: boolean) { this.toolbar?.setShowLabel(show); }\n public setVisible(visible: boolean) {\n if (this.toolbarContainer) {\n this.toolbarContainer.style.visibility = visible ? 'visible' : 'hidden';\n }\n }\n public setBackgroundColor(color: string) { this.toolbar?.setBackgroundColor(color); }\n public setColors(colors: ButtonGroupColors) { this.toolbar?.setColors(colors); }\n}\n","import { BimButtonGroup } from '../components/button-group';\nimport type { ButtonGroupOptions } from '../components/button-group/index.type';\nimport type { ThemeConfig } from '../themes/types';\n\n/**\n * 通用按钮组管理器\n * 负责创建和管理除底部工具栏以外的其他按钮组。\n */\nexport class ButtonGroupManager {\n private activeGroups: BimButtonGroup[] = [];\n private container: HTMLElement;\n\n constructor(container: HTMLElement) {\n this.container = container;\n }\n\n /**\n * 创建一个新的按钮组\n */\n public create(options: Omit): BimButtonGroup {\n // 自动创建一个 div 作为容器\n const groupContainer = document.createElement('div');\n this.container.appendChild(groupContainer);\n\n const group = new BimButtonGroup({\n container: groupContainer,\n ...options\n });\n \n // 立即初始化\n group.init();\n this.activeGroups.push(group);\n return group;\n }\n\n public updateTheme(theme: ThemeConfig) {\n this.activeGroups.forEach(g => g.setTheme(theme));\n }\n\n public refresh() {\n this.activeGroups.forEach(g => g.render());\n }\n\n public destroy() {\n this.activeGroups.forEach(g => g.destroy());\n this.activeGroups = [];\n }\n}\n","import './index.css';\nimport type { DialogOptions } from './index.type';\nimport type { ThemeConfig } from '../../themes/types';\nimport { IBimComponent } from '../../types/component';\nimport { themeManager } from '../../services/theme';\nimport { t, localeManager } from '../../services/locale';\n\n/**\n * 通用弹窗组件类\n * 支持拖拽、缩放、自定义内容和位置。\n */\nexport class BimDialog implements IBimComponent {\n private element: HTMLElement;\n private options: DialogOptions;\n private container: HTMLElement;\n private header: HTMLElement;\n private contentArea: HTMLElement;\n private _isDestroyed = false;\n private _isInitialized = false;\n private unsubscribeTheme: (() => void) | null = null;\n private unsubscribeLocale: (() => void) | null = null;\n\n /**\n * 构造函数\n * @param options 弹窗配置选项\n */\n constructor(options: DialogOptions) {\n // 合并默认配置\n this.options = {\n title: 'Dialog',\n width: 300,\n height: 'auto',\n position: 'center',\n draggable: true,\n resizable: false,\n minWidth: 200,\n minHeight: 100,\n ...options\n };\n this.container = options.container;\n\n // 创建 DOM 结构\n this.element = this.createDom();\n this.header = this.element.querySelector('.bim-dialog-header') as HTMLElement;\n this.contentArea = this.element.querySelector('.bim-dialog-content') as HTMLElement;\n\n // 自动初始化 (为了兼容现有逻辑)\n this.init();\n }\n\n /**\n * 设置主题\n * @param theme 全局主题配置\n */\n public setTheme(theme: ThemeConfig) {\n const style = this.element.style;\n if (!this.options.backgroundColor) style.setProperty('--bim-dialog-bg', theme.panelBackground);\n if (!this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', theme.componentHover);\n if (!this.options.titleColor) style.setProperty('--bim-dialog-title-color', theme.textPrimary);\n if (!this.options.textColor) style.setProperty('--bim-dialog-text-color', theme.textPrimary);\n if (!this.options.borderColor) style.setProperty('--bim-dialog-border-color', theme.border);\n }\n\n /**\n * 初始化组件功能 (接口实现)\n */\n public init() {\n if (this._isInitialized) return;\n\n this.container.appendChild(this.element);\n\n // 必须先挂载才能计算尺寸进行定位\n this.initPosition();\n\n if (this.options.draggable) {\n this.initDrag();\n }\n\n if (this.options.resizable) {\n this.initResize();\n }\n\n this._isInitialized = true;\n\n // 调用弹窗开启后回调\n if (this.options.onOpen) {\n this.options.onOpen();\n }\n\n // 自动订阅主题变更\n this.unsubscribeTheme = themeManager.subscribe((theme) => {\n this.setTheme(theme);\n });\n\n // 自动订阅语言变更\n this.unsubscribeLocale = localeManager.subscribe(() => {\n this.setLocales();\n });\n }\n\n public setLocales(): void {\n if (this.options.title) {\n const titleEl = this.header.querySelector('.bim-dialog-title');\n if (titleEl) {\n titleEl.textContent = t(this.options.title);\n }\n }\n }\n\n /**\n * 创建弹窗的 DOM 结构\n */\n private createDom(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'bim-dialog';\n\n if (this.options.id) el.id = this.options.id;\n\n // 应用颜色配置到 CSS 变量 (局部作用域)\n const style = el.style;\n if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor);\n if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor);\n if (this.options.titleColor) style.setProperty('--bim-dialog-title-color', this.options.titleColor);\n if (this.options.textColor) style.setProperty('--bim-dialog-text-color', this.options.textColor);\n if (this.options.borderColor) style.setProperty('--bim-dialog-border-color', this.options.borderColor);\n\n // 设置初始尺寸\n this.setSize(el, this.options.width, this.options.height);\n\n // 创建标题栏 (Header)\n const header = document.createElement('div');\n header.className = 'bim-dialog-header';\n if (this.options.draggable) header.classList.add('draggable');\n\n const title = document.createElement('span');\n title.className = 'bim-dialog-title';\n title.textContent = this.options.title ? t(this.options.title) : '';\n\n const closeBtn = document.createElement('span');\n closeBtn.className = 'bim-dialog-close';\n closeBtn.innerHTML = '×';\n closeBtn.onclick = () => this.close();\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // 创建内容区域 (Content)\n const content = document.createElement('div');\n content.className = 'bim-dialog-content';\n if (typeof this.options.content === 'string') {\n content.innerHTML = this.options.content;\n } else if (this.options.content instanceof HTMLElement) {\n content.appendChild(this.options.content);\n }\n\n el.appendChild(header);\n el.appendChild(content);\n\n // 如果允许缩放,创建缩放手柄\n if (this.options.resizable) {\n const resizeHandle = document.createElement('div');\n resizeHandle.className = 'bim-dialog-resize-handle';\n el.appendChild(resizeHandle);\n }\n\n return el;\n }\n\n /**\n * 设置元素尺寸\n */\n private setSize(el: HTMLElement, width?: number | string, height?: number | string) {\n if (width !== undefined) {\n el.style.width = typeof width === 'number' ? `${width}px` : width;\n }\n if (height !== undefined) {\n el.style.height = typeof height === 'number' ? `${height}px` : height;\n }\n }\n\n /**\n * 初始化弹窗位置\n */\n private initPosition() {\n const pos = this.options.position;\n\n const elRect = this.element.getBoundingClientRect();\n\n // 计算相对父容器的定位\n let left = 0;\n let top = 0;\n\n const pW = this.container.clientWidth;\n const pH = this.container.clientHeight;\n const elW = elRect.width;\n const elH = elRect.height;\n\n if (typeof pos === 'object' && 'x' in pos) {\n left = pos.x;\n top = pos.y;\n } else {\n switch (pos) {\n case 'center':\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n break;\n case 'top-left': left = 0; top = 0; break;\n case 'top-center': left = (pW - elW) / 2; top = 0; break;\n case 'top-right': left = pW - elW; top = 0; break;\n case 'left-center': left = 0; top = (pH - elH) / 2; break;\n case 'right-center': left = pW - elW; top = (pH - elH) / 2; break;\n case 'bottom-left': left = 0; top = pH - elH; break;\n case 'bottom-center': left = (pW - elW) / 2; top = pH - elH; break;\n case 'bottom-right': left = pW - elW; top = pH - elH; break;\n default:\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n }\n }\n\n // 简单的边界检查,防止初始位置溢出\n left = Math.max(0, Math.min(left, pW - elW));\n top = Math.max(0, Math.min(top, pH - elH));\n\n this.element.style.left = `${left}px`;\n this.element.style.top = `${top}px`;\n }\n\n /**\n * 初始化拖拽功能\n */\n private initDrag() {\n let startX = 0;\n let startY = 0;\n let startLeft = 0;\n let startTop = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n startX = e.clientX;\n startY = e.clientY;\n startLeft = this.element.offsetLeft;\n startTop = this.element.offsetTop;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n let newLeft = startLeft + dx;\n let newTop = startTop + dy;\n\n // 边界限制,防止拖出容器\n const maxLeft = this.container.clientWidth - this.element.offsetWidth;\n const maxTop = this.container.clientHeight - this.element.offsetHeight;\n\n newLeft = Math.max(0, Math.min(newLeft, maxLeft));\n newTop = Math.max(0, Math.min(newTop, maxTop));\n\n this.element.style.left = `${newLeft}px`;\n this.element.style.top = `${newTop}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n this.header.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 初始化缩放功能\n */\n private initResize() {\n const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;\n if (!handle) return;\n\n let startX = 0;\n let startY = 0;\n let startW = 0;\n let startH = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n startX = e.clientX;\n startY = e.clientY;\n startW = this.element.offsetWidth;\n startH = this.element.offsetHeight;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n const newW = Math.max(this.options.minWidth || 100, startW + dx);\n const newH = Math.max(this.options.minHeight || 50, startH + dy);\n\n this.element.style.width = `${newW}px`;\n this.element.style.height = `${newH}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 动态设置内容\n * @param content 内容元素或 HTML 字符串\n */\n public setContent(content: HTMLElement | string) {\n this.contentArea.innerHTML = '';\n if (typeof content === 'string') {\n this.contentArea.innerHTML = content;\n } else {\n this.contentArea.appendChild(content);\n }\n }\n\n /**\n * 关闭弹窗并销毁\n */\n public close() {\n if (this._isDestroyed) return;\n if (this.unsubscribeTheme) {\n this.unsubscribeTheme();\n this.unsubscribeTheme = null;\n }\n if (this.unsubscribeLocale) {\n this.unsubscribeLocale();\n this.unsubscribeLocale = null;\n }\n this.element.remove();\n this._isDestroyed = true;\n if (this.options.onClose) {\n this.options.onClose();\n }\n }\n\n /**\n * 销毁组件 (接口实现)\n */\n public destroy() {\n this.close();\n }\n}\n","import './index.css';\nimport { BimDialog } from '../index';\n\n/**\n * BimInfoDialog (继承版)\n * 这是一个展示项目信息的业务弹窗组件,直接继承自 BimDialog。\n */\nexport class BimInfoDialog extends BimDialog {\n /**\n * 构造函数\n * @param container 父容器\n */\n constructor(container: HTMLElement) {\n // 1. 准备内容 DOM\n const contentEl = document.createElement('div');\n contentEl.className = 'bim-info-dialog-content';\n\n const infoTitle = document.createElement('h3');\n infoTitle.textContent = 'Model Information';\n\n const infoList = document.createElement('ul');\n infoList.innerHTML = `\n Name: Sample Project \n Version: 1.0.0 \n Date: ${new Date().toLocaleDateString()} \n Status: Active \n `;\n\n const actionBtn = document.createElement('button');\n actionBtn.textContent = 'Update Status';\n actionBtn.style.marginTop = '10px';\n actionBtn.onclick = () => {\n alert('Status updated!');\n };\n\n contentEl.appendChild(infoTitle);\n contentEl.appendChild(infoList);\n contentEl.appendChild(actionBtn);\n\n // 2. 调用父类构造函数,传入特定的配置\n super({\n container: container,\n title: 'dialog.testTitle',\n content: contentEl,\n width: 320,\n height: 'auto',\n position: 'center',\n resizable: true,\n draggable: true,\n // 可以在这里添加特定的 onClose 逻辑\n onClose: () => {\n console.log('Info dialog closed');\n },\n onOpen: () => {\n console.log('Info dialog opened');\n }\n });\n\n // 3. 如果有特定于子类的初始化逻辑,可以在 super() 之后执行\n // 例如:this.element.classList.add('my-special-class');\n }\n\n // 不需要再手动实现 setTheme, destroy, close, init\n // 它们都已从 BimDialog 继承\n}","import { BimDialog } from '../components/dialog';\nimport { BimInfoDialog } from '../components/dialog/bimInfoDialog';\nimport type { DialogOptions } from '../components/dialog/index.type';\nimport type { ThemeConfig } from '../themes/types';\nimport { themeManager } from '../services/theme'; // 修正路径\n\n/**\n * 弹窗管理器\n * 负责创建和管理应用中的各类弹窗。\n */\nexport class DialogManager {\n /** 弹窗挂载的父容器 */\n private container: HTMLElement;\n /** 活跃的弹窗实例列表 */\n private activeDialogs: BimDialog[] = [];\n\n /**\n * 构造函数\n * @param container 弹窗挂载的目标容器\n */\n constructor(container: HTMLElement) {\n this.container = container;\n }\n\n /**\n * 创建一个通用弹窗\n * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)\n * @returns BimDialog 实例\n */\n public create(options: Omit): BimDialog {\n const dialog = new BimDialog({\n container: this.container,\n ...options,\n onClose: () => {\n // 从活跃列表中移除\n this.activeDialogs = this.activeDialogs.filter(d => d !== dialog);\n if (options.onClose) options.onClose();\n }\n });\n\n // 应用当前主题\n dialog.setTheme(themeManager.getTheme());\n\n this.activeDialogs.push(dialog);\n return dialog;\n }\n\n /**\n * 显示二次封装的模型信息弹窗\n * 演示如何调用特定的业务弹窗组件\n */\n public showInfoDialog() {\n // 最佳实践:所有弹窗应通过 create 统一管理,或者手动加入管理。\n new BimInfoDialog(this.container);\n // 暂时不做主题追踪,作为遗留逻辑保留\n }\n\n /**\n * 响应全局主题变更\n * @param theme 全局主题配置\n */\n public updateTheme(theme: ThemeConfig) {\n this.activeDialogs.forEach(dialog => {\n if (dialog.setTheme) {\n dialog.setTheme(theme);\n }\n });\n }\n}","import './bim-engine.css';\nimport { ToolbarManager } from './managers/toolbar-manager';\nimport { ButtonGroupManager } from './managers/button-group-manager';\nimport { DialogManager } from './managers/dialog-manager';\nimport { localeManager } from './services/locale';\nimport { themeManager } from './services/theme';\nimport type { LocaleType } from './locales/types';\nimport type { ThemeType, ThemeConfig } from './themes/types';\n\nexport class BimEngine {\n private container: HTMLElement;\n private wrapper: HTMLElement | null = null;\n private topLeftGroup: any = null; // 保存左上角按钮组的引用\n\n public toolbar: ToolbarManager | null = null; // 底部专用\n public buttonGroup: ButtonGroupManager | null = null; // 通用\n public dialog: DialogManager | null = null;\n\n public get localeManager() { return localeManager; }\n public get themeManager() { return themeManager; }\n\n constructor(container: HTMLElement | string, options?: { locale?: LocaleType; theme?: ThemeType }) {\n const el = typeof container === 'string' ? document.getElementById(container) : container;\n if (!el) throw new Error('Container not found');\n this.container = el;\n\n if (options?.locale) localeManager.setLocale(options.locale);\n if (options?.theme) {\n if (options.theme === 'custom') {\n console.warn('Custom theme should be set via setCustomTheme().');\n } else {\n themeManager.setTheme(options.theme);\n }\n }\n\n this.init();\n }\n\n public setLocale(locale: LocaleType) { localeManager.setLocale(locale); }\n public getLocale(): LocaleType { return localeManager.getLocale(); }\n public setTheme(theme: 'dark' | 'light') { themeManager.setTheme(theme); }\n public setCustomTheme(theme: ThemeConfig) { themeManager.setCustomTheme(theme); }\n\n private init() {\n this.container.innerHTML = '';\n this.wrapper = document.createElement('div');\n this.wrapper.className = 'bim-engine-wrapper';\n this.container.appendChild(this.wrapper);\n\n // 初始化管理器\n this.dialog = new DialogManager(this.wrapper);\n this.toolbar = new ToolbarManager(this.wrapper);\n this.buttonGroup = new ButtonGroupManager(this.wrapper);\n\n // --- 创建左上角按钮组 (需求 1 & 2) ---\n this.createTopLeftGroup();\n\n // 初始主题\n this.updateTheme(themeManager.getTheme());\n\n // 在主题更新后,设置左上角按钮组的自定义颜色\n if (this.topLeftGroup) {\n this.topLeftGroup.setColors({\n backgroundColor: '#ff00ff'\n });\n }\n themeManager.subscribe((theme) => {\n this.updateTheme(theme);\n });\n }\n\n private createTopLeftGroup() {\n if (!this.buttonGroup) return;\n\n this.topLeftGroup = this.buttonGroup.create({\n position: 'top-left',\n direction: 'column',\n align: 'vertical',\n backgroundColor: '#ff00ff', // 自定义背景色,不会被主题覆盖\n showLabel: false\n });\n\n this.topLeftGroup.addGroup('main');\n this.topLeftGroup.addButton({\n id: 'menu-btn',\n groupId: 'main',\n type: 'button',\n label: 'Menu', // 应该用 translation key\n icon: ' ',\n onClick: () => {\n alert(\"点击按钮\")\n }\n });\n\n // 手动 render 一次以显示\n this.topLeftGroup.render();\n }\n\n private updateTheme(theme: ThemeConfig) {\n if (this.wrapper) {\n this.wrapper.style.backgroundColor = theme.background;\n this.wrapper.style.color = theme.textPrimary;\n }\n }\n\n public destroy() {\n this.toolbar?.destroy();\n this.buttonGroup?.destroy();\n this.dialog = null;\n this.container.innerHTML = '';\n }\n}\n"],"names":["zhCN","enUS","LocaleManager","locale","key","keys","value","k","listener","l","localeManager","t","darkTheme","lightTheme","ThemeManager","themeName","theme","themeManager","BimButtonGroup","options","el","pos","style","margin","themeColors","colorKey","colors","groupId","beforeGroupId","g","newGroup","index","config","parentId","group","button","parentBtn","buttons","id","btn","found","groupElement","total","groupEl","btnWrapper","wrapper","btnEl","iconSize","minWidth","icon","textWrapper","label","arrow","dropdown","btnRect","expand","subBtn","item","dropdownRect","e","buttonId","visible","show","color","Toolbar","homeButton","locationButton","walkMenuButton","walkPersonButton","walkBirdButton","settingButton","infoButton","ToolbarManager","container","v","ButtonGroupManager","groupContainer","BimDialog","titleEl","header","title","closeBtn","content","resizeHandle","width","height","elRect","left","top","pW","pH","elW","elH","startX","startY","startLeft","startTop","onMouseDown","onMouseMove","onMouseUp","dx","dy","newLeft","newTop","maxLeft","maxTop","handle","startW","startH","newW","newH","BimInfoDialog","contentEl","infoTitle","infoList","actionBtn","DialogManager","dialog","d","BimEngine"],"mappings":"AAEO,MAAMA,IAA8B;AAAA,EACzC,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,EAAA;AAAA,EAElB,SAAS;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,UAAU;AAAA,EAAA;AAAA,EAEZ,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,EAAA;AAEjB,GCrBaC,IAA8B;AAAA,EACzC,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,EAAA;AAAA,EAElB,SAAS;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,UAAU;AAAA,EAAA;AAAA,EAEZ,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,EAAA;AAEjB;ACdO,MAAMC,EAAc;AAAA,EACjB,gBAA4B;AAAA,EAC5B,WAAsD;AAAA,IAC5D,SAASF;AAAA,IACT,SAASC;AAAA,EAAA;AAAA,EAEH,YAAoC,CAAA;AAAA,EAE5C,cAAc;AAAA,EAEd;AAAA;AAAA;AAAA;AAAA,EAKO,YAAwB;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,UAAUE,GAAoB;AACnC,IAAI,KAAK,kBAAkBA,MAC3B,KAAK,gBAAgBA,GACrB,KAAK,gBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKO,EAAEC,GAAqB;AAC5B,QAAI,CAACA,EAAK,QAAO;AAEjB,UAAMC,IAAOD,EAAI,MAAM,GAAG;AAC1B,QAAIE,IAAa,KAAK,SAAS,KAAK,aAAa;AAEjD,eAAWC,KAAKF;AACd,UAAIC,KAAS,OAAOA,KAAU,YAAYC,KAAKD;AAC7C,QAAAA,IAAQA,EAAMC,CAAC;AAAA;AAEf,eAAOH;AAGX,WAAOE;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKO,UAAUE,GAA4C;AAC3D,gBAAK,UAAU,KAAKA,CAAQ,GACrB,MAAM;AACX,WAAK,YAAY,KAAK,UAAU,OAAO,CAAAC,MAAKA,MAAMD,CAAQ;AAAA,IAC5D;AAAA,EACF;AAAA,EAEQ,kBAAkB;AACxB,SAAK,UAAU,QAAQ,CAAAA,MAAYA,EAAS,KAAK,aAAa,CAAC;AAAA,EACjE;AACF;AAGO,MAAME,IAAgB,IAAIR,EAAA,GAOpBS,IAAI,CAACP,MAAwBM,EAAc,EAAEN,CAAG,GC1EhDQ,IAAyB;AAAA,EAClC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,cAAc;AAAA;AAAA,EAGd,YAAY;AAAA,EACZ,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8CjB,aAAa;AAAA,EACb,eAAe;AAAA,EAEf,QAAQ;AAAA,EAER,MAAM;AAAA,EACN,YAAY;AAAA,EAEZ,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,iBAAiB;AACrB,GAKaC,IAA0B;AAAA,EACnC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,cAAc;AAAA;AAAA,EAGd,YAAY;AAAA,EACZ,iBAAiB;AAAA,EAEjB,aAAa;AAAA,EACb,eAAe;AAAA,EAEf,QAAQ;AAAA,EAER,MAAM;AAAA,EACN,YAAY;AAAA,EAEZ,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,iBAAiB;AACrB;ACtFO,MAAMC,EAAa;AAAA,EACd,eAA4BF;AAAA,EAC5B,YAAmC,CAAA;AAAA,EAE3C,cAAc;AAAA,EAEd;AAAA;AAAA;AAAA;AAAA,EAKO,WAAwB;AAC3B,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,SAASG,GAA6B;AACzC,IAAIA,MAAc,UACd,KAAK,WAAWF,CAAU,IAE1B,KAAK,WAAWD,CAAS;AAAA,EAEjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,eAAeI,GAAoB;AACtC,SAAK,WAAWA,CAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAWA,GAAoB;AACnC,SAAK,eAAeA,GACpB,KAAK,gBAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKO,UAAUR,GAA2C;AACxD,gBAAK,UAAU,KAAKA,CAAQ,GAE5BA,EAAS,KAAK,YAAY,GACnB,MAAM;AACT,WAAK,YAAY,KAAK,UAAU,OAAO,CAAAC,MAAKA,MAAMD,CAAQ;AAAA,IAC9D;AAAA,EACJ;AAAA,EAEQ,kBAAkB;AACtB,SAAK,UAAU,QAAQ,CAAAA,MAAYA,EAAS,KAAK,YAAY,CAAC;AAAA,EAClE;AACJ;AAGO,MAAMS,IAAe,IAAIH,EAAA;ACrDzB,MAAMI,EAAwC;AAAA,EACzC;AAAA,EACA;AAAA,EACA,SAAwB,CAAA;AAAA,EACxB,mCAAgC,IAAA;AAAA,EAChC,8BAAwC,IAAA;AAAA,EACxC,kBAAsC;AAAA,EACtC,eAA8B;AAAA,EAC9B,mCAAiD,IAAA;AAAA;AAAA,EACjD,oBAAyC;AAAA,EACzC,mBAAwC;AAAA,EAE/B,eAAe;AAAA,EAEhC,YAAYC,GAA6B;AACrC,UAAMC,IAAK,OAAOD,EAAQ,aAAc,WAClC,SAAS,eAAeA,EAAQ,SAAS,IACzCA,EAAQ;AAEd,QAAI,CAACC,EAAI,OAAM,IAAI,MAAM,qBAAqB;AAE9C,SAAK,YAAYA,GAEjB,KAAK,UAAU;AAAA,MACX,WAAW;AAAA,MACX,YAAY,CAAA;AAAA,MACZ,WAAW;AAAA;AAAA,MACX,UAAU;AAAA;AAAA,MACV,OAAO;AAAA;AAAA,MACP,QAAQ;AAAA;AAAA,MACR,GAAGD;AAAA,IAAA,GAIwC;AAAA,MAC3C;AAAA,MAAmB;AAAA,MAAsB;AAAA,MACzC;AAAA,MAAkB;AAAA,MAAa;AAAA,MAC/B;AAAA,MAAa;AAAA,IAAA,EAEP,QAAQ,CAAAf,MAAO;AACrB,MAAIe,EAAQf,CAAG,KACX,KAAK,aAAa,IAAIA,CAAG;AAAA,IAEjC,CAAC,GAED,KAAK,cAAA,GACL,KAAK,YAAA;AAAA,EACT;AAAA,EAEQ,gBAAsB;AAC1B,SAAK,UAAU,YAAY,IAC3B,KAAK,UAAU,UAAU,IAAI,oBAAoB,GAE7C,KAAK,QAAQ,cAAc,WAC3B,KAAK,UAAU,UAAU,IAAI,YAAY,IAEzC,KAAK,UAAU,UAAU,IAAI,SAAS,GAGtC,KAAK,QAAQ,aACb,KAAK,UAAU,UAAU,IAAI,KAAK,QAAQ,SAAS,GAGvD,KAAK,eAAA;AAAA,EACT;AAAA,EAEQ,iBAAiB;AACrB,UAAMiB,IAAM,KAAK,QAAQ,UACnBC,IAAQ,KAAK,UAAU;AAI7B,QAFAA,EAAM,MAAM,IAAIA,EAAM,SAAS,IAAIA,EAAM,OAAO,IAAIA,EAAM,QAAQ,IAAIA,EAAM,YAAY,IAEpFD,MAAQ,UAAU;AAClB,WAAK,UAAU,UAAU,IAAI,QAAQ;AACrC;AAAA,IACJ;AAKA,QAHA,KAAK,UAAU,UAAU,OAAO,QAAQ,GACxC,KAAK,UAAU,MAAM,WAAW,YAE5B,OAAOA,KAAQ,YAAY,OAAOA;AAClC,MAAAC,EAAM,OAAO,GAAGD,EAAI,CAAC,MACrBC,EAAM,MAAM,GAAGD,EAAI,CAAC;AAAA,SACjB;AACH,YAAME,IAAS;AACf,cAAQF,GAAA;AAAA,QACJ,KAAK;AACD,UAAAC,EAAM,MAAMC,GAAQD,EAAM,OAAOC;AACjC;AAAA,QACJ,KAAK;AACD,UAAAD,EAAM,MAAMC,GAAQD,EAAM,OAAO,OAAOA,EAAM,YAAY;AAC1D;AAAA,QACJ,KAAK;AACD,UAAAA,EAAM,MAAMC,GAAQD,EAAM,QAAQC;AAClC;AAAA,QACJ,KAAK;AACD,UAAAD,EAAM,SAASC,GAAQD,EAAM,OAAOC;AACpC;AAAA,QACJ,KAAK;AACD,UAAAD,EAAM,SAASC,GAAQD,EAAM,OAAO,OAAOA,EAAM,YAAY;AAC7D;AAAA,QACJ,KAAK;AACD,UAAAA,EAAM,SAASC,GAAQD,EAAM,QAAQC;AACrC;AAAA,QACJ,KAAK;AACD,UAAAD,EAAM,OAAOC,GAAQD,EAAM,MAAM,OAAOA,EAAM,YAAY;AAC1D;AAAA,QACJ,KAAK;AACD,UAAAA,EAAM,QAAQC,GAAQD,EAAM,MAAM,OAAOA,EAAM,YAAY;AAC3D;AAAA,QACJ,KAAK;AACD,UAAAA,EAAM,MAAM,OAAOA,EAAM,OAAO,OAAOA,EAAM,YAAY;AACzD;AAAA,MAAA;AAAA,IAEZ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AACxB,UAAMA,IAAQ,KAAK,UAAU;AAC7B,IAAI,KAAK,QAAQ,mBAAiBA,EAAM,YAAY,8BAA8B,KAAK,QAAQ,eAAe,GAC1G,KAAK,QAAQ,sBAAoBA,EAAM,YAAY,gBAAgB,KAAK,QAAQ,kBAAkB,GAClG,KAAK,QAAQ,iBAAeA,EAAM,YAAY,sBAAsB,KAAK,QAAQ,aAAa,GAC9F,KAAK,QAAQ,kBAAgBA,EAAM,YAAY,uBAAuB,KAAK,QAAQ,cAAc,GACjG,KAAK,QAAQ,aAAWA,EAAM,YAAY,oBAAoB,KAAK,QAAQ,SAAS,GACpF,KAAK,QAAQ,mBAAiBA,EAAM,YAAY,2BAA2B,KAAK,QAAQ,eAAe,GACvG,KAAK,QAAQ,aAAWA,EAAM,YAAY,wBAAwB,KAAK,QAAQ,SAAS,GACxF,KAAK,QAAQ,mBAAiBA,EAAM,YAAY,+BAA+B,KAAK,QAAQ,eAAe;AAAA,EACnH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,SAASN,GAA0B;AACtC,UAAMQ,IAAiC;AAAA,MACnC,iBAAiBR,EAAM;AAAA,MACvB,oBAAoBA,EAAM;AAAA,MAC1B,eAAeA,EAAM;AAAA,MACrB,gBAAgBA,EAAM;AAAA,MACtB,WAAWA,EAAM;AAAA,MACjB,iBAAiBA,EAAM;AAAA,MACvB,WAAWA,EAAM;AAAA,MACjB,iBAAiBA,EAAM;AAAA,IAAA;AAI3B,WAAO,QAAQQ,CAAW,EAAE,QAAQ,CAAC,CAACpB,GAAKE,CAAK,MAAM;AAClD,YAAMmB,IAAWrB;AACjB,MAAK,KAAK,aAAa,IAAIqB,CAAQ,MAC/B,KAAK,QAAQA,CAAQ,IAAInB;AAAA,IAEjC,CAAC,GAED,KAAK,YAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAUoB,GAAiC;AAE9C,SAAK,UAAU,EAAE,GAAG,KAAK,SAAS,GAAGA,EAAA,GAGrC,OAAO,KAAKA,CAAM,EAAE,QAAQ,CAAAtB,MAAO;AAC/B,WAAK,aAAa,IAAIA,CAA8B;AAAA,IACxD,CAAC,GAED,KAAK,YAAA;AAAA,EACT;AAAA,EAEA,MAAa,OAAsB;AAC/B,SAAK,OAAA,GAGL,KAAK,oBAAoBM,EAAc,UAAU,MAAM;AACnD,WAAK,WAAA;AAAA,IACT,CAAC,GAGD,KAAK,mBAAmBO,EAAa,UAAU,CAACD,MAAU;AACtD,WAAK,SAASA,CAAK;AAAA,IACvB,CAAC;AAAA,EACL;AAAA,EAEO,aAAmB;AACtB,SAAK,OAAA;AAAA,EACT;AAAA,EAEO,SAASW,GAAiBC,GAA8B;AAC3D,QAAI,KAAK,OAAO,KAAK,OAAKC,EAAE,OAAOF,CAAO,EAAG;AAC7C,UAAMG,IAAwB,EAAE,IAAIH,GAAS,SAAS,CAAA,EAAC;AACvD,QAAIC,GAAe;AACf,YAAMG,IAAQ,KAAK,OAAO,UAAU,CAAAF,MAAKA,EAAE,OAAOD,CAAa;AAC/D,MAAAG,MAAU,KAAK,KAAK,OAAO,OAAOA,GAAO,GAAGD,CAAQ,IAAI,KAAK,OAAO,KAAKA,CAAQ;AAAA,IACrF;AACI,WAAK,OAAO,KAAKA,CAAQ;AAAA,EAEjC;AAAA,EAEO,UAAUE,GAA4B;AACzC,UAAM,EAAE,SAAAL,GAAS,UAAAM,EAAA,IAAaD,GACxBE,IAAQ,KAAK,OAAO,KAAK,CAAAL,MAAKA,EAAE,OAAOF,CAAO;AACpD,QAAI,CAACO,EAAO;AAEZ,UAAMC,IAAoB,EAAE,GAAGH,GAAQ,UAAUA,EAAO,YAAY,GAAC;AACrE,QAAIC,GAAU;AACV,YAAMG,IAAY,KAAK,WAAWF,EAAM,SAASD,CAAQ;AACzD,MAAIG,MACKA,EAAU,aAAUA,EAAU,WAAW,CAAA,IAC9CA,EAAU,SAAS,KAAKD,CAAM;AAAA,IAEtC;AACI,MAAAD,EAAM,QAAQ,KAAKC,CAAM;AAAA,EAEjC;AAAA,EAEQ,WAAWE,GAAsBC,GAAmC;AACxE,eAAWC,KAAOF,GAAS;AACvB,UAAIE,EAAI,OAAOD,EAAI,QAAOC;AAC1B,UAAIA,EAAI,UAAU;AACd,cAAMC,IAAQ,KAAK,WAAWD,EAAI,UAAUD,CAAE;AAC9C,YAAIE,EAAO,QAAOA;AAAA,MACtB;AAAA,IACJ;AAAA,EAEJ;AAAA,EAEO,SAAe;AAClB,SAAK,UAAU,YAAY,IAC3B,KAAK,QAAQ,MAAA,GAEb,KAAK,OAAO,QAAQ,CAACN,GAAOH,MAAU;AAClC,YAAMU,IAAe,KAAK,YAAYP,GAAOH,GAAO,KAAK,OAAO,MAAM;AACtE,WAAK,UAAU,YAAYU,CAAY;AAAA,IAC3C,CAAC;AAAA,EACL;AAAA,EAEQ,YAAYP,GAAoBH,GAAeW,GAA4B;AAC/E,UAAMC,IAAU,SAAS,cAAc,KAAK;AAC5C,WAAAA,EAAQ,YAAY,yBAEhBZ,IAAQW,IAAQ,KAChBC,EAAQ,UAAU,IAAI,aAAa,GAGvCT,EAAM,QAAQ,QAAQ,CAAAC,MAAU;AAC5B,UAAI,KAAK,UAAUA,EAAO,EAAE,GAAG;AAC3B,cAAMS,IAAa,KAAK,aAAaT,CAAM;AAC3C,QAAAQ,EAAQ,YAAYC,CAAU;AAAA,MAClC;AAAA,IACJ,CAAC,GACMD;AAAA,EACX;AAAA,EAEQ,aAAaR,GAAgC;AACjD,UAAMU,IAAU,SAAS,cAAc,KAAK;AAC5C,IAAAA,EAAQ,YAAY;AAEpB,UAAMC,IAAQ,SAAS,cAAc,KAAK;AAC1C,IAAAA,EAAM,YAAY,YAGJX,EAAO,SAAS,KAAK,QAAQ,SAAS,gBACtC,eACVW,EAAM,UAAU,IAAI,kBAAkB,IAEtCA,EAAM,UAAU,IAAI,gBAAgB,GAGpC,KAAK,aAAa,IAAIX,EAAO,EAAE,KAAGW,EAAM,UAAU,IAAI,QAAQ,GAC9DX,EAAO,YAAUW,EAAM,UAAU,IAAI,UAAU,GAGlC,KAAK,QAAQ,aAAaX,EAAO,SAE9CW,EAAM,UAAU,IAAI,UAAU;AAIlC,UAAMC,IAAWZ,EAAO,YAAY,IAC9Ba,IAAWb,EAAO,YAAY;AACpC,IAAAW,EAAM,MAAM,WAAW,GAAGE,CAAQ;AAElC,UAAMC,IAAO,SAAS,cAAc,KAAK;AACzC,IAAAA,EAAK,YAAY,gBACjBA,EAAK,MAAM,QAAQ,GAAGF,CAAQ,MAC9BE,EAAK,MAAM,SAAS,GAAGF,CAAQ,MAC/BE,EAAK,YAAY,KAAK,QAAQd,EAAO,IAAI,GACzCW,EAAM,YAAYG,CAAI;AAGtB,UAAMC,IAAc,SAAS,cAAc,KAAK;AAGhD,QAFAA,EAAY,YAAY,wBAEpB,KAAK,QAAQ,aAAaf,EAAO,OAAO;AACxC,YAAMgB,IAAQ,SAAS,cAAc,MAAM;AAC3C,MAAAA,EAAM,YAAY,iBAClBA,EAAM,cAAcxC,EAAEwB,EAAO,KAAK,GAClCe,EAAY,YAAYC,CAAK;AAAA,IACjC;AAEA,QAAIhB,EAAO,YAAYA,EAAO,SAAS,SAAS,GAAG;AAC/C,YAAMiB,IAAQ,SAAS,cAAc,MAAM;AAC3C,MAAAA,EAAM,YAAY,iBAClBA,EAAM,cAAc,KACpBF,EAAY,YAAYE,CAAK;AAAA,IACjC;AAGA,WAAIF,EAAY,mBACZJ,EAAM,YAAYI,CAAW,GAGjCJ,EAAM,iBAAiB,SAAS,MAAM,KAAK,YAAYX,CAAM,CAAC,GAC9DW,EAAM,iBAAiB,cAAc,MAAM,KAAK,iBAAiBX,GAAQW,CAAK,CAAC,GAC/EA,EAAM,iBAAiB,cAAc,MAAM,KAAK,kBAAkB,GAElE,KAAK,QAAQ,IAAIX,EAAO,IAAIW,CAAK,GACjCD,EAAQ,YAAYC,CAAK,GAClBD;AAAA,EACX;AAAA,EAEQ,YAAYV,GAAyB;AACzC,IAAIA,EAAO,aACP,CAACA,EAAO,YAAYA,EAAO,SAAS,WAAW,OAC3CA,EAAO,eACW,KAAK,aAAa,IAAIA,EAAO,EAAE,IAClC,KAAK,aAAa,OAAOA,EAAO,EAAE,IAC5C,KAAK,aAAa,IAAIA,EAAO,EAAE,GACpC,KAAK,kBAAkBA,EAAO,EAAE,IAEpC,KAAK,cAAA,GACDA,EAAO,WAASA,EAAO,QAAQA,CAAM;AAAA,EAEjD;AAAA,EAEQ,iBAAiBA,GAAmBW,GAA0B;AAClE,IAAI,KAAK,gBAAc,aAAa,KAAK,YAAY,GACjDX,EAAO,YAAYA,EAAO,SAAS,SAAS,IAC5C,KAAK,aAAaA,GAAQW,CAAK,IAE/B,KAAK,cAAA;AAAA,EAEb;AAAA,EAEQ,mBAAyB;AAC7B,SAAK,eAAe,OAAO,WAAW,MAAM,KAAK,cAAA,GAAiB,GAAG;AAAA,EACzE;AAAA,EAEQ,aAAaX,GAAmBW,GAA0B;AAE9D,QADA,KAAK,cAAA,GACD,CAACX,EAAO,SAAU;AAEtB,UAAMkB,IAAW,SAAS,cAAc,KAAK;AAC7C,IAAAA,EAAS,YAAY,oBACjB,KAAK,QAAQ,mBAAiBA,EAAS,MAAM,YAAY,oBAAoB,KAAK,QAAQ,eAAe;AAG7G,UAAMC,IAAUR,EAAM,sBAAA,GAChBS,IAAS,KAAK,QAAQ,UAAU;AAGtC,IAAI,KAAK,QAAQ,cAAc,QAC3BF,EAAS,MAAM,gBAAgB,WAE/BA,EAAS,MAAM,gBAAgB,OAInC,SAAS,KAAK,YAAYA,CAAQ,GAGlClB,EAAO,SAAS,QAAQ,CAAAqB,MAAU;AAC9B,UAAI,KAAK,UAAUA,EAAO,EAAE,GAAG;AAC3B,cAAMC,IAAO,KAAK,mBAAmBD,CAAM;AAC3C,QAAAH,EAAS,YAAYI,CAAI;AAAA,MAC7B;AAAA,IACJ,CAAC;AAGD,UAAMC,IAAeL,EAAS,sBAAA;AAE9B,IAAIE,MAAW,QAEXF,EAAS,MAAM,SAAU,OAAO,cAAcC,EAAQ,MAAM,IAAK,MACjED,EAAS,MAAM,OAAQC,EAAQ,QAAQA,EAAQ,QAAQI,EAAa,SAAS,IAAK,QAC3EH,MAAW,UAElBF,EAAS,MAAM,MAAOC,EAAQ,SAAS,IAAK,MAC5CD,EAAS,MAAM,OAAQC,EAAQ,QAAQA,EAAQ,QAAQI,EAAa,SAAS,IAAK,QAC3EH,MAAW,WAElBF,EAAS,MAAM,MAAOC,EAAQ,OAAOA,EAAQ,SAASI,EAAa,UAAU,IAAK,MAClFL,EAAS,MAAM,OAAQC,EAAQ,QAAQ,IAAK,QACrCC,MAAW,WAElBF,EAAS,MAAM,MAAOC,EAAQ,OAAOA,EAAQ,SAASI,EAAa,UAAU,IAAK,MAClFL,EAAS,MAAM,QAAS,OAAO,aAAaC,EAAQ,OAAO,IAAK,OAGpED,EAAS,iBAAiB,cAAc,MAAM;AAAE,MAAI,KAAK,gBAAc,aAAa,KAAK,YAAY;AAAA,IAAG,CAAC,GACzGA,EAAS,iBAAiB,cAAc,MAAM,KAAK,kBAAkB,GACrE,KAAK,kBAAkBA;AAAA,EAC3B;AAAA,EAEQ,mBAAmBlB,GAAgC;AACvD,UAAMsB,IAAO,SAAS,cAAc,KAAK;AACzC,IAAAA,EAAK,YAAY,0BAGHtB,EAAO,SAAS,kBAChB,eACVsB,EAAK,UAAU,IAAI,kBAAkB,IAErCA,EAAK,UAAU,IAAI,gBAAgB;AAIvC,UAAMV,IAAWZ,EAAO,YAAY,IAC9Ba,IAAWb,EAAO;AACxB,IAAIa,MACAS,EAAK,MAAM,WAAW,GAAGT,CAAQ;AAGrC,UAAMC,IAAO,SAAS,cAAc,KAAK;AAQzC,QAPAA,EAAK,YAAY,gBACjBA,EAAK,MAAM,QAAQ,GAAGF,CAAQ,MAC9BE,EAAK,MAAM,SAAS,GAAGF,CAAQ,MAC/BE,EAAK,YAAY,KAAK,QAAQd,EAAO,IAAI,GACzCsB,EAAK,YAAYR,CAAI,GAGjB,KAAK,QAAQ,aAAad,EAAO,OAAO;AACxC,YAAMgB,IAAQ,SAAS,cAAc,MAAM;AAC3C,MAAAA,EAAM,YAAY,0BAClBA,EAAM,cAAcxC,EAAEwB,EAAO,KAAK,GAClCsB,EAAK,YAAYN,CAAK;AAAA,IAC1B;AAEA,WAAAM,EAAK,iBAAiB,SAAS,CAACE,MAAM;AAAE,MAAAA,EAAE,gBAAA,GAAmB,KAAK,YAAYxB,CAAM;AAAA,IAAG,CAAC,GACjFsB;AAAA,EACX;AAAA,EAEQ,gBAAsB;AAC1B,IAAI,KAAK,oBACL,KAAK,gBAAgB,OAAA,GACrB,KAAK,kBAAkB,OAE3B,KAAK,QAAQ,QAAQ,CAAAX,MAAS;AAC1B,YAAMM,IAAQN,EAAM,cAAc,gBAAgB;AAClD,MAAIM,KAAOA,EAAM,UAAU,OAAO,SAAS;AAAA,IAC/C,CAAC;AAAA,EACL;AAAA,EAEQ,kBAAkBQ,GAAwB;AAC9C,UAAMd,IAAQ,KAAK,QAAQ,IAAIc,CAAQ;AACvC,IAAId,MACA,KAAK,aAAa,IAAIc,CAAQ,IAAId,EAAM,UAAU,IAAI,QAAQ,IAAIA,EAAM,UAAU,OAAO,QAAQ;AAAA,EAEzG;AAAA,EAEQ,QAAQG,GAAuB;AAAE,WAAOA,KAAQ,KAAK;AAAA,EAAc;AAAA,EACpE,uBAAuBX,GAAYuB,GAAwB;AAC9D,IAAK,KAAK,QAAQ,eAAY,KAAK,QAAQ,aAAa,CAAA,IACxD,KAAK,QAAQ,WAAWvB,CAAE,IAAIuB,GAC9B,KAAK,OAAA;AAAA,EACT;AAAA,EACO,aAAaC,GAAqB;AACrC,SAAK,QAAQ,YAAYA,GACzB,KAAK,uBAAA;AAAA,EACT;AAAA,EAEQ,yBAA+B;AACnC,SAAK,QAAQ,QAAQ,CAAChB,GAAOc,MAAa;AAEtC,YAAMzB,IAAS,KAAK,eAAeyB,CAAQ;AAC3C,UAAI,CAACzB,EAAQ;AAKb,MAHiB,KAAK,QAAQ,aAAaA,EAAO,QAI9CW,EAAM,UAAU,OAAO,UAAU,IAEjCA,EAAM,UAAU,IAAI,UAAU;AAAA,IAEtC,CAAC;AAAA,EACL;AAAA,EAEQ,eAAeR,GAAmC;AACtD,eAAWJ,KAAS,KAAK,QAAQ;AAC7B,YAAMM,IAAQ,KAAK,WAAWN,EAAM,SAASI,CAAE;AAC/C,UAAIE,EAAO,QAAOA;AAAA,IACtB;AAAA,EAEJ;AAAA,EACO,mBAAmBuB,GAAqB;AAAE,SAAK,UAAU,EAAE,iBAAiBA,EAAA,CAAO;AAAA,EAAG;AAAA,EACrF,UAAUzB,GAAqB;AAAE,WAAO,KAAK,QAAQ,aAAaA,CAAE,MAAM;AAAA,EAAO;AAAA,EAClF,UAAgB;AACnB,IAAI,KAAK,sBACL,KAAK,kBAAA,GACL,KAAK,oBAAoB,OAEzB,KAAK,qBACL,KAAK,iBAAA,GACL,KAAK,mBAAmB,OAE5B,KAAK,cAAA,GACL,KAAK,UAAU,YAAY,IAC3B,KAAK,QAAQ,MAAA;AAAA,EACjB;AACJ;AC9gBO,MAAM0B,UAAgB9C,EAAe;AAAA;AAAA;AAAA;AAAA,EAIxC,MAAa,OAAsB;AAC/B,UAAM,MAAM,KAAA;AAGZ,UAAM,EAAE,YAAA+C,EAAA,IAAe,MAAM,OAAO,sBAAgB,GAC9C,EAAE,gBAAAC,EAAA,IAAmB,MAAM,OAAO,sBAAoB,GACtD,EAAE,gBAAAC,EAAA,IAAmB,MAAM,OAAO,sBAA0B,GAC5D,EAAE,kBAAAC,EAAA,IAAqB,MAAM,OAAO,sBAA4B,GAChE,EAAE,gBAAAC,EAAA,IAAmB,MAAM,OAAO,sBAA0B,GAC5D,EAAE,eAAAC,EAAA,IAAkB,MAAM,OAAO,sBAAmB,GACpD,EAAE,YAAAC,EAAA,IAAe,MAAM,OAAO,sBAAgB;AAEpD,SAAK,SAAS,SAAS,GACvB,KAAK,UAAUN,CAAU,GACzB,KAAK,UAAUE,CAAc,GAC7B,KAAK,UAAUC,CAAgB,GAC/B,KAAK,UAAUC,CAAc,GAC7B,KAAK,UAAUH,CAAc,GAC7B,KAAK,SAAS,SAAS,GACvB,KAAK,UAAUI,CAAa,GAC5B,KAAK,UAAUC,CAAU,GAEzB,KAAK,OAAA;AAAA,EACT;AACJ;AC1BO,MAAMC,EAAe;AAAA,EAChB,UAA0B;AAAA,EAC1B,mBAAuC;AAAA,EACvC;AAAA,EAER,YAAYC,GAAwB;AAChC,SAAK,YAAYA,GACjB,KAAK,KAAA;AAAA,EACT;AAAA,EAEQ,OAAO;AAEX,SAAK,mBAAmB,SAAS,cAAc,KAAK,GACpD,KAAK,iBAAiB,KAAK,kBAC3B,KAAK,iBAAiB,YAAY,kDAClC,KAAK,UAAU,YAAY,KAAK,gBAAgB,GAEhD,KAAK,UAAU,IAAIT,EAAQ;AAAA,MACvB,WAAW,KAAK;AAAA,MAChB,WAAW;AAAA,MACX,WAAW;AAAA,MACX,UAAU;AAAA;AAAA,MACV,OAAO;AAAA;AAAA,MACP,QAAQ;AAAA;AAAA,IAAA,CACX,GAED,KAAK,QAAQ,KAAA;AAAA,EACjB;AAAA,EAEO,YAAYhD,GAAoB;AACnC,SAAK,SAAS,SAASA,CAAK;AAAA,EAChC;AAAA,EAEO,UAAU;AACb,SAAK,SAAS,OAAA;AAAA,EAClB;AAAA,EAEO,UAAU;AACb,SAAK,SAAS,QAAA,GACd,KAAK,UAAU;AAAA,EACnB;AAAA;AAAA,EAGO,SAASW,GAAiBC,GAAwB;AAAE,SAAK,SAAS,SAASD,GAASC,CAAa,GAAG,KAAK,SAAS,OAAA;AAAA,EAAU;AAAA,EAC5H,UAAUI,GAAsB;AAAE,SAAK,SAAS,UAAUA,CAAM,GAAG,KAAK,SAAS,OAAA;AAAA,EAAU;AAAA,EAC3F,oBAAoBM,GAAYoC,GAAY;AAAE,SAAK,SAAS,uBAAuBpC,GAAIoC,CAAC;AAAA,EAAG;AAAA,EAC3F,aAAaZ,GAAe;AAAE,SAAK,SAAS,aAAaA,CAAI;AAAA,EAAG;AAAA,EAChE,WAAWD,GAAkB;AAChC,IAAI,KAAK,qBACL,KAAK,iBAAiB,MAAM,aAAaA,IAAU,YAAY;AAAA,EAEvE;AAAA,EACO,mBAAmBE,GAAe;AAAE,SAAK,SAAS,mBAAmBA,CAAK;AAAA,EAAG;AAAA,EAC7E,UAAUrC,GAA2B;AAAE,SAAK,SAAS,UAAUA,CAAM;AAAA,EAAG;AACnF;ACtDO,MAAMiD,EAAmB;AAAA,EACpB,eAAiC,CAAA;AAAA,EACjC;AAAA,EAER,YAAYF,GAAwB;AAChC,SAAK,YAAYA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKO,OAAOtD,GAAgE;AAE1E,UAAMyD,IAAiB,SAAS,cAAc,KAAK;AACnD,SAAK,UAAU,YAAYA,CAAc;AAEzC,UAAM1C,IAAQ,IAAIhB,EAAe;AAAA,MAC7B,WAAW0D;AAAA,MACX,GAAGzD;AAAA,IAAA,CACN;AAGD,WAAAe,EAAM,KAAA,GACN,KAAK,aAAa,KAAKA,CAAK,GACrBA;AAAA,EACX;AAAA,EAEO,YAAYlB,GAAoB;AACnC,SAAK,aAAa,QAAQ,CAAAa,MAAKA,EAAE,SAASb,CAAK,CAAC;AAAA,EACpD;AAAA,EAEO,UAAU;AACb,SAAK,aAAa,QAAQ,CAAAa,MAAKA,EAAE,QAAQ;AAAA,EAC7C;AAAA,EAEO,UAAU;AACb,SAAK,aAAa,QAAQ,CAAAA,MAAKA,EAAE,SAAS,GAC1C,KAAK,eAAe,CAAA;AAAA,EACxB;AACJ;ACpCO,MAAMgD,EAAmC;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,mBAAwC;AAAA,EACxC,oBAAyC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjD,YAAY1D,GAAwB;AAEhC,SAAK,UAAU;AAAA,MACX,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,MACX,UAAU;AAAA,MACV,WAAW;AAAA,MACX,GAAGA;AAAA,IAAA,GAEP,KAAK,YAAYA,EAAQ,WAGzB,KAAK,UAAU,KAAK,UAAA,GACpB,KAAK,SAAS,KAAK,QAAQ,cAAc,oBAAoB,GAC7D,KAAK,cAAc,KAAK,QAAQ,cAAc,qBAAqB,GAGnE,KAAK,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,SAASH,GAAoB;AAChC,UAAMM,IAAQ,KAAK,QAAQ;AAC3B,IAAK,KAAK,QAAQ,qBAAuB,YAAY,mBAAmBN,EAAM,eAAe,GACxF,KAAK,QAAQ,2BAA6B,YAAY,0BAA0BA,EAAM,cAAc,GACpG,KAAK,QAAQ,gBAAkB,YAAY,4BAA4BA,EAAM,WAAW,GACxF,KAAK,QAAQ,eAAiB,YAAY,2BAA2BA,EAAM,WAAW,GACtF,KAAK,QAAQ,iBAAmB,YAAY,6BAA6BA,EAAM,MAAM;AAAA,EAC9F;AAAA;AAAA;AAAA;AAAA,EAKO,OAAO;AACV,IAAI,KAAK,mBAET,KAAK,UAAU,YAAY,KAAK,OAAO,GAGvC,KAAK,aAAA,GAED,KAAK,QAAQ,aACb,KAAK,SAAA,GAGL,KAAK,QAAQ,aACb,KAAK,WAAA,GAGT,KAAK,iBAAiB,IAGlB,KAAK,QAAQ,UACb,KAAK,QAAQ,OAAA,GAIjB,KAAK,mBAAmBC,EAAa,UAAU,CAACD,MAAU;AACtD,WAAK,SAASA,CAAK;AAAA,IACvB,CAAC,GAGD,KAAK,oBAAoBN,EAAc,UAAU,MAAM;AACnD,WAAK,WAAA;AAAA,IACT,CAAC;AAAA,EACL;AAAA,EAEO,aAAmB;AACtB,QAAI,KAAK,QAAQ,OAAO;AACpB,YAAMoE,IAAU,KAAK,OAAO,cAAc,mBAAmB;AAC7D,MAAIA,MACAA,EAAQ,cAAcnE,EAAE,KAAK,QAAQ,KAAK;AAAA,IAElD;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAyB;AAC7B,UAAMS,IAAK,SAAS,cAAc,KAAK;AACvC,IAAAA,EAAG,YAAY,cAEX,KAAK,QAAQ,OAAIA,EAAG,KAAK,KAAK,QAAQ;AAG1C,UAAME,IAAQF,EAAG;AACjB,IAAI,KAAK,QAAQ,mBAAiBE,EAAM,YAAY,mBAAmB,KAAK,QAAQ,eAAe,GAC/F,KAAK,QAAQ,yBAAuBA,EAAM,YAAY,0BAA0B,KAAK,QAAQ,qBAAqB,GAClH,KAAK,QAAQ,cAAYA,EAAM,YAAY,4BAA4B,KAAK,QAAQ,UAAU,GAC9F,KAAK,QAAQ,aAAWA,EAAM,YAAY,2BAA2B,KAAK,QAAQ,SAAS,GAC3F,KAAK,QAAQ,eAAaA,EAAM,YAAY,6BAA6B,KAAK,QAAQ,WAAW,GAGrG,KAAK,QAAQF,GAAI,KAAK,QAAQ,OAAO,KAAK,QAAQ,MAAM;AAGxD,UAAM2D,IAAS,SAAS,cAAc,KAAK;AAC3C,IAAAA,EAAO,YAAY,qBACf,KAAK,QAAQ,aAAWA,EAAO,UAAU,IAAI,WAAW;AAE5D,UAAMC,IAAQ,SAAS,cAAc,MAAM;AAC3C,IAAAA,EAAM,YAAY,oBAClBA,EAAM,cAAc,KAAK,QAAQ,QAAQrE,EAAE,KAAK,QAAQ,KAAK,IAAI;AAEjE,UAAMsE,IAAW,SAAS,cAAc,MAAM;AAC9C,IAAAA,EAAS,YAAY,oBACrBA,EAAS,YAAY,WACrBA,EAAS,UAAU,MAAM,KAAK,MAAA,GAE9BF,EAAO,YAAYC,CAAK,GACxBD,EAAO,YAAYE,CAAQ;AAG3B,UAAMC,IAAU,SAAS,cAAc,KAAK;AAY5C,QAXAA,EAAQ,YAAY,sBAChB,OAAO,KAAK,QAAQ,WAAY,WAChCA,EAAQ,YAAY,KAAK,QAAQ,UAC1B,KAAK,QAAQ,mBAAmB,eACvCA,EAAQ,YAAY,KAAK,QAAQ,OAAO,GAG5C9D,EAAG,YAAY2D,CAAM,GACrB3D,EAAG,YAAY8D,CAAO,GAGlB,KAAK,QAAQ,WAAW;AACxB,YAAMC,IAAe,SAAS,cAAc,KAAK;AACjD,MAAAA,EAAa,YAAY,4BACzB/D,EAAG,YAAY+D,CAAY;AAAA,IAC/B;AAEA,WAAO/D;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,QAAQA,GAAiBgE,GAAyBC,GAA0B;AAChF,IAAID,MAAU,WACVhE,EAAG,MAAM,QAAQ,OAAOgE,KAAU,WAAW,GAAGA,CAAK,OAAOA,IAE5DC,MAAW,WACXjE,EAAG,MAAM,SAAS,OAAOiE,KAAW,WAAW,GAAGA,CAAM,OAAOA;AAAA,EAEvE;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe;AACnB,UAAMhE,IAAM,KAAK,QAAQ,UAEnBiE,IAAS,KAAK,QAAQ,sBAAA;AAG5B,QAAIC,IAAO,GACPC,IAAM;AAEV,UAAMC,IAAK,KAAK,UAAU,aACpBC,IAAK,KAAK,UAAU,cACpBC,IAAML,EAAO,OACbM,IAAMN,EAAO;AAEnB,QAAI,OAAOjE,KAAQ,YAAY,OAAOA;AAClC,MAAAkE,IAAOlE,EAAI,GACXmE,IAAMnE,EAAI;AAAA;AAEV,cAAQA,GAAA;AAAA,QACJ,KAAK;AACD,UAAAkE,KAAQE,IAAKE,KAAO,GACpBH,KAAOE,IAAKE,KAAO;AACnB;AAAA,QACJ,KAAK;AAAY,UAAAL,IAAO,GAAGC,IAAM;AAAG;AAAA,QACpC,KAAK;AAAc,UAAAD,KAAQE,IAAKE,KAAO,GAAGH,IAAM;AAAG;AAAA,QACnD,KAAK;AAAa,UAAAD,IAAOE,IAAKE,GAAKH,IAAM;AAAG;AAAA,QAC5C,KAAK;AAAe,UAAAD,IAAO,GAAGC,KAAOE,IAAKE,KAAO;AAAG;AAAA,QACpD,KAAK;AAAgB,UAAAL,IAAOE,IAAKE,GAAKH,KAAOE,IAAKE,KAAO;AAAG;AAAA,QAC5D,KAAK;AAAe,UAAAL,IAAO,GAAGC,IAAME,IAAKE;AAAK;AAAA,QAC9C,KAAK;AAAiB,UAAAL,KAAQE,IAAKE,KAAO,GAAGH,IAAME,IAAKE;AAAK;AAAA,QAC7D,KAAK;AAAgB,UAAAL,IAAOE,IAAKE,GAAKH,IAAME,IAAKE;AAAK;AAAA,QACtD;AACI,UAAAL,KAAQE,IAAKE,KAAO,GACpBH,KAAOE,IAAKE,KAAO;AAAA,MAAA;AAK/B,IAAAL,IAAO,KAAK,IAAI,GAAG,KAAK,IAAIA,GAAME,IAAKE,CAAG,CAAC,GAC3CH,IAAM,KAAK,IAAI,GAAG,KAAK,IAAIA,GAAKE,IAAKE,CAAG,CAAC,GAEzC,KAAK,QAAQ,MAAM,OAAO,GAAGL,CAAI,MACjC,KAAK,QAAQ,MAAM,MAAM,GAAGC,CAAG;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW;AACf,QAAIK,IAAS,GACTC,IAAS,GACTC,IAAY,GACZC,IAAW;AAEf,UAAMC,IAAc,CAACtC,MAAkB;AACnC,MAAAA,EAAE,eAAA,GACFkC,IAASlC,EAAE,SACXmC,IAASnC,EAAE,SACXoC,IAAY,KAAK,QAAQ,YACzBC,IAAW,KAAK,QAAQ,WAExB,SAAS,iBAAiB,aAAaE,CAAW,GAClD,SAAS,iBAAiB,WAAWC,CAAS;AAAA,IAClD,GAEMD,IAAc,CAACvC,MAAkB;AACnC,YAAMyC,IAAKzC,EAAE,UAAUkC,GACjBQ,IAAK1C,EAAE,UAAUmC;AAEvB,UAAIQ,IAAUP,IAAYK,GACtBG,IAASP,IAAWK;AAGxB,YAAMG,IAAU,KAAK,UAAU,cAAc,KAAK,QAAQ,aACpDC,IAAS,KAAK,UAAU,eAAe,KAAK,QAAQ;AAE1D,MAAAH,IAAU,KAAK,IAAI,GAAG,KAAK,IAAIA,GAASE,CAAO,CAAC,GAChDD,IAAS,KAAK,IAAI,GAAG,KAAK,IAAIA,GAAQE,CAAM,CAAC,GAE7C,KAAK,QAAQ,MAAM,OAAO,GAAGH,CAAO,MACpC,KAAK,QAAQ,MAAM,MAAM,GAAGC,CAAM;AAAA,IACtC,GAEMJ,IAAY,MAAM;AACpB,eAAS,oBAAoB,aAAaD,CAAW,GACrD,SAAS,oBAAoB,WAAWC,CAAS;AAAA,IACrD;AAEA,SAAK,OAAO,iBAAiB,aAAaF,CAAW;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa;AACjB,UAAMS,IAAS,KAAK,QAAQ,cAAc,2BAA2B;AACrE,QAAI,CAACA,EAAQ;AAEb,QAAIb,IAAS,GACTC,IAAS,GACTa,IAAS,GACTC,IAAS;AAEb,UAAMX,IAAc,CAACtC,MAAkB;AACnC,MAAAA,EAAE,eAAA,GACFA,EAAE,gBAAA,GACFkC,IAASlC,EAAE,SACXmC,IAASnC,EAAE,SACXgD,IAAS,KAAK,QAAQ,aACtBC,IAAS,KAAK,QAAQ,cAEtB,SAAS,iBAAiB,aAAaV,CAAW,GAClD,SAAS,iBAAiB,WAAWC,CAAS;AAAA,IAClD,GAEMD,IAAc,CAACvC,MAAkB;AACnC,YAAMyC,IAAKzC,EAAE,UAAUkC,GACjBQ,IAAK1C,EAAE,UAAUmC,GAEjBe,IAAO,KAAK,IAAI,KAAK,QAAQ,YAAY,KAAKF,IAASP,CAAE,GACzDU,IAAO,KAAK,IAAI,KAAK,QAAQ,aAAa,IAAIF,IAASP,CAAE;AAE/D,WAAK,QAAQ,MAAM,QAAQ,GAAGQ,CAAI,MAClC,KAAK,QAAQ,MAAM,SAAS,GAAGC,CAAI;AAAA,IACvC,GAEMX,IAAY,MAAM;AACpB,eAAS,oBAAoB,aAAaD,CAAW,GACrD,SAAS,oBAAoB,WAAWC,CAAS;AAAA,IACrD;AAEA,IAAAO,EAAO,iBAAiB,aAAaT,CAAW;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,WAAWf,GAA+B;AAC7C,SAAK,YAAY,YAAY,IACzB,OAAOA,KAAY,WACnB,KAAK,YAAY,YAAYA,IAE7B,KAAK,YAAY,YAAYA,CAAO;AAAA,EAE5C;AAAA;AAAA;AAAA;AAAA,EAKO,QAAQ;AACX,IAAI,KAAK,iBACL,KAAK,qBACL,KAAK,iBAAA,GACL,KAAK,mBAAmB,OAExB,KAAK,sBACL,KAAK,kBAAA,GACL,KAAK,oBAAoB,OAE7B,KAAK,QAAQ,OAAA,GACb,KAAK,eAAe,IAChB,KAAK,QAAQ,WACb,KAAK,QAAQ,QAAA;AAAA,EAErB;AAAA;AAAA;AAAA;AAAA,EAKO,UAAU;AACb,SAAK,MAAA;AAAA,EACT;AACJ;AC7VO,MAAM6B,UAAsBlC,EAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKzC,YAAYJ,GAAwB;AAEhC,UAAMuC,IAAY,SAAS,cAAc,KAAK;AAC9C,IAAAA,EAAU,YAAY;AAEtB,UAAMC,IAAY,SAAS,cAAc,IAAI;AAC7C,IAAAA,EAAU,cAAc;AAExB,UAAMC,IAAW,SAAS,cAAc,IAAI;AAC5C,IAAAA,EAAS,YAAY;AAAA;AAAA;AAAA,0CAGY,oBAAI,QAAO,oBAAoB;AAAA;AAAA;AAIhE,UAAMC,IAAY,SAAS,cAAc,QAAQ;AACjD,IAAAA,EAAU,cAAc,iBACxBA,EAAU,MAAM,YAAY,QAC5BA,EAAU,UAAU,MAAM;AACtB,YAAM,iBAAiB;AAAA,IAC3B,GAEAH,EAAU,YAAYC,CAAS,GAC/BD,EAAU,YAAYE,CAAQ,GAC9BF,EAAU,YAAYG,CAAS,GAG/B,MAAM;AAAA,MACF,WAAA1C;AAAA,MACA,OAAO;AAAA,MACP,SAASuC;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA;AAAA,MAEX,SAAS,MAAM;AACX,gBAAQ,IAAI,oBAAoB;AAAA,MACpC;AAAA,MACA,QAAQ,MAAM;AACV,gBAAQ,IAAI,oBAAoB;AAAA,MACpC;AAAA,IAAA,CACH;AAAA,EAIL;AAAA;AAAA;AAIJ;ACtDO,MAAMI,EAAc;AAAA;AAAA,EAEf;AAAA;AAAA,EAEA,gBAA6B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrC,YAAY3C,GAAwB;AAChC,SAAK,YAAYA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,OAAOtD,GAAsD;AAChE,UAAMkG,IAAS,IAAIxC,EAAU;AAAA,MACzB,WAAW,KAAK;AAAA,MAChB,GAAG1D;AAAA,MACH,SAAS,MAAM;AAEX,aAAK,gBAAgB,KAAK,cAAc,OAAO,CAAAmG,MAAKA,MAAMD,CAAM,GAC5DlG,EAAQ,WAASA,EAAQ,QAAA;AAAA,MACjC;AAAA,IAAA,CACH;AAGD,WAAAkG,EAAO,SAASpG,EAAa,UAAU,GAEvC,KAAK,cAAc,KAAKoG,CAAM,GACvBA;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,iBAAiB;AAEpB,QAAIN,EAAc,KAAK,SAAS;AAAA,EAEpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,YAAY/F,GAAoB;AACnC,SAAK,cAAc,QAAQ,CAAAqG,MAAU;AACjC,MAAIA,EAAO,YACPA,EAAO,SAASrG,CAAK;AAAA,IAE7B,CAAC;AAAA,EACL;AACJ;AC3DO,MAAMuG,EAAU;AAAA,EACX;AAAA,EACA,UAA8B;AAAA,EAC9B,eAAoB;AAAA;AAAA,EAErB,UAAiC;AAAA;AAAA,EACjC,cAAyC;AAAA;AAAA,EACzC,SAA+B;AAAA,EAEtC,IAAW,gBAAgB;AAAE,WAAO7G;AAAA,EAAe;AAAA,EACnD,IAAW,eAAe;AAAE,WAAOO;AAAA,EAAc;AAAA,EAEjD,YAAYwD,GAAiCtD,GAAsD;AAC/F,UAAMC,IAAK,OAAOqD,KAAc,WAAW,SAAS,eAAeA,CAAS,IAAIA;AAChF,QAAI,CAACrD,EAAI,OAAM,IAAI,MAAM,qBAAqB;AAC9C,SAAK,YAAYA,GAEbD,GAAS,UAAQT,EAAc,UAAUS,EAAQ,MAAM,GACvDA,GAAS,UACLA,EAAQ,UAAU,WAClB,QAAQ,KAAK,kDAAkD,IAE/DF,EAAa,SAASE,EAAQ,KAAK,IAI3C,KAAK,KAAA;AAAA,EACT;AAAA,EAEO,UAAUhB,GAAoB;AAAE,IAAAO,EAAc,UAAUP,CAAM;AAAA,EAAG;AAAA,EACjE,YAAwB;AAAE,WAAOO,EAAc,UAAA;AAAA,EAAa;AAAA,EAC5D,SAASM,GAAyB;AAAE,IAAAC,EAAa,SAASD,CAAK;AAAA,EAAG;AAAA,EAClE,eAAeA,GAAoB;AAAE,IAAAC,EAAa,eAAeD,CAAK;AAAA,EAAG;AAAA,EAExE,OAAO;AACX,SAAK,UAAU,YAAY,IAC3B,KAAK,UAAU,SAAS,cAAc,KAAK,GAC3C,KAAK,QAAQ,YAAY,sBACzB,KAAK,UAAU,YAAY,KAAK,OAAO,GAGvC,KAAK,SAAS,IAAIoG,EAAc,KAAK,OAAO,GAC5C,KAAK,UAAU,IAAI5C,EAAe,KAAK,OAAO,GAC9C,KAAK,cAAc,IAAIG,EAAmB,KAAK,OAAO,GAGtD,KAAK,mBAAA,GAGL,KAAK,YAAY1D,EAAa,UAAU,GAGpC,KAAK,gBACL,KAAK,aAAa,UAAU;AAAA,MACxB,iBAAiB;AAAA,IAAA,CACpB,GAELA,EAAa,UAAU,CAACD,MAAU;AAC9B,WAAK,YAAYA,CAAK;AAAA,IAC1B,CAAC;AAAA,EACL;AAAA,EAEQ,qBAAqB;AACzB,IAAK,KAAK,gBAEV,KAAK,eAAe,KAAK,YAAY,OAAO;AAAA,MACxC,UAAU;AAAA,MACV,WAAW;AAAA,MACX,OAAO;AAAA,MACP,iBAAiB;AAAA;AAAA,MACjB,WAAW;AAAA,IAAA,CACd,GAED,KAAK,aAAa,SAAS,MAAM,GACjC,KAAK,aAAa,UAAU;AAAA,MACxB,IAAI;AAAA,MACJ,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA;AAAA,MACP,MAAM;AAAA,MACN,SAAS,MAAM;AACX,cAAM,MAAM;AAAA,MAChB;AAAA,IAAA,CACH,GAGD,KAAK,aAAa,OAAA;AAAA,EACtB;AAAA,EAEQ,YAAYA,GAAoB;AACpC,IAAI,KAAK,YACL,KAAK,QAAQ,MAAM,kBAAkBA,EAAM,YAC3C,KAAK,QAAQ,MAAM,QAAQA,EAAM;AAAA,EAEzC;AAAA,EAEO,UAAU;AACb,SAAK,SAAS,QAAA,GACd,KAAK,aAAa,QAAA,GAClB,KAAK,SAAS,MACd,KAAK,UAAU,YAAY;AAAA,EAC/B;AACJ;"}
\ No newline at end of file
diff --git a/dist/bim-engine-sdk.umd.js b/dist/bim-engine-sdk.umd.js
index 309d8c8..a4701d4 100644
--- a/dist/bim-engine-sdk.umd.js
+++ b/dist/bim-engine-sdk.umd.js
@@ -1,8 +1,8 @@
-(function(){"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.appendChild(document.createTextNode('.bim-engine-wrapper{position:relative;width:100%;height:100%;font-family:sans-serif;color:#333;padding:20px;background-color:#e16969;border-radius:8px;border:1px solid #e0e0e0;box-sizing:border-box}.bim-engine-opt-btn-container{position:absolute;bottom:20px;left:50%;transform:translate(-50%);z-index:100}:root{--bim-toolbar-bg: rgba(17, 17, 17, .88);--bim-btn-bg: transparent;--bim-btn-hover-bg: #444;--bim-btn-active-bg: rgba(255, 255, 255, .15);--bim-icon-color: #ccc;--bim-icon-active-color: #fff;--bim-btn-text-color: #ccc;--bim-btn-text-active-color: #fff}.toolbar-container{display:flex;align-items:center;max-width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.toolbar-container::-webkit-scrollbar{display:none}.opt-btn-group{overflow:hidden;display:flex;align-items:center;flex-shrink:0;background-color:var(--bim-toolbar-bg);border-radius:4px;padding:4px 8px}.has-divider{margin-right:16px}.opt-btn-wrapper{position:relative}.opt-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;width:50px;min-height:50px;padding:4px;cursor:pointer;background-color:var(--bim-btn-bg);color:var(--bim-icon-color);transition:all .2s;border-bottom:2px solid transparent}.opt-btn:hover{background-color:var(--bim-btn-hover-bg);color:var(--bim-icon-active-color)}.opt-btn.active{background-color:var(--bim-btn-active-bg);color:var(--bim-icon-active-color);border-bottom:2px solid #fff}.opt-btn.disabled{opacity:.5;cursor:not-allowed}.opt-btn-icon{width:32px;height:32px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.opt-btn-icon svg{width:100%;height:100%}.opt-btn-label{font-size:10px;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{font-size:8px;position:absolute;top:2px;right:2px;opacity:.6;transition:transform .2s ease}.opt-btn-arrow.rotated{transform:rotate(180deg)}.opt-btn.no-label .opt-btn-arrow{top:2px;right:2px}.opt-btn-dropdown{position:fixed;transform:translate(-50%,-100%);background-color:var(--bim-toolbar-bg);border-radius:4px;overflow:hidden;box-shadow:0 4px 12px #0000004d;min-width:50px;z-index:9999;display:flex;flex-direction:column}.opt-btn-dropdown-item{display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--bim-icon-color);cursor:pointer;transition:background .2s;white-space:nowrap;min-width:50px;min-height:50px;padding:4px;background-color:var(--bim-btn-bg)}.opt-btn-dropdown-item:hover{background-color:var(--bim-btn-hover-bg);color:var(--bim-icon-active-color)}.opt-btn-dropdown-item .opt-btn-icon.small{width:30px;height:30px;margin-right:0;margin-bottom:4px}.opt-btn-dropdown-item span{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{width:32px;height:32px}:root{--bim-dialog-bg: rgba(17, 17, 17, .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 #0000004d;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;-webkit-user-select:none;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}.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}')),document.head.appendChild(o)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})();
-(function(d,h){typeof exports=="object"&&typeof module<"u"?h(exports):typeof define=="function"&&define.amd?define(["exports"],h):(d=typeof globalThis<"u"?globalThis:d||self,h(d.LyzBimEngineSDK={}))})(this,(function(d){"use strict";class h{container;options;groups=[];activeBtnIds=new Set;btnRefs=new Map;dropdownElement=null;hoverTimeout=null;DEFAULT_ICON=' ';constructor(t){const o=typeof t.container=="string"?document.getElementById(t.container):t.container;if(!o)throw new Error("Container not found");this.container=o,this.options={showLabel:!0,visibility:{},...t},this.initContainer(),this.applyStyles()}initContainer(){this.container.innerHTML="",this.container.classList.add("toolbar-root")}applyStyles(){const t=this.container.style;this.options.backgroundColor&&t.setProperty("--bim-toolbar-bg",this.options.backgroundColor),this.options.btnBackgroundColor&&t.setProperty("--bim-btn-bg",this.options.btnBackgroundColor),this.options.btnHoverColor&&t.setProperty("--bim-btn-hover-bg",this.options.btnHoverColor),this.options.btnActiveColor&&t.setProperty("--bim-btn-active-bg",this.options.btnActiveColor),this.options.iconColor&&t.setProperty("--bim-icon-color",this.options.iconColor),this.options.iconActiveColor&&t.setProperty("--bim-icon-active-color",this.options.iconActiveColor),this.options.textColor&&t.setProperty("--bim-btn-text-color",this.options.textColor),this.options.textActiveColor&&t.setProperty("--bim-btn-text-active-color",this.options.textActiveColor)}setColors(t){this.options={...this.options,...t},this.applyStyles()}addGroup(t,o){if(this.groups.some(i=>i.id===t)){console.warn("Group "+t+" already exists");return}const e={id:t,buttons:[]};if(o){const i=this.groups.findIndex(n=>n.id===o);i!==-1?this.groups.splice(i,0,e):(console.warn(`Target group ${o} not found, appending ${t} to end.`),this.groups.push(e))}else this.groups.push(e)}addButton(t){const{groupId:o,parentId:e}=t;if(!o)throw new Error(`Button ${t.id} config must contain 'groupId'`);const i=this.groups.find(s=>s.id===o);if(!i)throw new Error(`Group ${o} not found. Please call addGroup first.`);const n={...t,children:t.children||[]};if(e){const s=this.findButton(i.buttons,e);if(!s)throw new Error(`Parent button ${e} not found in group ${o}`);s.children||(s.children=[]),s.children.push(n)}else i.buttons.push(n)}findButton(t,o){for(const e of t){if(e.id===o)return e;if(e.children){const i=this.findButton(e.children,o);if(i)return i}}}async init(){const{homeButton:t}=await Promise.resolve().then(()=>y),{locationButton:o}=await Promise.resolve().then(()=>B),{walkMenuButton:e}=await Promise.resolve().then(()=>T),{walkPersonButton:i}=await Promise.resolve().then(()=>E),{walkBirdButton:n}=await Promise.resolve().then(()=>x),{settingButton:s}=await Promise.resolve().then(()=>L),{infoButton:r}=await Promise.resolve().then(()=>k);this.addGroup("group-1"),this.addButton(t),this.addButton(e),this.addButton(i),this.addButton(n),this.addButton(o),this.addGroup("group-2"),this.addButton(s),this.addButton(r),this.render()}render(){this.container.innerHTML="",this.btnRefs.clear();const t=document.createElement("div");t.className="toolbar-container",this.groups.forEach((o,e)=>{const i=this.renderGroup(o,e,this.groups.length);t.appendChild(i)}),this.container.appendChild(t)}renderGroup(t,o,e){const i=document.createElement("div");return i.className="opt-btn-group",o{if(this.isVisible(n.id)){const s=this.renderButton(n);i.appendChild(s)}}),i}renderButton(t){const o=document.createElement("div");o.className="opt-btn-wrapper";const e=document.createElement("div");e.className="opt-btn",this.activeBtnIds.has(t.id)&&e.classList.add("active"),t.disabled&&e.classList.add("disabled"),this.options.showLabel||(e.classList.add("no-label"),t.label&&(e.title=t.label));const i=document.createElement("div");if(i.className="opt-btn-icon",i.innerHTML=this.getIcon(t.icon),e.appendChild(i),this.options.showLabel&&t.label){const n=document.createElement("span");n.className="opt-btn-label",n.textContent=t.label,e.appendChild(n)}if(t.children&&t.children.length>0){const n=document.createElement("span");n.className="opt-btn-arrow",n.textContent="▼",e.appendChild(n)}return e.addEventListener("click",()=>this.handleClick(t)),e.addEventListener("mouseenter",()=>this.handleMouseEnter(t,e)),e.addEventListener("mouseleave",()=>this.handleMouseLeave()),this.btnRefs.set(t.id,e),o.appendChild(e),o}handleClick(t){t.disabled||(!t.children||t.children.length===0)&&(t.keepActive&&(this.activeBtnIds.has(t.id)?this.activeBtnIds.delete(t.id):this.activeBtnIds.add(t.id),this.updateButtonState(t.id)),this.closeDropdown(),t.onClick&&t.onClick(t))}handleSubClick(t){t.keepActive&&(this.activeBtnIds.has(t.id)?this.activeBtnIds.delete(t.id):this.activeBtnIds.add(t.id),this.updateButtonState(t.id)),this.closeDropdown(),t.onClick&&t.onClick(t)}handleMouseEnter(t,o){if(this.hoverTimeout&&(clearTimeout(this.hoverTimeout),this.hoverTimeout=null),t.children&&t.children.length>0){this.showDropdown(t,o);const e=o.querySelector(".opt-btn-arrow");e&&e.classList.add("rotated")}else this.closeDropdown()}handleMouseLeave(){this.hoverTimeout=window.setTimeout(()=>{this.closeDropdown()},200)}showDropdown(t,o){if(this.closeDropdown(),!t.children)return;const e=document.createElement("div");e.className="opt-btn-dropdown";const i=e.style;this.options.backgroundColor&&i.setProperty("--bim-toolbar-bg",this.options.backgroundColor),this.options.btnBackgroundColor&&i.setProperty("--bim-btn-bg",this.options.btnBackgroundColor),this.options.btnHoverColor&&i.setProperty("--bim-btn-hover-bg",this.options.btnHoverColor),this.options.btnActiveColor&&i.setProperty("--bim-btn-active-bg",this.options.btnActiveColor),this.options.iconColor&&i.setProperty("--bim-icon-color",this.options.iconColor),this.options.iconActiveColor&&i.setProperty("--bim-icon-active-color",this.options.iconActiveColor),this.options.textColor&&i.setProperty("--bim-btn-text-color",this.options.textColor),this.options.textActiveColor&&i.setProperty("--bim-btn-text-active-color",this.options.textActiveColor);const n=o.getBoundingClientRect(),s=n.left+n.width/2;e.style.top=n.top-8+"px",e.style.left=s+"px",t.children.forEach(r=>{if(this.isVisible(r.id)){const l=this.renderDropdownItem(r);e.appendChild(l)}}),e.addEventListener("mouseenter",()=>{this.hoverTimeout&&(clearTimeout(this.hoverTimeout),this.hoverTimeout=null)}),e.addEventListener("mouseleave",()=>this.handleMouseLeave()),document.body.appendChild(e),this.dropdownElement=e}renderDropdownItem(t){const o=document.createElement("div");o.className="opt-btn-dropdown-item";const e=document.createElement("div");if(e.className="opt-btn-icon small",e.innerHTML=this.getIcon(t.icon),o.appendChild(e),this.options.showLabel){const i=document.createElement("span");i.textContent=t.label,o.appendChild(i)}return o.addEventListener("click",i=>{i.stopPropagation(),this.handleSubClick(t)}),o}closeDropdown(){this.dropdownElement&&(this.dropdownElement.remove(),this.dropdownElement=null),this.btnRefs.forEach(t=>{const o=t.querySelector(".opt-btn-arrow");o&&o.classList.remove("rotated")})}updateButtonState(t){const o=this.btnRefs.get(t);o&&(this.activeBtnIds.has(t)?o.classList.add("active"):o.classList.remove("active"))}getIcon(t){return t||this.DEFAULT_ICON}updateButtonVisibility(t,o){this.options.visibility||(this.options.visibility={}),this.options.visibility[t]=o,this.render()}setShowLabel(t){this.options.showLabel=t,this.render()}setBackgroundColor(t){this.setColors({backgroundColor:t})}isVisible(t){return this.options.visibility?.[t]!==!1}destroy(){this.closeDropdown(),this.hoverTimeout&&clearTimeout(this.hoverTimeout),this.container.innerHTML="",this.btnRefs.clear(),this.activeBtnIds.clear(),this.groups=[]}}class f{optBtnGroups=null;container;constructor(t){this.container=t,this.init()}init(){this.optBtnGroups=new h({container:this.container,showLabel:!0}),this.optBtnGroups.init().catch(t=>{console.error("Failed to initialize OptBtnGroups:",t)})}addGroup(t,o){this.optBtnGroups?(this.optBtnGroups.addGroup(t,o),this.optBtnGroups.render()):console.warn("Toolbar not initialized yet.")}addButton(t){this.optBtnGroups?(this.optBtnGroups.addButton(t),this.optBtnGroups.render()):console.warn("Toolbar not initialized yet.")}setButtonVisibility(t,o){this.optBtnGroups?this.optBtnGroups.updateButtonVisibility(t,o):console.warn("Toolbar not initialized yet.")}setShowLabel(t){this.optBtnGroups?this.optBtnGroups.setShowLabel(t):console.warn("Toolbar not initialized yet.")}setVisible(t){this.container.style.display=t?"block":"none"}setBackgroundColor(t){this.optBtnGroups?this.optBtnGroups.setBackgroundColor(t):console.warn("Toolbar not initialized yet.")}setColors(t){this.optBtnGroups?this.optBtnGroups.setColors(t):console.warn("Toolbar not initialized yet.")}destroy(){this.optBtnGroups&&(this.optBtnGroups.destroy(),this.optBtnGroups=null)}}class b{element;options;container;header;contentArea;_isDestroyed=!1;constructor(t){this.options={title:"Dialog",width:300,height:"auto",position:"center",draggable:!0,resizable:!1,minWidth:200,minHeight:100,...t},this.container=t.container,this.element=this.createDom(),this.header=this.element.querySelector(".bim-dialog-header"),this.contentArea=this.element.querySelector(".bim-dialog-content"),this.init()}createDom(){const t=document.createElement("div");t.className="bim-dialog",this.options.id&&(t.id=this.options.id);const o=t.style;this.options.backgroundColor&&o.setProperty("--bim-dialog-bg",this.options.backgroundColor),this.options.headerBackgroundColor&&o.setProperty("--bim-dialog-header-bg",this.options.headerBackgroundColor),this.options.titleColor&&o.setProperty("--bim-dialog-title-color",this.options.titleColor),this.options.textColor&&o.setProperty("--bim-dialog-text-color",this.options.textColor),this.options.borderColor&&o.setProperty("--bim-dialog-border-color",this.options.borderColor),this.setSize(t,this.options.width,this.options.height);const e=document.createElement("div");e.className="bim-dialog-header",this.options.draggable&&e.classList.add("draggable");const i=document.createElement("span");i.className="bim-dialog-title",i.textContent=this.options.title||"";const n=document.createElement("span");n.className="bim-dialog-close",n.innerHTML="×",n.onclick=()=>this.close(),e.appendChild(i),e.appendChild(n);const s=document.createElement("div");if(s.className="bim-dialog-content",typeof this.options.content=="string"?s.innerHTML=this.options.content:this.options.content instanceof HTMLElement&&s.appendChild(this.options.content),t.appendChild(e),t.appendChild(s),this.options.resizable){const r=document.createElement("div");r.className="bim-dialog-resize-handle",t.appendChild(r)}return t}setSize(t,o,e){o!==void 0&&(t.style.width=typeof o=="number"?`${o}px`:o),e!==void 0&&(t.style.height=typeof e=="number"?`${e}px`:e)}init(){this.container.appendChild(this.element),this.initPosition(),this.options.draggable&&this.initDrag(),this.options.resizable&&this.initResize()}initPosition(){const t=this.options.position,o=this.element.getBoundingClientRect();let e=0,i=0;const n=this.container.clientWidth,s=this.container.clientHeight,r=o.width,l=o.height;if(typeof t=="object"&&"x"in t)e=t.x,i=t.y;else switch(t){case"center":e=(n-r)/2,i=(s-l)/2;break;case"top-left":e=0,i=0;break;case"top-center":e=(n-r)/2,i=0;break;case"top-right":e=n-r,i=0;break;case"left-center":e=0,i=(s-l)/2;break;case"right-center":e=n-r,i=(s-l)/2;break;case"bottom-left":e=0,i=s-l;break;case"bottom-center":e=(n-r)/2,i=s-l;break;case"bottom-right":e=n-r,i=s-l;break;default:e=(n-r)/2,i=(s-l)/2}e=Math.max(0,Math.min(e,n-r)),i=Math.max(0,Math.min(i,s-l)),this.element.style.left=`${e}px`,this.element.style.top=`${i}px`}initDrag(){let t=0,o=0,e=0,i=0;const n=l=>{l.preventDefault(),t=l.clientX,o=l.clientY,e=this.element.offsetLeft,i=this.element.offsetTop,document.addEventListener("mousemove",s),document.addEventListener("mouseup",r)},s=l=>{const c=l.clientX-t,m=l.clientY-o;let p=e+c,u=i+m;const g=this.container.clientWidth-this.element.offsetWidth,M=this.container.clientHeight-this.element.offsetHeight;p=Math.max(0,Math.min(p,g)),u=Math.max(0,Math.min(u,M)),this.element.style.left=`${p}px`,this.element.style.top=`${u}px`},r=()=>{document.removeEventListener("mousemove",s),document.removeEventListener("mouseup",r)};this.header.addEventListener("mousedown",n)}initResize(){const t=this.element.querySelector(".bim-dialog-resize-handle");if(!t)return;let o=0,e=0,i=0,n=0;const s=c=>{c.preventDefault(),c.stopPropagation(),o=c.clientX,e=c.clientY,i=this.element.offsetWidth,n=this.element.offsetHeight,document.addEventListener("mousemove",r),document.addEventListener("mouseup",l)},r=c=>{const m=c.clientX-o,p=c.clientY-e,u=Math.max(this.options.minWidth||100,i+m),g=Math.max(this.options.minHeight||50,n+p);this.element.style.width=`${u}px`,this.element.style.height=`${g}px`},l=()=>{document.removeEventListener("mousemove",r),document.removeEventListener("mouseup",l)};t.addEventListener("mousedown",s)}setContent(t){this.contentArea.innerHTML="",typeof t=="string"?this.contentArea.innerHTML=t:this.contentArea.appendChild(t)}close(){this._isDestroyed||(this.element.remove(),this._isDestroyed=!0,this.options.onClose&&this.options.onClose())}}class v{dialog;constructor(t){const o=document.createElement("div");o.className="bim-info-dialog-content";const e=document.createElement("h3");e.textContent="Model Information";const i=document.createElement("ul");i.innerHTML=`
+(function(){"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.appendChild(document.createTextNode('.bim-engine-wrapper{position:relative;width:100%;height:100%;font-family:sans-serif;color:#333;padding:20px;background-color:#e16969;border-radius:8px;border:1px solid #e0e0e0;box-sizing:border-box}.bim-engine-opt-btn-container{position:absolute;bottom:20px;left:50%;transform:translate(-50%);z-index:100}.bim-btn-group-root{display:flex;gap:8px;z-index:1000;position:absolute;pointer-events:auto}.bim-btn-group-root.static{position:relative;inset:auto;transform:none}.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;gap:4px;background-color:var(--bim-btn-group-section-bg, rgba(17, 17, 17, .88));border-radius:6px;padding:4px}.bim-btn-group-root.dir-row .bim-btn-group-section{flex-direction:row;align-items:center}.bim-btn-group-root.dir-column .bim-btn-group-section{flex-direction:column}.opt-btn-wrapper{position:relative}.opt-btn{display:flex;cursor:pointer;border-radius:4px;transition:background-color .2s,color .2s;color:var(--bim-btn-text-color, #ccc);background-color:var(--bim-btn-bg, transparent);padding:6px;align-items:center;position:relative;justify-content:center}.opt-btn:hover{background-color:var(--bim-btn-hover-bg, #444)}.opt-btn.active{background-color:var(--bim-btn-active-bg, rgba(255, 255, 255, .15));color:var(--bim-btn-text-active-color, #fff)}.opt-btn.active .opt-btn-icon{color:var(--bim-icon-active-color, #fff)}.opt-btn.disabled{opacity:.5;cursor:not-allowed}.opt-btn-icon{width:var(--bim-icon-size, 24px);height:var(--bim-icon-size, 24px);display:flex;align-items:center;justify-content:center;color:var(--bim-icon-color, #ccc);flex-shrink:0}.opt-btn-icon svg{width:100%;height:100%;fill:currentColor}.opt-btn-arrow{font-size:10px;opacity:.6;transition:transform .2s;display:inline-block;margin-left:4px}.opt-btn-arrow.rotated{transform:rotate(180deg)}.opt-btn-text-wrapper{display: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:not(.no-label){flex-direction:column;text-align:center}.opt-btn.align-vertical:not(.no-label) .opt-btn-text-wrapper{margin-top:4px}.opt-btn.align-vertical:not(.no-label) .opt-btn-label{font-size:12px;line-height:1.2}.opt-btn.align-horizontal:not(.no-label){flex-direction:row}.opt-btn.align-horizontal:not(.no-label) .opt-btn-text-wrapper{margin-left:8px}.opt-btn.align-horizontal:not(.no-label) .opt-btn-label{font-size:14px}.opt-btn.no-label .opt-btn-text-wrapper{width:0;height:0;margin:0;padding:0;overflow:visible;position:absolute;top:0;right:0}.opt-btn.no-label .opt-btn-arrow{position:absolute;top:2px;right:2px;margin:0;font-size:8px}.opt-btn-dropdown{position:absolute;background-color:var(--bim-toolbar-bg, rgba(17, 17, 17, .95));border-radius:4px;padding:4px;box-shadow:0 4px 12px #0003;z-index:1001;display:flex;flex-direction:column;border:1px solid rgba(255,255,255,.1);opacity:0;visibility:hidden;transform:translateY(-10px);transition:opacity .2s ease,transform .2s cubic-bezier(.2,0,.2,1),visibility .2s}@keyframes dropdown-fade-in{0%{opacity:0;transform:translateY(-8px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.opt-btn-dropdown{animation:dropdown-fade-in .2s cubic-bezier(.2,0,.2,1) forwards;opacity:1;visibility:visible;transform:none}.opt-btn-dropdown-item{display:flex;align-items:center;padding:8px 12px;cursor:pointer;border-radius:4px;color:var(--bim-btn-text-color, #ccc);transition:background .2s;box-sizing:border-box}.opt-btn-dropdown-item:hover{background-color:var(--bim-btn-hover-bg, #444);color:#fff}.opt-btn-dropdown-item.align-horizontal{flex-direction:row}.opt-btn-dropdown-item.align-horizontal .opt-btn-icon{width:18px;height:18px;margin-right:8px}.opt-btn-dropdown-item.align-vertical{flex-direction:column;text-align:center}.opt-btn-dropdown-item.align-vertical .opt-btn-icon{width:24px;height:24px;margin-bottom:4px}.opt-btn-dropdown-item.align-vertical .opt-btn-dropdown-label{font-size:12px}.bim-btn-group-root.is-bottom-toolbar .opt-btn-icon{width:32px;height:32px}.bim-btn-group-root.is-bottom-toolbar .opt-btn{padding:8px}:root{--bim-dialog-bg: rgba(17, 17, 17, .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 #0000004d;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;-webkit-user-select:none;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}.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}')),document.head.appendChild(o)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})();
+(function(p,m){typeof exports=="object"&&typeof module<"u"?m(exports):typeof define=="function"&&define.amd?define(["exports"],m):(p=typeof globalThis<"u"?globalThis:p||self,m(p.LyzBimEngineSDK={}))})(this,(function(p){"use strict";const m={common:{title:"BimEngine",description:"这是一个使用 BIM-ENGINE。",openTestDialog:"打开测试弹窗",openInfoDialog:"打开信息弹窗 (封装版)"},toolbar:{home:"首页",info:"信息",location:"定位",setting:"设置",walk:"漫游",walkPerson:"人视",walkBird:"鸟瞰",walkMenu:"菜单"},dialog:{testTitle:"测试弹窗",testContent:'这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
'}},L={common:{title:"BimEngine",description:"This is a BIM-ENGINE demo.",openTestDialog:"Open Test Dialog",openInfoDialog:"Open Info Dialog (Wrapped)"},toolbar:{home:"Home",info:"Info",location:"Location",setting:"Settings",walk:"Walk",walkPerson:"Person",walkBird:"Bird Eye",walkMenu:"Menu"},dialog:{testTitle:"Test Dialog",testContent:'This is a draggable and resizable dialog. Try dragging the title bar or resizing from the bottom-right corner.
'}};class B{currentLocale="zh-CN";messages={"zh-CN":m,"en-US":L};listeners=[];constructor(){}getLocale(){return this.currentLocale}setLocale(t){this.currentLocale!==t&&(this.currentLocale=t,this.notifyListeners())}t(t){if(!t)return"";const e=t.split(".");let o=this.messages[this.currentLocale];for(const i of e)if(o&&typeof o=="object"&&i in o)o=o[i];else return t;return o}subscribe(t){return this.listeners.push(t),()=>{this.listeners=this.listeners.filter(e=>e!==t)}}notifyListeners(){this.listeners.forEach(t=>t(this.currentLocale))}}const u=new B,g=a=>u.t(a),w={name:"dark",primary:"#0078d4",primaryHover:"#0063b1",background:"#f5f5f5",panelBackground:"rgba(30, 30, 30, 0.9)",textPrimary:"#ffffff",textSecondary:"#cccccc",border:"#444444",icon:"#cccccc",iconActive:"#ffffff",componentBackground:"transparent",componentHover:"#333333",componentActive:"rgba(255, 255, 255, 0.1)"},k={name:"light",primary:"#0078d4",primaryHover:"#106ebe",background:"#f5f5f5",panelBackground:"#ffffff",textPrimary:"#333333",textSecondary:"#666666",border:"#e0e0e0",icon:"#555555",iconActive:"#0078d4",componentBackground:"transparent",componentHover:"#f0f0f0",componentActive:"#e0e0e0"};class x{currentTheme=w;listeners=[];constructor(){}getTheme(){return this.currentTheme}setTheme(t){t==="light"?this.applyTheme(k):this.applyTheme(w)}setCustomTheme(t){this.applyTheme(t)}applyTheme(t){this.currentTheme=t,this.notifyListeners()}subscribe(t){return this.listeners.push(t),t(this.currentTheme),()=>{this.listeners=this.listeners.filter(e=>e!==t)}}notifyListeners(){this.listeners.forEach(t=>t(this.currentTheme))}}const d=new x;class v{container;options;groups=[];activeBtnIds=new Set;btnRefs=new Map;dropdownElement=null;hoverTimeout=null;customColors=new Set;unsubscribeLocale=null;unsubscribeTheme=null;DEFAULT_ICON=' ';constructor(t){const e=typeof t.container=="string"?document.getElementById(t.container):t.container;if(!e)throw new Error("Container not found");this.container=e,this.options={showLabel:!0,visibility:{},direction:"row",position:"static",align:"vertical",expand:"down",...t},["backgroundColor","btnBackgroundColor","btnHoverColor","btnActiveColor","iconColor","iconActiveColor","textColor","textActiveColor"].forEach(i=>{t[i]&&this.customColors.add(i)}),this.initContainer(),this.applyStyles()}initContainer(){this.container.innerHTML="",this.container.classList.add("bim-btn-group-root"),this.options.direction==="column"?this.container.classList.add("dir-column"):this.container.classList.add("dir-row"),this.options.className&&this.container.classList.add(this.options.className),this.updatePosition()}updatePosition(){const t=this.options.position,e=this.container.style;if(e.top="",e.bottom="",e.left="",e.right="",e.transform="",t==="static"){this.container.classList.add("static");return}if(this.container.classList.remove("static"),this.container.style.position="absolute",typeof t=="object"&&"x"in t)e.left=`${t.x}px`,e.top=`${t.y}px`;else{const o="20px";switch(t){case"top-left":e.top=o,e.left=o;break;case"top-center":e.top=o,e.left="50%",e.transform="translateX(-50%)";break;case"top-right":e.top=o,e.right=o;break;case"bottom-left":e.bottom=o,e.left=o;break;case"bottom-center":e.bottom=o,e.left="50%",e.transform="translateX(-50%)";break;case"bottom-right":e.bottom=o,e.right=o;break;case"left-center":e.left=o,e.top="50%",e.transform="translateY(-50%)";break;case"right-center":e.right=o,e.top="50%",e.transform="translateY(-50%)";break;case"center":e.top="50%",e.left="50%",e.transform="translate(-50%, -50%)";break}}}applyStyles(){const t=this.container.style;this.options.backgroundColor&&t.setProperty("--bim-btn-group-section-bg",this.options.backgroundColor),this.options.btnBackgroundColor&&t.setProperty("--bim-btn-bg",this.options.btnBackgroundColor),this.options.btnHoverColor&&t.setProperty("--bim-btn-hover-bg",this.options.btnHoverColor),this.options.btnActiveColor&&t.setProperty("--bim-btn-active-bg",this.options.btnActiveColor),this.options.iconColor&&t.setProperty("--bim-icon-color",this.options.iconColor),this.options.iconActiveColor&&t.setProperty("--bim-icon-active-color",this.options.iconActiveColor),this.options.textColor&&t.setProperty("--bim-btn-text-color",this.options.textColor),this.options.textActiveColor&&t.setProperty("--bim-btn-text-active-color",this.options.textActiveColor)}setTheme(t){const e={backgroundColor:t.panelBackground,btnBackgroundColor:t.componentBackground,btnHoverColor:t.componentHover,btnActiveColor:t.componentActive,iconColor:t.icon,iconActiveColor:t.iconActive,textColor:t.textSecondary,textActiveColor:t.textPrimary};Object.entries(e).forEach(([o,i])=>{const n=o;this.customColors.has(n)||(this.options[n]=i)}),this.applyStyles()}setColors(t){this.options={...this.options,...t},Object.keys(t).forEach(e=>{this.customColors.add(e)}),this.applyStyles()}async init(){this.render(),this.unsubscribeLocale=u.subscribe(()=>{this.setLocales()}),this.unsubscribeTheme=d.subscribe(t=>{this.setTheme(t)})}setLocales(){this.render()}addGroup(t,e){if(this.groups.some(i=>i.id===t))return;const o={id:t,buttons:[]};if(e){const i=this.groups.findIndex(n=>n.id===e);i!==-1?this.groups.splice(i,0,o):this.groups.push(o)}else this.groups.push(o)}addButton(t){const{groupId:e,parentId:o}=t,i=this.groups.find(s=>s.id===e);if(!i)return;const n={...t,children:t.children||[]};if(o){const s=this.findButton(i.buttons,o);s&&(s.children||(s.children=[]),s.children.push(n))}else i.buttons.push(n)}findButton(t,e){for(const o of t){if(o.id===e)return o;if(o.children){const i=this.findButton(o.children,e);if(i)return i}}}render(){this.container.innerHTML="",this.btnRefs.clear(),this.groups.forEach((t,e)=>{const o=this.renderGroup(t,e,this.groups.length);this.container.appendChild(o)})}renderGroup(t,e,o){const i=document.createElement("div");return i.className="bim-btn-group-section",e{if(this.isVisible(n.id)){const s=this.renderButton(n);i.appendChild(s)}}),i}renderButton(t){const e=document.createElement("div");e.className="opt-btn-wrapper";const o=document.createElement("div");o.className="opt-btn",(t.align||this.options.align||"vertical")==="horizontal"?o.classList.add("align-horizontal"):o.classList.add("align-vertical"),this.activeBtnIds.has(t.id)&&o.classList.add("active"),t.disabled&&o.classList.add("disabled"),this.options.showLabel&&t.label||o.classList.add("no-label");const s=t.iconSize||32,r=t.minWidth||50;o.style.minWidth=`${r}px`;const l=document.createElement("div");l.className="opt-btn-icon",l.style.width=`${s}px`,l.style.height=`${s}px`,l.innerHTML=this.getIcon(t.icon),o.appendChild(l);const c=document.createElement("div");if(c.className="opt-btn-text-wrapper",this.options.showLabel&&t.label){const h=document.createElement("span");h.className="opt-btn-label",h.textContent=g(t.label),c.appendChild(h)}if(t.children&&t.children.length>0){const h=document.createElement("span");h.className="opt-btn-arrow",h.textContent="▼",c.appendChild(h)}return c.hasChildNodes()&&o.appendChild(c),o.addEventListener("click",()=>this.handleClick(t)),o.addEventListener("mouseenter",()=>this.handleMouseEnter(t,o)),o.addEventListener("mouseleave",()=>this.handleMouseLeave()),this.btnRefs.set(t.id,o),e.appendChild(o),e}handleClick(t){t.disabled||(!t.children||t.children.length===0)&&(t.keepActive&&(this.activeBtnIds.has(t.id)?this.activeBtnIds.delete(t.id):this.activeBtnIds.add(t.id),this.updateButtonState(t.id)),this.closeDropdown(),t.onClick&&t.onClick(t))}handleMouseEnter(t,e){this.hoverTimeout&&clearTimeout(this.hoverTimeout),t.children&&t.children.length>0?this.showDropdown(t,e):this.closeDropdown()}handleMouseLeave(){this.hoverTimeout=window.setTimeout(()=>this.closeDropdown(),200)}showDropdown(t,e){if(this.closeDropdown(),!t.children)return;const o=document.createElement("div");o.className="opt-btn-dropdown",this.options.backgroundColor&&o.style.setProperty("--bim-toolbar-bg",this.options.backgroundColor);const i=e.getBoundingClientRect(),n=this.options.expand||"down";this.options.direction==="row"?o.style.flexDirection="column":o.style.flexDirection="row",document.body.appendChild(o),t.children.forEach(r=>{if(this.isVisible(r.id)){const l=this.renderDropdownItem(r);o.appendChild(l)}});const s=o.getBoundingClientRect();n==="up"?(o.style.bottom=window.innerHeight-i.top+8+"px",o.style.left=i.left+(i.width-s.width)/2+"px"):n==="down"?(o.style.top=i.bottom+8+"px",o.style.left=i.left+(i.width-s.width)/2+"px"):n==="right"?(o.style.top=i.top+(i.height-s.height)/2+"px",o.style.left=i.right+8+"px"):n==="left"&&(o.style.top=i.top+(i.height-s.height)/2+"px",o.style.right=window.innerWidth-i.left+8+"px"),o.addEventListener("mouseenter",()=>{this.hoverTimeout&&clearTimeout(this.hoverTimeout)}),o.addEventListener("mouseleave",()=>this.handleMouseLeave()),this.dropdownElement=o}renderDropdownItem(t){const e=document.createElement("div");e.className="opt-btn-dropdown-item",(t.align||"horizontal")==="horizontal"?e.classList.add("align-horizontal"):e.classList.add("align-vertical");const i=t.iconSize||32,n=t.minWidth;n&&(e.style.minWidth=`${n}px`);const s=document.createElement("div");if(s.className="opt-btn-icon",s.style.width=`${i}px`,s.style.height=`${i}px`,s.innerHTML=this.getIcon(t.icon),e.appendChild(s),this.options.showLabel&&t.label){const r=document.createElement("span");r.className="opt-btn-dropdown-label",r.textContent=g(t.label),e.appendChild(r)}return e.addEventListener("click",r=>{r.stopPropagation(),this.handleClick(t)}),e}closeDropdown(){this.dropdownElement&&(this.dropdownElement.remove(),this.dropdownElement=null),this.btnRefs.forEach(t=>{const e=t.querySelector(".opt-btn-arrow");e&&e.classList.remove("rotated")})}updateButtonState(t){const e=this.btnRefs.get(t);e&&(this.activeBtnIds.has(t)?e.classList.add("active"):e.classList.remove("active"))}getIcon(t){return t||this.DEFAULT_ICON}updateButtonVisibility(t,e){this.options.visibility||(this.options.visibility={}),this.options.visibility[t]=e,this.render()}setShowLabel(t){this.options.showLabel=t,this.updateLabelsVisibility()}updateLabelsVisibility(){this.btnRefs.forEach((t,e)=>{const o=this.findButtonById(e);if(!o)return;this.options.showLabel&&o.label?t.classList.remove("no-label"):t.classList.add("no-label")})}findButtonById(t){for(const e of this.groups){const o=this.findButton(e.buttons,t);if(o)return o}}setBackgroundColor(t){this.setColors({backgroundColor:t})}isVisible(t){return this.options.visibility?.[t]!==!1}destroy(){this.unsubscribeLocale&&(this.unsubscribeLocale(),this.unsubscribeLocale=null),this.unsubscribeTheme&&(this.unsubscribeTheme(),this.unsubscribeTheme=null),this.closeDropdown(),this.container.innerHTML="",this.btnRefs.clear()}}class C extends v{async init(){await super.init();const{homeButton:t}=await Promise.resolve().then(()=>D),{locationButton:e}=await Promise.resolve().then(()=>q),{walkMenuButton:o}=await Promise.resolve().then(()=>I),{walkPersonButton:i}=await Promise.resolve().then(()=>H),{walkBirdButton:n}=await Promise.resolve().then(()=>_),{settingButton:s}=await Promise.resolve().then(()=>A),{infoButton:r}=await Promise.resolve().then(()=>G);this.addGroup("group-1"),this.addButton(t),this.addButton(o),this.addButton(i),this.addButton(n),this.addButton(e),this.addGroup("group-2"),this.addButton(s),this.addButton(r),this.render()}}class E{toolbar=null;toolbarContainer=null;container;constructor(t){this.container=t,this.init()}init(){this.toolbarContainer=document.createElement("div"),this.toolbarContainer.id="opt-btn-groups",this.toolbarContainer.className="bim-engine-opt-btn-container is-bottom-toolbar",this.container.appendChild(this.toolbarContainer),this.toolbar=new C({container:this.toolbarContainer,showLabel:!0,direction:"row",position:"bottom-center",align:"vertical",expand:"up"}),this.toolbar.init()}updateTheme(t){this.toolbar?.setTheme(t)}refresh(){this.toolbar?.render()}destroy(){this.toolbar?.destroy(),this.toolbar=null}addGroup(t,e){this.toolbar?.addGroup(t,e),this.toolbar?.render()}addButton(t){this.toolbar?.addButton(t),this.toolbar?.render()}setButtonVisibility(t,e){this.toolbar?.updateButtonVisibility(t,e)}setShowLabel(t){this.toolbar?.setShowLabel(t)}setVisible(t){this.toolbarContainer&&(this.toolbarContainer.style.visibility=t?"visible":"hidden")}setBackgroundColor(t){this.toolbar?.setBackgroundColor(t)}setColors(t){this.toolbar?.setColors(t)}}class M{activeGroups=[];container;constructor(t){this.container=t}create(t){const e=document.createElement("div");this.container.appendChild(e);const o=new v({container:e,...t});return o.init(),this.activeGroups.push(o),o}updateTheme(t){this.activeGroups.forEach(e=>e.setTheme(t))}refresh(){this.activeGroups.forEach(t=>t.render())}destroy(){this.activeGroups.forEach(t=>t.destroy()),this.activeGroups=[]}}class T{element;options;container;header;contentArea;_isDestroyed=!1;_isInitialized=!1;unsubscribeTheme=null;unsubscribeLocale=null;constructor(t){this.options={title:"Dialog",width:300,height:"auto",position:"center",draggable:!0,resizable:!1,minWidth:200,minHeight:100,...t},this.container=t.container,this.element=this.createDom(),this.header=this.element.querySelector(".bim-dialog-header"),this.contentArea=this.element.querySelector(".bim-dialog-content"),this.init()}setTheme(t){const e=this.element.style;this.options.backgroundColor||e.setProperty("--bim-dialog-bg",t.panelBackground),this.options.headerBackgroundColor||e.setProperty("--bim-dialog-header-bg",t.componentHover),this.options.titleColor||e.setProperty("--bim-dialog-title-color",t.textPrimary),this.options.textColor||e.setProperty("--bim-dialog-text-color",t.textPrimary),this.options.borderColor||e.setProperty("--bim-dialog-border-color",t.border)}init(){this._isInitialized||(this.container.appendChild(this.element),this.initPosition(),this.options.draggable&&this.initDrag(),this.options.resizable&&this.initResize(),this._isInitialized=!0,this.options.onOpen&&this.options.onOpen(),this.unsubscribeTheme=d.subscribe(t=>{this.setTheme(t)}),this.unsubscribeLocale=u.subscribe(()=>{this.setLocales()}))}setLocales(){if(this.options.title){const t=this.header.querySelector(".bim-dialog-title");t&&(t.textContent=g(this.options.title))}}createDom(){const t=document.createElement("div");t.className="bim-dialog",this.options.id&&(t.id=this.options.id);const e=t.style;this.options.backgroundColor&&e.setProperty("--bim-dialog-bg",this.options.backgroundColor),this.options.headerBackgroundColor&&e.setProperty("--bim-dialog-header-bg",this.options.headerBackgroundColor),this.options.titleColor&&e.setProperty("--bim-dialog-title-color",this.options.titleColor),this.options.textColor&&e.setProperty("--bim-dialog-text-color",this.options.textColor),this.options.borderColor&&e.setProperty("--bim-dialog-border-color",this.options.borderColor),this.setSize(t,this.options.width,this.options.height);const o=document.createElement("div");o.className="bim-dialog-header",this.options.draggable&&o.classList.add("draggable");const i=document.createElement("span");i.className="bim-dialog-title",i.textContent=this.options.title?g(this.options.title):"";const n=document.createElement("span");n.className="bim-dialog-close",n.innerHTML="×",n.onclick=()=>this.close(),o.appendChild(i),o.appendChild(n);const s=document.createElement("div");if(s.className="bim-dialog-content",typeof this.options.content=="string"?s.innerHTML=this.options.content:this.options.content instanceof HTMLElement&&s.appendChild(this.options.content),t.appendChild(o),t.appendChild(s),this.options.resizable){const r=document.createElement("div");r.className="bim-dialog-resize-handle",t.appendChild(r)}return t}setSize(t,e,o){e!==void 0&&(t.style.width=typeof e=="number"?`${e}px`:e),o!==void 0&&(t.style.height=typeof o=="number"?`${o}px`:o)}initPosition(){const t=this.options.position,e=this.element.getBoundingClientRect();let o=0,i=0;const n=this.container.clientWidth,s=this.container.clientHeight,r=e.width,l=e.height;if(typeof t=="object"&&"x"in t)o=t.x,i=t.y;else switch(t){case"center":o=(n-r)/2,i=(s-l)/2;break;case"top-left":o=0,i=0;break;case"top-center":o=(n-r)/2,i=0;break;case"top-right":o=n-r,i=0;break;case"left-center":o=0,i=(s-l)/2;break;case"right-center":o=n-r,i=(s-l)/2;break;case"bottom-left":o=0,i=s-l;break;case"bottom-center":o=(n-r)/2,i=s-l;break;case"bottom-right":o=n-r,i=s-l;break;default:o=(n-r)/2,i=(s-l)/2}o=Math.max(0,Math.min(o,n-r)),i=Math.max(0,Math.min(i,s-l)),this.element.style.left=`${o}px`,this.element.style.top=`${i}px`}initDrag(){let t=0,e=0,o=0,i=0;const n=l=>{l.preventDefault(),t=l.clientX,e=l.clientY,o=this.element.offsetLeft,i=this.element.offsetTop,document.addEventListener("mousemove",s),document.addEventListener("mouseup",r)},s=l=>{const c=l.clientX-t,h=l.clientY-e;let b=o+c,f=i+h;const y=this.container.clientWidth-this.element.offsetWidth,N=this.container.clientHeight-this.element.offsetHeight;b=Math.max(0,Math.min(b,y)),f=Math.max(0,Math.min(f,N)),this.element.style.left=`${b}px`,this.element.style.top=`${f}px`},r=()=>{document.removeEventListener("mousemove",s),document.removeEventListener("mouseup",r)};this.header.addEventListener("mousedown",n)}initResize(){const t=this.element.querySelector(".bim-dialog-resize-handle");if(!t)return;let e=0,o=0,i=0,n=0;const s=c=>{c.preventDefault(),c.stopPropagation(),e=c.clientX,o=c.clientY,i=this.element.offsetWidth,n=this.element.offsetHeight,document.addEventListener("mousemove",r),document.addEventListener("mouseup",l)},r=c=>{const h=c.clientX-e,b=c.clientY-o,f=Math.max(this.options.minWidth||100,i+h),y=Math.max(this.options.minHeight||50,n+b);this.element.style.width=`${f}px`,this.element.style.height=`${y}px`},l=()=>{document.removeEventListener("mousemove",r),document.removeEventListener("mouseup",l)};t.addEventListener("mousedown",s)}setContent(t){this.contentArea.innerHTML="",typeof t=="string"?this.contentArea.innerHTML=t:this.contentArea.appendChild(t)}close(){this._isDestroyed||(this.unsubscribeTheme&&(this.unsubscribeTheme(),this.unsubscribeTheme=null),this.unsubscribeLocale&&(this.unsubscribeLocale(),this.unsubscribeLocale=null),this.element.remove(),this._isDestroyed=!0,this.options.onClose&&this.options.onClose())}destroy(){this.close()}}class P extends T{constructor(t){const e=document.createElement("div");e.className="bim-info-dialog-content";const o=document.createElement("h3");o.textContent="Model Information";const i=document.createElement("ul");i.innerHTML=`
Name: Sample Project
Version: 1.0.0
Date: ${new Date().toLocaleDateString()}
Status: Active
- `;const n=document.createElement("button");n.textContent="Update Status",n.style.marginTop="10px",n.onclick=()=>{alert("Status updated!")},o.appendChild(e),o.appendChild(i),o.appendChild(n),this.dialog=new b({container:t,title:"Project Info (Wrapped)",content:o,width:320,height:"auto",position:"center",resizable:!0,draggable:!0})}close(){this.dialog.close()}}class w{container;constructor(t){this.container=t}create(t){return new b({container:this.container,...t})}showInfoDialog(){new v(this.container)}}class C{container;wrapper=null;toolbar=null;dialog=null;constructor(t){const o=typeof t=="string"?document.getElementById(t):t;if(!o)throw new Error("Container not found");this.container=o,this.init()}init(){this.container.innerHTML="",this.wrapper=document.createElement("div"),this.wrapper.className="bim-engine-wrapper";const t=document.createElement("h1");t.textContent="BimEngine",t.className="bim-engine-title";const o=document.createElement("p");o.textContent="这是一个使用BIM-ENGINE。",o.className="bim-engine-desc";const e=document.createElement("div");e.id="opt-btn-groups",e.className="bim-engine-opt-btn-container",this.wrapper.appendChild(t),this.wrapper.appendChild(o),this.dialog=new w(this.wrapper),this.toolbar=new f(e);const i=document.createElement("button");i.textContent="打开测试弹窗",i.className="bim-engine-btn",i.onclick=()=>{this.dialog?.create({title:"测试弹窗",content:'这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',width:300,height:400,position:"top-left",draggable:!0,resizable:!0})};const n=document.createElement("button");n.textContent="打开信息弹窗 (封装版)",n.className="bim-engine-btn",n.style.marginLeft="10px",n.onclick=()=>{this.dialog?.showInfoDialog()},this.wrapper.appendChild(i),this.wrapper.appendChild(n),this.wrapper.appendChild(e),this.container.appendChild(this.wrapper)}destroy(){this.toolbar&&(this.toolbar.destroy(),this.toolbar=null),this.dialog=null,this.container.innerHTML=""}}const y=Object.freeze(Object.defineProperty({__proto__:null,homeButton:{id:"home",groupId:"group-1",type:"button",label:"首页",icon:' ',keepActive:!0,onClick:a=>{console.log("首页按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),B=Object.freeze(Object.defineProperty({__proto__:null,locationButton:{id:"location",groupId:"group-1",type:"button",label:"定位",icon:' ',keepActive:!1,onClick:a=>{console.log("定位按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),T=Object.freeze(Object.defineProperty({__proto__:null,walkMenuButton:{id:"walk",groupId:"group-1",type:"menu",label:"漫游",icon:' ',keepActive:!0,onClick:a=>{console.log("漫游按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),E=Object.freeze(Object.defineProperty({__proto__:null,walkPersonButton:{id:"walk-person",groupId:"group-1",parentId:"walk",type:"button",label:"人视漫游",icon:' ',onClick:a=>{console.log("人视漫游被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),x=Object.freeze(Object.defineProperty({__proto__:null,walkBirdButton:{id:"walk-bird",groupId:"group-1",parentId:"walk",type:"button",label:"鸟瞰漫游",icon:' ',onClick:a=>{console.log("鸟瞰漫游被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),L=Object.freeze(Object.defineProperty({__proto__:null,settingButton:{id:"setting",groupId:"group-2",type:"button",label:"设置",icon:' ',keepActive:!1,onClick:a=>{console.log("设置按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),k=Object.freeze(Object.defineProperty({__proto__:null,infoButton:{id:"info",groupId:"group-2",type:"button",label:"信息",icon:' ',keepActive:!1,onClick:a=>{console.log("信息按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"}));d.BimEngine=C,d.OptBtnGroups=h,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
+ `;const n=document.createElement("button");n.textContent="Update Status",n.style.marginTop="10px",n.onclick=()=>{alert("Status updated!")},e.appendChild(o),e.appendChild(i),e.appendChild(n),super({container:t,title:"dialog.testTitle",content:e,width:320,height:"auto",position:"center",resizable:!0,draggable:!0,onClose:()=>{console.log("Info dialog closed")},onOpen:()=>{console.log("Info dialog opened")}})}}class S{container;activeDialogs=[];constructor(t){this.container=t}create(t){const e=new T({container:this.container,...t,onClose:()=>{this.activeDialogs=this.activeDialogs.filter(o=>o!==e),t.onClose&&t.onClose()}});return e.setTheme(d.getTheme()),this.activeDialogs.push(e),e}showInfoDialog(){new P(this.container)}updateTheme(t){this.activeDialogs.forEach(e=>{e.setTheme&&e.setTheme(t)})}}class z{container;wrapper=null;topLeftGroup=null;toolbar=null;buttonGroup=null;dialog=null;get localeManager(){return u}get themeManager(){return d}constructor(t,e){const o=typeof t=="string"?document.getElementById(t):t;if(!o)throw new Error("Container not found");this.container=o,e?.locale&&u.setLocale(e.locale),e?.theme&&(e.theme==="custom"?console.warn("Custom theme should be set via setCustomTheme()."):d.setTheme(e.theme)),this.init()}setLocale(t){u.setLocale(t)}getLocale(){return u.getLocale()}setTheme(t){d.setTheme(t)}setCustomTheme(t){d.setCustomTheme(t)}init(){this.container.innerHTML="",this.wrapper=document.createElement("div"),this.wrapper.className="bim-engine-wrapper",this.container.appendChild(this.wrapper),this.dialog=new S(this.wrapper),this.toolbar=new E(this.wrapper),this.buttonGroup=new M(this.wrapper),this.createTopLeftGroup(),this.updateTheme(d.getTheme()),this.topLeftGroup&&this.topLeftGroup.setColors({backgroundColor:"#ff00ff"}),d.subscribe(t=>{this.updateTheme(t)})}createTopLeftGroup(){this.buttonGroup&&(this.topLeftGroup=this.buttonGroup.create({position:"top-left",direction:"column",align:"vertical",backgroundColor:"#ff00ff",showLabel:!1}),this.topLeftGroup.addGroup("main"),this.topLeftGroup.addButton({id:"menu-btn",groupId:"main",type:"button",label:"Menu",icon:' ',onClick:()=>{alert("点击按钮")}}),this.topLeftGroup.render())}updateTheme(t){this.wrapper&&(this.wrapper.style.backgroundColor=t.background,this.wrapper.style.color=t.textPrimary)}destroy(){this.toolbar?.destroy(),this.buttonGroup?.destroy(),this.dialog=null,this.container.innerHTML=""}}const D=Object.freeze(Object.defineProperty({__proto__:null,homeButton:{id:"home",groupId:"group-1",type:"button",label:"toolbar.home",icon:' ',keepActive:!0,onClick:a=>{console.log("首页按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),q=Object.freeze(Object.defineProperty({__proto__:null,locationButton:{id:"location",groupId:"group-1",type:"button",label:"toolbar.location",icon:' ',keepActive:!1,onClick:a=>{console.log("定位按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),I=Object.freeze(Object.defineProperty({__proto__:null,walkMenuButton:{id:"walk",groupId:"group-1",type:"menu",label:"toolbar.walk",align:"vertical",icon:' ',keepActive:!0,onClick:a=>{console.log("漫游按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),H=Object.freeze(Object.defineProperty({__proto__:null,walkPersonButton:{id:"walk-person",groupId:"group-1",parentId:"walk",type:"button",align:"vertical",label:"toolbar.walkPerson",icon:' ',onClick:a=>{console.log("人视漫游被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),_=Object.freeze(Object.defineProperty({__proto__:null,walkBirdButton:{id:"walk-bird",groupId:"group-1",parentId:"walk",align:"vertical",type:"button",label:"toolbar.walkBird",icon:' ',onClick:a=>{console.log("鸟瞰漫游被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),A=Object.freeze(Object.defineProperty({__proto__:null,settingButton:{id:"setting",groupId:"group-2",type:"button",label:"toolbar.setting",icon:' ',keepActive:!1,onClick:a=>{console.log("设置按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"})),G=Object.freeze(Object.defineProperty({__proto__:null,infoButton:{id:"info",groupId:"group-2",type:"button",label:"toolbar.info",icon:' ',keepActive:!1,onClick:a=>{console.log("信息按钮被点击:",a.id)}}},Symbol.toStringTag,{value:"Module"}));p.BimButtonGroup=v,p.BimEngine=z,p.Toolbar=C,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})}));
//# sourceMappingURL=bim-engine-sdk.umd.js.map
diff --git a/dist/bim-engine-sdk.umd.js.map b/dist/bim-engine-sdk.umd.js.map
index 0a12d8e..830a32c 100644
--- a/dist/bim-engine-sdk.umd.js.map
+++ b/dist/bim-engine-sdk.umd.js.map
@@ -1 +1 @@
-{"version":3,"file":"bim-engine-sdk.umd.js","sources":["../src/toolbar/index.ts","../src/modules/toolbar-manager.ts","../src/dialog/index.ts","../src/dialog/bimInfoDialog/index.ts","../src/modules/dialog-manager.ts","../src/bim-engine.ts","../src/toolbar/buttons/home/index.ts","../src/toolbar/buttons/location/index.ts","../src/toolbar/buttons/walk/walk-menu/index.ts","../src/toolbar/buttons/walk/walk-person/index.ts","../src/toolbar/buttons/walk/walk-bird/index.ts","../src/toolbar/buttons/setting/index.ts","../src/toolbar/buttons/info/index.ts"],"sourcesContent":["import './index.css';\nimport type {\n OptButton,\n ButtonGroup,\n OptBtnGroupsOptions,\n ButtonConfig,\n ToolbarColors\n} from './index.type';\n\n/**\n * 底部操作按钮组组件\n * 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。\n */\nexport class OptBtnGroups {\n /** 挂载容器 */\n private container: HTMLElement;\n /** 组件配置选项 */\n private options: OptBtnGroupsOptions;\n /** 按钮组列表,按顺序存储 */\n private groups: ButtonGroup[] = [];\n /** 当前处于激活状态的按钮 ID 集合 */\n private activeBtnIds: Set = new Set();\n /** 按钮 DOM 元素的引用映射,方便快速查找 */\n private btnRefs: Map = new Map();\n /** 当��显示的下拉菜单元素 */\n private dropdownElement: HTMLElement | null = null;\n /** 鼠标悬停计时器,用于处理菜单显示的防抖 */\n private hoverTimeout: number | null = null;\n\n /** 默认图标 SVG */\n private readonly DEFAULT_ICON = ' ';\n\n /**\n * 构造函数\n * @param options 配置选项\n */\n constructor(options: OptBtnGroupsOptions) {\n const el = typeof options.container === 'string'\n ? document.getElementById(options.container)\n : options.container;\n\n if (!el) throw new Error('Container not found');\n\n this.container = el;\n this.options = {\n showLabel: true,\n visibility: {},\n ...options\n };\n\n this.initContainer();\n this.applyStyles(); // 应用初始样式配置\n }\n\n /**\n * 初始化容器\n */\n private initContainer(): void {\n this.container.innerHTML = '';\n this.container.classList.add('toolbar-root'); // 添加一个根类方便定位样式\n }\n\n /**\n * 应用样式配置到 CSS 变量\n */\n private applyStyles(): void {\n const style = this.container.style;\n if (this.options.backgroundColor) style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);\n if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);\n if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);\n if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);\n if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);\n if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);\n if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);\n if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);\n }\n\n /**\n * 更新颜色配置\n * @param colors 颜色配置对象\n */\n public setColors(colors: ToolbarColors): void {\n // 更新 options\n this.options = { ...this.options, ...colors };\n // 应用到 CSS 变量\n this.applyStyles();\n }\n\n /**\n * 添加按钮组\n * @param groupId 组ID\n * @param beforeGroupId 在哪个组之前插入(可选���,不传则插入到最后\n */\n public addGroup(groupId: string, beforeGroupId?: string): void {\n if (this.groups.some(g => g.id === groupId)) {\n console.warn('Group ' + groupId + ' already exists');\n return;\n }\n\n const newGroup: ButtonGroup = { id: groupId, buttons: [] };\n\n if (beforeGroupId) {\n const index = this.groups.findIndex(g => g.id === beforeGroupId);\n if (index !== -1) {\n this.groups.splice(index, 0, newGroup);\n } else {\n console.warn(`Target group ${beforeGroupId} not found, appending ${groupId} to end.`);\n this.groups.push(newGroup);\n }\n } else {\n this.groups.push(newGroup);\n }\n }\n\n /**\n * 添加按钮到指定组\n * @param config 按钮配置(必须包含 groupId,可选包含 parentId)\n */\n public addButton(config: ButtonConfig): void {\n const { groupId, parentId } = config;\n\n if (!groupId) {\n throw new Error(`Button ${config.id} config must contain 'groupId'`);\n }\n\n const group = this.groups.find(g => g.id === groupId);\n if (!group) {\n throw new Error(`Group ${groupId} not found. Please call addGroup first.`);\n }\n\n const button: OptButton = {\n ...config,\n children: config.children || []\n };\n\n if (parentId) {\n // 添加为子按钮(菜单项)\n const parentBtn = this.findButton(group.buttons, parentId);\n if (!parentBtn) {\n throw new Error(`Parent button ${parentId} not found in group ${groupId}`);\n }\n if (!parentBtn.children) {\n parentBtn.children = [];\n }\n parentBtn.children.push(button);\n } else {\n // 添加为主按钮\n group.buttons.push(button);\n }\n }\n\n /**\n * 递归查找按钮\n */\n private findButton(buttons: OptButton[], id: string): OptButton | undefined {\n for (const btn of buttons) {\n if (btn.id === id) return btn;\n if (btn.children) {\n const found = this.findButton(btn.children, id);\n if (found) return found;\n }\n }\n return undefined;\n }\n\n /**\n * 初始化组件,加载默认按钮配置\n */\n public async init(): Promise {\n // 动态导入默认按钮配置\n const { homeButton } = await import('./buttons/home');\n const { locationButton } = await import('./buttons/location');\n const { walkMenuButton } = await import('./buttons/walk/walk-menu');\n const { walkPersonButton } = await import('./buttons/walk/walk-person');\n const { walkBirdButton } = await import('./buttons/walk/walk-bird');\n const { settingButton } = await import('./buttons/setting');\n const { infoButton } = await import('./buttons/info');\n\n // 配置默认组和按钮\n this.addGroup('group-1');\n this.addButton(homeButton);\n this.addButton(walkMenuButton);\n this.addButton(walkPersonButton);\n this.addButton(walkBirdButton);\n this.addButton(locationButton);\n this.addGroup('group-2');\n this.addButton(settingButton);\n this.addButton(infoButton);\n this.render();\n }\n\n /**\n * 渲染整个工具栏\n */\n public render(): void {\n this.container.innerHTML = '';\n this.btnRefs.clear();\n\n const wrapper = document.createElement('div');\n wrapper.className = 'toolbar-container';\n\n // 渲染所有组\n this.groups.forEach((group, index) => {\n const groupElement = this.renderGroup(group, index, this.groups.length);\n wrapper.appendChild(groupElement);\n });\n\n this.container.appendChild(wrapper);\n }\n\n /**\n * 渲染单个按钮组\n */\n private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {\n const groupEl = document.createElement('div');\n groupEl.className = 'opt-btn-group';\n\n if (index < total - 1) {\n groupEl.classList.add('has-divider');\n }\n\n group.buttons.forEach(button => {\n if (this.isVisible(button.id)) {\n const btnWrapper = this.renderButton(button);\n groupEl.appendChild(btnWrapper);\n }\n });\n\n return groupEl;\n }\n\n /**\n * 渲染单个按钮\n */\n private renderButton(button: OptButton): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'opt-btn-wrapper';\n\n const btnEl = document.createElement('div');\n btnEl.className = 'opt-btn';\n\n // 设置激活状态\n if (this.activeBtnIds.has(button.id)) {\n btnEl.classList.add('active');\n }\n\n if (button.disabled) {\n btnEl.classList.add('disabled');\n }\n\n if (!this.options.showLabel) {\n btnEl.classList.add('no-label');\n if (button.label) {\n btnEl.title = button.label;\n }\n }\n\n // 渲染图标\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.innerHTML = this.getIcon(button.icon);\n btnEl.appendChild(icon);\n\n // 渲染标签\n if (this.options.showLabel && button.label) {\n const label = document.createElement('span');\n label.className = 'opt-btn-label';\n label.textContent = button.label;\n btnEl.appendChild(label);\n }\n\n // 如果有子菜单,渲染箭头\n if (button.children && button.children.length > 0) {\n const arrow = document.createElement('span');\n arrow.className = 'opt-btn-arrow';\n arrow.textContent = '▼';\n btnEl.appendChild(arrow);\n }\n\n // 绑定事件\n btnEl.addEventListener('click', () => this.handleClick(button));\n btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));\n btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());\n\n this.btnRefs.set(button.id, btnEl);\n\n wrapper.appendChild(btnEl);\n return wrapper;\n }\n\n /**\n * 处理按钮点击事件\n */\n private handleClick(button: OptButton): void {\n if (button.disabled) return;\n\n // 如果没有子菜单,直接触发\n if (!button.children || button.children.length === 0) {\n if (button.keepActive) {\n const wasActive = this.activeBtnIds.has(button.id);\n if (wasActive) {\n this.activeBtnIds.delete(button.id);\n } else {\n this.activeBtnIds.add(button.id);\n }\n this.updateButtonState(button.id);\n }\n\n this.closeDropdown();\n\n if (button.onClick) {\n button.onClick(button);\n }\n }\n }\n\n /**\n * 处理子菜单项点击事件\n */\n private handleSubClick(button: OptButton): void {\n if (button.keepActive) {\n const wasActive = this.activeBtnIds.has(button.id);\n if (wasActive) {\n this.activeBtnIds.delete(button.id);\n } else {\n this.activeBtnIds.add(button.id);\n }\n this.updateButtonState(button.id);\n }\n\n this.closeDropdown();\n\n if (button.onClick) {\n button.onClick(button);\n }\n }\n\n /**\n * 处理鼠标移入事件(显示菜单)\n */\n private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {\n if (this.hoverTimeout) {\n clearTimeout(this.hoverTimeout);\n this.hoverTimeout = null;\n }\n\n if (button.children && button.children.length > 0) {\n this.showDropdown(button, btnEl);\n\n const arrow = btnEl.querySelector('.opt-btn-arrow');\n if (arrow) {\n arrow.classList.add('rotated');\n }\n } else {\n this.closeDropdown();\n }\n }\n\n /**\n * 处理鼠标移出事件(隐藏菜单)\n */\n private handleMouseLeave(): void {\n this.hoverTimeout = window.setTimeout(() => {\n this.closeDropdown();\n }, 200);\n }\n\n /**\n * 显示下拉菜单\n */\n private showDropdown(button: OptButton, btnEl: HTMLElement): void {\n this.closeDropdown();\n\n if (!button.children) return;\n\n const dropdown = document.createElement('div');\n dropdown.className = 'opt-btn-dropdown';\n \n // 下拉菜单也应用当前的 CSS 变量样式,因为它们通常挂载在 body 上,所以需要单独设置或者确保能继承\n // 简单起见,我们可以直接将容器上的 CSS 变量复制过来,或者设置内联样式\n // 更好的是:如果我们在 this.container 上设置 CSS 变量,\n // 而 dropdown 挂载在 body 上,它无法继承。\n // 所以我们需要将 CSS 变量也应用到 dropdown 上。\n \n const style = dropdown.style;\n if (this.options.backgroundColor) style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);\n if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);\n if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);\n if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);\n if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);\n if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);\n if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);\n if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);\n\n const rect = btnEl.getBoundingClientRect();\n const centerX = rect.left + rect.width / 2;\n\n dropdown.style.top = rect.top - 8 + 'px';\n dropdown.style.left = centerX + 'px';\n\n button.children.forEach(subBtn => {\n if (this.isVisible(subBtn.id)) {\n const item = this.renderDropdownItem(subBtn);\n dropdown.appendChild(item);\n }\n });\n\n // 保持菜单显���\n dropdown.addEventListener('mouseenter', () => {\n if (this.hoverTimeout) {\n clearTimeout(this.hoverTimeout);\n this.hoverTimeout = null;\n }\n });\n\n dropdown.addEventListener('mouseleave', () => this.handleMouseLeave());\n\n document.body.appendChild(dropdown);\n this.dropdownElement = dropdown;\n }\n\n /**\n * 渲染下拉菜单项\n */\n private renderDropdownItem(button: OptButton): HTMLElement {\n const item = document.createElement('div');\n item.className = 'opt-btn-dropdown-item';\n\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon small';\n icon.innerHTML = this.getIcon(button.icon);\n item.appendChild(icon);\n\n if (this.options.showLabel) {\n const label = document.createElement('span');\n label.textContent = button.label;\n item.appendChild(label);\n }\n\n item.addEventListener('click', (e) => {\n e.stopPropagation();\n this.handleSubClick(button);\n });\n\n return item;\n }\n\n /**\n * 关闭所有下拉菜单\n */\n private closeDropdown(): void {\n if (this.dropdownElement) {\n this.dropdownElement.remove();\n this.dropdownElement = null;\n }\n\n this.btnRefs.forEach(btnEl => {\n const arrow = btnEl.querySelector('.opt-btn-arrow');\n if (arrow) {\n arrow.classList.remove('rotated');\n }\n });\n }\n\n /**\n * 更新按钮的激活状态样式\n */\n private updateButtonState(buttonId: string): void {\n const btnEl = this.btnRefs.get(buttonId);\n if (btnEl) {\n if (this.activeBtnIds.has(buttonId)) {\n btnEl.classList.add('active');\n } else {\n btnEl.classList.remove('active');\n }\n }\n }\n\n /**\n * 获取图标 SVG 字符串\n */\n private getIcon(icon?: string): string {\n return icon || this.DEFAULT_ICON;\n }\n\n /**\n * 更新按钮可见性\n * @param buttonId 按钮ID\n * @param visible 是否可见\n */\n public updateButtonVisibility(buttonId: string, visible: boolean): void {\n if (!this.options.visibility) {\n this.options.visibility = {};\n }\n this.options.visibility[buttonId] = visible;\n this.render();\n }\n\n /**\n * 设置是否显示标签\n * @param show 是否显示\n */\n public setShowLabel(show: boolean): void {\n this.options.showLabel = show;\n this.render();\n }\n\n /**\n * 设置背景颜色 (兼容旧接口)\n * @param color CSS 颜色值\n */\n public setBackgroundColor(color: string): void {\n this.setColors({ backgroundColor: color });\n }\n\n /**\n * 检查按钮是否可见\n */\n private isVisible(id: string): boolean {\n return this.options.visibility?.[id] !== false;\n }\n\n /**\n * 销毁组件,清理资源\n */\n public destroy(): void {\n this.closeDropdown();\n if (this.hoverTimeout) {\n clearTimeout(this.hoverTimeout);\n }\n this.container.innerHTML = '';\n this.btnRefs.clear();\n this.activeBtnIds.clear();\n this.groups = [];\n }\n}","import type { ToolbarColors } from '../toolbar/index.type';\nimport { OptBtnGroups } from '../toolbar';\nimport type { ButtonConfig } from '../toolbar/index.type';\n\n/**\n * 工具栏管理器\n * 负责管理底部操作栏的按钮组、按钮及其可见性等状态。\n */\nexport class ToolbarManager {\n /** 内部工具栏组件实例 */\n private optBtnGroups: OptBtnGroups | null = null;\n /** 工具栏挂载的容器 */\n private container: HTMLElement;\n\n /**\n * 构造函数\n * @param container 工具栏挂载的容器元素\n */\n constructor(container: HTMLElement) {\n this.container = container;\n this.init();\n }\n\n /**\n * 初始化工具栏\n */\n private init() {\n this.optBtnGroups = new OptBtnGroups({\n container: this.container,\n showLabel: true\n });\n\n // 初始化并加载默认按钮配置\n this.optBtnGroups.init().catch(err => {\n console.error('Failed to initialize OptBtnGroups:', err);\n });\n }\n\n /**\n * 添加一个工具栏按钮组\n * @param groupId 新组的 ID\n * @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后\n */\n public addGroup(groupId: string, beforeGroupId?: string) {\n if (this.optBtnGroups) {\n this.optBtnGroups.addGroup(groupId, beforeGroupId);\n this.optBtnGroups.render(); // 重新渲染以更新 UI\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 添加一个工具栏按钮\n * @param config 按钮配置对象\n */\n public addButton(config: ButtonConfig) {\n if (this.optBtnGroups) {\n this.optBtnGroups.addButton(config);\n this.optBtnGroups.render(); // 重新渲染以更新 UI\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置按钮的可见性\n * @param buttonId 按钮 ID\n * @param visible 是否可见\n */\n public setButtonVisibility(buttonId: string, visible: boolean) {\n if (this.optBtnGroups) {\n this.optBtnGroups.updateButtonVisibility(buttonId, visible);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置是否显示按钮下方的文字标签\n * @param show 是否显示\n */\n public setShowLabel(show: boolean) {\n if (this.optBtnGroups) {\n this.optBtnGroups.setShowLabel(show);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置整个工具栏的可见性\n * @param visible 是否可见\n */\n public setVisible(visible: boolean) {\n this.container.style.display = visible ? 'block' : 'none';\n }\n\n /**\n * 设置工具栏背景颜色\n * @param color CSS 颜色值\n */\n public setBackgroundColor(color: string) {\n if (this.optBtnGroups) {\n this.optBtnGroups.setBackgroundColor(color);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 设置工具栏详细颜色配置\n * @param colors 颜色配置对象\n */\n public setColors(colors: ToolbarColors) {\n if (this.optBtnGroups) {\n this.optBtnGroups.setColors(colors);\n } else {\n console.warn('Toolbar not initialized yet.');\n }\n }\n\n /**\n * 销毁工具栏管理器\n */\n public destroy() {\n if (this.optBtnGroups) {\n this.optBtnGroups.destroy();\n this.optBtnGroups = null;\n }\n }\n}\n","import './index.css';\nimport type { DialogOptions } from './index.type';\n\n/**\n * 通用弹窗组件类\n * 支持拖拽、缩放、自定义内容和位置。\n */\nexport class BimDialog {\n private element: HTMLElement;\n private options: DialogOptions;\n private container: HTMLElement;\n private header: HTMLElement;\n private contentArea: HTMLElement;\n private _isDestroyed = false;\n\n /**\n * 构造函数\n * @param options 弹窗配置选项\n */\n constructor(options: DialogOptions) {\n // 合并默认配置\n this.options = {\n title: 'Dialog',\n width: 300,\n height: 'auto',\n position: 'center',\n draggable: true,\n resizable: false,\n minWidth: 200,\n minHeight: 100,\n ...options\n };\n this.container = options.container;\n\n // 创建 DOM 结构\n this.element = this.createDom();\n this.header = this.element.querySelector('.bim-dialog-header') as HTMLElement;\n this.contentArea = this.element.querySelector('.bim-dialog-content') as HTMLElement;\n\n // 初始化\n this.init();\n }\n\n /**\n * 创建弹窗的 DOM 结构\n */\n private createDom(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'bim-dialog';\n\n if (this.options.id) el.id = this.options.id;\n \n // 应用颜色配置到 CSS 变量 (局部作用域)\n const style = el.style;\n if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor);\n if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor);\n if (this.options.titleColor) style.setProperty('--bim-dialog-title-color', this.options.titleColor);\n if (this.options.textColor) style.setProperty('--bim-dialog-text-color', this.options.textColor);\n if (this.options.borderColor) style.setProperty('--bim-dialog-border-color', this.options.borderColor);\n\n // 设置初始尺寸\n this.setSize(el, this.options.width, this.options.height);\n\n // 创建标题栏 (Header)\n const header = document.createElement('div');\n header.className = 'bim-dialog-header';\n if (this.options.draggable) header.classList.add('draggable');\n\n const title = document.createElement('span');\n title.className = 'bim-dialog-title';\n title.textContent = this.options.title || '';\n\n const closeBtn = document.createElement('span');\n closeBtn.className = 'bim-dialog-close';\n closeBtn.innerHTML = '×';\n closeBtn.onclick = () => this.close();\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // 创建内容区域 (Content)\n const content = document.createElement('div');\n content.className = 'bim-dialog-content';\n if (typeof this.options.content === 'string') {\n content.innerHTML = this.options.content;\n } else if (this.options.content instanceof HTMLElement) {\n content.appendChild(this.options.content);\n }\n\n el.appendChild(header);\n el.appendChild(content);\n\n // 如果允许缩放,创建缩放手柄\n if (this.options.resizable) {\n const resizeHandle = document.createElement('div');\n resizeHandle.className = 'bim-dialog-resize-handle';\n el.appendChild(resizeHandle);\n }\n\n return el;\n }\n\n /**\n * 设置元素尺寸\n */\n private setSize(el: HTMLElement, width?: number | string, height?: number | string) {\n if (width !== undefined) {\n el.style.width = typeof width === 'number' ? `${width}px` : width;\n }\n if (height !== undefined) {\n el.style.height = typeof height === 'number' ? `${height}px` : height;\n }\n }\n\n /**\n * 初始化组件功能\n */\n private init() {\n this.container.appendChild(this.element);\n\n // 必须先挂载才能计算尺寸进行定位\n this.initPosition();\n\n if (this.options.draggable) {\n this.initDrag();\n }\n\n if (this.options.resizable) {\n this.initResize();\n }\n }\n\n /**\n * 初始化弹窗位置\n */\n private initPosition() {\n const pos = this.options.position;\n\n const elRect = this.element.getBoundingClientRect();\n \n // 计算相对父容器的定位\n let left = 0;\n let top = 0;\n\n const pW = this.container.clientWidth;\n const pH = this.container.clientHeight;\n const elW = elRect.width;\n const elH = elRect.height;\n\n if (typeof pos === 'object' && 'x' in pos) {\n left = pos.x;\n top = pos.y;\n } else {\n switch (pos) {\n case 'center':\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n break;\n case 'top-left': left = 0; top = 0; break;\n case 'top-center': left = (pW - elW) / 2; top = 0; break;\n case 'top-right': left = pW - elW; top = 0; break;\n case 'left-center': left = 0; top = (pH - elH) / 2; break;\n case 'right-center': left = pW - elW; top = (pH - elH) / 2; break;\n case 'bottom-left': left = 0; top = pH - elH; break;\n case 'bottom-center': left = (pW - elW) / 2; top = pH - elH; break;\n case 'bottom-right': left = pW - elW; top = pH - elH; break;\n default:\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n }\n }\n\n // 简单的边界检查,防止初始位置溢出\n left = Math.max(0, Math.min(left, pW - elW));\n top = Math.max(0, Math.min(top, pH - elH));\n\n this.element.style.left = `${left}px`;\n this.element.style.top = `${top}px`;\n }\n\n /**\n * 初始化拖拽功能\n */\n private initDrag() {\n let startX = 0;\n let startY = 0;\n let startLeft = 0;\n let startTop = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n startX = e.clientX;\n startY = e.clientY;\n startLeft = this.element.offsetLeft;\n startTop = this.element.offsetTop;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n let newLeft = startLeft + dx;\n let newTop = startTop + dy;\n\n // 边界限制,防止拖出容器\n const maxLeft = this.container.clientWidth - this.element.offsetWidth;\n const maxTop = this.container.clientHeight - this.element.offsetHeight;\n\n newLeft = Math.max(0, Math.min(newLeft, maxLeft));\n newTop = Math.max(0, Math.min(newTop, maxTop));\n\n this.element.style.left = `${newLeft}px`;\n this.element.style.top = `${newTop}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n this.header.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 初始化缩放功能\n */\n private initResize() {\n const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;\n if (!handle) return;\n\n let startX = 0;\n let startY = 0;\n let startW = 0;\n let startH = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n startX = e.clientX;\n startY = e.clientY;\n startW = this.element.offsetWidth;\n startH = this.element.offsetHeight;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n const newW = Math.max(this.options.minWidth || 100, startW + dx);\n const newH = Math.max(this.options.minHeight || 50, startH + dy);\n\n this.element.style.width = `${newW}px`;\n this.element.style.height = `${newH}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 动态设置内容\n * @param content 内容元素或 HTML 字符串\n */\n public setContent(content: HTMLElement | string) {\n this.contentArea.innerHTML = '';\n if (typeof content === 'string') {\n this.contentArea.innerHTML = content;\n } else {\n this.contentArea.appendChild(content);\n }\n }\n\n /**\n * 关闭弹窗并销毁\n */\n public close() {\n if (this._isDestroyed) return;\n this.element.remove();\n this._isDestroyed = true;\n if (this.options.onClose) {\n this.options.onClose();\n }\n }\n}\n","import './index.css';\nimport { BimDialog } from '../index';\n\n/**\n * BimInfoDialog (二次封装示例)\n * 这是一个展示项目信息的业务弹窗组件,内部封装了 BimDialog。\n */\nexport class BimInfoDialog {\n private dialog: BimDialog;\n\n /**\n * 构造函数\n * @param container 父容器\n */\n constructor(container: HTMLElement) {\n // 创建自定义的 DOM 内容\n const contentEl = document.createElement('div');\n contentEl.className = 'bim-info-dialog-content';\n\n const infoTitle = document.createElement('h3');\n infoTitle.textContent = 'Model Information';\n\n const infoList = document.createElement('ul');\n infoList.innerHTML = `\n Name: Sample Project \n Version: 1.0.0 \n Date: ${new Date().toLocaleDateString()} \n Status: Active \n `;\n\n const actionBtn = document.createElement('button');\n actionBtn.textContent = 'Update Status';\n actionBtn.style.marginTop = '10px';\n actionBtn.onclick = () => {\n alert('Status updated!');\n };\n\n contentEl.appendChild(infoTitle);\n contentEl.appendChild(infoList);\n contentEl.appendChild(actionBtn);\n\n // 初始化 BimDialog,直接传入构建好的 HTMLElement\n this.dialog = new BimDialog({\n container: container,\n title: 'Project Info (Wrapped)',\n content: contentEl, \n width: 320,\n height: 'auto',\n position: 'center',\n resizable: true,\n draggable: true\n });\n }\n\n /**\n * 关闭弹窗\n */\n public close() {\n this.dialog.close();\n }\n}","import { BimDialog } from '../dialog';\nimport { BimInfoDialog } from '../dialog/bimInfoDialog';\nimport type { DialogOptions } from '../dialog/index.type';\n\n/**\n * 弹窗管理器\n * 负责创建和管理应用中的各类弹窗。\n */\nexport class DialogManager {\n /** 弹窗挂载的父容器 */\n private container: HTMLElement;\n\n /**\n * 构造函数\n * @param container 弹窗挂载的目标容器\n */\n constructor(container: HTMLElement) {\n this.container = container;\n }\n\n /**\n * 创建一个通用弹窗\n * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)\n * @returns BimDialog 实例\n */\n public create(options: Omit): BimDialog {\n return new BimDialog({\n container: this.container,\n ...options\n });\n }\n\n /**\n * 显示二次封装的模型信息弹窗\n * 演示如何调用特定的业务弹窗组件\n */\n public showInfoDialog() {\n new BimInfoDialog(this.container);\n }\n}","import './bim-engine.css';\nimport { ToolbarManager } from './modules/toolbar-manager';\nimport { DialogManager } from './modules/dialog-manager';\n\n/**\n * BimEngine 主类\n * 负责初始化整个应用界面,协调各个子模块(如工具栏、弹窗等)。\n */\nexport class BimEngine {\n /** 主容器元素 */\n private container: HTMLElement;\n /** 内部包装器元素,用于承载所有 UI 组件 */\n private wrapper: HTMLElement | null = null;\n \n /** 工具栏管理器实例 */\n public toolbar: ToolbarManager | null = null;\n /** 弹窗管理器实例 */\n public dialog: DialogManager | null = null;\n\n /**\n * 构造函数\n * @param container 容器元素或容器 ID\n */\n constructor(container: HTMLElement | string) {\n const el = typeof container === 'string' ? document.getElementById(container) : container;\n if (!el) throw new Error('Container not found');\n this.container = el;\n this.init();\n }\n\n /**\n * 初始化方法\n * 创建 DOM 结构并初始化各子模块\n */\n private init() {\n // 1. 清空容器可能存在的旧内容\n this.container.innerHTML = '';\n\n // 2. 创建外层容器 div\n this.wrapper = document.createElement('div');\n this.wrapper.className = 'bim-engine-wrapper';\n\n // 3. 创建标题 h1\n const title = document.createElement('h1');\n title.textContent = 'BimEngine';\n title.className = 'bim-engine-title';\n\n // 4. 创建描述段落 p\n const desc = document.createElement('p');\n desc.textContent = '这是一个使用BIM-ENGINE。';\n desc.className = 'bim-engine-desc';\n\n // 6. 创建操作按钮组容器\n const btnGroupContainer = document.createElement('div');\n btnGroupContainer.id = 'opt-btn-groups';\n btnGroupContainer.className = 'bim-engine-opt-btn-container';\n\n // 7. 组装元素\n this.wrapper.appendChild(title);\n this.wrapper.appendChild(desc);\n \n // 初始化管理器\n this.dialog = new DialogManager(this.wrapper);\n this.toolbar = new ToolbarManager(btnGroupContainer);\n\n // 5. 测试按钮(更新为使用管理器)\n // 5.1 创建普通测试弹窗按钮\n const dialogBtn = document.createElement('button');\n dialogBtn.textContent = '打开测试弹窗';\n dialogBtn.className = 'bim-engine-btn';\n dialogBtn.onclick = () => {\n this.dialog?.create({\n title: '测试弹窗',\n content: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',\n width: 300,\n height: 400,\n position: 'top-left',\n draggable: true,\n resizable: true\n });\n };\n\n // 5.2 创建二次封装信息弹窗按钮\n const infoDialogBtn = document.createElement('button');\n infoDialogBtn.textContent = '打开信息弹窗 (封装版)';\n infoDialogBtn.className = 'bim-engine-btn';\n infoDialogBtn.style.marginLeft = '10px';\n infoDialogBtn.onclick = () => {\n this.dialog?.showInfoDialog();\n };\n\n // 将按钮和工具栏容器添加到包装器中\n this.wrapper.appendChild(dialogBtn);\n this.wrapper.appendChild(infoDialogBtn);\n this.wrapper.appendChild(btnGroupContainer);\n\n // 8. 将包装器挂载到主容器\n this.container.appendChild(this.wrapper);\n }\n\n /**\n * 销毁实例\n * 清理所有资源和 DOM 元素\n */\n public destroy() {\n if (this.toolbar) {\n this.toolbar.destroy();\n this.toolbar = null;\n }\n this.dialog = null;\n this.container.innerHTML = '';\n }\n}","import type { ButtonConfig } from '../../index.type';\n\n/**\n * 首页按钮配置\n */\nexport const homeButton: ButtonConfig = {\n id: 'home',\n groupId: 'group-1',\n type: 'button',\n label: '首页',\n icon: ' ',\n keepActive: true,\n onClick: (button) => {\n console.log('首页按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../index.type';\n\n/**\n * 定位按钮配置\n */\nexport const locationButton: ButtonConfig = {\n id: 'location',\n groupId: 'group-1',\n type: 'button',\n label: '定位',\n icon: ' ',\n keepActive: false,\n onClick: (button) => {\n console.log('定位按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../index.type';\n\n/**\n * 漫游菜单按钮配置\n */\nexport const walkMenuButton: ButtonConfig = {\n id: 'walk',\n groupId: 'group-1',\n type: 'menu',\n label: '漫游',\n icon: ' ',\n keepActive: true,\n onClick: (button) => {\n console.log('漫游按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../index.type';\n\nexport const walkPersonButton: ButtonConfig = {\n id: 'walk-person',\n groupId: 'group-1',\n parentId: 'walk',\n type: 'button',\n label: '人视漫游',\n icon: ' ',\n onClick: (button) => {\n console.log('人视漫游被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../index.type';\n\nexport const walkBirdButton: ButtonConfig = {\n id: 'walk-bird',\n groupId: 'group-1',\n parentId: 'walk',\n type: 'button',\n label: '鸟瞰漫游',\n icon: ' ',\n onClick: (button) => {\n console.log('鸟瞰漫游被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../index.type';\n\n/**\n * 定位按钮配置\n */\nexport const settingButton: ButtonConfig = {\n id: 'setting',\n groupId: 'group-2',\n type: 'button',\n label: '设置',\n icon: ' ',\n keepActive: false,\n onClick: (button) => {\n console.log('设置按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../index.type';\n\n/**\n * 定位按钮配置\n */\nexport const infoButton: ButtonConfig = {\n id: 'info',\n groupId: 'group-2',\n type: 'button',\n label: '信息',\n icon: ' ',\n keepActive: false,\n onClick: (button) => {\n console.log('信息按钮被点击:', button.id);\n }\n};\n"],"names":["OptBtnGroups","options","el","style","colors","groupId","beforeGroupId","g","newGroup","index","config","parentId","group","button","parentBtn","buttons","id","btn","found","homeButton","index$6","locationButton","index$5","walkMenuButton","index$4","walkPersonButton","index$3","walkBirdButton","index$2","settingButton","index$1","infoButton","wrapper","groupElement","total","groupEl","btnWrapper","btnEl","icon","label","arrow","dropdown","rect","centerX","subBtn","item","e","buttonId","visible","show","color","ToolbarManager","container","err","BimDialog","header","title","closeBtn","content","resizeHandle","width","height","pos","elRect","left","top","pW","pH","elW","elH","startX","startY","startLeft","startTop","onMouseDown","onMouseMove","onMouseUp","dx","dy","newLeft","newTop","maxLeft","maxTop","handle","startW","startH","newW","newH","BimInfoDialog","contentEl","infoTitle","infoList","actionBtn","DialogManager","BimEngine","desc","btnGroupContainer","dialogBtn","infoDialogBtn"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"bim-engine-sdk.umd.js","sources":["../src/locales/zh-CN.ts","../src/locales/en-US.ts","../src/services/locale.ts","../src/themes/presets.ts","../src/services/theme.ts","../src/components/button-group/index.ts","../src/components/button-group/toolbar/index.ts","../src/managers/toolbar-manager.ts","../src/managers/button-group-manager.ts","../src/components/dialog/index.ts","../src/components/dialog/bimInfoDialog/index.ts","../src/managers/dialog-manager.ts","../src/bim-engine.ts","../src/components/button-group/toolbar/buttons/home/index.ts","../src/components/button-group/toolbar/buttons/location/index.ts","../src/components/button-group/toolbar/buttons/walk/walk-menu/index.ts","../src/components/button-group/toolbar/buttons/walk/walk-person/index.ts","../src/components/button-group/toolbar/buttons/walk/walk-bird/index.ts","../src/components/button-group/toolbar/buttons/setting/index.ts","../src/components/button-group/toolbar/buttons/info/index.ts"],"sourcesContent":["import { TranslationDictionary } from './types';\n\nexport const zhCN: TranslationDictionary = {\n common: {\n title: 'BimEngine',\n description: '这是一个使用 BIM-ENGINE。',\n openTestDialog: '打开测试弹窗',\n openInfoDialog: '打开信息弹窗 (封装版)',\n },\n toolbar: {\n home: '首页',\n info: '信息',\n location: '定位',\n setting: '设置',\n walk: '漫游',\n walkPerson: '人视',\n walkBird: '鸟瞰',\n walkMenu: '菜单',\n },\n dialog: {\n testTitle: '测试弹窗',\n testContent: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',\n },\n};\n","import { TranslationDictionary } from './types';\n\nexport const enUS: TranslationDictionary = {\n common: {\n title: 'BimEngine',\n description: 'This is a BIM-ENGINE demo.',\n openTestDialog: 'Open Test Dialog',\n openInfoDialog: 'Open Info Dialog (Wrapped)',\n },\n toolbar: {\n home: 'Home',\n info: 'Info',\n location: 'Location',\n setting: 'Settings',\n walk: 'Walk',\n walkPerson: 'Person',\n walkBird: 'Bird Eye',\n walkMenu: 'Menu',\n },\n dialog: {\n testTitle: 'Test Dialog',\n testContent: 'This is a draggable and resizable dialog. Try dragging the title bar or resizing from the bottom-right corner.
',\n },\n};\n","import { LocaleType, TranslationDictionary } from '../locales/types';\nimport { zhCN } from '../locales/zh-CN';\nimport { enUS } from '../locales/en-US';\n\ntype LocaleChangeListener = (locale: LocaleType) => void;\n\n/**\n * 语言管理器类\n */\nexport class LocaleManager {\n private currentLocale: LocaleType = 'zh-CN';\n private messages: Record = {\n 'zh-CN': zhCN,\n 'en-US': enUS,\n };\n private listeners: LocaleChangeListener[] = [];\n\n constructor() {\n // 默认初始化\n }\n\n /**\n * 获取当前语言\n */\n public getLocale(): LocaleType {\n return this.currentLocale;\n }\n\n /**\n * 切换语言\n */\n public setLocale(locale: LocaleType) {\n if (this.currentLocale === locale) return;\n this.currentLocale = locale;\n this.notifyListeners();\n }\n\n /**\n * 翻译核心方法\n */\n public t(key: string): string {\n if (!key) return '';\n \n const keys = key.split('.');\n let value: any = this.messages[this.currentLocale];\n \n for (const k of keys) {\n if (value && typeof value === 'object' && k in value) {\n value = value[k];\n } else {\n return key;\n }\n }\n return value as string;\n }\n\n /**\n * 订阅变更\n */\n public subscribe(listener: LocaleChangeListener): () => void {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n private notifyListeners() {\n this.listeners.forEach(listener => listener(this.currentLocale));\n }\n}\n\n// --- 导出单例 ---\nexport const localeManager = new LocaleManager();\n\n// --- 导出便捷方法 ---\n/**\n * 全局翻译函数\n * @param key 键路径 (如 'toolbar.home')\n */\nexport const t = (key: string): string => localeManager.t(key);\n","import { ThemeConfig } from './types';\n\n/**\n * 深色主题 (默认)\n */\nexport const darkTheme: ThemeConfig = {\n name: 'dark',\n primary: '#0078d4',\n primaryHover: '#0063b1',\n \n // 修改:背景色统一为浅灰,不再跟随深色模式变黑\n background: '#f5f5f5', \n panelBackground: 'rgba(30, 30, 30, 0.9)',\n \n // 注意:如果背景是浅色,主文字颜色通常需要是深色才能看清\n // 但这里的 textPrimary 主要是用于 UI 组件内部的。\n // 如果 BimEngine wrapper 上的文字直接显示在 background 上,\n // 我们可能需要区分 \"UI文字\" 和 \"页面文字\"。\n // 目前架构中:\n // theme.textPrimary 会应用到 wrapper.style.color (BimEngine.ts)\n // 以及 Toolbar/Dialog 的文字颜色。\n \n // 如果背景是浅灰,而 wrapper 文字设置为白色 (#ffffff),那就看不清了。\n // 这是一个语义冲突:\n // 1. Panel (Toolbar/Dialog) 是黑底,需要白字。\n // 2. Background (Wrapper) 是白底,需要黑字。\n \n // 既然您要求背景统一浅灰,那么 Wrapper 上的“直接子文本”应该是深色。\n // 但 Toolbar/Dialog 仍然是深色模式(黑底),它们需要白字。\n \n // 妥协方案:\n // 保持 textPrimary 为白色(为了适配黑���的 Toolbar/Dialog)。\n // 但是在 BimEngine 中,如果背景强制改为浅色,Wrapper 的默认文字颜色可能需要单独处理,\n // 或者我们可以认为 \"Wrapper\" 主要是承载 UI 组件的,直接写在 Wrapper 上的文字(标题/描述)\n // 应该有自己的样式,而不是直接继承 theme.textPrimary。\n \n // 在之前的 BimEngine.ts 中:\n // this.wrapper.style.color = theme.textPrimary;\n \n // 如果背景变浅灰,这里 textPrimary 还是白色的话,标题就看不见了。\n // 所以,深色模式下:\n // 背景:浅灰\n // 组件:深黑\n // 组件文字:白\n // 页面文字:黑 (问题点)\n \n // 让我们先按您的要求改背景。通常这种情况下,ThemeConfig 可能需要区分 \n // contentText (页面内容文字) 和 uiText (组件文字)。\n // 但为了不破坏现有结构,我将假定 textPrimary 主要服务于 UI 组件。\n // 为了让 Wrapper 上的标题可见,我们可能需要在 BimEngine 中移除对 wrapper.style.color 的强制设置,\n // 或者在 presets 里把 textPrimary 改回来?不对,改回来 Toolbar 就看不清了。\n \n // 方案:我将仅修改 background。\n // 至于 Wrapper 上的标题(BimEngine 标题),由于在最新的 BimEngine.ts 中\n // ���们已经移除了 titleEl 和 descEl(在之前的重构中),\n // 所以现在 Wrapper 里主要是 Toolbar 和 Dialog,它们有自己的 panelBackground。\n // 只要 Toolbar/Dialog 内部正常即可。\n \n textPrimary: '#ffffff', \n textSecondary: '#cccccc',\n \n border: '#444444',\n \n icon: '#cccccc',\n iconActive: '#ffffff',\n \n componentBackground: 'transparent',\n componentHover: '#333333',\n componentActive: 'rgba(255, 255, 255, 0.1)'\n};\n\n/**\n * 浅色主题\n */\nexport const lightTheme: ThemeConfig = {\n name: 'light',\n primary: '#0078d4',\n primaryHover: '#106ebe',\n \n // 统一为浅灰\n background: '#f5f5f5',\n panelBackground: '#ffffff',\n \n textPrimary: '#333333',\n textSecondary: '#666666',\n \n border: '#e0e0e0',\n \n icon: '#555555',\n iconActive: '#0078d4',\n \n componentBackground: 'transparent',\n componentHover: '#f0f0f0',\n componentActive: '#e0e0e0'\n};","import { ThemeConfig } from '../themes/types';\nimport { darkTheme, lightTheme } from '../themes/presets';\n\ntype ThemeChangeListener = (theme: ThemeConfig) => void;\n\n/**\n * 主题管理器 (单例)\n */\nexport class ThemeManager {\n private currentTheme: ThemeConfig = darkTheme;\n private listeners: ThemeChangeListener[] = [];\n\n constructor() {\n // 默认初始化\n }\n\n /**\n * 获取当前主题配置\n */\n public getTheme(): ThemeConfig {\n return this.currentTheme;\n }\n\n /**\n * 切换预设主题\n * @param themeName 'dark' | 'light'\n */\n public setTheme(themeName: 'dark' | 'light') {\n if (themeName === 'light') {\n this.applyTheme(lightTheme);\n } else {\n this.applyTheme(darkTheme);\n }\n }\n\n /**\n * 应用自定义主题配置\n * @param theme 配置对象\n */\n public setCustomTheme(theme: ThemeConfig) {\n this.applyTheme(theme);\n }\n\n /**\n * 内部应用主题逻辑\n */\n private applyTheme(theme: ThemeConfig) {\n this.currentTheme = theme;\n this.notifyListeners();\n }\n\n /**\n * 订阅主题变更\n */\n public subscribe(listener: ThemeChangeListener): () => void {\n this.listeners.push(listener);\n // 立即回调一次当前状态\n listener(this.currentTheme);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n private notifyListeners() {\n this.listeners.forEach(listener => listener(this.currentTheme));\n }\n}\n\n// 导出单例\nexport const themeManager = new ThemeManager();","import './index.css';\nimport type {\n OptButton,\n ButtonGroup,\n ButtonGroupOptions,\n ButtonConfig,\n ButtonGroupColors\n} from './index.type';\nimport { t, localeManager } from '../../services/locale';\nimport { themeManager } from '../../services/theme';\nimport type { ThemeConfig } from '../../themes/types';\nimport { IBimComponent } from '../../types/component';\n\n/**\n * 通用按钮组组件 (BimButtonGroup)\n */\nexport class BimButtonGroup implements IBimComponent {\n private container: HTMLElement;\n private options: ButtonGroupOptions;\n private groups: ButtonGroup[] = [];\n private activeBtnIds: Set = new Set();\n private btnRefs: Map = new Map();\n private dropdownElement: HTMLElement | null = null;\n private hoverTimeout: number | null = null;\n private customColors: Set = new Set(); // 记录用户自定义的颜色属性\n private unsubscribeLocale: (() => void) | null = null;\n private unsubscribeTheme: (() => void) | null = null;\n\n private readonly DEFAULT_ICON = ' ';\n\n constructor(options: ButtonGroupOptions) {\n const el = typeof options.container === 'string'\n ? document.getElementById(options.container)\n : options.container;\n\n if (!el) throw new Error('Container not found');\n\n this.container = el;\n // 合并默认配置\n this.options = {\n showLabel: true,\n visibility: {},\n direction: 'row', // 默认横向\n position: 'static', // 默认静态定位\n align: 'vertical', // 默认图标在上\n expand: 'down', // 默认向下展开\n ...options\n };\n\n // 记录初始传入的自定义颜色\n const colorKeys: (keyof ButtonGroupColors)[] = [\n 'backgroundColor', 'btnBackgroundColor', 'btnHoverColor',\n 'btnActiveColor', 'iconColor', 'iconActiveColor',\n 'textColor', 'textActiveColor'\n ];\n colorKeys.forEach(key => {\n if (options[key]) {\n this.customColors.add(key);\n }\n });\n\n this.initContainer();\n this.applyStyles();\n }\n\n private initContainer(): void {\n this.container.innerHTML = '';\n this.container.classList.add('bim-btn-group-root');\n\n if (this.options.direction === 'column') {\n this.container.classList.add('dir-column');\n } else {\n this.container.classList.add('dir-row');\n }\n\n if (this.options.className) {\n this.container.classList.add(this.options.className);\n }\n\n this.updatePosition();\n }\n\n private updatePosition() {\n const pos = this.options.position;\n const style = this.container.style;\n\n style.top = ''; style.bottom = ''; style.left = ''; style.right = ''; style.transform = '';\n\n if (pos === 'static') {\n this.container.classList.add('static');\n return;\n }\n\n this.container.classList.remove('static');\n this.container.style.position = 'absolute';\n\n if (typeof pos === 'object' && 'x' in pos) {\n style.left = `${pos.x}px`;\n style.top = `${pos.y}px`;\n } else {\n const margin = '20px';\n switch (pos) {\n case 'top-left':\n style.top = margin; style.left = margin;\n break;\n case 'top-center':\n style.top = margin; style.left = '50%'; style.transform = 'translateX(-50%)';\n break;\n case 'top-right':\n style.top = margin; style.right = margin;\n break;\n case 'bottom-left':\n style.bottom = margin; style.left = margin;\n break;\n case 'bottom-center':\n style.bottom = margin; style.left = '50%'; style.transform = 'translateX(-50%)';\n break;\n case 'bottom-right':\n style.bottom = margin; style.right = margin;\n break;\n case 'left-center':\n style.left = margin; style.top = '50%'; style.transform = 'translateY(-50%)';\n break;\n case 'right-center':\n style.right = margin; style.top = '50%'; style.transform = 'translateY(-50%)';\n break;\n case 'center':\n style.top = '50%'; style.left = '50%'; style.transform = 'translate(-50%, -50%)';\n break;\n }\n }\n }\n\n /**\n * 应用样式到容器\n */\n private applyStyles(): void {\n const style = this.container.style;\n if (this.options.backgroundColor) style.setProperty('--bim-btn-group-section-bg', this.options.backgroundColor);\n if (this.options.btnBackgroundColor) style.setProperty('--bim-btn-bg', this.options.btnBackgroundColor);\n if (this.options.btnHoverColor) style.setProperty('--bim-btn-hover-bg', this.options.btnHoverColor);\n if (this.options.btnActiveColor) style.setProperty('--bim-btn-active-bg', this.options.btnActiveColor);\n if (this.options.iconColor) style.setProperty('--bim-icon-color', this.options.iconColor);\n if (this.options.iconActiveColor) style.setProperty('--bim-icon-active-color', this.options.iconActiveColor);\n if (this.options.textColor) style.setProperty('--bim-btn-text-color', this.options.textColor);\n if (this.options.textActiveColor) style.setProperty('--bim-btn-text-active-color', this.options.textActiveColor);\n }\n\n /**\n * 设置主题颜色\n * 只会应用到没有被用户自定义的颜色属性上\n */\n public setTheme(theme: ThemeConfig): void {\n const themeColors: ButtonGroupColors = {\n backgroundColor: theme.panelBackground,\n btnBackgroundColor: theme.componentBackground,\n btnHoverColor: theme.componentHover,\n btnActiveColor: theme.componentActive,\n iconColor: theme.icon,\n iconActiveColor: theme.iconActive,\n textColor: theme.textSecondary,\n textActiveColor: theme.textPrimary\n };\n\n // 只应用没有被自定义的颜色\n Object.entries(themeColors).forEach(([key, value]) => {\n const colorKey = key as keyof ButtonGroupColors;\n if (!this.customColors.has(colorKey)) {\n this.options[colorKey] = value;\n }\n });\n\n this.applyStyles();\n }\n\n /**\n * 直接设置颜色(强制覆盖)\n * 设置的颜色会被标记为自定义,后续的 setTheme 不会覆盖它们\n */\n public setColors(colors: ButtonGroupColors): void {\n // 更新 options\n this.options = { ...this.options, ...colors };\n\n // 标记这些颜色为自定义\n Object.keys(colors).forEach(key => {\n this.customColors.add(key as keyof ButtonGroupColors);\n });\n\n this.applyStyles();\n }\n\n public async init(): Promise {\n this.render();\n\n // 自动订阅语言变更\n this.unsubscribeLocale = localeManager.subscribe(() => {\n this.setLocales();\n });\n\n // 自动订阅主题变更\n this.unsubscribeTheme = themeManager.subscribe((theme) => {\n this.setTheme(theme);\n });\n }\n\n public setLocales(): void {\n this.render();\n }\n\n public addGroup(groupId: string, beforeGroupId?: string): void {\n if (this.groups.some(g => g.id === groupId)) return;\n const newGroup: ButtonGroup = { id: groupId, buttons: [] };\n if (beforeGroupId) {\n const index = this.groups.findIndex(g => g.id === beforeGroupId);\n index !== -1 ? this.groups.splice(index, 0, newGroup) : this.groups.push(newGroup);\n } else {\n this.groups.push(newGroup);\n }\n }\n\n public addButton(config: ButtonConfig): void {\n const { groupId, parentId } = config;\n const group = this.groups.find(g => g.id === groupId);\n if (!group) return;\n\n const button: OptButton = { ...config, children: config.children || [] };\n if (parentId) {\n const parentBtn = this.findButton(group.buttons, parentId);\n if (parentBtn) {\n if (!parentBtn.children) parentBtn.children = [];\n parentBtn.children.push(button);\n }\n } else {\n group.buttons.push(button);\n }\n }\n\n private findButton(buttons: OptButton[], id: string): OptButton | undefined {\n for (const btn of buttons) {\n if (btn.id === id) return btn;\n if (btn.children) {\n const found = this.findButton(btn.children, id);\n if (found) return found;\n }\n }\n return undefined;\n }\n\n public render(): void {\n this.container.innerHTML = '';\n this.btnRefs.clear();\n\n this.groups.forEach((group, index) => {\n const groupElement = this.renderGroup(group, index, this.groups.length);\n this.container.appendChild(groupElement);\n });\n }\n\n private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {\n const groupEl = document.createElement('div');\n groupEl.className = 'bim-btn-group-section';\n\n if (index < total - 1) {\n groupEl.classList.add('has-divider');\n }\n\n group.buttons.forEach(button => {\n if (this.isVisible(button.id)) {\n const btnWrapper = this.renderButton(button);\n groupEl.appendChild(btnWrapper);\n }\n });\n return groupEl;\n }\n\n private renderButton(button: OptButton): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'opt-btn-wrapper';\n\n const btnEl = document.createElement('div');\n btnEl.className = 'opt-btn';\n\n // 按钮优先使用自己的 align,否则使用全局配置,默认为 vertical\n const align = button.align || this.options.align || 'vertical';\n if (align === 'horizontal') {\n btnEl.classList.add('align-horizontal');\n } else {\n btnEl.classList.add('align-vertical');\n }\n\n if (this.activeBtnIds.has(button.id)) btnEl.classList.add('active');\n if (button.disabled) btnEl.classList.add('disabled');\n\n // 判断是否显示 label\n const hasLabel = this.options.showLabel && button.label;\n if (!hasLabel) {\n btnEl.classList.add('no-label');\n }\n\n // 应用按钮的自定义样式\n const iconSize = button.iconSize || 32;\n const minWidth = button.minWidth || 50;\n btnEl.style.minWidth = `${minWidth}px`;\n\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.style.width = `${iconSize}px`;\n icon.style.height = `${iconSize}px`;\n icon.innerHTML = this.getIcon(button.icon);\n btnEl.appendChild(icon);\n\n // 创建文字和箭头的容器,确保它们始终在一起(无论主轴是横是竖)\n const textWrapper = document.createElement('div');\n textWrapper.className = 'opt-btn-text-wrapper';\n\n if (this.options.showLabel && button.label) {\n const label = document.createElement('span');\n label.className = 'opt-btn-label';\n label.textContent = t(button.label);\n textWrapper.appendChild(label);\n }\n\n if (button.children && button.children.length > 0) {\n const arrow = document.createElement('span');\n arrow.className = 'opt-btn-arrow';\n arrow.textContent = '▼';\n textWrapper.appendChild(arrow);\n }\n\n // 只有当有内容时才添加 wrapper\n if (textWrapper.hasChildNodes()) {\n btnEl.appendChild(textWrapper);\n }\n\n btnEl.addEventListener('click', () => this.handleClick(button));\n btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));\n btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());\n\n this.btnRefs.set(button.id, btnEl);\n wrapper.appendChild(btnEl);\n return wrapper;\n }\n\n private handleClick(button: OptButton): void {\n if (button.disabled) return;\n if (!button.children || button.children.length === 0) {\n if (button.keepActive) {\n const wasActive = this.activeBtnIds.has(button.id);\n if (wasActive) this.activeBtnIds.delete(button.id);\n else this.activeBtnIds.add(button.id);\n this.updateButtonState(button.id);\n }\n this.closeDropdown();\n if (button.onClick) button.onClick(button);\n }\n }\n\n private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {\n if (this.hoverTimeout) clearTimeout(this.hoverTimeout);\n if (button.children && button.children.length > 0) {\n this.showDropdown(button, btnEl);\n } else {\n this.closeDropdown();\n }\n }\n\n private handleMouseLeave(): void {\n this.hoverTimeout = window.setTimeout(() => this.closeDropdown(), 200);\n }\n\n private showDropdown(button: OptButton, btnEl: HTMLElement): void {\n this.closeDropdown();\n if (!button.children) return;\n\n const dropdown = document.createElement('div');\n dropdown.className = 'opt-btn-dropdown';\n if (this.options.backgroundColor) dropdown.style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);\n\n // 获取按钮的位置信息\n const btnRect = btnEl.getBoundingClientRect();\n const expand = this.options.expand || 'down';\n\n // 根据主按钮组的方向设置下拉菜单的布局方向\n if (this.options.direction === 'row') {\n dropdown.style.flexDirection = 'column'; // 横向按钮组,菜单纵向排列\n } else {\n dropdown.style.flexDirection = 'row'; // 纵向按钮组,菜单横向排列\n }\n\n // 先添加到 DOM 以便计算尺寸\n document.body.appendChild(dropdown);\n\n // 添加菜单项\n button.children.forEach(subBtn => {\n if (this.isVisible(subBtn.id)) {\n const item = this.renderDropdownItem(subBtn);\n dropdown.appendChild(item);\n }\n });\n\n // 获取下拉菜单的实际尺寸\n const dropdownRect = dropdown.getBoundingClientRect();\n\n if (expand === 'up') {\n // 向上展开,与按钮水平居中对齐\n dropdown.style.bottom = (window.innerHeight - btnRect.top + 8) + 'px';\n dropdown.style.left = (btnRect.left + (btnRect.width - dropdownRect.width) / 2) + 'px';\n } else if (expand === 'down') {\n // 向下展开,与按钮水平居中对齐\n dropdown.style.top = (btnRect.bottom + 8) + 'px';\n dropdown.style.left = (btnRect.left + (btnRect.width - dropdownRect.width) / 2) + 'px';\n } else if (expand === 'right') {\n // 向右展开,与按钮垂直居中对齐\n dropdown.style.top = (btnRect.top + (btnRect.height - dropdownRect.height) / 2) + 'px';\n dropdown.style.left = (btnRect.right + 8) + 'px';\n } else if (expand === 'left') {\n // 向左展开,与按钮垂直居中对齐\n dropdown.style.top = (btnRect.top + (btnRect.height - dropdownRect.height) / 2) + 'px';\n dropdown.style.right = (window.innerWidth - btnRect.left + 8) + 'px';\n }\n\n dropdown.addEventListener('mouseenter', () => { if (this.hoverTimeout) clearTimeout(this.hoverTimeout); });\n dropdown.addEventListener('mouseleave', () => this.handleMouseLeave());\n this.dropdownElement = dropdown;\n }\n\n private renderDropdownItem(button: OptButton): HTMLElement {\n const item = document.createElement('div');\n item.className = 'opt-btn-dropdown-item';\n\n // 应用按钮的 align 设置,默认为 horizontal(图标在左)\n const align = button.align || 'horizontal';\n if (align === 'horizontal') {\n item.classList.add('align-horizontal');\n } else {\n item.classList.add('align-vertical');\n }\n\n // 应用按钮的自定义样式\n const iconSize = button.iconSize || 32; // 二级菜单默认图标更小\n const minWidth = button.minWidth; // 不设置默认值,让下拉菜单项保持紧凑\n if (minWidth) {\n item.style.minWidth = `${minWidth}px`;\n }\n\n const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.style.width = `${iconSize}px`;\n icon.style.height = `${iconSize}px`;\n icon.innerHTML = this.getIcon(button.icon);\n item.appendChild(icon);\n\n // 只有在 showLabel 为 true 时才显示 label\n if (this.options.showLabel && button.label) {\n const label = document.createElement('span');\n label.className = 'opt-btn-dropdown-label';\n label.textContent = t(button.label);\n item.appendChild(label);\n }\n\n item.addEventListener('click', (e) => { e.stopPropagation(); this.handleClick(button); });\n return item;\n }\n\n private closeDropdown(): void {\n if (this.dropdownElement) {\n this.dropdownElement.remove();\n this.dropdownElement = null;\n }\n this.btnRefs.forEach(btnEl => {\n const arrow = btnEl.querySelector('.opt-btn-arrow');\n if (arrow) arrow.classList.remove('rotated');\n });\n }\n\n private updateButtonState(buttonId: string): void {\n const btnEl = this.btnRefs.get(buttonId);\n if (btnEl) {\n this.activeBtnIds.has(buttonId) ? btnEl.classList.add('active') : btnEl.classList.remove('active');\n }\n }\n\n private getIcon(icon?: string): string { return icon || this.DEFAULT_ICON; }\n public updateButtonVisibility(id: string, visible: boolean): void {\n if (!this.options.visibility) this.options.visibility = {};\n this.options.visibility[id] = visible;\n this.render();\n }\n public setShowLabel(show: boolean): void {\n this.options.showLabel = show;\n this.updateLabelsVisibility();\n }\n\n private updateLabelsVisibility(): void {\n this.btnRefs.forEach((btnEl, buttonId) => {\n // 查找按钮配置\n const button = this.findButtonById(buttonId);\n if (!button) return;\n\n const hasLabel = this.options.showLabel && button.label;\n\n // 只需要更新 no-label 类,CSS 会处理显示/隐藏\n if (hasLabel) {\n btnEl.classList.remove('no-label');\n } else {\n btnEl.classList.add('no-label');\n }\n });\n }\n\n private findButtonById(id: string): OptButton | undefined {\n for (const group of this.groups) {\n const found = this.findButton(group.buttons, id);\n if (found) return found;\n }\n return undefined;\n }\n public setBackgroundColor(color: string): void { this.setColors({ backgroundColor: color }); }\n private isVisible(id: string): boolean { return this.options.visibility?.[id] !== false; }\n public destroy(): void {\n if (this.unsubscribeLocale) {\n this.unsubscribeLocale();\n this.unsubscribeLocale = null;\n }\n if (this.unsubscribeTheme) {\n this.unsubscribeTheme();\n this.unsubscribeTheme = null;\n }\n this.closeDropdown();\n this.container.innerHTML = '';\n this.btnRefs.clear();\n }\n}\n","import { BimButtonGroup } from '../index';\n\n/**\n * 底部工具栏 (Toolbar)\n * BimButtonGroup 的子类,专门用于加载工具栏默认按钮。\n */\nexport class Toolbar extends BimButtonGroup {\n /**\n * 重写初始化,加载默认按钮\n */\n public async init(): Promise {\n await super.init();\n\n // 动态加载默认按钮配置\n const { homeButton } = await import('./buttons/home');\n const { locationButton } = await import('./buttons/location');\n const { walkMenuButton } = await import('./buttons/walk/walk-menu');\n const { walkPersonButton } = await import('./buttons/walk/walk-person');\n const { walkBirdButton } = await import('./buttons/walk/walk-bird');\n const { settingButton } = await import('./buttons/setting');\n const { infoButton } = await import('./buttons/info');\n\n this.addGroup('group-1');\n this.addButton(homeButton);\n this.addButton(walkMenuButton);\n this.addButton(walkPersonButton);\n this.addButton(walkBirdButton);\n this.addButton(locationButton);\n this.addGroup('group-2');\n this.addButton(settingButton);\n this.addButton(infoButton);\n\n this.render();\n }\n}\n","import type { ButtonGroupColors, ButtonConfig } from '../components/button-group/index.type';\nimport { Toolbar } from '../components/button-group/toolbar';\nimport type { ThemeConfig } from '../themes/types';\n\n/**\n * 底部工具栏管理器 (ToolbarManager)\n * 仅负责管理底部工具栏实例。\n */\nexport class ToolbarManager {\n private toolbar: Toolbar | null = null;\n private toolbarContainer: HTMLElement | null = null;\n private container: HTMLElement;\n\n constructor(container: HTMLElement) {\n this.container = container;\n this.init();\n }\n\n private init() {\n // 创建底部工具栏专用容器\n this.toolbarContainer = document.createElement('div');\n this.toolbarContainer.id = 'opt-btn-groups';\n this.toolbarContainer.className = 'bim-engine-opt-btn-container is-bottom-toolbar';\n this.container.appendChild(this.toolbarContainer);\n\n this.toolbar = new Toolbar({\n container: this.toolbarContainer,\n showLabel: true,\n direction: 'row',\n position: 'bottom-center', // 底部居中\n align: 'vertical', // 图标在上\n expand: 'up' // 向上展开\n });\n\n this.toolbar.init();\n }\n\n public updateTheme(theme: ThemeConfig) {\n this.toolbar?.setTheme(theme);\n }\n\n public refresh() {\n this.toolbar?.render();\n }\n\n public destroy() {\n this.toolbar?.destroy();\n this.toolbar = null;\n }\n\n // --- 转发 API ---\n public addGroup(groupId: string, beforeGroupId?: string) { this.toolbar?.addGroup(groupId, beforeGroupId); this.toolbar?.render(); }\n public addButton(config: ButtonConfig) { this.toolbar?.addButton(config); this.toolbar?.render(); }\n public setButtonVisibility(id: string, v: boolean) { this.toolbar?.updateButtonVisibility(id, v); }\n public setShowLabel(show: boolean) { this.toolbar?.setShowLabel(show); }\n public setVisible(visible: boolean) {\n if (this.toolbarContainer) {\n this.toolbarContainer.style.visibility = visible ? 'visible' : 'hidden';\n }\n }\n public setBackgroundColor(color: string) { this.toolbar?.setBackgroundColor(color); }\n public setColors(colors: ButtonGroupColors) { this.toolbar?.setColors(colors); }\n}\n","import { BimButtonGroup } from '../components/button-group';\nimport type { ButtonGroupOptions } from '../components/button-group/index.type';\nimport type { ThemeConfig } from '../themes/types';\n\n/**\n * 通用按钮组管理器\n * 负责创建和管理除底部工具栏以外的其他按钮组。\n */\nexport class ButtonGroupManager {\n private activeGroups: BimButtonGroup[] = [];\n private container: HTMLElement;\n\n constructor(container: HTMLElement) {\n this.container = container;\n }\n\n /**\n * 创建一个新的按钮组\n */\n public create(options: Omit): BimButtonGroup {\n // 自动创建一个 div 作为容器\n const groupContainer = document.createElement('div');\n this.container.appendChild(groupContainer);\n\n const group = new BimButtonGroup({\n container: groupContainer,\n ...options\n });\n \n // 立即初始化\n group.init();\n this.activeGroups.push(group);\n return group;\n }\n\n public updateTheme(theme: ThemeConfig) {\n this.activeGroups.forEach(g => g.setTheme(theme));\n }\n\n public refresh() {\n this.activeGroups.forEach(g => g.render());\n }\n\n public destroy() {\n this.activeGroups.forEach(g => g.destroy());\n this.activeGroups = [];\n }\n}\n","import './index.css';\nimport type { DialogOptions } from './index.type';\nimport type { ThemeConfig } from '../../themes/types';\nimport { IBimComponent } from '../../types/component';\nimport { themeManager } from '../../services/theme';\nimport { t, localeManager } from '../../services/locale';\n\n/**\n * 通用弹窗组件类\n * 支持拖拽、缩放、自定义内容和位置。\n */\nexport class BimDialog implements IBimComponent {\n private element: HTMLElement;\n private options: DialogOptions;\n private container: HTMLElement;\n private header: HTMLElement;\n private contentArea: HTMLElement;\n private _isDestroyed = false;\n private _isInitialized = false;\n private unsubscribeTheme: (() => void) | null = null;\n private unsubscribeLocale: (() => void) | null = null;\n\n /**\n * 构造函数\n * @param options 弹窗配置选项\n */\n constructor(options: DialogOptions) {\n // 合并默认配置\n this.options = {\n title: 'Dialog',\n width: 300,\n height: 'auto',\n position: 'center',\n draggable: true,\n resizable: false,\n minWidth: 200,\n minHeight: 100,\n ...options\n };\n this.container = options.container;\n\n // 创建 DOM 结构\n this.element = this.createDom();\n this.header = this.element.querySelector('.bim-dialog-header') as HTMLElement;\n this.contentArea = this.element.querySelector('.bim-dialog-content') as HTMLElement;\n\n // 自动初始化 (为了兼容现有逻辑)\n this.init();\n }\n\n /**\n * 设置主题\n * @param theme 全局主题配置\n */\n public setTheme(theme: ThemeConfig) {\n const style = this.element.style;\n if (!this.options.backgroundColor) style.setProperty('--bim-dialog-bg', theme.panelBackground);\n if (!this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', theme.componentHover);\n if (!this.options.titleColor) style.setProperty('--bim-dialog-title-color', theme.textPrimary);\n if (!this.options.textColor) style.setProperty('--bim-dialog-text-color', theme.textPrimary);\n if (!this.options.borderColor) style.setProperty('--bim-dialog-border-color', theme.border);\n }\n\n /**\n * 初始化组件功能 (接口实现)\n */\n public init() {\n if (this._isInitialized) return;\n\n this.container.appendChild(this.element);\n\n // 必须先挂载才能计算尺寸进行定位\n this.initPosition();\n\n if (this.options.draggable) {\n this.initDrag();\n }\n\n if (this.options.resizable) {\n this.initResize();\n }\n\n this._isInitialized = true;\n\n // 调用弹窗开启后回调\n if (this.options.onOpen) {\n this.options.onOpen();\n }\n\n // 自动订阅主题变更\n this.unsubscribeTheme = themeManager.subscribe((theme) => {\n this.setTheme(theme);\n });\n\n // 自动订阅语言变更\n this.unsubscribeLocale = localeManager.subscribe(() => {\n this.setLocales();\n });\n }\n\n public setLocales(): void {\n if (this.options.title) {\n const titleEl = this.header.querySelector('.bim-dialog-title');\n if (titleEl) {\n titleEl.textContent = t(this.options.title);\n }\n }\n }\n\n /**\n * 创建弹窗的 DOM 结构\n */\n private createDom(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'bim-dialog';\n\n if (this.options.id) el.id = this.options.id;\n\n // 应用颜色配置到 CSS 变量 (局部作用域)\n const style = el.style;\n if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor);\n if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor);\n if (this.options.titleColor) style.setProperty('--bim-dialog-title-color', this.options.titleColor);\n if (this.options.textColor) style.setProperty('--bim-dialog-text-color', this.options.textColor);\n if (this.options.borderColor) style.setProperty('--bim-dialog-border-color', this.options.borderColor);\n\n // 设置初始尺寸\n this.setSize(el, this.options.width, this.options.height);\n\n // 创建标题栏 (Header)\n const header = document.createElement('div');\n header.className = 'bim-dialog-header';\n if (this.options.draggable) header.classList.add('draggable');\n\n const title = document.createElement('span');\n title.className = 'bim-dialog-title';\n title.textContent = this.options.title ? t(this.options.title) : '';\n\n const closeBtn = document.createElement('span');\n closeBtn.className = 'bim-dialog-close';\n closeBtn.innerHTML = '×';\n closeBtn.onclick = () => this.close();\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // 创建内容区域 (Content)\n const content = document.createElement('div');\n content.className = 'bim-dialog-content';\n if (typeof this.options.content === 'string') {\n content.innerHTML = this.options.content;\n } else if (this.options.content instanceof HTMLElement) {\n content.appendChild(this.options.content);\n }\n\n el.appendChild(header);\n el.appendChild(content);\n\n // 如果允许缩放,创建缩放手柄\n if (this.options.resizable) {\n const resizeHandle = document.createElement('div');\n resizeHandle.className = 'bim-dialog-resize-handle';\n el.appendChild(resizeHandle);\n }\n\n return el;\n }\n\n /**\n * 设置元素尺寸\n */\n private setSize(el: HTMLElement, width?: number | string, height?: number | string) {\n if (width !== undefined) {\n el.style.width = typeof width === 'number' ? `${width}px` : width;\n }\n if (height !== undefined) {\n el.style.height = typeof height === 'number' ? `${height}px` : height;\n }\n }\n\n /**\n * 初始化弹窗位置\n */\n private initPosition() {\n const pos = this.options.position;\n\n const elRect = this.element.getBoundingClientRect();\n\n // 计算相对父容器的定位\n let left = 0;\n let top = 0;\n\n const pW = this.container.clientWidth;\n const pH = this.container.clientHeight;\n const elW = elRect.width;\n const elH = elRect.height;\n\n if (typeof pos === 'object' && 'x' in pos) {\n left = pos.x;\n top = pos.y;\n } else {\n switch (pos) {\n case 'center':\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n break;\n case 'top-left': left = 0; top = 0; break;\n case 'top-center': left = (pW - elW) / 2; top = 0; break;\n case 'top-right': left = pW - elW; top = 0; break;\n case 'left-center': left = 0; top = (pH - elH) / 2; break;\n case 'right-center': left = pW - elW; top = (pH - elH) / 2; break;\n case 'bottom-left': left = 0; top = pH - elH; break;\n case 'bottom-center': left = (pW - elW) / 2; top = pH - elH; break;\n case 'bottom-right': left = pW - elW; top = pH - elH; break;\n default:\n left = (pW - elW) / 2;\n top = (pH - elH) / 2;\n }\n }\n\n // 简单的边界检查,防止初始位置溢出\n left = Math.max(0, Math.min(left, pW - elW));\n top = Math.max(0, Math.min(top, pH - elH));\n\n this.element.style.left = `${left}px`;\n this.element.style.top = `${top}px`;\n }\n\n /**\n * 初始化拖拽功能\n */\n private initDrag() {\n let startX = 0;\n let startY = 0;\n let startLeft = 0;\n let startTop = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n startX = e.clientX;\n startY = e.clientY;\n startLeft = this.element.offsetLeft;\n startTop = this.element.offsetTop;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n let newLeft = startLeft + dx;\n let newTop = startTop + dy;\n\n // 边界限制,防止拖出容器\n const maxLeft = this.container.clientWidth - this.element.offsetWidth;\n const maxTop = this.container.clientHeight - this.element.offsetHeight;\n\n newLeft = Math.max(0, Math.min(newLeft, maxLeft));\n newTop = Math.max(0, Math.min(newTop, maxTop));\n\n this.element.style.left = `${newLeft}px`;\n this.element.style.top = `${newTop}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n this.header.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 初始化缩放功能\n */\n private initResize() {\n const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;\n if (!handle) return;\n\n let startX = 0;\n let startY = 0;\n let startW = 0;\n let startH = 0;\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n startX = e.clientX;\n startY = e.clientY;\n startW = this.element.offsetWidth;\n startH = this.element.offsetHeight;\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n const onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n const newW = Math.max(this.options.minWidth || 100, startW + dx);\n const newH = Math.max(this.options.minHeight || 50, startH + dy);\n\n this.element.style.width = `${newW}px`;\n this.element.style.height = `${newH}px`;\n };\n\n const onMouseUp = () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n }\n\n /**\n * 动态设置内容\n * @param content 内容元素或 HTML 字符串\n */\n public setContent(content: HTMLElement | string) {\n this.contentArea.innerHTML = '';\n if (typeof content === 'string') {\n this.contentArea.innerHTML = content;\n } else {\n this.contentArea.appendChild(content);\n }\n }\n\n /**\n * 关闭弹窗并销毁\n */\n public close() {\n if (this._isDestroyed) return;\n if (this.unsubscribeTheme) {\n this.unsubscribeTheme();\n this.unsubscribeTheme = null;\n }\n if (this.unsubscribeLocale) {\n this.unsubscribeLocale();\n this.unsubscribeLocale = null;\n }\n this.element.remove();\n this._isDestroyed = true;\n if (this.options.onClose) {\n this.options.onClose();\n }\n }\n\n /**\n * 销毁组件 (接口实现)\n */\n public destroy() {\n this.close();\n }\n}\n","import './index.css';\nimport { BimDialog } from '../index';\n\n/**\n * BimInfoDialog (继承版)\n * 这是一个展示项目信息的业务弹窗组件,直接继承自 BimDialog。\n */\nexport class BimInfoDialog extends BimDialog {\n /**\n * 构造函数\n * @param container 父容器\n */\n constructor(container: HTMLElement) {\n // 1. 准备内容 DOM\n const contentEl = document.createElement('div');\n contentEl.className = 'bim-info-dialog-content';\n\n const infoTitle = document.createElement('h3');\n infoTitle.textContent = 'Model Information';\n\n const infoList = document.createElement('ul');\n infoList.innerHTML = `\n Name: Sample Project \n Version: 1.0.0 \n Date: ${new Date().toLocaleDateString()} \n Status: Active \n `;\n\n const actionBtn = document.createElement('button');\n actionBtn.textContent = 'Update Status';\n actionBtn.style.marginTop = '10px';\n actionBtn.onclick = () => {\n alert('Status updated!');\n };\n\n contentEl.appendChild(infoTitle);\n contentEl.appendChild(infoList);\n contentEl.appendChild(actionBtn);\n\n // 2. 调用父类构造函数,传入特定的配置\n super({\n container: container,\n title: 'dialog.testTitle',\n content: contentEl,\n width: 320,\n height: 'auto',\n position: 'center',\n resizable: true,\n draggable: true,\n // 可以在这里添加特定的 onClose 逻辑\n onClose: () => {\n console.log('Info dialog closed');\n },\n onOpen: () => {\n console.log('Info dialog opened');\n }\n });\n\n // 3. 如果有特定于子类的初始化逻辑,可以在 super() 之后执行\n // 例如:this.element.classList.add('my-special-class');\n }\n\n // 不需要再手动实现 setTheme, destroy, close, init\n // 它们都已从 BimDialog 继承\n}","import { BimDialog } from '../components/dialog';\nimport { BimInfoDialog } from '../components/dialog/bimInfoDialog';\nimport type { DialogOptions } from '../components/dialog/index.type';\nimport type { ThemeConfig } from '../themes/types';\nimport { themeManager } from '../services/theme'; // 修正路径\n\n/**\n * 弹窗管理器\n * 负责创建和管理应用中的各类弹窗。\n */\nexport class DialogManager {\n /** 弹窗挂载的父容器 */\n private container: HTMLElement;\n /** 活跃的弹窗实例列表 */\n private activeDialogs: BimDialog[] = [];\n\n /**\n * 构造函数\n * @param container 弹窗挂载的目标容器\n */\n constructor(container: HTMLElement) {\n this.container = container;\n }\n\n /**\n * 创建一个通用弹窗\n * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)\n * @returns BimDialog 实例\n */\n public create(options: Omit): BimDialog {\n const dialog = new BimDialog({\n container: this.container,\n ...options,\n onClose: () => {\n // 从活跃列表中移除\n this.activeDialogs = this.activeDialogs.filter(d => d !== dialog);\n if (options.onClose) options.onClose();\n }\n });\n\n // 应用当前主题\n dialog.setTheme(themeManager.getTheme());\n\n this.activeDialogs.push(dialog);\n return dialog;\n }\n\n /**\n * 显示二次封装的模型信息弹窗\n * 演示如何调用特定的业务弹窗组件\n */\n public showInfoDialog() {\n // 最佳实践:所有弹窗应通过 create 统一管理,或者手动加入管理。\n new BimInfoDialog(this.container);\n // 暂时不做主题追踪,作为遗留逻辑保留\n }\n\n /**\n * 响应全局主题变更\n * @param theme 全局主题配置\n */\n public updateTheme(theme: ThemeConfig) {\n this.activeDialogs.forEach(dialog => {\n if (dialog.setTheme) {\n dialog.setTheme(theme);\n }\n });\n }\n}","import './bim-engine.css';\nimport { ToolbarManager } from './managers/toolbar-manager';\nimport { ButtonGroupManager } from './managers/button-group-manager';\nimport { DialogManager } from './managers/dialog-manager';\nimport { localeManager } from './services/locale';\nimport { themeManager } from './services/theme';\nimport type { LocaleType } from './locales/types';\nimport type { ThemeType, ThemeConfig } from './themes/types';\n\nexport class BimEngine {\n private container: HTMLElement;\n private wrapper: HTMLElement | null = null;\n private topLeftGroup: any = null; // 保存左上角按钮组的引用\n\n public toolbar: ToolbarManager | null = null; // 底部专用\n public buttonGroup: ButtonGroupManager | null = null; // 通用\n public dialog: DialogManager | null = null;\n\n public get localeManager() { return localeManager; }\n public get themeManager() { return themeManager; }\n\n constructor(container: HTMLElement | string, options?: { locale?: LocaleType; theme?: ThemeType }) {\n const el = typeof container === 'string' ? document.getElementById(container) : container;\n if (!el) throw new Error('Container not found');\n this.container = el;\n\n if (options?.locale) localeManager.setLocale(options.locale);\n if (options?.theme) {\n if (options.theme === 'custom') {\n console.warn('Custom theme should be set via setCustomTheme().');\n } else {\n themeManager.setTheme(options.theme);\n }\n }\n\n this.init();\n }\n\n public setLocale(locale: LocaleType) { localeManager.setLocale(locale); }\n public getLocale(): LocaleType { return localeManager.getLocale(); }\n public setTheme(theme: 'dark' | 'light') { themeManager.setTheme(theme); }\n public setCustomTheme(theme: ThemeConfig) { themeManager.setCustomTheme(theme); }\n\n private init() {\n this.container.innerHTML = '';\n this.wrapper = document.createElement('div');\n this.wrapper.className = 'bim-engine-wrapper';\n this.container.appendChild(this.wrapper);\n\n // 初始化管理器\n this.dialog = new DialogManager(this.wrapper);\n this.toolbar = new ToolbarManager(this.wrapper);\n this.buttonGroup = new ButtonGroupManager(this.wrapper);\n\n // --- 创建左上角按钮组 (需求 1 & 2) ---\n this.createTopLeftGroup();\n\n // 初始主题\n this.updateTheme(themeManager.getTheme());\n\n // 在主题更新后,设置左上角按钮组的自定义颜色\n if (this.topLeftGroup) {\n this.topLeftGroup.setColors({\n backgroundColor: '#ff00ff'\n });\n }\n themeManager.subscribe((theme) => {\n this.updateTheme(theme);\n });\n }\n\n private createTopLeftGroup() {\n if (!this.buttonGroup) return;\n\n this.topLeftGroup = this.buttonGroup.create({\n position: 'top-left',\n direction: 'column',\n align: 'vertical',\n backgroundColor: '#ff00ff', // 自定义背景色,不会被主题覆盖\n showLabel: false\n });\n\n this.topLeftGroup.addGroup('main');\n this.topLeftGroup.addButton({\n id: 'menu-btn',\n groupId: 'main',\n type: 'button',\n label: 'Menu', // 应该用 translation key\n icon: ' ',\n onClick: () => {\n alert(\"点击按钮\")\n }\n });\n\n // 手动 render 一次以显示\n this.topLeftGroup.render();\n }\n\n private updateTheme(theme: ThemeConfig) {\n if (this.wrapper) {\n this.wrapper.style.backgroundColor = theme.background;\n this.wrapper.style.color = theme.textPrimary;\n }\n }\n\n public destroy() {\n this.toolbar?.destroy();\n this.buttonGroup?.destroy();\n this.dialog = null;\n this.container.innerHTML = '';\n }\n}\n","import type { ButtonConfig } from '../../../index.type';\n\n/**\n * 首页按钮配置\n */\nexport const homeButton: ButtonConfig = {\n id: 'home',\n groupId: 'group-1',\n type: 'button',\n label: 'toolbar.home',\n icon: ' ',\n keepActive: true,\n onClick: (button) => {\n console.log('首页按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../index.type';\n\n/**\n * 定位按钮配置\n */\nexport const locationButton: ButtonConfig = {\n id: 'location',\n groupId: 'group-1',\n type: 'button',\n label: 'toolbar.location',\n icon: ' ',\n keepActive: false,\n onClick: (button) => {\n console.log('定位按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../../index.type';\n\n/**\n * 漫游菜单按钮配置\n */\nexport const walkMenuButton: ButtonConfig = {\n id: 'walk',\n groupId: 'group-1',\n type: 'menu',\n label: 'toolbar.walk',\n align: 'vertical',\n icon: ' ',\n keepActive: true,\n onClick: (button) => {\n console.log('漫游按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../../index.type';\n\nexport const walkPersonButton: ButtonConfig = {\n id: 'walk-person',\n groupId: 'group-1',\n parentId: 'walk',\n type: 'button',\n align: 'vertical',\n label: 'toolbar.walkPerson',\n icon: ' ',\n onClick: (button) => {\n console.log('人视漫游被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../../index.type';\n\nexport const walkBirdButton: ButtonConfig = {\n id: 'walk-bird',\n groupId: 'group-1',\n parentId: 'walk',\n align: 'vertical',\n type: 'button',\n label: 'toolbar.walkBird',\n icon: ' ',\n onClick: (button) => {\n console.log('鸟瞰漫游被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../index.type';\n\n/**\n * 定位按钮配置\n */\nexport const settingButton: ButtonConfig = {\n id: 'setting',\n groupId: 'group-2',\n type: 'button',\n label: 'toolbar.setting',\n icon: ' ',\n keepActive: false,\n onClick: (button) => {\n console.log('设置按钮被点击:', button.id);\n }\n};\n","import type { ButtonConfig } from '../../../index.type';\n\n/**\n * 定位按钮配置\n */\nexport const infoButton: ButtonConfig = {\n id: 'info',\n groupId: 'group-2',\n type: 'button',\n label: 'toolbar.info',\n icon: ' ',\n keepActive: false,\n onClick: (button) => {\n console.log('信息按钮被点击:', button.id);\n }\n};\n"],"names":["zhCN","enUS","LocaleManager","locale","key","keys","value","k","listener","l","localeManager","t","darkTheme","lightTheme","ThemeManager","themeName","theme","themeManager","BimButtonGroup","options","el","pos","style","margin","themeColors","colorKey","colors","groupId","beforeGroupId","g","newGroup","index","config","parentId","group","button","parentBtn","buttons","id","btn","found","groupElement","total","groupEl","btnWrapper","wrapper","btnEl","iconSize","minWidth","icon","textWrapper","label","arrow","dropdown","btnRect","expand","subBtn","item","dropdownRect","e","buttonId","visible","show","color","Toolbar","homeButton","index$6","locationButton","index$5","walkMenuButton","index$4","walkPersonButton","index$3","walkBirdButton","index$2","settingButton","index$1","infoButton","ToolbarManager","container","v","ButtonGroupManager","groupContainer","BimDialog","titleEl","header","title","closeBtn","content","resizeHandle","width","height","elRect","left","top","pW","pH","elW","elH","startX","startY","startLeft","startTop","onMouseDown","onMouseMove","onMouseUp","dx","dy","newLeft","newTop","maxLeft","maxTop","handle","startW","startH","newW","newH","BimInfoDialog","contentEl","infoTitle","infoList","actionBtn","DialogManager","dialog","d","BimEngine"],"mappings":"wOAEO,MAAMA,EAA8B,CACzC,OAAQ,CACN,MAAO,YACP,YAAa,qBACb,eAAgB,SAChB,eAAgB,cAAA,EAElB,QAAS,CACP,KAAM,KACN,KAAM,KACN,SAAU,KACV,QAAS,KACT,KAAM,KACN,WAAY,KACZ,SAAU,KACV,SAAU,IAAA,EAEZ,OAAQ,CACN,UAAW,OACX,YAAa,oGAAA,CAEjB,ECrBaC,EAA8B,CACzC,OAAQ,CACN,MAAO,YACP,YAAa,6BACb,eAAgB,mBAChB,eAAgB,4BAAA,EAElB,QAAS,CACP,KAAM,OACN,KAAM,OACN,SAAU,WACV,QAAS,WACT,KAAM,OACN,WAAY,SACZ,SAAU,WACV,SAAU,MAAA,EAEZ,OAAQ,CACN,UAAW,cACX,YAAa,uKAAA,CAEjB,ECdO,MAAMC,CAAc,CACjB,cAA4B,QAC5B,SAAsD,CAC5D,QAASF,EACT,QAASC,CAAA,EAEH,UAAoC,CAAA,EAE5C,aAAc,CAEd,CAKO,WAAwB,CAC7B,OAAO,KAAK,aACd,CAKO,UAAUE,EAAoB,CAC/B,KAAK,gBAAkBA,IAC3B,KAAK,cAAgBA,EACrB,KAAK,gBAAA,EACP,CAKO,EAAEC,EAAqB,CAC5B,GAAI,CAACA,EAAK,MAAO,GAEjB,MAAMC,EAAOD,EAAI,MAAM,GAAG,EAC1B,IAAIE,EAAa,KAAK,SAAS,KAAK,aAAa,EAEjD,UAAWC,KAAKF,EACd,GAAIC,GAAS,OAAOA,GAAU,UAAYC,KAAKD,EAC7CA,EAAQA,EAAMC,CAAC,MAEf,QAAOH,EAGX,OAAOE,CACT,CAKO,UAAUE,EAA4C,CAC3D,YAAK,UAAU,KAAKA,CAAQ,EACrB,IAAM,CACX,KAAK,UAAY,KAAK,UAAU,OAAOC,GAAKA,IAAMD,CAAQ,CAC5D,CACF,CAEQ,iBAAkB,CACxB,KAAK,UAAU,QAAQA,GAAYA,EAAS,KAAK,aAAa,CAAC,CACjE,CACF,CAGO,MAAME,EAAgB,IAAIR,EAOpBS,EAAKP,GAAwBM,EAAc,EAAEN,CAAG,EC1EhDQ,EAAyB,CAClC,KAAM,OACN,QAAS,UACT,aAAc,UAGd,WAAY,UACZ,gBAAiB,wBA8CjB,YAAa,UACb,cAAe,UAEf,OAAQ,UAER,KAAM,UACN,WAAY,UAEZ,oBAAqB,cACrB,eAAgB,UAChB,gBAAiB,0BACrB,EAKaC,EAA0B,CACnC,KAAM,QACN,QAAS,UACT,aAAc,UAGd,WAAY,UACZ,gBAAiB,UAEjB,YAAa,UACb,cAAe,UAEf,OAAQ,UAER,KAAM,UACN,WAAY,UAEZ,oBAAqB,cACrB,eAAgB,UAChB,gBAAiB,SACrB,ECtFO,MAAMC,CAAa,CACd,aAA4BF,EAC5B,UAAmC,CAAA,EAE3C,aAAc,CAEd,CAKO,UAAwB,CAC3B,OAAO,KAAK,YAChB,CAMO,SAASG,EAA6B,CACrCA,IAAc,QACd,KAAK,WAAWF,CAAU,EAE1B,KAAK,WAAWD,CAAS,CAEjC,CAMO,eAAeI,EAAoB,CACtC,KAAK,WAAWA,CAAK,CACzB,CAKQ,WAAWA,EAAoB,CACnC,KAAK,aAAeA,EACpB,KAAK,gBAAA,CACT,CAKO,UAAUR,EAA2C,CACxD,YAAK,UAAU,KAAKA,CAAQ,EAE5BA,EAAS,KAAK,YAAY,EACnB,IAAM,CACT,KAAK,UAAY,KAAK,UAAU,OAAOC,GAAKA,IAAMD,CAAQ,CAC9D,CACJ,CAEQ,iBAAkB,CACtB,KAAK,UAAU,QAAQA,GAAYA,EAAS,KAAK,YAAY,CAAC,CAClE,CACJ,CAGO,MAAMS,EAAe,IAAIH,ECrDzB,MAAMI,CAAwC,CACzC,UACA,QACA,OAAwB,CAAA,EACxB,iBAAgC,IAChC,YAAwC,IACxC,gBAAsC,KACtC,aAA8B,KAC9B,iBAAiD,IACjD,kBAAyC,KACzC,iBAAwC,KAE/B,aAAe,mJAEhC,YAAYC,EAA6B,CACrC,MAAMC,EAAK,OAAOD,EAAQ,WAAc,SAClC,SAAS,eAAeA,EAAQ,SAAS,EACzCA,EAAQ,UAEd,GAAI,CAACC,EAAI,MAAM,IAAI,MAAM,qBAAqB,EAE9C,KAAK,UAAYA,EAEjB,KAAK,QAAU,CACX,UAAW,GACX,WAAY,CAAA,EACZ,UAAW,MACX,SAAU,SACV,MAAO,WACP,OAAQ,OACR,GAAGD,CAAA,EAIwC,CAC3C,kBAAmB,qBAAsB,gBACzC,iBAAkB,YAAa,kBAC/B,YAAa,iBAAA,EAEP,QAAQf,GAAO,CACjBe,EAAQf,CAAG,GACX,KAAK,aAAa,IAAIA,CAAG,CAEjC,CAAC,EAED,KAAK,cAAA,EACL,KAAK,YAAA,CACT,CAEQ,eAAsB,CAC1B,KAAK,UAAU,UAAY,GAC3B,KAAK,UAAU,UAAU,IAAI,oBAAoB,EAE7C,KAAK,QAAQ,YAAc,SAC3B,KAAK,UAAU,UAAU,IAAI,YAAY,EAEzC,KAAK,UAAU,UAAU,IAAI,SAAS,EAGtC,KAAK,QAAQ,WACb,KAAK,UAAU,UAAU,IAAI,KAAK,QAAQ,SAAS,EAGvD,KAAK,eAAA,CACT,CAEQ,gBAAiB,CACrB,MAAMiB,EAAM,KAAK,QAAQ,SACnBC,EAAQ,KAAK,UAAU,MAI7B,GAFAA,EAAM,IAAM,GAAIA,EAAM,OAAS,GAAIA,EAAM,KAAO,GAAIA,EAAM,MAAQ,GAAIA,EAAM,UAAY,GAEpFD,IAAQ,SAAU,CAClB,KAAK,UAAU,UAAU,IAAI,QAAQ,EACrC,MACJ,CAKA,GAHA,KAAK,UAAU,UAAU,OAAO,QAAQ,EACxC,KAAK,UAAU,MAAM,SAAW,WAE5B,OAAOA,GAAQ,UAAY,MAAOA,EAClCC,EAAM,KAAO,GAAGD,EAAI,CAAC,KACrBC,EAAM,IAAM,GAAGD,EAAI,CAAC,SACjB,CACH,MAAME,EAAS,OACf,OAAQF,EAAA,CACJ,IAAK,WACDC,EAAM,IAAMC,EAAQD,EAAM,KAAOC,EACjC,MACJ,IAAK,aACDD,EAAM,IAAMC,EAAQD,EAAM,KAAO,MAAOA,EAAM,UAAY,mBAC1D,MACJ,IAAK,YACDA,EAAM,IAAMC,EAAQD,EAAM,MAAQC,EAClC,MACJ,IAAK,cACDD,EAAM,OAASC,EAAQD,EAAM,KAAOC,EACpC,MACJ,IAAK,gBACDD,EAAM,OAASC,EAAQD,EAAM,KAAO,MAAOA,EAAM,UAAY,mBAC7D,MACJ,IAAK,eACDA,EAAM,OAASC,EAAQD,EAAM,MAAQC,EACrC,MACJ,IAAK,cACDD,EAAM,KAAOC,EAAQD,EAAM,IAAM,MAAOA,EAAM,UAAY,mBAC1D,MACJ,IAAK,eACDA,EAAM,MAAQC,EAAQD,EAAM,IAAM,MAAOA,EAAM,UAAY,mBAC3D,MACJ,IAAK,SACDA,EAAM,IAAM,MAAOA,EAAM,KAAO,MAAOA,EAAM,UAAY,wBACzD,KAAA,CAEZ,CACJ,CAKQ,aAAoB,CACxB,MAAMA,EAAQ,KAAK,UAAU,MACzB,KAAK,QAAQ,iBAAiBA,EAAM,YAAY,6BAA8B,KAAK,QAAQ,eAAe,EAC1G,KAAK,QAAQ,oBAAoBA,EAAM,YAAY,eAAgB,KAAK,QAAQ,kBAAkB,EAClG,KAAK,QAAQ,eAAeA,EAAM,YAAY,qBAAsB,KAAK,QAAQ,aAAa,EAC9F,KAAK,QAAQ,gBAAgBA,EAAM,YAAY,sBAAuB,KAAK,QAAQ,cAAc,EACjG,KAAK,QAAQ,WAAWA,EAAM,YAAY,mBAAoB,KAAK,QAAQ,SAAS,EACpF,KAAK,QAAQ,iBAAiBA,EAAM,YAAY,0BAA2B,KAAK,QAAQ,eAAe,EACvG,KAAK,QAAQ,WAAWA,EAAM,YAAY,uBAAwB,KAAK,QAAQ,SAAS,EACxF,KAAK,QAAQ,iBAAiBA,EAAM,YAAY,8BAA+B,KAAK,QAAQ,eAAe,CACnH,CAMO,SAASN,EAA0B,CACtC,MAAMQ,EAAiC,CACnC,gBAAiBR,EAAM,gBACvB,mBAAoBA,EAAM,oBAC1B,cAAeA,EAAM,eACrB,eAAgBA,EAAM,gBACtB,UAAWA,EAAM,KACjB,gBAAiBA,EAAM,WACvB,UAAWA,EAAM,cACjB,gBAAiBA,EAAM,WAAA,EAI3B,OAAO,QAAQQ,CAAW,EAAE,QAAQ,CAAC,CAACpB,EAAKE,CAAK,IAAM,CAClD,MAAMmB,EAAWrB,EACZ,KAAK,aAAa,IAAIqB,CAAQ,IAC/B,KAAK,QAAQA,CAAQ,EAAInB,EAEjC,CAAC,EAED,KAAK,YAAA,CACT,CAMO,UAAUoB,EAAiC,CAE9C,KAAK,QAAU,CAAE,GAAG,KAAK,QAAS,GAAGA,CAAA,EAGrC,OAAO,KAAKA,CAAM,EAAE,QAAQtB,GAAO,CAC/B,KAAK,aAAa,IAAIA,CAA8B,CACxD,CAAC,EAED,KAAK,YAAA,CACT,CAEA,MAAa,MAAsB,CAC/B,KAAK,OAAA,EAGL,KAAK,kBAAoBM,EAAc,UAAU,IAAM,CACnD,KAAK,WAAA,CACT,CAAC,EAGD,KAAK,iBAAmBO,EAAa,UAAWD,GAAU,CACtD,KAAK,SAASA,CAAK,CACvB,CAAC,CACL,CAEO,YAAmB,CACtB,KAAK,OAAA,CACT,CAEO,SAASW,EAAiBC,EAA8B,CAC3D,GAAI,KAAK,OAAO,QAAUC,EAAE,KAAOF,CAAO,EAAG,OAC7C,MAAMG,EAAwB,CAAE,GAAIH,EAAS,QAAS,CAAA,CAAC,EACvD,GAAIC,EAAe,CACf,MAAMG,EAAQ,KAAK,OAAO,UAAUF,GAAKA,EAAE,KAAOD,CAAa,EAC/DG,IAAU,GAAK,KAAK,OAAO,OAAOA,EAAO,EAAGD,CAAQ,EAAI,KAAK,OAAO,KAAKA,CAAQ,CACrF,MACI,KAAK,OAAO,KAAKA,CAAQ,CAEjC,CAEO,UAAUE,EAA4B,CACzC,KAAM,CAAE,QAAAL,EAAS,SAAAM,CAAA,EAAaD,EACxBE,EAAQ,KAAK,OAAO,KAAKL,GAAKA,EAAE,KAAOF,CAAO,EACpD,GAAI,CAACO,EAAO,OAEZ,MAAMC,EAAoB,CAAE,GAAGH,EAAQ,SAAUA,EAAO,UAAY,EAAC,EACrE,GAAIC,EAAU,CACV,MAAMG,EAAY,KAAK,WAAWF,EAAM,QAASD,CAAQ,EACrDG,IACKA,EAAU,WAAUA,EAAU,SAAW,CAAA,GAC9CA,EAAU,SAAS,KAAKD,CAAM,EAEtC,MACID,EAAM,QAAQ,KAAKC,CAAM,CAEjC,CAEQ,WAAWE,EAAsBC,EAAmC,CACxE,UAAWC,KAAOF,EAAS,CACvB,GAAIE,EAAI,KAAOD,EAAI,OAAOC,EAC1B,GAAIA,EAAI,SAAU,CACd,MAAMC,EAAQ,KAAK,WAAWD,EAAI,SAAUD,CAAE,EAC9C,GAAIE,EAAO,OAAOA,CACtB,CACJ,CAEJ,CAEO,QAAe,CAClB,KAAK,UAAU,UAAY,GAC3B,KAAK,QAAQ,MAAA,EAEb,KAAK,OAAO,QAAQ,CAACN,EAAOH,IAAU,CAClC,MAAMU,EAAe,KAAK,YAAYP,EAAOH,EAAO,KAAK,OAAO,MAAM,EACtE,KAAK,UAAU,YAAYU,CAAY,CAC3C,CAAC,CACL,CAEQ,YAAYP,EAAoBH,EAAeW,EAA4B,CAC/E,MAAMC,EAAU,SAAS,cAAc,KAAK,EAC5C,OAAAA,EAAQ,UAAY,wBAEhBZ,EAAQW,EAAQ,GAChBC,EAAQ,UAAU,IAAI,aAAa,EAGvCT,EAAM,QAAQ,QAAQC,GAAU,CAC5B,GAAI,KAAK,UAAUA,EAAO,EAAE,EAAG,CAC3B,MAAMS,EAAa,KAAK,aAAaT,CAAM,EAC3CQ,EAAQ,YAAYC,CAAU,CAClC,CACJ,CAAC,EACMD,CACX,CAEQ,aAAaR,EAAgC,CACjD,MAAMU,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,kBAEpB,MAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,WAGJX,EAAO,OAAS,KAAK,QAAQ,OAAS,cACtC,aACVW,EAAM,UAAU,IAAI,kBAAkB,EAEtCA,EAAM,UAAU,IAAI,gBAAgB,EAGpC,KAAK,aAAa,IAAIX,EAAO,EAAE,GAAGW,EAAM,UAAU,IAAI,QAAQ,EAC9DX,EAAO,UAAUW,EAAM,UAAU,IAAI,UAAU,EAGlC,KAAK,QAAQ,WAAaX,EAAO,OAE9CW,EAAM,UAAU,IAAI,UAAU,EAIlC,MAAMC,EAAWZ,EAAO,UAAY,GAC9Ba,EAAWb,EAAO,UAAY,GACpCW,EAAM,MAAM,SAAW,GAAGE,CAAQ,KAElC,MAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,eACjBA,EAAK,MAAM,MAAQ,GAAGF,CAAQ,KAC9BE,EAAK,MAAM,OAAS,GAAGF,CAAQ,KAC/BE,EAAK,UAAY,KAAK,QAAQd,EAAO,IAAI,EACzCW,EAAM,YAAYG,CAAI,EAGtB,MAAMC,EAAc,SAAS,cAAc,KAAK,EAGhD,GAFAA,EAAY,UAAY,uBAEpB,KAAK,QAAQ,WAAaf,EAAO,MAAO,CACxC,MAAMgB,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,gBAClBA,EAAM,YAAcxC,EAAEwB,EAAO,KAAK,EAClCe,EAAY,YAAYC,CAAK,CACjC,CAEA,GAAIhB,EAAO,UAAYA,EAAO,SAAS,OAAS,EAAG,CAC/C,MAAMiB,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,gBAClBA,EAAM,YAAc,IACpBF,EAAY,YAAYE,CAAK,CACjC,CAGA,OAAIF,EAAY,iBACZJ,EAAM,YAAYI,CAAW,EAGjCJ,EAAM,iBAAiB,QAAS,IAAM,KAAK,YAAYX,CAAM,CAAC,EAC9DW,EAAM,iBAAiB,aAAc,IAAM,KAAK,iBAAiBX,EAAQW,CAAK,CAAC,EAC/EA,EAAM,iBAAiB,aAAc,IAAM,KAAK,kBAAkB,EAElE,KAAK,QAAQ,IAAIX,EAAO,GAAIW,CAAK,EACjCD,EAAQ,YAAYC,CAAK,EAClBD,CACX,CAEQ,YAAYV,EAAyB,CACrCA,EAAO,WACP,CAACA,EAAO,UAAYA,EAAO,SAAS,SAAW,KAC3CA,EAAO,aACW,KAAK,aAAa,IAAIA,EAAO,EAAE,EAClC,KAAK,aAAa,OAAOA,EAAO,EAAE,EAC5C,KAAK,aAAa,IAAIA,EAAO,EAAE,EACpC,KAAK,kBAAkBA,EAAO,EAAE,GAEpC,KAAK,cAAA,EACDA,EAAO,SAASA,EAAO,QAAQA,CAAM,EAEjD,CAEQ,iBAAiBA,EAAmBW,EAA0B,CAC9D,KAAK,cAAc,aAAa,KAAK,YAAY,EACjDX,EAAO,UAAYA,EAAO,SAAS,OAAS,EAC5C,KAAK,aAAaA,EAAQW,CAAK,EAE/B,KAAK,cAAA,CAEb,CAEQ,kBAAyB,CAC7B,KAAK,aAAe,OAAO,WAAW,IAAM,KAAK,cAAA,EAAiB,GAAG,CACzE,CAEQ,aAAaX,EAAmBW,EAA0B,CAE9D,GADA,KAAK,cAAA,EACD,CAACX,EAAO,SAAU,OAEtB,MAAMkB,EAAW,SAAS,cAAc,KAAK,EAC7CA,EAAS,UAAY,mBACjB,KAAK,QAAQ,iBAAiBA,EAAS,MAAM,YAAY,mBAAoB,KAAK,QAAQ,eAAe,EAG7G,MAAMC,EAAUR,EAAM,sBAAA,EAChBS,EAAS,KAAK,QAAQ,QAAU,OAGlC,KAAK,QAAQ,YAAc,MAC3BF,EAAS,MAAM,cAAgB,SAE/BA,EAAS,MAAM,cAAgB,MAInC,SAAS,KAAK,YAAYA,CAAQ,EAGlClB,EAAO,SAAS,QAAQqB,GAAU,CAC9B,GAAI,KAAK,UAAUA,EAAO,EAAE,EAAG,CAC3B,MAAMC,EAAO,KAAK,mBAAmBD,CAAM,EAC3CH,EAAS,YAAYI,CAAI,CAC7B,CACJ,CAAC,EAGD,MAAMC,EAAeL,EAAS,sBAAA,EAE1BE,IAAW,MAEXF,EAAS,MAAM,OAAU,OAAO,YAAcC,EAAQ,IAAM,EAAK,KACjED,EAAS,MAAM,KAAQC,EAAQ,MAAQA,EAAQ,MAAQI,EAAa,OAAS,EAAK,MAC3EH,IAAW,QAElBF,EAAS,MAAM,IAAOC,EAAQ,OAAS,EAAK,KAC5CD,EAAS,MAAM,KAAQC,EAAQ,MAAQA,EAAQ,MAAQI,EAAa,OAAS,EAAK,MAC3EH,IAAW,SAElBF,EAAS,MAAM,IAAOC,EAAQ,KAAOA,EAAQ,OAASI,EAAa,QAAU,EAAK,KAClFL,EAAS,MAAM,KAAQC,EAAQ,MAAQ,EAAK,MACrCC,IAAW,SAElBF,EAAS,MAAM,IAAOC,EAAQ,KAAOA,EAAQ,OAASI,EAAa,QAAU,EAAK,KAClFL,EAAS,MAAM,MAAS,OAAO,WAAaC,EAAQ,KAAO,EAAK,MAGpED,EAAS,iBAAiB,aAAc,IAAM,CAAM,KAAK,cAAc,aAAa,KAAK,YAAY,CAAG,CAAC,EACzGA,EAAS,iBAAiB,aAAc,IAAM,KAAK,kBAAkB,EACrE,KAAK,gBAAkBA,CAC3B,CAEQ,mBAAmBlB,EAAgC,CACvD,MAAMsB,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,yBAGHtB,EAAO,OAAS,gBAChB,aACVsB,EAAK,UAAU,IAAI,kBAAkB,EAErCA,EAAK,UAAU,IAAI,gBAAgB,EAIvC,MAAMV,EAAWZ,EAAO,UAAY,GAC9Ba,EAAWb,EAAO,SACpBa,IACAS,EAAK,MAAM,SAAW,GAAGT,CAAQ,MAGrC,MAAMC,EAAO,SAAS,cAAc,KAAK,EAQzC,GAPAA,EAAK,UAAY,eACjBA,EAAK,MAAM,MAAQ,GAAGF,CAAQ,KAC9BE,EAAK,MAAM,OAAS,GAAGF,CAAQ,KAC/BE,EAAK,UAAY,KAAK,QAAQd,EAAO,IAAI,EACzCsB,EAAK,YAAYR,CAAI,EAGjB,KAAK,QAAQ,WAAad,EAAO,MAAO,CACxC,MAAMgB,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,yBAClBA,EAAM,YAAcxC,EAAEwB,EAAO,KAAK,EAClCsB,EAAK,YAAYN,CAAK,CAC1B,CAEA,OAAAM,EAAK,iBAAiB,QAAUE,GAAM,CAAEA,EAAE,gBAAA,EAAmB,KAAK,YAAYxB,CAAM,CAAG,CAAC,EACjFsB,CACX,CAEQ,eAAsB,CACtB,KAAK,kBACL,KAAK,gBAAgB,OAAA,EACrB,KAAK,gBAAkB,MAE3B,KAAK,QAAQ,QAAQX,GAAS,CAC1B,MAAMM,EAAQN,EAAM,cAAc,gBAAgB,EAC9CM,GAAOA,EAAM,UAAU,OAAO,SAAS,CAC/C,CAAC,CACL,CAEQ,kBAAkBQ,EAAwB,CAC9C,MAAMd,EAAQ,KAAK,QAAQ,IAAIc,CAAQ,EACnCd,IACA,KAAK,aAAa,IAAIc,CAAQ,EAAId,EAAM,UAAU,IAAI,QAAQ,EAAIA,EAAM,UAAU,OAAO,QAAQ,EAEzG,CAEQ,QAAQG,EAAuB,CAAE,OAAOA,GAAQ,KAAK,YAAc,CACpE,uBAAuBX,EAAYuB,EAAwB,CACzD,KAAK,QAAQ,aAAY,KAAK,QAAQ,WAAa,CAAA,GACxD,KAAK,QAAQ,WAAWvB,CAAE,EAAIuB,EAC9B,KAAK,OAAA,CACT,CACO,aAAaC,EAAqB,CACrC,KAAK,QAAQ,UAAYA,EACzB,KAAK,uBAAA,CACT,CAEQ,wBAA+B,CACnC,KAAK,QAAQ,QAAQ,CAAChB,EAAOc,IAAa,CAEtC,MAAMzB,EAAS,KAAK,eAAeyB,CAAQ,EAC3C,GAAI,CAACzB,EAAQ,OAEI,KAAK,QAAQ,WAAaA,EAAO,MAI9CW,EAAM,UAAU,OAAO,UAAU,EAEjCA,EAAM,UAAU,IAAI,UAAU,CAEtC,CAAC,CACL,CAEQ,eAAeR,EAAmC,CACtD,UAAWJ,KAAS,KAAK,OAAQ,CAC7B,MAAMM,EAAQ,KAAK,WAAWN,EAAM,QAASI,CAAE,EAC/C,GAAIE,EAAO,OAAOA,CACtB,CAEJ,CACO,mBAAmBuB,EAAqB,CAAE,KAAK,UAAU,CAAE,gBAAiBA,CAAA,CAAO,CAAG,CACrF,UAAUzB,EAAqB,CAAE,OAAO,KAAK,QAAQ,aAAaA,CAAE,IAAM,EAAO,CAClF,SAAgB,CACf,KAAK,oBACL,KAAK,kBAAA,EACL,KAAK,kBAAoB,MAEzB,KAAK,mBACL,KAAK,iBAAA,EACL,KAAK,iBAAmB,MAE5B,KAAK,cAAA,EACL,KAAK,UAAU,UAAY,GAC3B,KAAK,QAAQ,MAAA,CACjB,CACJ,CC9gBO,MAAM0B,UAAgB9C,CAAe,CAIxC,MAAa,MAAsB,CAC/B,MAAM,MAAM,KAAA,EAGZ,KAAM,CAAE,WAAA+C,CAAA,EAAe,MAAM,QAAA,QAAA,EAAA,KAAA,IAAAC,CAAA,EACvB,CAAE,eAAAC,CAAA,EAAmB,MAAM,QAAA,QAAA,EAAA,KAAA,IAAAC,CAAA,EAC3B,CAAE,eAAAC,CAAA,EAAmB,MAAM,QAAA,QAAA,EAAA,KAAA,IAAAC,CAAA,EAC3B,CAAE,iBAAAC,CAAA,EAAqB,MAAM,QAAA,QAAA,EAAA,KAAA,IAAAC,CAAA,EAC7B,CAAE,eAAAC,CAAA,EAAmB,MAAM,QAAA,QAAA,EAAA,KAAA,IAAAC,CAAA,EAC3B,CAAE,cAAAC,CAAA,EAAkB,MAAM,QAAA,QAAA,EAAA,KAAA,IAAAC,CAAA,EAC1B,CAAE,WAAAC,CAAA,EAAe,MAAM,QAAA,QAAA,EAAA,KAAA,IAAA9C,CAAA,EAE7B,KAAK,SAAS,SAAS,EACvB,KAAK,UAAUkC,CAAU,EACzB,KAAK,UAAUI,CAAc,EAC7B,KAAK,UAAUE,CAAgB,EAC/B,KAAK,UAAUE,CAAc,EAC7B,KAAK,UAAUN,CAAc,EAC7B,KAAK,SAAS,SAAS,EACvB,KAAK,UAAUQ,CAAa,EAC5B,KAAK,UAAUE,CAAU,EAEzB,KAAK,OAAA,CACT,CACJ,CC1BO,MAAMC,CAAe,CAChB,QAA0B,KAC1B,iBAAuC,KACvC,UAER,YAAYC,EAAwB,CAChC,KAAK,UAAYA,EACjB,KAAK,KAAA,CACT,CAEQ,MAAO,CAEX,KAAK,iBAAmB,SAAS,cAAc,KAAK,EACpD,KAAK,iBAAiB,GAAK,iBAC3B,KAAK,iBAAiB,UAAY,iDAClC,KAAK,UAAU,YAAY,KAAK,gBAAgB,EAEhD,KAAK,QAAU,IAAIf,EAAQ,CACvB,UAAW,KAAK,iBAChB,UAAW,GACX,UAAW,MACX,SAAU,gBACV,MAAO,WACP,OAAQ,IAAA,CACX,EAED,KAAK,QAAQ,KAAA,CACjB,CAEO,YAAYhD,EAAoB,CACnC,KAAK,SAAS,SAASA,CAAK,CAChC,CAEO,SAAU,CACb,KAAK,SAAS,OAAA,CAClB,CAEO,SAAU,CACb,KAAK,SAAS,QAAA,EACd,KAAK,QAAU,IACnB,CAGO,SAASW,EAAiBC,EAAwB,CAAE,KAAK,SAAS,SAASD,EAASC,CAAa,EAAG,KAAK,SAAS,OAAA,CAAU,CAC5H,UAAUI,EAAsB,CAAE,KAAK,SAAS,UAAUA,CAAM,EAAG,KAAK,SAAS,OAAA,CAAU,CAC3F,oBAAoBM,EAAY0C,EAAY,CAAE,KAAK,SAAS,uBAAuB1C,EAAI0C,CAAC,CAAG,CAC3F,aAAalB,EAAe,CAAE,KAAK,SAAS,aAAaA,CAAI,CAAG,CAChE,WAAWD,EAAkB,CAC5B,KAAK,mBACL,KAAK,iBAAiB,MAAM,WAAaA,EAAU,UAAY,SAEvE,CACO,mBAAmBE,EAAe,CAAE,KAAK,SAAS,mBAAmBA,CAAK,CAAG,CAC7E,UAAUrC,EAA2B,CAAE,KAAK,SAAS,UAAUA,CAAM,CAAG,CACnF,CCtDO,MAAMuD,CAAmB,CACpB,aAAiC,CAAA,EACjC,UAER,YAAYF,EAAwB,CAChC,KAAK,UAAYA,CACrB,CAKO,OAAO5D,EAAgE,CAE1E,MAAM+D,EAAiB,SAAS,cAAc,KAAK,EACnD,KAAK,UAAU,YAAYA,CAAc,EAEzC,MAAMhD,EAAQ,IAAIhB,EAAe,CAC7B,UAAWgE,EACX,GAAG/D,CAAA,CACN,EAGD,OAAAe,EAAM,KAAA,EACN,KAAK,aAAa,KAAKA,CAAK,EACrBA,CACX,CAEO,YAAYlB,EAAoB,CACnC,KAAK,aAAa,QAAQa,GAAKA,EAAE,SAASb,CAAK,CAAC,CACpD,CAEO,SAAU,CACb,KAAK,aAAa,QAAQa,GAAKA,EAAE,QAAQ,CAC7C,CAEO,SAAU,CACb,KAAK,aAAa,QAAQA,GAAKA,EAAE,SAAS,EAC1C,KAAK,aAAe,CAAA,CACxB,CACJ,CCpCO,MAAMsD,CAAmC,CACpC,QACA,QACA,UACA,OACA,YACA,aAAe,GACf,eAAiB,GACjB,iBAAwC,KACxC,kBAAyC,KAMjD,YAAYhE,EAAwB,CAEhC,KAAK,QAAU,CACX,MAAO,SACP,MAAO,IACP,OAAQ,OACR,SAAU,SACV,UAAW,GACX,UAAW,GACX,SAAU,IACV,UAAW,IACX,GAAGA,CAAA,EAEP,KAAK,UAAYA,EAAQ,UAGzB,KAAK,QAAU,KAAK,UAAA,EACpB,KAAK,OAAS,KAAK,QAAQ,cAAc,oBAAoB,EAC7D,KAAK,YAAc,KAAK,QAAQ,cAAc,qBAAqB,EAGnE,KAAK,KAAA,CACT,CAMO,SAASH,EAAoB,CAChC,MAAMM,EAAQ,KAAK,QAAQ,MACtB,KAAK,QAAQ,mBAAuB,YAAY,kBAAmBN,EAAM,eAAe,EACxF,KAAK,QAAQ,yBAA6B,YAAY,yBAA0BA,EAAM,cAAc,EACpG,KAAK,QAAQ,cAAkB,YAAY,2BAA4BA,EAAM,WAAW,EACxF,KAAK,QAAQ,aAAiB,YAAY,0BAA2BA,EAAM,WAAW,EACtF,KAAK,QAAQ,eAAmB,YAAY,4BAA6BA,EAAM,MAAM,CAC9F,CAKO,MAAO,CACN,KAAK,iBAET,KAAK,UAAU,YAAY,KAAK,OAAO,EAGvC,KAAK,aAAA,EAED,KAAK,QAAQ,WACb,KAAK,SAAA,EAGL,KAAK,QAAQ,WACb,KAAK,WAAA,EAGT,KAAK,eAAiB,GAGlB,KAAK,QAAQ,QACb,KAAK,QAAQ,OAAA,EAIjB,KAAK,iBAAmBC,EAAa,UAAWD,GAAU,CACtD,KAAK,SAASA,CAAK,CACvB,CAAC,EAGD,KAAK,kBAAoBN,EAAc,UAAU,IAAM,CACnD,KAAK,WAAA,CACT,CAAC,EACL,CAEO,YAAmB,CACtB,GAAI,KAAK,QAAQ,MAAO,CACpB,MAAM0E,EAAU,KAAK,OAAO,cAAc,mBAAmB,EACzDA,IACAA,EAAQ,YAAczE,EAAE,KAAK,QAAQ,KAAK,EAElD,CACJ,CAKQ,WAAyB,CAC7B,MAAMS,EAAK,SAAS,cAAc,KAAK,EACvCA,EAAG,UAAY,aAEX,KAAK,QAAQ,KAAIA,EAAG,GAAK,KAAK,QAAQ,IAG1C,MAAME,EAAQF,EAAG,MACb,KAAK,QAAQ,iBAAiBE,EAAM,YAAY,kBAAmB,KAAK,QAAQ,eAAe,EAC/F,KAAK,QAAQ,uBAAuBA,EAAM,YAAY,yBAA0B,KAAK,QAAQ,qBAAqB,EAClH,KAAK,QAAQ,YAAYA,EAAM,YAAY,2BAA4B,KAAK,QAAQ,UAAU,EAC9F,KAAK,QAAQ,WAAWA,EAAM,YAAY,0BAA2B,KAAK,QAAQ,SAAS,EAC3F,KAAK,QAAQ,aAAaA,EAAM,YAAY,4BAA6B,KAAK,QAAQ,WAAW,EAGrG,KAAK,QAAQF,EAAI,KAAK,QAAQ,MAAO,KAAK,QAAQ,MAAM,EAGxD,MAAMiE,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,oBACf,KAAK,QAAQ,WAAWA,EAAO,UAAU,IAAI,WAAW,EAE5D,MAAMC,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,mBAClBA,EAAM,YAAc,KAAK,QAAQ,MAAQ3E,EAAE,KAAK,QAAQ,KAAK,EAAI,GAEjE,MAAM4E,EAAW,SAAS,cAAc,MAAM,EAC9CA,EAAS,UAAY,mBACrBA,EAAS,UAAY,UACrBA,EAAS,QAAU,IAAM,KAAK,MAAA,EAE9BF,EAAO,YAAYC,CAAK,EACxBD,EAAO,YAAYE,CAAQ,EAG3B,MAAMC,EAAU,SAAS,cAAc,KAAK,EAY5C,GAXAA,EAAQ,UAAY,qBAChB,OAAO,KAAK,QAAQ,SAAY,SAChCA,EAAQ,UAAY,KAAK,QAAQ,QAC1B,KAAK,QAAQ,mBAAmB,aACvCA,EAAQ,YAAY,KAAK,QAAQ,OAAO,EAG5CpE,EAAG,YAAYiE,CAAM,EACrBjE,EAAG,YAAYoE,CAAO,EAGlB,KAAK,QAAQ,UAAW,CACxB,MAAMC,EAAe,SAAS,cAAc,KAAK,EACjDA,EAAa,UAAY,2BACzBrE,EAAG,YAAYqE,CAAY,CAC/B,CAEA,OAAOrE,CACX,CAKQ,QAAQA,EAAiBsE,EAAyBC,EAA0B,CAC5ED,IAAU,SACVtE,EAAG,MAAM,MAAQ,OAAOsE,GAAU,SAAW,GAAGA,CAAK,KAAOA,GAE5DC,IAAW,SACXvE,EAAG,MAAM,OAAS,OAAOuE,GAAW,SAAW,GAAGA,CAAM,KAAOA,EAEvE,CAKQ,cAAe,CACnB,MAAMtE,EAAM,KAAK,QAAQ,SAEnBuE,EAAS,KAAK,QAAQ,sBAAA,EAG5B,IAAIC,EAAO,EACPC,EAAM,EAEV,MAAMC,EAAK,KAAK,UAAU,YACpBC,EAAK,KAAK,UAAU,aACpBC,EAAML,EAAO,MACbM,EAAMN,EAAO,OAEnB,GAAI,OAAOvE,GAAQ,UAAY,MAAOA,EAClCwE,EAAOxE,EAAI,EACXyE,EAAMzE,EAAI,MAEV,QAAQA,EAAA,CACJ,IAAK,SACDwE,GAAQE,EAAKE,GAAO,EACpBH,GAAOE,EAAKE,GAAO,EACnB,MACJ,IAAK,WAAYL,EAAO,EAAGC,EAAM,EAAG,MACpC,IAAK,aAAcD,GAAQE,EAAKE,GAAO,EAAGH,EAAM,EAAG,MACnD,IAAK,YAAaD,EAAOE,EAAKE,EAAKH,EAAM,EAAG,MAC5C,IAAK,cAAeD,EAAO,EAAGC,GAAOE,EAAKE,GAAO,EAAG,MACpD,IAAK,eAAgBL,EAAOE,EAAKE,EAAKH,GAAOE,EAAKE,GAAO,EAAG,MAC5D,IAAK,cAAeL,EAAO,EAAGC,EAAME,EAAKE,EAAK,MAC9C,IAAK,gBAAiBL,GAAQE,EAAKE,GAAO,EAAGH,EAAME,EAAKE,EAAK,MAC7D,IAAK,eAAgBL,EAAOE,EAAKE,EAAKH,EAAME,EAAKE,EAAK,MACtD,QACIL,GAAQE,EAAKE,GAAO,EACpBH,GAAOE,EAAKE,GAAO,CAAA,CAK/BL,EAAO,KAAK,IAAI,EAAG,KAAK,IAAIA,EAAME,EAAKE,CAAG,CAAC,EAC3CH,EAAM,KAAK,IAAI,EAAG,KAAK,IAAIA,EAAKE,EAAKE,CAAG,CAAC,EAEzC,KAAK,QAAQ,MAAM,KAAO,GAAGL,CAAI,KACjC,KAAK,QAAQ,MAAM,IAAM,GAAGC,CAAG,IACnC,CAKQ,UAAW,CACf,IAAIK,EAAS,EACTC,EAAS,EACTC,EAAY,EACZC,EAAW,EAEf,MAAMC,EAAe5C,GAAkB,CACnCA,EAAE,eAAA,EACFwC,EAASxC,EAAE,QACXyC,EAASzC,EAAE,QACX0C,EAAY,KAAK,QAAQ,WACzBC,EAAW,KAAK,QAAQ,UAExB,SAAS,iBAAiB,YAAaE,CAAW,EAClD,SAAS,iBAAiB,UAAWC,CAAS,CAClD,EAEMD,EAAe7C,GAAkB,CACnC,MAAM+C,EAAK/C,EAAE,QAAUwC,EACjBQ,EAAKhD,EAAE,QAAUyC,EAEvB,IAAIQ,EAAUP,EAAYK,EACtBG,EAASP,EAAWK,EAGxB,MAAMG,EAAU,KAAK,UAAU,YAAc,KAAK,QAAQ,YACpDC,EAAS,KAAK,UAAU,aAAe,KAAK,QAAQ,aAE1DH,EAAU,KAAK,IAAI,EAAG,KAAK,IAAIA,EAASE,CAAO,CAAC,EAChDD,EAAS,KAAK,IAAI,EAAG,KAAK,IAAIA,EAAQE,CAAM,CAAC,EAE7C,KAAK,QAAQ,MAAM,KAAO,GAAGH,CAAO,KACpC,KAAK,QAAQ,MAAM,IAAM,GAAGC,CAAM,IACtC,EAEMJ,EAAY,IAAM,CACpB,SAAS,oBAAoB,YAAaD,CAAW,EACrD,SAAS,oBAAoB,UAAWC,CAAS,CACrD,EAEA,KAAK,OAAO,iBAAiB,YAAaF,CAAW,CACzD,CAKQ,YAAa,CACjB,MAAMS,EAAS,KAAK,QAAQ,cAAc,2BAA2B,EACrE,GAAI,CAACA,EAAQ,OAEb,IAAIb,EAAS,EACTC,EAAS,EACTa,EAAS,EACTC,EAAS,EAEb,MAAMX,EAAe5C,GAAkB,CACnCA,EAAE,eAAA,EACFA,EAAE,gBAAA,EACFwC,EAASxC,EAAE,QACXyC,EAASzC,EAAE,QACXsD,EAAS,KAAK,QAAQ,YACtBC,EAAS,KAAK,QAAQ,aAEtB,SAAS,iBAAiB,YAAaV,CAAW,EAClD,SAAS,iBAAiB,UAAWC,CAAS,CAClD,EAEMD,EAAe7C,GAAkB,CACnC,MAAM+C,EAAK/C,EAAE,QAAUwC,EACjBQ,EAAKhD,EAAE,QAAUyC,EAEjBe,EAAO,KAAK,IAAI,KAAK,QAAQ,UAAY,IAAKF,EAASP,CAAE,EACzDU,EAAO,KAAK,IAAI,KAAK,QAAQ,WAAa,GAAIF,EAASP,CAAE,EAE/D,KAAK,QAAQ,MAAM,MAAQ,GAAGQ,CAAI,KAClC,KAAK,QAAQ,MAAM,OAAS,GAAGC,CAAI,IACvC,EAEMX,EAAY,IAAM,CACpB,SAAS,oBAAoB,YAAaD,CAAW,EACrD,SAAS,oBAAoB,UAAWC,CAAS,CACrD,EAEAO,EAAO,iBAAiB,YAAaT,CAAW,CACpD,CAMO,WAAWf,EAA+B,CAC7C,KAAK,YAAY,UAAY,GACzB,OAAOA,GAAY,SACnB,KAAK,YAAY,UAAYA,EAE7B,KAAK,YAAY,YAAYA,CAAO,CAE5C,CAKO,OAAQ,CACP,KAAK,eACL,KAAK,mBACL,KAAK,iBAAA,EACL,KAAK,iBAAmB,MAExB,KAAK,oBACL,KAAK,kBAAA,EACL,KAAK,kBAAoB,MAE7B,KAAK,QAAQ,OAAA,EACb,KAAK,aAAe,GAChB,KAAK,QAAQ,SACb,KAAK,QAAQ,QAAA,EAErB,CAKO,SAAU,CACb,KAAK,MAAA,CACT,CACJ,CC7VO,MAAM6B,UAAsBlC,CAAU,CAKzC,YAAYJ,EAAwB,CAEhC,MAAMuC,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,UAAY,0BAEtB,MAAMC,EAAY,SAAS,cAAc,IAAI,EAC7CA,EAAU,YAAc,oBAExB,MAAMC,EAAW,SAAS,cAAc,IAAI,EAC5CA,EAAS,UAAY;AAAA;AAAA;AAAA,yCAGY,IAAI,OAAO,oBAAoB;AAAA;AAAA,UAIhE,MAAMC,EAAY,SAAS,cAAc,QAAQ,EACjDA,EAAU,YAAc,gBACxBA,EAAU,MAAM,UAAY,OAC5BA,EAAU,QAAU,IAAM,CACtB,MAAM,iBAAiB,CAC3B,EAEAH,EAAU,YAAYC,CAAS,EAC/BD,EAAU,YAAYE,CAAQ,EAC9BF,EAAU,YAAYG,CAAS,EAG/B,MAAM,CACF,UAAA1C,EACA,MAAO,mBACP,QAASuC,EACT,MAAO,IACP,OAAQ,OACR,SAAU,SACV,UAAW,GACX,UAAW,GAEX,QAAS,IAAM,CACX,QAAQ,IAAI,oBAAoB,CACpC,EACA,OAAQ,IAAM,CACV,QAAQ,IAAI,oBAAoB,CACpC,CAAA,CACH,CAIL,CAIJ,CCtDO,MAAMI,CAAc,CAEf,UAEA,cAA6B,CAAA,EAMrC,YAAY3C,EAAwB,CAChC,KAAK,UAAYA,CACrB,CAOO,OAAO5D,EAAsD,CAChE,MAAMwG,EAAS,IAAIxC,EAAU,CACzB,UAAW,KAAK,UAChB,GAAGhE,EACH,QAAS,IAAM,CAEX,KAAK,cAAgB,KAAK,cAAc,OAAOyG,GAAKA,IAAMD,CAAM,EAC5DxG,EAAQ,SAASA,EAAQ,QAAA,CACjC,CAAA,CACH,EAGD,OAAAwG,EAAO,SAAS1G,EAAa,UAAU,EAEvC,KAAK,cAAc,KAAK0G,CAAM,EACvBA,CACX,CAMO,gBAAiB,CAEpB,IAAIN,EAAc,KAAK,SAAS,CAEpC,CAMO,YAAYrG,EAAoB,CACnC,KAAK,cAAc,QAAQ2G,GAAU,CAC7BA,EAAO,UACPA,EAAO,SAAS3G,CAAK,CAE7B,CAAC,CACL,CACJ,CC3DO,MAAM6G,CAAU,CACX,UACA,QAA8B,KAC9B,aAAoB,KAErB,QAAiC,KACjC,YAAyC,KACzC,OAA+B,KAEtC,IAAW,eAAgB,CAAE,OAAOnH,CAAe,CACnD,IAAW,cAAe,CAAE,OAAOO,CAAc,CAEjD,YAAY8D,EAAiC5D,EAAsD,CAC/F,MAAMC,EAAK,OAAO2D,GAAc,SAAW,SAAS,eAAeA,CAAS,EAAIA,EAChF,GAAI,CAAC3D,EAAI,MAAM,IAAI,MAAM,qBAAqB,EAC9C,KAAK,UAAYA,EAEbD,GAAS,QAAQT,EAAc,UAAUS,EAAQ,MAAM,EACvDA,GAAS,QACLA,EAAQ,QAAU,SAClB,QAAQ,KAAK,kDAAkD,EAE/DF,EAAa,SAASE,EAAQ,KAAK,GAI3C,KAAK,KAAA,CACT,CAEO,UAAUhB,EAAoB,CAAEO,EAAc,UAAUP,CAAM,CAAG,CACjE,WAAwB,CAAE,OAAOO,EAAc,UAAA,CAAa,CAC5D,SAASM,EAAyB,CAAEC,EAAa,SAASD,CAAK,CAAG,CAClE,eAAeA,EAAoB,CAAEC,EAAa,eAAeD,CAAK,CAAG,CAExE,MAAO,CACX,KAAK,UAAU,UAAY,GAC3B,KAAK,QAAU,SAAS,cAAc,KAAK,EAC3C,KAAK,QAAQ,UAAY,qBACzB,KAAK,UAAU,YAAY,KAAK,OAAO,EAGvC,KAAK,OAAS,IAAI0G,EAAc,KAAK,OAAO,EAC5C,KAAK,QAAU,IAAI5C,EAAe,KAAK,OAAO,EAC9C,KAAK,YAAc,IAAIG,EAAmB,KAAK,OAAO,EAGtD,KAAK,mBAAA,EAGL,KAAK,YAAYhE,EAAa,UAAU,EAGpC,KAAK,cACL,KAAK,aAAa,UAAU,CACxB,gBAAiB,SAAA,CACpB,EAELA,EAAa,UAAWD,GAAU,CAC9B,KAAK,YAAYA,CAAK,CAC1B,CAAC,CACL,CAEQ,oBAAqB,CACpB,KAAK,cAEV,KAAK,aAAe,KAAK,YAAY,OAAO,CACxC,SAAU,WACV,UAAW,SACX,MAAO,WACP,gBAAiB,UACjB,UAAW,EAAA,CACd,EAED,KAAK,aAAa,SAAS,MAAM,EACjC,KAAK,aAAa,UAAU,CACxB,GAAI,WACJ,QAAS,OACT,KAAM,SACN,MAAO,OACP,KAAM,sIACN,QAAS,IAAM,CACX,MAAM,MAAM,CAChB,CAAA,CACH,EAGD,KAAK,aAAa,OAAA,EACtB,CAEQ,YAAYA,EAAoB,CAChC,KAAK,UACL,KAAK,QAAQ,MAAM,gBAAkBA,EAAM,WAC3C,KAAK,QAAQ,MAAM,MAAQA,EAAM,YAEzC,CAEO,SAAU,CACb,KAAK,SAAS,QAAA,EACd,KAAK,aAAa,QAAA,EAClB,KAAK,OAAS,KACd,KAAK,UAAU,UAAY,EAC/B,CACJ,wEC1GwC,CACpC,GAAI,OACJ,QAAS,UACT,KAAM,SACN,MAAO,eACP,KAAM,uHACN,WAAY,GACZ,QAAUmB,GAAW,CACjB,QAAQ,IAAI,WAAYA,EAAO,EAAE,CACrC,CACJ,8GCV4C,CACxC,GAAI,WACJ,QAAS,UACT,KAAM,SACN,MAAO,mBACP,KAAM,qOACN,WAAY,GACZ,QAAUA,GAAW,CACjB,QAAQ,IAAI,WAAYA,EAAO,EAAE,CACrC,CACJ,8GCV4C,CACxC,GAAI,OACJ,QAAS,UACT,KAAM,OACN,MAAO,eACP,MAAO,WACP,KAAM,+SACN,WAAY,GACZ,QAAUA,GAAW,CACjB,QAAQ,IAAI,WAAYA,EAAO,EAAE,CACrC,CACJ,gHCd8C,CAC1C,GAAI,cACJ,QAAS,UACT,SAAU,OACV,KAAM,SACN,MAAO,WACP,MAAO,qBACP,KAAM,+SACN,QAAUA,GAAW,CACjB,QAAQ,IAAI,WAAYA,EAAO,EAAE,CACrC,CACJ,8GCX4C,CACxC,GAAI,YACJ,QAAS,UACT,SAAU,OACV,MAAO,WACP,KAAM,SACN,MAAO,mBACP,KAAM,+SACN,QAAUA,GAAW,CACjB,QAAQ,IAAI,WAAYA,EAAO,EAAE,CACrC,CACJ,6GCR2C,CACvC,GAAI,UACJ,QAAS,UACT,KAAM,SACN,MAAO,kBACP,KAAM,+6BACN,WAAY,GACZ,QAAUA,GAAW,CACjB,QAAQ,IAAI,WAAYA,EAAO,EAAE,CACrC,CACJ,0GCVwC,CACpC,GAAI,OACJ,QAAS,UACT,KAAM,SACN,MAAO,eACP,KAAM,sZACN,WAAY,GACZ,QAAUA,GAAW,CACjB,QAAQ,IAAI,WAAYA,EAAO,EAAE,CACrC,CACJ"}
\ No newline at end of file
diff --git a/dist/index.d.ts b/dist/index.d.ts
index 47f5956..71ca9ff 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -1,19 +1,89 @@
+/**
+ * 通用按钮组组件 (BimButtonGroup)
+ */
+export declare class BimButtonGroup implements IBimComponent {
+ private container;
+ private options;
+ private groups;
+ private activeBtnIds;
+ private btnRefs;
+ private dropdownElement;
+ private hoverTimeout;
+ private customColors;
+ private unsubscribeLocale;
+ private unsubscribeTheme;
+ private readonly DEFAULT_ICON;
+ constructor(options: ButtonGroupOptions);
+ private initContainer;
+ private updatePosition;
+ /**
+ * 应用样式到容器
+ */
+ private applyStyles;
+ /**
+ * 设置主题颜色
+ * 只会应用到没有被用户自定义的颜色属性上
+ */
+ setTheme(theme: ThemeConfig): void;
+ /**
+ * 直接设置颜色(强制覆盖)
+ * 设置的颜色会被标记为自定义,后续的 setTheme 不会覆盖它们
+ */
+ setColors(colors: ButtonGroupColors): void;
+ init(): Promise;
+ setLocales(): void;
+ addGroup(groupId: string, beforeGroupId?: string): void;
+ addButton(config: ButtonConfig): void;
+ private findButton;
+ render(): void;
+ private renderGroup;
+ private renderButton;
+ private handleClick;
+ private handleMouseEnter;
+ private handleMouseLeave;
+ private showDropdown;
+ private renderDropdownItem;
+ private closeDropdown;
+ private updateButtonState;
+ private getIcon;
+ updateButtonVisibility(id: string, visible: boolean): void;
+ setShowLabel(show: boolean): void;
+ private updateLabelsVisibility;
+ private findButtonById;
+ setBackgroundColor(color: string): void;
+ private isVisible;
+ destroy(): void;
+}
+
/**
* 通用弹窗组件类
* 支持拖拽、缩放、自定义内容和位置。
*/
-declare class BimDialog {
+declare class BimDialog implements IBimComponent {
private element;
private options;
private container;
private header;
private contentArea;
private _isDestroyed;
+ private _isInitialized;
+ private unsubscribeTheme;
+ private unsubscribeLocale;
/**
* 构造函数
* @param options 弹窗配置选项
*/
constructor(options: DialogOptions);
+ /**
+ * 设置主题
+ * @param theme 全局主题配置
+ */
+ setTheme(theme: ThemeConfig): void;
+ /**
+ * 初始化组件功能 (接口实现)
+ */
+ init(): void;
+ setLocales(): void;
/**
* 创建弹窗的 DOM 结构
*/
@@ -22,10 +92,6 @@ declare class BimDialog {
* 设置元素尺寸
*/
private setSize;
- /**
- * 初始化组件功能
- */
- private init;
/**
* 初始化弹窗位置
*/
@@ -47,85 +113,111 @@ declare class BimDialog {
* 关闭弹窗并销毁
*/
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;
}
-/**
- * 按钮配置接口(用于外部定义按钮)
- */
+export declare class BimEngine {
+ private container;
+ private wrapper;
+ private topLeftGroup;
+ toolbar: ToolbarManager | null;
+ buttonGroup: ButtonGroupManager | null;
+ dialog: DialogManager | null;
+ get localeManager(): LocaleManager;
+ get themeManager(): ThemeManager;
+ constructor(container: HTMLElement | string, options?: {
+ locale?: LocaleType;
+ theme?: ThemeType;
+ });
+ setLocale(locale: LocaleType): void;
+ getLocale(): LocaleType;
+ setTheme(theme: 'dark' | 'light'): void;
+ setCustomTheme(theme: ThemeConfig): void;
+ private init;
+ private createTopLeftGroup;
+ private updateTheme;
+ destroy(): void;
+}
+
+/** 按钮内部文字图标排列 */
+declare type ButtonAlign = 'vertical' | 'horizontal';
+
+/** 按钮配置 */
declare interface ButtonConfig {
- /** 唯一标识 */
id: string;
- /** 按钮类型:普通按钮或菜单按钮 */
type: ButtonType;
- /** 按钮显示文字 */
label: string;
- /** SVG 图标(内联 SVG 字符串) */
icon?: string;
- /** 是否保持激活状态(默认 false) */
keepActive?: boolean;
- /** 是否禁用 */
disabled?: boolean;
- /** 点击回调函数 */
onClick?: (button: OptButton) => void;
- /** 子按钮配置(可选,用于菜单按钮) */
children?: ButtonConfig[];
- /** 所属组ID */
groupId?: string;
- /** 父按钮ID(如果是子按钮,则必填) */
parentId?: string;
+ /** 按钮内部图标文字排列 (默认 vertical,即图标在上) */
+ align?: ButtonAlign;
+ /** 图标大小 (正方形,单位 px,默认 32) */
+ iconSize?: number;
+ /** 按钮最小宽度 (单位 px,默认 50) */
+ minWidth?: number;
+}
+
+export declare interface ButtonGroup {
+ id: string;
+ buttons: OptButton[];
+}
+
+declare interface ButtonGroupColors {
+ backgroundColor?: string;
+ btnBackgroundColor?: string;
+ btnHoverColor?: string;
+ btnActiveColor?: string;
+ iconColor?: string;
+ iconActiveColor?: string;
+ textColor?: string;
+ textActiveColor?: string;
}
/**
- * 按钮组接口
+ * 通用按钮组管理器
+ * 负责创建和管理除底部工具栏以外的其他按钮组。
*/
-export declare interface ButtonGroup {
- /** 组 ID */
- id: string;
- /** 组内按钮列表 */
- buttons: OptButton[];
+declare class ButtonGroupManager {
+ private activeGroups;
+ private container;
+ constructor(container: HTMLElement);
+ /**
+ * 创建一个新的按钮组
+ */
+ create(options: Omit): BimButtonGroup;
+ updateTheme(theme: ThemeConfig): void;
+ refresh(): void;
+ destroy(): void;
+}
+
+export declare interface ButtonGroupOptions extends ButtonGroupColors {
+ container: HTMLElement | string;
+ /** 屏幕位置 (如 top-left) */
+ position?: GroupPosition;
+ /** 按钮组排列方向 (默认 row) */
+ direction?: GroupDirection;
+ /** 按钮内部图标文字排列 (默认 vertical) */
+ align?: ButtonAlign;
+ /** 菜单展开方向 */
+ expand?: ExpandDirection;
+ showLabel?: boolean;
+ visibility?: Record;
+ className?: string;
}
declare type ButtonType = 'button' | 'menu';
-/**
- * 点击事件载荷
- */
export declare interface ClickPayload {
- /** 被点击的按钮对象 */
button: OptButton;
- /** 触发的动作类型 */
action: 'activate' | 'deactivate' | 'trigger';
- /** 当前激活状态 */
isActive?: boolean;
}
@@ -152,6 +244,8 @@ declare interface DialogColors {
declare class DialogManager {
/** 弹窗挂载的父容器 */
private container;
+ /** 活跃的弹窗实例列表 */
+ private activeDialogs;
/**
* 构造函数
* @param container 弹窗挂载的目标容器
@@ -168,6 +262,11 @@ declare class DialogManager {
* 演示如何调用特定的业务弹窗组件
*/
showInfoDialog(): void;
+ /**
+ * 响应全局主题变更
+ * @param theme 全局主题配置
+ */
+ updateTheme(theme: ThemeConfig): void;
}
/**
@@ -196,6 +295,8 @@ declare interface DialogOptions extends DialogColors {
minHeight?: number;
/** 关闭时的回调函数 */
onClose?: () => void;
+ /** 打开时的回调函数 */
+ onOpen?: () => void;
/** 弹窗唯一标识 ID (可选) */
id?: string;
}
@@ -210,239 +311,186 @@ declare type DialogPosition = 'center' | 'top-left' | 'top-center' | 'top-right'
y: number;
};
+/** 二级菜单展开方向 */
+declare type ExpandDirection = 'up' | 'down' | 'left' | 'right';
+
+/** 按钮组排列方向 (Flex-direction) */
+declare type GroupDirection = 'row' | 'column';
+
+/** 弹窗/按钮组位置 */
+declare type GroupPosition = 'center' | 'top-left' | 'top-center' | 'top-right' | 'left-center' | 'right-center' | 'bottom-left' | 'bottom-center' | 'bottom-right' | {
+ x: number;
+ y: number;
+} | 'static';
+
/**
- * 底部操作按钮组组件
- * 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。
+ * BIM 引擎组件通用接口
+ * 所有受引擎管理的 UI 组件都必须实现此接口
*/
-export declare class OptBtnGroups {
- /** 挂载容器 */
- private container;
- /** 组件配置选项 */
- private options;
- /** 按钮组列表,按顺序存储 */
- private groups;
- /** 当前处于激活状态的按钮 ID 集合 */
- private activeBtnIds;
- /** 按钮 DOM 元素的引用映射,方便快速查找 */
- private btnRefs;
- /** 当��显示的下拉菜单元素 */
- private dropdownElement;
- /** 鼠标悬停计时器,用于处理菜单显示的防抖 */
- private hoverTimeout;
- /** 默认图标 SVG */
- private readonly DEFAULT_ICON;
+declare interface IBimComponent {
/**
- * 构造函数
- * @param options 配置选项
+ * 初始化组件
+ * 用于创建 DOM、绑定事件、加载资源等
+ * 支持同步或异步操作
*/
- constructor(options: OptBtnGroupsOptions);
+ init(): void | Promise;
/**
- * 初始化容器
+ * 设置主题
+ * 组件应在此方法中将 ThemeConfig 映射为自身的 CSS 变量或样式
*/
- private initContainer;
+ setTheme(theme: ThemeConfig): void;
/**
- * 应用样式配置到 CSS 变量
+ * 设置语言
*/
- private applyStyles;
+ setLocales(): void;
/**
- * 更新颜色配置
- * @param colors 颜色配置对象
- */
- setColors(colors: ToolbarColors): void;
- /**
- * 添加按钮组
- * @param groupId 组ID
- * @param beforeGroupId 在哪个组之前插入(可选���,不传则插入到最后
- */
- addGroup(groupId: string, beforeGroupId?: string): void;
- /**
- * 添加按钮到指定组
- * @param config 按钮配置(必须包含 groupId,可选包含 parentId)
- */
- addButton(config: ButtonConfig): void;
- /**
- * 递归查找按钮
- */
- private findButton;
- /**
- * 初始化组件,加载默认按钮配置
- */
- init(): Promise;
- /**
- * 渲染整个工具栏
- */
- render(): void;
- /**
- * 渲染单个按钮组
- */
- private renderGroup;
- /**
- * 渲染单个按钮
- */
- private renderButton;
- /**
- * 处理按钮点击事件
- */
- private handleClick;
- /**
- * 处理子菜单项点击事件
- */
- private handleSubClick;
- /**
- * 处理鼠标移入事件(显示菜单)
- */
- private handleMouseEnter;
- /**
- * 处理鼠标移出事件(隐藏菜单)
- */
- private handleMouseLeave;
- /**
- * 显示下拉菜单
- */
- private showDropdown;
- /**
- * 渲染下拉菜单项
- */
- private renderDropdownItem;
- /**
- * 关闭所有下拉菜单
- */
- private closeDropdown;
- /**
- * 更新按钮的激活状态样式
- */
- private updateButtonState;
- /**
- * 获取图标 SVG 字符串
- */
- 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;
- /**
- * 销毁组件,清理资源
+ * 销毁组件
+ * 清理 DOM 事件监听、定时器和引用
*/
destroy(): void;
}
+declare type LocaleChangeListener = (locale: LocaleType) => void;
+
/**
- * OptBtnGroups 配置选项
+ * 语言管理器类
*/
-export declare interface OptBtnGroupsOptions extends ToolbarColors {
- /** 容器元素或 ID */
- container: HTMLElement | string;
- /** 是否显示标签 */
- showLabel?: boolean;
- /** 按钮可见性配置 Map */
- visibility?: Record;
+declare class LocaleManager {
+ private currentLocale;
+ private messages;
+ private listeners;
+ constructor();
+ /**
+ * 获取当前语言
+ */
+ getLocale(): LocaleType;
+ /**
+ * 切换语言
+ */
+ setLocale(locale: LocaleType): void;
+ /**
+ * 翻译核心方法
+ */
+ t(key: string): string;
+ /**
+ * 订阅变更
+ */
+ subscribe(listener: LocaleChangeListener): () => void;
+ private notifyListeners;
}
/**
- * 操作按钮接口(内部使用,继承配置)
+ * 语言代码类型
*/
+declare type LocaleType = 'zh-CN' | 'en-US';
+
export declare interface OptButton extends ButtonConfig {
- /** 内部使用的子按钮列表 */
children?: OptButton[];
}
+declare type ThemeChangeListener = (theme: ThemeConfig) => void;
+
/**
- * 工具栏颜色配置接口
+ * 全局主题配置接口
+ * 定义系统通用的语义化颜色
*/
-declare interface ToolbarColors {
- /** 工具栏背景颜色 */
- backgroundColor?: string;
- /** 按钮默认背景颜色 */
- btnBackgroundColor?: string;
- /** 按钮 Hover 背景颜色 */
- btnHoverColor?: string;
- /** 按钮激活状态背景颜色 */
- btnActiveColor?: string;
+declare interface ThemeConfig {
+ /** 主题名称 */
+ name: string;
+ /** 品牌色/主色 */
+ primary: string;
+ /** 主色悬停/激活态 */
+ primaryHover: string;
+ /** 基础背景色 (应用整体背景) */
+ background: string;
+ /** 面板背景色 (工具栏、弹窗背景) */
+ panelBackground: string;
+ /** 主要文字颜色 */
+ textPrimary: string;
+ /** 次要文字颜色 */
+ textSecondary: string;
+ /** 边框/分割线颜色 */
+ border: string;
/** 图标默认颜色 */
- iconColor?: string;
- /** 图标激活/Hover 颜色 */
- iconActiveColor?: string;
- /** 文字默认颜色 */
- textColor?: string;
- /** 文字激活/Hover 颜色 */
- textActiveColor?: string;
+ icon: string;
+ /** 图标激活颜色 */
+ iconActive: string;
+ /** 交互组件背景 (如按钮默认背景) */
+ componentBackground: string;
+ /** 交互组件悬停背景 */
+ componentHover: string;
+ /** 交互组件激活背景 */
+ componentActive: string;
}
/**
- * 工具栏管理器
- * 负责管理底部操作栏的按钮组、按钮及其可见性等状态。
+ * 主题管理器 (单例)
+ */
+declare class ThemeManager {
+ private currentTheme;
+ private listeners;
+ constructor();
+ /**
+ * 获取当前主题配置
+ */
+ getTheme(): ThemeConfig;
+ /**
+ * 切换预设主题
+ * @param themeName 'dark' | 'light'
+ */
+ setTheme(themeName: 'dark' | 'light'): void;
+ /**
+ * 应用自定义主题配置
+ * @param theme 配置对象
+ */
+ setCustomTheme(theme: ThemeConfig): void;
+ /**
+ * 内部应用主题逻辑
+ */
+ private applyTheme;
+ /**
+ * 订阅主题变更
+ */
+ subscribe(listener: ThemeChangeListener): () => void;
+ private notifyListeners;
+}
+
+/**
+ * 主题类型定义
+ */
+declare type ThemeType = 'dark' | 'light' | 'custom';
+
+/**
+ * 底部工具栏 (Toolbar)
+ * BimButtonGroup 的子类,专门用于加载工具栏默认按钮。
+ */
+export declare class Toolbar extends BimButtonGroup {
+ /**
+ * 重写初始化,加载默认按钮
+ */
+ init(): Promise;
+}
+
+/**
+ * 底部工具栏管理器 (ToolbarManager)
+ * 仅负责管理底部工具栏实例。
*/
declare class ToolbarManager {
- /** 内部工具栏组件实例 */
- private optBtnGroups;
- /** 工具栏挂载的容器 */
+ private toolbar;
+ private toolbarContainer;
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;
- /**
- * 销毁工具栏管理器
- */
+ updateTheme(theme: ThemeConfig): void;
+ refresh(): void;
destroy(): void;
+ addGroup(groupId: string, beforeGroupId?: string): void;
+ addButton(config: ButtonConfig): void;
+ setButtonVisibility(id: string, v: boolean): void;
+ setShowLabel(show: boolean): void;
+ setVisible(visible: boolean): void;
+ setBackgroundColor(color: string): void;
+ setColors(colors: ButtonGroupColors): void;
}
export { }
diff --git a/package-lock.json b/package-lock.json
index 25d6f71..7b11a34 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,7 +17,7 @@
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
@@ -27,7 +27,7 @@
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"dev": true,
"license": "MIT",
@@ -37,7 +37,7 @@
},
"node_modules/@babel/parser": {
"version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
"dev": true,
"license": "MIT",
@@ -53,7 +53,7 @@
},
"node_modules/@babel/types": {
"version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz",
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
"dev": true,
"license": "MIT",
@@ -67,7 +67,7 @@
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
"cpu": [
"ppc64"
@@ -84,7 +84,7 @@
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
"cpu": [
"arm"
@@ -101,7 +101,7 @@
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
"cpu": [
"arm64"
@@ -118,7 +118,7 @@
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
"cpu": [
"x64"
@@ -135,7 +135,7 @@
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
"cpu": [
"arm64"
@@ -152,7 +152,7 @@
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
"cpu": [
"x64"
@@ -169,7 +169,7 @@
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
"cpu": [
"arm64"
@@ -186,7 +186,7 @@
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
"cpu": [
"x64"
@@ -203,7 +203,7 @@
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
"cpu": [
"arm"
@@ -220,7 +220,7 @@
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
"cpu": [
"arm64"
@@ -237,7 +237,7 @@
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
"cpu": [
"ia32"
@@ -254,7 +254,7 @@
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
"cpu": [
"loong64"
@@ -271,7 +271,7 @@
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
"cpu": [
"mips64el"
@@ -288,7 +288,7 @@
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
"cpu": [
"ppc64"
@@ -305,7 +305,7 @@
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
"cpu": [
"riscv64"
@@ -322,7 +322,7 @@
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
"cpu": [
"s390x"
@@ -339,7 +339,7 @@
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
"cpu": [
"x64"
@@ -356,7 +356,7 @@
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
"cpu": [
"arm64"
@@ -373,7 +373,7 @@
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
"cpu": [
"x64"
@@ -390,7 +390,7 @@
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
"cpu": [
"arm64"
@@ -407,7 +407,7 @@
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
"cpu": [
"x64"
@@ -424,7 +424,7 @@
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
"cpu": [
"arm64"
@@ -441,7 +441,7 @@
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
"cpu": [
"x64"
@@ -458,7 +458,7 @@
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
"cpu": [
"arm64"
@@ -475,7 +475,7 @@
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
"cpu": [
"ia32"
@@ -492,7 +492,7 @@
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
"cpu": [
"x64"
@@ -509,7 +509,7 @@
},
"node_modules/@isaacs/balanced-match": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
+ "resolved": "https://registry.npmmirror.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
"dev": true,
"license": "MIT",
@@ -519,7 +519,7 @@
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
"dev": true,
"license": "MIT",
@@ -532,14 +532,14 @@
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@microsoft/api-extractor": {
"version": "7.55.1",
- "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.55.1.tgz",
+ "resolved": "https://registry.npmmirror.com/@microsoft/api-extractor/-/api-extractor-7.55.1.tgz",
"integrity": "sha512-l8Z+8qrLkZFM3HM95Dbpqs6G39fpCa7O5p8A7AkA6hSevxkgwsOlLrEuPv0ADOyj5dI1Af5WVDiwpKG/ya5G3w==",
"dev": true,
"license": "MIT",
@@ -565,7 +565,7 @@
},
"node_modules/@microsoft/api-extractor-model": {
"version": "7.32.1",
- "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.32.1.tgz",
+ "resolved": "https://registry.npmmirror.com/@microsoft/api-extractor-model/-/api-extractor-model-7.32.1.tgz",
"integrity": "sha512-u4yJytMYiUAnhcNQcZDTh/tVtlrzKlyKrQnLOV+4Qr/5gV+cpufWzCYAB1Q23URFqD6z2RoL2UYncM9xJVGNKA==",
"dev": true,
"license": "MIT",
@@ -577,7 +577,7 @@
},
"node_modules/@microsoft/api-extractor/node_modules/typescript": {
"version": "5.8.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.2.tgz",
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"dev": true,
"license": "Apache-2.0",
@@ -591,14 +591,14 @@
},
"node_modules/@microsoft/tsdoc": {
"version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz",
+ "resolved": "https://registry.npmmirror.com/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz",
"integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==",
"dev": true,
"license": "MIT"
},
"node_modules/@microsoft/tsdoc-config": {
"version": "0.18.0",
- "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.0.tgz",
+ "resolved": "https://registry.npmmirror.com/@microsoft/tsdoc-config/-/tsdoc-config-0.18.0.tgz",
"integrity": "sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==",
"dev": true,
"license": "MIT",
@@ -611,7 +611,7 @@
},
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"dev": true,
"license": "MIT",
@@ -634,7 +634,7 @@
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
"integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
"cpu": [
"arm"
@@ -648,7 +648,7 @@
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
"integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
"cpu": [
"arm64"
@@ -662,7 +662,7 @@
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
"integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
"cpu": [
"arm64"
@@ -676,7 +676,7 @@
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
"integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
"cpu": [
"x64"
@@ -690,7 +690,7 @@
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
"integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
"cpu": [
"arm64"
@@ -704,7 +704,7 @@
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
"integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
"cpu": [
"x64"
@@ -718,7 +718,7 @@
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
"integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
"cpu": [
"arm"
@@ -732,7 +732,7 @@
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
"integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
"cpu": [
"arm"
@@ -746,7 +746,7 @@
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
"integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
"cpu": [
"arm64"
@@ -760,7 +760,7 @@
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
"integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
"cpu": [
"arm64"
@@ -774,7 +774,7 @@
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
"integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
"cpu": [
"loong64"
@@ -788,7 +788,7 @@
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
"integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
"cpu": [
"ppc64"
@@ -802,7 +802,7 @@
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
"integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
"cpu": [
"riscv64"
@@ -816,7 +816,7 @@
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
"integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
"cpu": [
"riscv64"
@@ -830,7 +830,7 @@
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
"integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
"cpu": [
"s390x"
@@ -844,7 +844,7 @@
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
"integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
"cpu": [
"x64"
@@ -858,7 +858,7 @@
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
"integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
"cpu": [
"x64"
@@ -872,7 +872,7 @@
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
"integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
"cpu": [
"arm64"
@@ -886,7 +886,7 @@
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
"integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
"cpu": [
"arm64"
@@ -900,7 +900,7 @@
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
"integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
"cpu": [
"ia32"
@@ -914,7 +914,7 @@
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
"integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
"cpu": [
"x64"
@@ -928,7 +928,7 @@
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
"integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
"cpu": [
"x64"
@@ -942,7 +942,7 @@
},
"node_modules/@rushstack/node-core-library": {
"version": "5.19.0",
- "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.19.0.tgz",
+ "resolved": "https://registry.npmmirror.com/@rushstack/node-core-library/-/node-core-library-5.19.0.tgz",
"integrity": "sha512-BxAopbeWBvNJ6VGiUL+5lbJXywTdsnMeOS8j57Cn/xY10r6sV/gbsTlfYKjzVCUBZATX2eRzJHSMCchsMTGN6A==",
"dev": true,
"license": "MIT",
@@ -967,7 +967,7 @@
},
"node_modules/@rushstack/node-core-library/node_modules/ajv": {
"version": "8.13.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
+ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.13.0.tgz",
"integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
"dev": true,
"license": "MIT",
@@ -984,7 +984,7 @@
},
"node_modules/@rushstack/problem-matcher": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.1.1.tgz",
+ "resolved": "https://registry.npmmirror.com/@rushstack/problem-matcher/-/problem-matcher-0.1.1.tgz",
"integrity": "sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==",
"dev": true,
"license": "MIT",
@@ -999,7 +999,7 @@
},
"node_modules/@rushstack/rig-package": {
"version": "0.6.0",
- "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.6.0.tgz",
+ "resolved": "https://registry.npmmirror.com/@rushstack/rig-package/-/rig-package-0.6.0.tgz",
"integrity": "sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==",
"dev": true,
"license": "MIT",
@@ -1010,7 +1010,7 @@
},
"node_modules/@rushstack/terminal": {
"version": "0.19.4",
- "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.19.4.tgz",
+ "resolved": "https://registry.npmmirror.com/@rushstack/terminal/-/terminal-0.19.4.tgz",
"integrity": "sha512-f4XQk02CrKfrMgyOfhYd3qWI944dLC21S4I/LUhrlAP23GTMDNG6EK5effQtFkISwUKCgD9vMBrJZaPSUquxWQ==",
"dev": true,
"license": "MIT",
@@ -1030,7 +1030,7 @@
},
"node_modules/@rushstack/ts-command-line": {
"version": "5.1.4",
- "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.1.4.tgz",
+ "resolved": "https://registry.npmmirror.com/@rushstack/ts-command-line/-/ts-command-line-5.1.4.tgz",
"integrity": "sha512-H0I6VdJ6sOUbktDFpP2VW5N29w8v4hRoNZOQz02vtEi6ZTYL1Ju8u+TcFiFawUDrUsx/5MQTUhd79uwZZVwVlA==",
"dev": true,
"license": "MIT",
@@ -1043,21 +1043,21 @@
},
"node_modules/@types/argparse": {
"version": "1.0.38",
- "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz",
+ "resolved": "https://registry.npmmirror.com/@types/argparse/-/argparse-1.0.38.tgz",
"integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@volar/language-core": {
"version": "2.4.26",
- "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.26.tgz",
+ "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.26.tgz",
"integrity": "sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A==",
"dev": true,
"license": "MIT",
@@ -1067,14 +1067,14 @@
},
"node_modules/@volar/source-map": {
"version": "2.4.26",
- "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.26.tgz",
+ "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.26.tgz",
"integrity": "sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw==",
"dev": true,
"license": "MIT"
},
"node_modules/@volar/typescript": {
"version": "2.4.26",
- "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.26.tgz",
+ "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.26.tgz",
"integrity": "sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA==",
"dev": true,
"license": "MIT",
@@ -1086,7 +1086,7 @@
},
"node_modules/@vue/compiler-core": {
"version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
"integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
"dev": true,
"license": "MIT",
@@ -1100,7 +1100,7 @@
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
"integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
"dev": true,
"license": "MIT",
@@ -1111,7 +1111,7 @@
},
"node_modules/@vue/compiler-vue2": {
"version": "2.7.16",
- "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
"integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
"dev": true,
"license": "MIT",
@@ -1122,7 +1122,7 @@
},
"node_modules/@vue/language-core": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz",
+ "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.0.tgz",
"integrity": "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==",
"dev": true,
"license": "MIT",
@@ -1147,7 +1147,7 @@
},
"node_modules/@vue/language-core/node_modules/minimatch": {
"version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
@@ -1163,14 +1163,14 @@
},
"node_modules/@vue/shared": {
"version": "3.5.25",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz",
+ "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz",
"integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
"dev": true,
"license": "MIT"
},
"node_modules/acorn": {
"version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
@@ -1183,7 +1183,7 @@
},
"node_modules/ajv": {
"version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
+ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"license": "MIT",
@@ -1200,7 +1200,7 @@
},
"node_modules/ajv-draft-04": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
"dev": true,
"license": "MIT",
@@ -1215,7 +1215,7 @@
},
"node_modules/ajv-formats": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz",
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
"dev": true,
"license": "MIT",
@@ -1233,14 +1233,14 @@
},
"node_modules/alien-signals": {
"version": "0.4.14",
- "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.14.tgz",
+ "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-0.4.14.tgz",
"integrity": "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
"version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"license": "MIT",
@@ -1250,14 +1250,14 @@
},
"node_modules/balanced-match": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
@@ -1267,28 +1267,28 @@
},
"node_modules/compare-versions": {
"version": "6.1.1",
- "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz",
+ "resolved": "https://registry.npmmirror.com/compare-versions/-/compare-versions-6.1.1.tgz",
"integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==",
"dev": true,
"license": "MIT"
},
"node_modules/confbox": {
"version": "0.2.2",
- "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
+ "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
"dev": true,
"license": "MIT"
},
"node_modules/de-indent": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
"dev": true,
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
@@ -1306,7 +1306,7 @@
},
"node_modules/diff": {
"version": "8.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz",
+ "resolved": "https://registry.npmmirror.com/diff/-/diff-8.0.2.tgz",
"integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==",
"dev": true,
"license": "BSD-3-Clause",
@@ -1316,7 +1316,7 @@
},
"node_modules/entities": {
"version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
"license": "BSD-2-Clause",
@@ -1329,7 +1329,7 @@
},
"node_modules/esbuild": {
"version": "0.25.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz",
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
"dev": true,
"hasInstallScript": true,
@@ -1371,28 +1371,28 @@
},
"node_modules/estree-walker": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/exsolve": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
+ "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz",
"integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT"
},
"node_modules/fdir": {
"version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
@@ -1410,7 +1410,7 @@
},
"node_modules/fs-extra": {
"version": "11.3.2",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.2.tgz",
"integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
"dev": true,
"license": "MIT",
@@ -1425,7 +1425,7 @@
},
"node_modules/fsevents": {
"version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
@@ -1440,7 +1440,7 @@
},
"node_modules/function-bind": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
@@ -1450,14 +1450,14 @@
},
"node_modules/graceful-fs": {
"version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
@@ -1467,7 +1467,7 @@
},
"node_modules/hasown": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
@@ -1480,7 +1480,7 @@
},
"node_modules/he": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true,
"license": "MIT",
@@ -1490,7 +1490,7 @@
},
"node_modules/import-lazy": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/import-lazy/-/import-lazy-4.0.0.tgz",
"integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
"dev": true,
"license": "MIT",
@@ -1500,7 +1500,7 @@
},
"node_modules/is-core-module": {
"version": "2.16.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
@@ -1516,21 +1516,21 @@
},
"node_modules/jju": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
+ "resolved": "https://registry.npmmirror.com/jju/-/jju-1.4.0.tgz",
"integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"license": "MIT"
},
"node_modules/jsonfile": {
"version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
@@ -1543,14 +1543,14 @@
},
"node_modules/kolorist": {
"version": "1.8.0",
- "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
+ "resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz",
"integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
"dev": true,
"license": "MIT"
},
"node_modules/local-pkg": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz",
"integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
"dev": true,
"license": "MIT",
@@ -1568,14 +1568,14 @@
},
"node_modules/lodash": {
"version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"license": "ISC",
@@ -1588,7 +1588,7 @@
},
"node_modules/magic-string": {
"version": "0.30.21",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT",
@@ -1598,7 +1598,7 @@
},
"node_modules/minimatch": {
"version": "10.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.0.3.tgz",
"integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
"dev": true,
"license": "ISC",
@@ -1614,7 +1614,7 @@
},
"node_modules/mlly": {
"version": "1.8.0",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+ "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz",
"integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
"dev": true,
"license": "MIT",
@@ -1627,14 +1627,14 @@
},
"node_modules/mlly/node_modules/confbox": {
"version": "0.1.8",
- "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+ "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz",
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
"dev": true,
"license": "MIT"
},
"node_modules/mlly/node_modules/pkg-types": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+ "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz",
"integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
"dev": true,
"license": "MIT",
@@ -1646,21 +1646,21 @@
},
"node_modules/ms": {
"version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/muggle-string": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz",
"integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
"dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
@@ -1679,35 +1679,35 @@
},
"node_modules/path-browserify": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
"dev": true,
"license": "MIT"
},
"node_modules/path-parse": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/pathe": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
@@ -1720,7 +1720,7 @@
},
"node_modules/pkg-types": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
+ "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz",
"integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
"dev": true,
"license": "MIT",
@@ -1732,7 +1732,7 @@
},
"node_modules/postcss": {
"version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
@@ -1761,7 +1761,7 @@
},
"node_modules/punycode": {
"version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
@@ -1771,7 +1771,7 @@
},
"node_modules/quansync": {
"version": "0.2.11",
- "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
+ "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz",
"integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
"dev": true,
"funding": [
@@ -1788,7 +1788,7 @@
},
"node_modules/require-from-string": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true,
"license": "MIT",
@@ -1798,7 +1798,7 @@
},
"node_modules/resolve": {
"version": "1.22.11",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz",
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
"dev": true,
"license": "MIT",
@@ -1819,7 +1819,7 @@
},
"node_modules/rollup": {
"version": "4.53.3",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
+ "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz",
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
"dev": true,
"license": "MIT",
@@ -1861,7 +1861,7 @@
},
"node_modules/semver": {
"version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"license": "ISC",
@@ -1877,7 +1877,7 @@
},
"node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
@@ -1887,7 +1887,7 @@
},
"node_modules/source-map-js": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
@@ -1897,14 +1897,14 @@
},
"node_modules/sprintf-js": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/string-argv": {
"version": "0.3.2",
- "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "resolved": "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz",
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
"dev": true,
"license": "MIT",
@@ -1914,7 +1914,7 @@
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"license": "MIT",
@@ -1927,7 +1927,7 @@
},
"node_modules/supports-color": {
"version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
@@ -1943,7 +1943,7 @@
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
@@ -1956,7 +1956,7 @@
},
"node_modules/tinyglobby": {
"version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
@@ -1973,7 +1973,7 @@
},
"node_modules/typescript": {
"version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
@@ -1987,14 +1987,14 @@
},
"node_modules/ufo": {
"version": "1.6.1",
- "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
+ "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz",
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
"dev": true,
"license": "MIT"
},
"node_modules/universalify": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
@@ -2004,7 +2004,7 @@
},
"node_modules/uri-js": {
"version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
@@ -2014,7 +2014,7 @@
},
"node_modules/vite": {
"version": "7.2.6",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.6.tgz",
"integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==",
"dev": true,
"license": "MIT",
@@ -2099,7 +2099,7 @@
},
"node_modules/vite-plugin-dts": {
"version": "4.5.4",
- "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz",
+ "resolved": "https://registry.npmmirror.com/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz",
"integrity": "sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==",
"dev": true,
"license": "MIT",
@@ -2126,14 +2126,14 @@
},
"node_modules/vscode-uri": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
+ "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz",
"integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
"dev": true,
"license": "MIT"
},
"node_modules/yallist": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true,
"license": "ISC"
diff --git a/src/bim-engine.ts b/src/bim-engine.ts
index 31b2e2a..56fb223 100644
--- a/src/bim-engine.ts
+++ b/src/bim-engine.ts
@@ -1,113 +1,112 @@
import './bim-engine.css';
-import { ToolbarManager } from './modules/toolbar-manager';
-import { DialogManager } from './modules/dialog-manager';
+import { ToolbarManager } from './managers/toolbar-manager';
+import { ButtonGroupManager } from './managers/button-group-manager';
+import { DialogManager } from './managers/dialog-manager';
+import { localeManager } from './services/locale';
+import { themeManager } from './services/theme';
+import type { LocaleType } from './locales/types';
+import type { ThemeType, ThemeConfig } from './themes/types';
-/**
- * BimEngine 主类
- * 负责初始化整个应用界面,协调各个子模块(如工具栏、弹窗等)。
- */
export class BimEngine {
- /** 主容器元素 */
private container: HTMLElement;
- /** 内部包装器元素,用于承载所有 UI 组件 */
private wrapper: HTMLElement | null = null;
-
- /** 工具栏管理器实例 */
- public toolbar: ToolbarManager | null = null;
- /** 弹窗管理器实例 */
+ private topLeftGroup: any = null; // 保存左上角按钮组的引用
+
+ public toolbar: ToolbarManager | null = null; // 底部专用
+ public buttonGroup: ButtonGroupManager | null = null; // 通用
public dialog: DialogManager | null = null;
- /**
- * 构造函数
- * @param container 容器元素或容器 ID
- */
- constructor(container: HTMLElement | string) {
+ public get localeManager() { return localeManager; }
+ public get themeManager() { return themeManager; }
+
+ constructor(container: HTMLElement | string, options?: { locale?: LocaleType; theme?: ThemeType }) {
const el = typeof container === 'string' ? document.getElementById(container) : container;
if (!el) throw new Error('Container not found');
this.container = el;
+
+ if (options?.locale) localeManager.setLocale(options.locale);
+ if (options?.theme) {
+ if (options.theme === 'custom') {
+ console.warn('Custom theme should be set via setCustomTheme().');
+ } else {
+ themeManager.setTheme(options.theme);
+ }
+ }
+
this.init();
}
- /**
- * 初始化方法
- * 创建 DOM 结构并初始化各子模块
- */
- private init() {
- // 1. 清空容器可能存在的旧内容
- this.container.innerHTML = '';
+ public setLocale(locale: LocaleType) { localeManager.setLocale(locale); }
+ public getLocale(): LocaleType { return localeManager.getLocale(); }
+ public setTheme(theme: 'dark' | 'light') { themeManager.setTheme(theme); }
+ public setCustomTheme(theme: ThemeConfig) { themeManager.setCustomTheme(theme); }
- // 2. 创建外层容器 div
+ private init() {
+ this.container.innerHTML = '';
this.wrapper = document.createElement('div');
this.wrapper.className = 'bim-engine-wrapper';
+ this.container.appendChild(this.wrapper);
- // 3. 创建标题 h1
- const title = document.createElement('h1');
- title.textContent = 'BimEngine';
- title.className = 'bim-engine-title';
-
- // 4. 创建描述段落 p
- const desc = document.createElement('p');
- desc.textContent = '这是一个使用BIM-ENGINE。';
- desc.className = 'bim-engine-desc';
-
- // 6. 创建操作按钮组容器
- const btnGroupContainer = document.createElement('div');
- btnGroupContainer.id = 'opt-btn-groups';
- btnGroupContainer.className = 'bim-engine-opt-btn-container';
-
- // 7. 组装元素
- this.wrapper.appendChild(title);
- this.wrapper.appendChild(desc);
-
// 初始化管理器
this.dialog = new DialogManager(this.wrapper);
- this.toolbar = new ToolbarManager(btnGroupContainer);
+ this.toolbar = new ToolbarManager(this.wrapper);
+ this.buttonGroup = new ButtonGroupManager(this.wrapper);
- // 5. 测试按钮(更新为使用管理器)
- // 5.1 创建普通测试弹窗按钮
- const dialogBtn = document.createElement('button');
- dialogBtn.textContent = '打开测试弹窗';
- dialogBtn.className = 'bim-engine-btn';
- dialogBtn.onclick = () => {
- this.dialog?.create({
- title: '测试弹窗',
- content: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',
- width: 300,
- height: 400,
- position: 'top-left',
- draggable: true,
- resizable: true
+ // --- 创建左上角按钮组 (需求 1 & 2) ---
+ this.createTopLeftGroup();
+
+ // 初始主题
+ this.updateTheme(themeManager.getTheme());
+
+ // 在主题更新后,设置左上角按钮组的自定义颜色
+ if (this.topLeftGroup) {
+ this.topLeftGroup.setColors({
+ backgroundColor: '#ff00ff'
});
- };
-
- // 5.2 创建二次封装信息弹窗按钮
- const infoDialogBtn = document.createElement('button');
- infoDialogBtn.textContent = '打开信息弹窗 (封装版)';
- infoDialogBtn.className = 'bim-engine-btn';
- infoDialogBtn.style.marginLeft = '10px';
- infoDialogBtn.onclick = () => {
- this.dialog?.showInfoDialog();
- };
-
- // 将按钮和工具栏容器添加到包装器中
- this.wrapper.appendChild(dialogBtn);
- this.wrapper.appendChild(infoDialogBtn);
- this.wrapper.appendChild(btnGroupContainer);
-
- // 8. 将包装器挂载到主容器
- this.container.appendChild(this.wrapper);
+ }
+ themeManager.subscribe((theme) => {
+ this.updateTheme(theme);
+ });
}
- /**
- * 销毁实例
- * 清理所有资源和 DOM 元素
- */
- public destroy() {
- if (this.toolbar) {
- this.toolbar.destroy();
- this.toolbar = null;
+ private createTopLeftGroup() {
+ if (!this.buttonGroup) return;
+
+ this.topLeftGroup = this.buttonGroup.create({
+ position: 'top-left',
+ direction: 'column',
+ align: 'vertical',
+ backgroundColor: '#ff00ff', // 自定义背景色,不会被主题覆盖
+ showLabel: false
+ });
+
+ this.topLeftGroup.addGroup('main');
+ this.topLeftGroup.addButton({
+ id: 'menu-btn',
+ groupId: 'main',
+ type: 'button',
+ label: 'Menu', // 应该用 translation key
+ icon: ' ',
+ onClick: () => {
+ alert("点击按钮")
+ }
+ });
+
+ // 手动 render 一次以显示
+ this.topLeftGroup.render();
+ }
+
+ private updateTheme(theme: ThemeConfig) {
+ if (this.wrapper) {
+ this.wrapper.style.backgroundColor = theme.background;
+ this.wrapper.style.color = theme.textPrimary;
}
+ }
+
+ public destroy() {
+ this.toolbar?.destroy();
+ this.buttonGroup?.destroy();
this.dialog = null;
this.container.innerHTML = '';
}
-}
\ No newline at end of file
+}
diff --git a/src/components/button-group/index.css b/src/components/button-group/index.css
new file mode 100644
index 0000000..889bd8e
--- /dev/null
+++ b/src/components/button-group/index.css
@@ -0,0 +1,291 @@
+/* 根容器 */
+.bim-btn-group-root {
+ display: flex;
+ gap: 8px;
+ z-index: 1000;
+ position: absolute;
+ pointer-events: auto;
+}
+
+.bim-btn-group-root.static {
+ position: relative;
+ top: auto;
+ left: auto;
+ bottom: auto;
+ right: auto;
+ transform: none;
+}
+
+.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;
+ gap: 4px;
+ background-color: var(--bim-btn-group-section-bg, rgba(17, 17, 17, 0.88));
+ border-radius: 6px;
+ padding: 4px;
+}
+
+.bim-btn-group-root.dir-row .bim-btn-group-section {
+ flex-direction: row;
+ align-items: center;
+}
+
+.bim-btn-group-root.dir-column .bim-btn-group-section {
+ flex-direction: column;
+}
+
+/* 按钮外层 */
+.opt-btn-wrapper {
+ position: relative;
+}
+
+/* 按钮本体 */
+.opt-btn {
+ display: flex;
+ cursor: pointer;
+ border-radius: 4px;
+ transition: background-color 0.2s, color 0.2s;
+ color: var(--bim-btn-text-color, #ccc);
+ background-color: var(--bim-btn-bg, transparent);
+ padding: 6px;
+ align-items: center;
+ position: relative;
+ /* 为绝对定位提供锚点 */
+ justify-content: center;
+}
+
+.opt-btn:hover {
+ background-color: var(--bim-btn-hover-bg, #444);
+}
+
+.opt-btn.active {
+ background-color: var(--bim-btn-active-bg, rgba(255, 255, 255, 0.15));
+ color: var(--bim-btn-text-active-color, #fff);
+}
+
+.opt-btn.active .opt-btn-icon {
+ color: var(--bim-icon-active-color, #fff);
+}
+
+.opt-btn.disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+/* --- 图标 --- */
+.opt-btn-icon {
+ width: var(--bim-icon-size, 24px);
+ height: var(--bim-icon-size, 24px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--bim-icon-color, #ccc);
+ flex-shrink: 0;
+}
+
+.opt-btn-icon svg {
+ width: 100%;
+ height: 100%;
+ fill: currentColor;
+}
+
+/* --- 箭头 --- */
+.opt-btn-arrow {
+ font-size: 10px;
+ opacity: 0.6;
+ transition: transform 0.2s;
+ display: inline-block;
+ /* 默认情况 (有Label) */
+ margin-left: 4px;
+}
+
+.opt-btn-arrow.rotated {
+ transform: rotate(180deg);
+}
+
+/* --- 文字容器 --- */
+.opt-btn-text-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+}
+
+/* --- Label 显示控制 --- */
+.opt-btn-label {
+ display: inline;
+}
+
+.opt-btn.no-label .opt-btn-label {
+ display: none;
+}
+
+/* --- 场景 A: 有 Label (常规布局) --- */
+
+.opt-btn.align-vertical:not(.no-label) {
+ flex-direction: column;
+ text-align: center;
+}
+
+.opt-btn.align-vertical:not(.no-label) .opt-btn-text-wrapper {
+ margin-top: 4px;
+}
+
+.opt-btn.align-vertical:not(.no-label) .opt-btn-label {
+ font-size: 12px;
+ line-height: 1.2;
+}
+
+.opt-btn.align-horizontal:not(.no-label) {
+ flex-direction: row;
+}
+
+.opt-btn.align-horizontal:not(.no-label) .opt-btn-text-wrapper {
+ margin-left: 8px;
+}
+
+.opt-btn.align-horizontal:not(.no-label) .opt-btn-label {
+ font-size: 14px;
+}
+
+/* --- 场景 B: 无 Label (强制绝对定位) --- */
+
+/* 当没有 label 时,text-wrapper 其实只包裹了 arrow */
+/* 我们需要让 wrapper 失去布局影响,直接定位内部的 arrow */
+
+.opt-btn.no-label .opt-btn-text-wrapper {
+ /* 让 wrapper 变为 0 尺寸,不影响 flex 布局 */
+ width: 0;
+ height: 0;
+ margin: 0;
+ padding: 0;
+ overflow: visible;
+ /* 关键:允许子元素溢出 */
+ position: absolute;
+ /* 脱离文档流,相对于 .opt-btn */
+ top: 0;
+ right: 0;
+}
+
+.opt-btn.no-label .opt-btn-arrow {
+ position: absolute;
+ top: 2px;
+ right: 2px;
+ margin: 0;
+ /* 清除之前的 margin */
+ font-size: 8px;
+ /*
+ 如果父级 wrapper 已经是 top:0, right:0
+ 那么 arrow 相对 wrapper 定位即可
+ */
+}
+
+/* --- 下拉菜单 & 动画 --- */
+.opt-btn-dropdown {
+ position: absolute;
+ background-color: var(--bim-toolbar-bg, rgba(17, 17, 17, 0.95));
+ border-radius: 4px;
+ padding: 4px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+ z-index: 1001;
+ display: flex;
+ flex-direction: column;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+
+ /* 动画起始状态 */
+ opacity: 0;
+ visibility: hidden;
+ transform: translateY(-10px);
+ /* 默认向上偏移一点 */
+ transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.2, 0, 0.2, 1), visibility 0.2s;
+}
+
+/* 动画激活状态 (需要 JS 添加 .show 类,或者直接在 display:flex 时生效?) */
+/* 这里的实现有点 tricky,因为 JS 里直接 appendChild */
+/* 我们可以利用 CSS 动画关键帧,或者简单的 transition */
+/* 由于 DOM 是动态插入的,插入瞬间是 opacity: 0 -> requestAnimationFrame -> opacity: 1 */
+/* 简单的办法:添加动画 keyframes */
+
+@keyframes dropdown-fade-in {
+ from {
+ opacity: 0;
+ transform: translateY(-8px) scale(0.98);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+.opt-btn-dropdown {
+ /* 覆盖上面的 transition,直接用 animation */
+ animation: dropdown-fade-in 0.2s cubic-bezier(0.2, 0, 0.2, 1) forwards;
+ /* 初始可见性由 JS 控制(append 即显示) */
+ opacity: 1;
+ visibility: visible;
+ transform: none;
+}
+
+.opt-btn-dropdown-item {
+ display: flex;
+ align-items: center;
+ padding: 8px 12px;
+ cursor: pointer;
+ border-radius: 4px;
+ color: var(--bim-btn-text-color, #ccc);
+ transition: background 0.2s;
+ box-sizing: border-box;
+}
+
+.opt-btn-dropdown-item:hover {
+ background-color: var(--bim-btn-hover-bg, #444);
+ color: #fff;
+}
+
+/* 下拉菜单项 - 横向布局(图标在左,默认) */
+.opt-btn-dropdown-item.align-horizontal {
+ flex-direction: row;
+}
+
+.opt-btn-dropdown-item.align-horizontal .opt-btn-icon {
+ width: 18px;
+ height: 18px;
+ margin-right: 8px;
+}
+
+/* 下拉菜单项 - 纵向布局(图标在上) */
+.opt-btn-dropdown-item.align-vertical {
+ flex-direction: column;
+ text-align: center;
+}
+
+.opt-btn-dropdown-item.align-vertical .opt-btn-icon {
+ width: 24px;
+ height: 24px;
+ margin-bottom: 4px;
+}
+
+.opt-btn-dropdown-item.align-vertical .opt-btn-dropdown-label {
+ font-size: 12px;
+}
+
+/* --- 特定样式覆盖:底部工具栏 --- */
+.bim-btn-group-root.is-bottom-toolbar .opt-btn-icon {
+ width: 32px;
+ height: 32px;
+}
+
+.bim-btn-group-root.is-bottom-toolbar .opt-btn {
+ padding: 8px;
+}
\ No newline at end of file
diff --git a/src/components/button-group/index.ts b/src/components/button-group/index.ts
new file mode 100644
index 0000000..3579d3b
--- /dev/null
+++ b/src/components/button-group/index.ts
@@ -0,0 +1,533 @@
+import './index.css';
+import type {
+ OptButton,
+ ButtonGroup,
+ ButtonGroupOptions,
+ ButtonConfig,
+ ButtonGroupColors
+} from './index.type';
+import { t, localeManager } from '../../services/locale';
+import { themeManager } from '../../services/theme';
+import type { ThemeConfig } from '../../themes/types';
+import { IBimComponent } from '../../types/component';
+
+/**
+ * 通用按钮组组件 (BimButtonGroup)
+ */
+export class BimButtonGroup implements IBimComponent {
+ private container: HTMLElement;
+ private options: ButtonGroupOptions;
+ private groups: ButtonGroup[] = [];
+ private activeBtnIds: Set = new Set();
+ private btnRefs: Map = new Map();
+ private dropdownElement: HTMLElement | null = null;
+ private hoverTimeout: number | null = null;
+ private customColors: Set = new Set(); // 记录用户自定义的颜色属性
+ private unsubscribeLocale: (() => void) | null = null;
+ private unsubscribeTheme: (() => void) | null = null;
+
+ private readonly DEFAULT_ICON = ' ';
+
+ constructor(options: ButtonGroupOptions) {
+ const el = typeof options.container === 'string'
+ ? document.getElementById(options.container)
+ : options.container;
+
+ if (!el) throw new Error('Container not found');
+
+ this.container = el;
+ // 合并默认配置
+ this.options = {
+ showLabel: true,
+ visibility: {},
+ direction: 'row', // 默认横向
+ position: 'static', // 默认静态定位
+ align: 'vertical', // 默认图标在上
+ expand: 'down', // 默认向下展开
+ ...options
+ };
+
+ // 记录初始传入的自定义颜色
+ const colorKeys: (keyof ButtonGroupColors)[] = [
+ 'backgroundColor', 'btnBackgroundColor', 'btnHoverColor',
+ 'btnActiveColor', 'iconColor', 'iconActiveColor',
+ 'textColor', 'textActiveColor'
+ ];
+ colorKeys.forEach(key => {
+ if (options[key]) {
+ this.customColors.add(key);
+ }
+ });
+
+ this.initContainer();
+ this.applyStyles();
+ }
+
+ private initContainer(): void {
+ this.container.innerHTML = '';
+ this.container.classList.add('bim-btn-group-root');
+
+ if (this.options.direction === 'column') {
+ this.container.classList.add('dir-column');
+ } else {
+ this.container.classList.add('dir-row');
+ }
+
+ if (this.options.className) {
+ this.container.classList.add(this.options.className);
+ }
+
+ this.updatePosition();
+ }
+
+ private updatePosition() {
+ const pos = this.options.position;
+ const style = this.container.style;
+
+ style.top = ''; style.bottom = ''; style.left = ''; style.right = ''; style.transform = '';
+
+ if (pos === 'static') {
+ this.container.classList.add('static');
+ return;
+ }
+
+ this.container.classList.remove('static');
+ this.container.style.position = 'absolute';
+
+ if (typeof pos === 'object' && 'x' in pos) {
+ style.left = `${pos.x}px`;
+ style.top = `${pos.y}px`;
+ } else {
+ const margin = '20px';
+ switch (pos) {
+ case 'top-left':
+ style.top = margin; style.left = margin;
+ break;
+ case 'top-center':
+ style.top = margin; style.left = '50%'; style.transform = 'translateX(-50%)';
+ break;
+ case 'top-right':
+ style.top = margin; style.right = margin;
+ break;
+ case 'bottom-left':
+ style.bottom = margin; style.left = margin;
+ break;
+ case 'bottom-center':
+ style.bottom = margin; style.left = '50%'; style.transform = 'translateX(-50%)';
+ break;
+ case 'bottom-right':
+ style.bottom = margin; style.right = margin;
+ break;
+ case 'left-center':
+ style.left = margin; style.top = '50%'; style.transform = 'translateY(-50%)';
+ break;
+ case 'right-center':
+ style.right = margin; style.top = '50%'; style.transform = 'translateY(-50%)';
+ break;
+ case 'center':
+ style.top = '50%'; style.left = '50%'; style.transform = 'translate(-50%, -50%)';
+ break;
+ }
+ }
+ }
+
+ /**
+ * 应用样式到容器
+ */
+ private applyStyles(): void {
+ const style = this.container.style;
+ if (this.options.backgroundColor) style.setProperty('--bim-btn-group-section-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);
+ }
+
+ /**
+ * 设置主题颜色
+ * 只会应用到没有被用户自定义的颜色属性上
+ */
+ public setTheme(theme: ThemeConfig): void {
+ const themeColors: ButtonGroupColors = {
+ backgroundColor: theme.panelBackground,
+ btnBackgroundColor: theme.componentBackground,
+ btnHoverColor: theme.componentHover,
+ btnActiveColor: theme.componentActive,
+ iconColor: theme.icon,
+ iconActiveColor: theme.iconActive,
+ textColor: theme.textSecondary,
+ textActiveColor: theme.textPrimary
+ };
+
+ // 只应用没有被自定义的颜色
+ Object.entries(themeColors).forEach(([key, value]) => {
+ const colorKey = key as keyof ButtonGroupColors;
+ if (!this.customColors.has(colorKey)) {
+ this.options[colorKey] = value;
+ }
+ });
+
+ this.applyStyles();
+ }
+
+ /**
+ * 直接设置颜色(强制覆盖)
+ * 设置的颜色会被标记为自定义,后续的 setTheme 不会覆盖它们
+ */
+ public setColors(colors: ButtonGroupColors): void {
+ // 更新 options
+ this.options = { ...this.options, ...colors };
+
+ // 标记这些颜色为自定义
+ Object.keys(colors).forEach(key => {
+ this.customColors.add(key as keyof ButtonGroupColors);
+ });
+
+ this.applyStyles();
+ }
+
+ public async init(): Promise {
+ this.render();
+
+ // 自动订阅语言变更
+ this.unsubscribeLocale = localeManager.subscribe(() => {
+ this.setLocales();
+ });
+
+ // 自动订阅主题变更
+ this.unsubscribeTheme = themeManager.subscribe((theme) => {
+ this.setTheme(theme);
+ });
+ }
+
+ public setLocales(): void {
+ this.render();
+ }
+
+ public addGroup(groupId: string, beforeGroupId?: string): void {
+ if (this.groups.some(g => g.id === groupId)) return;
+ const newGroup: ButtonGroup = { id: groupId, buttons: [] };
+ if (beforeGroupId) {
+ const index = this.groups.findIndex(g => g.id === beforeGroupId);
+ index !== -1 ? this.groups.splice(index, 0, newGroup) : this.groups.push(newGroup);
+ } else {
+ this.groups.push(newGroup);
+ }
+ }
+
+ public addButton(config: ButtonConfig): void {
+ const { groupId, parentId } = config;
+ const group = this.groups.find(g => g.id === groupId);
+ if (!group) return;
+
+ const button: OptButton = { ...config, children: config.children || [] };
+ if (parentId) {
+ const parentBtn = this.findButton(group.buttons, parentId);
+ if (parentBtn) {
+ if (!parentBtn.children) parentBtn.children = [];
+ parentBtn.children.push(button);
+ }
+ } else {
+ group.buttons.push(button);
+ }
+ }
+
+ private findButton(buttons: OptButton[], id: string): OptButton | undefined {
+ for (const btn of buttons) {
+ if (btn.id === id) return btn;
+ if (btn.children) {
+ const found = this.findButton(btn.children, id);
+ if (found) return found;
+ }
+ }
+ return undefined;
+ }
+
+ public render(): void {
+ this.container.innerHTML = '';
+ this.btnRefs.clear();
+
+ this.groups.forEach((group, index) => {
+ const groupElement = this.renderGroup(group, index, this.groups.length);
+ this.container.appendChild(groupElement);
+ });
+ }
+
+ private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {
+ const groupEl = document.createElement('div');
+ groupEl.className = 'bim-btn-group-section';
+
+ if (index < total - 1) {
+ groupEl.classList.add('has-divider');
+ }
+
+ group.buttons.forEach(button => {
+ if (this.isVisible(button.id)) {
+ const btnWrapper = this.renderButton(button);
+ groupEl.appendChild(btnWrapper);
+ }
+ });
+ return groupEl;
+ }
+
+ private renderButton(button: OptButton): HTMLElement {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'opt-btn-wrapper';
+
+ const btnEl = document.createElement('div');
+ btnEl.className = 'opt-btn';
+
+ // 按钮优先使用自己的 align,否则使用全局配置,默认为 vertical
+ const align = button.align || this.options.align || 'vertical';
+ if (align === 'horizontal') {
+ btnEl.classList.add('align-horizontal');
+ } else {
+ btnEl.classList.add('align-vertical');
+ }
+
+ if (this.activeBtnIds.has(button.id)) btnEl.classList.add('active');
+ if (button.disabled) btnEl.classList.add('disabled');
+
+ // 判断是否显示 label
+ const hasLabel = this.options.showLabel && button.label;
+ if (!hasLabel) {
+ btnEl.classList.add('no-label');
+ }
+
+ // 应用按钮的自定义样式
+ const iconSize = button.iconSize || 32;
+ const minWidth = button.minWidth || 50;
+ btnEl.style.minWidth = `${minWidth}px`;
+
+ const icon = document.createElement('div');
+ icon.className = 'opt-btn-icon';
+ icon.style.width = `${iconSize}px`;
+ icon.style.height = `${iconSize}px`;
+ icon.innerHTML = this.getIcon(button.icon);
+ btnEl.appendChild(icon);
+
+ // 创建文字和箭头的容器,确保它们始终在一起(无论主轴是横是竖)
+ const textWrapper = document.createElement('div');
+ textWrapper.className = 'opt-btn-text-wrapper';
+
+ if (this.options.showLabel && button.label) {
+ const label = document.createElement('span');
+ label.className = 'opt-btn-label';
+ label.textContent = t(button.label);
+ textWrapper.appendChild(label);
+ }
+
+ if (button.children && button.children.length > 0) {
+ const arrow = document.createElement('span');
+ arrow.className = 'opt-btn-arrow';
+ arrow.textContent = '▼';
+ textWrapper.appendChild(arrow);
+ }
+
+ // 只有当有内容时才添加 wrapper
+ if (textWrapper.hasChildNodes()) {
+ btnEl.appendChild(textWrapper);
+ }
+
+ btnEl.addEventListener('click', () => this.handleClick(button));
+ btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));
+ btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());
+
+ this.btnRefs.set(button.id, btnEl);
+ wrapper.appendChild(btnEl);
+ return wrapper;
+ }
+
+ private handleClick(button: OptButton): void {
+ if (button.disabled) return;
+ if (!button.children || button.children.length === 0) {
+ if (button.keepActive) {
+ const wasActive = this.activeBtnIds.has(button.id);
+ if (wasActive) this.activeBtnIds.delete(button.id);
+ else this.activeBtnIds.add(button.id);
+ this.updateButtonState(button.id);
+ }
+ this.closeDropdown();
+ if (button.onClick) button.onClick(button);
+ }
+ }
+
+ private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {
+ if (this.hoverTimeout) clearTimeout(this.hoverTimeout);
+ if (button.children && button.children.length > 0) {
+ this.showDropdown(button, btnEl);
+ } else {
+ this.closeDropdown();
+ }
+ }
+
+ private handleMouseLeave(): void {
+ this.hoverTimeout = window.setTimeout(() => this.closeDropdown(), 200);
+ }
+
+ private showDropdown(button: OptButton, btnEl: HTMLElement): void {
+ this.closeDropdown();
+ if (!button.children) return;
+
+ const dropdown = document.createElement('div');
+ dropdown.className = 'opt-btn-dropdown';
+ if (this.options.backgroundColor) dropdown.style.setProperty('--bim-toolbar-bg', this.options.backgroundColor);
+
+ // 获取按钮的位置信息
+ const btnRect = btnEl.getBoundingClientRect();
+ const expand = this.options.expand || 'down';
+
+ // 根据主按钮组的方向设置下拉菜单的布局方向
+ if (this.options.direction === 'row') {
+ dropdown.style.flexDirection = 'column'; // 横向按钮组,菜单纵向排列
+ } else {
+ dropdown.style.flexDirection = 'row'; // 纵向按钮组,菜单横向排列
+ }
+
+ // 先添加到 DOM 以便计算尺寸
+ document.body.appendChild(dropdown);
+
+ // 添加菜单项
+ button.children.forEach(subBtn => {
+ if (this.isVisible(subBtn.id)) {
+ const item = this.renderDropdownItem(subBtn);
+ dropdown.appendChild(item);
+ }
+ });
+
+ // 获取下拉菜单的实际尺寸
+ const dropdownRect = dropdown.getBoundingClientRect();
+
+ if (expand === 'up') {
+ // 向上展开,与按钮水平居中对齐
+ dropdown.style.bottom = (window.innerHeight - btnRect.top + 8) + 'px';
+ dropdown.style.left = (btnRect.left + (btnRect.width - dropdownRect.width) / 2) + 'px';
+ } else if (expand === 'down') {
+ // 向下展开,与按钮水平居中对齐
+ dropdown.style.top = (btnRect.bottom + 8) + 'px';
+ dropdown.style.left = (btnRect.left + (btnRect.width - dropdownRect.width) / 2) + 'px';
+ } else if (expand === 'right') {
+ // 向右展开,与按钮垂直居中对齐
+ dropdown.style.top = (btnRect.top + (btnRect.height - dropdownRect.height) / 2) + 'px';
+ dropdown.style.left = (btnRect.right + 8) + 'px';
+ } else if (expand === 'left') {
+ // 向左展开,与按钮垂直居中对齐
+ dropdown.style.top = (btnRect.top + (btnRect.height - dropdownRect.height) / 2) + 'px';
+ dropdown.style.right = (window.innerWidth - btnRect.left + 8) + 'px';
+ }
+
+ dropdown.addEventListener('mouseenter', () => { if (this.hoverTimeout) clearTimeout(this.hoverTimeout); });
+ dropdown.addEventListener('mouseleave', () => this.handleMouseLeave());
+ this.dropdownElement = dropdown;
+ }
+
+ private renderDropdownItem(button: OptButton): HTMLElement {
+ const item = document.createElement('div');
+ item.className = 'opt-btn-dropdown-item';
+
+ // 应用按钮的 align 设置,默认为 horizontal(图标在左)
+ const align = button.align || 'horizontal';
+ if (align === 'horizontal') {
+ item.classList.add('align-horizontal');
+ } else {
+ item.classList.add('align-vertical');
+ }
+
+ // 应用按钮的自定义样式
+ const iconSize = button.iconSize || 32; // 二级菜单默认图标更小
+ const minWidth = button.minWidth; // 不设置默认值,让下拉菜单项保持紧凑
+ if (minWidth) {
+ item.style.minWidth = `${minWidth}px`;
+ }
+
+ const icon = document.createElement('div');
+ icon.className = 'opt-btn-icon';
+ icon.style.width = `${iconSize}px`;
+ icon.style.height = `${iconSize}px`;
+ icon.innerHTML = this.getIcon(button.icon);
+ item.appendChild(icon);
+
+ // 只有在 showLabel 为 true 时才显示 label
+ if (this.options.showLabel && button.label) {
+ const label = document.createElement('span');
+ label.className = 'opt-btn-dropdown-label';
+ label.textContent = t(button.label);
+ item.appendChild(label);
+ }
+
+ item.addEventListener('click', (e) => { e.stopPropagation(); this.handleClick(button); });
+ return item;
+ }
+
+ private closeDropdown(): void {
+ if (this.dropdownElement) {
+ this.dropdownElement.remove();
+ this.dropdownElement = null;
+ }
+ this.btnRefs.forEach(btnEl => {
+ const arrow = btnEl.querySelector('.opt-btn-arrow');
+ if (arrow) arrow.classList.remove('rotated');
+ });
+ }
+
+ private updateButtonState(buttonId: string): void {
+ const btnEl = this.btnRefs.get(buttonId);
+ if (btnEl) {
+ this.activeBtnIds.has(buttonId) ? btnEl.classList.add('active') : btnEl.classList.remove('active');
+ }
+ }
+
+ private getIcon(icon?: string): string { return icon || this.DEFAULT_ICON; }
+ public updateButtonVisibility(id: string, visible: boolean): void {
+ if (!this.options.visibility) this.options.visibility = {};
+ this.options.visibility[id] = visible;
+ this.render();
+ }
+ public setShowLabel(show: boolean): void {
+ this.options.showLabel = show;
+ this.updateLabelsVisibility();
+ }
+
+ private updateLabelsVisibility(): void {
+ this.btnRefs.forEach((btnEl, buttonId) => {
+ // 查找按钮配置
+ const button = this.findButtonById(buttonId);
+ if (!button) return;
+
+ const hasLabel = this.options.showLabel && button.label;
+
+ // 只需要更新 no-label 类,CSS 会处理显示/隐藏
+ if (hasLabel) {
+ btnEl.classList.remove('no-label');
+ } else {
+ btnEl.classList.add('no-label');
+ }
+ });
+ }
+
+ private findButtonById(id: string): OptButton | undefined {
+ for (const group of this.groups) {
+ const found = this.findButton(group.buttons, id);
+ if (found) return found;
+ }
+ return undefined;
+ }
+ public setBackgroundColor(color: string): void { this.setColors({ backgroundColor: color }); }
+ private isVisible(id: string): boolean { return this.options.visibility?.[id] !== false; }
+ public destroy(): void {
+ if (this.unsubscribeLocale) {
+ this.unsubscribeLocale();
+ this.unsubscribeLocale = null;
+ }
+ if (this.unsubscribeTheme) {
+ this.unsubscribeTheme();
+ this.unsubscribeTheme = null;
+ }
+ this.closeDropdown();
+ this.container.innerHTML = '';
+ this.btnRefs.clear();
+ }
+}
diff --git a/src/components/button-group/index.type.ts b/src/components/button-group/index.type.ts
new file mode 100644
index 0000000..379ea9e
--- /dev/null
+++ b/src/components/button-group/index.type.ts
@@ -0,0 +1,87 @@
+export type ButtonType = 'button' | 'menu';
+
+/** 按钮配置 */
+export interface ButtonConfig {
+ id: string;
+ type: ButtonType;
+ label: string;
+ icon?: string;
+ keepActive?: boolean;
+ disabled?: boolean;
+ onClick?: (button: OptButton) => void;
+ children?: ButtonConfig[];
+ groupId?: string;
+ parentId?: string;
+ /** 按钮内部图标文字排列 (默认 vertical,即图标在上) */
+ align?: ButtonAlign;
+ /** 图标大小 (正方形,单位 px,默认 32) */
+ iconSize?: number;
+ /** 按钮最小宽度 (单位 px,默认 50) */
+ minWidth?: number;
+}
+
+export interface OptButton extends ButtonConfig {
+ children?: OptButton[];
+}
+
+export interface ButtonGroup {
+ id: string;
+ buttons: OptButton[];
+}
+
+export interface ButtonGroupColors {
+ backgroundColor?: string;
+ btnBackgroundColor?: string;
+ btnHoverColor?: string;
+ btnActiveColor?: string;
+ iconColor?: string;
+ iconActiveColor?: string;
+ textColor?: string;
+ textActiveColor?: string;
+}
+
+// --- 新增布局类型 ---
+
+/** 弹窗/按钮组位置 */
+export type GroupPosition =
+ | 'center'
+ | 'top-left' | 'top-center' | 'top-right'
+ | 'left-center' | 'right-center'
+ | 'bottom-left' | 'bottom-center' | 'bottom-right'
+ | { x: number; y: number }
+ | 'static'; // static 表示不绝对定位,随文档流
+
+/** 按钮组排列方向 (Flex-direction) */
+export type GroupDirection = 'row' | 'column';
+
+/** 按钮内部文字图标排列 */
+export type ButtonAlign = 'vertical' /* 图标在上 */ | 'horizontal' /* 图标在左 */;
+
+/** 二级菜单展开方向 */
+export type ExpandDirection = 'up' | 'down' | 'left' | 'right';
+
+export interface ButtonGroupOptions extends ButtonGroupColors {
+ container: HTMLElement | string;
+
+ /** 屏幕位置 (如 top-left) */
+ position?: GroupPosition;
+
+ /** 按钮组排列方向 (默认 row) */
+ direction?: GroupDirection;
+
+ /** 按钮内部图标文字排列 (默认 vertical) */
+ align?: ButtonAlign;
+
+ /** 菜单展开方向 */
+ expand?: ExpandDirection;
+
+ showLabel?: boolean;
+ visibility?: Record;
+ className?: string;
+}
+
+export interface ClickPayload {
+ button: OptButton;
+ action: 'activate' | 'deactivate' | 'trigger';
+ isActive?: boolean;
+}
\ No newline at end of file
diff --git a/src/toolbar/buttons/home/index.ts b/src/components/button-group/toolbar/buttons/home/index.ts
similarity index 81%
rename from src/toolbar/buttons/home/index.ts
rename to src/components/button-group/toolbar/buttons/home/index.ts
index d151aa2..0dfb117 100644
--- a/src/toolbar/buttons/home/index.ts
+++ b/src/components/button-group/toolbar/buttons/home/index.ts
@@ -1,4 +1,4 @@
-import type { ButtonConfig } from '../../index.type';
+import type { ButtonConfig } from '../../../index.type';
/**
* 首页按钮配置
@@ -7,7 +7,7 @@ export const homeButton: ButtonConfig = {
id: 'home',
groupId: 'group-1',
type: 'button',
- label: '首页',
+ label: 'toolbar.home',
icon: ' ',
keepActive: true,
onClick: (button) => {
diff --git a/src/toolbar/buttons/info/index.ts b/src/components/button-group/toolbar/buttons/info/index.ts
similarity index 88%
rename from src/toolbar/buttons/info/index.ts
rename to src/components/button-group/toolbar/buttons/info/index.ts
index ad990c3..52142bf 100644
--- a/src/toolbar/buttons/info/index.ts
+++ b/src/components/button-group/toolbar/buttons/info/index.ts
@@ -1,4 +1,4 @@
-import type { ButtonConfig } from '../../index.type';
+import type { ButtonConfig } from '../../../index.type';
/**
* 定位按钮配置
@@ -7,7 +7,7 @@ export const infoButton: ButtonConfig = {
id: 'info',
groupId: 'group-2',
type: 'button',
- label: '信息',
+ label: 'toolbar.info',
icon: ' ',
keepActive: false,
onClick: (button) => {
diff --git a/src/toolbar/buttons/location/index.ts b/src/components/button-group/toolbar/buttons/location/index.ts
similarity index 84%
rename from src/toolbar/buttons/location/index.ts
rename to src/components/button-group/toolbar/buttons/location/index.ts
index 0099756..763374e 100644
--- a/src/toolbar/buttons/location/index.ts
+++ b/src/components/button-group/toolbar/buttons/location/index.ts
@@ -1,4 +1,4 @@
-import type { ButtonConfig } from '../../index.type';
+import type { ButtonConfig } from '../../../index.type';
/**
* 定位按钮配置
@@ -7,7 +7,7 @@ export const locationButton: ButtonConfig = {
id: 'location',
groupId: 'group-1',
type: 'button',
- label: '定位',
+ label: 'toolbar.location',
icon: ' ',
keepActive: false,
onClick: (button) => {
diff --git a/src/toolbar/buttons/setting/index.ts b/src/components/button-group/toolbar/buttons/setting/index.ts
similarity index 93%
rename from src/toolbar/buttons/setting/index.ts
rename to src/components/button-group/toolbar/buttons/setting/index.ts
index bf3d459..ff977f8 100644
--- a/src/toolbar/buttons/setting/index.ts
+++ b/src/components/button-group/toolbar/buttons/setting/index.ts
@@ -1,4 +1,4 @@
-import type { ButtonConfig } from '../../index.type';
+import type { ButtonConfig } from '../../../index.type';
/**
* 定位按钮配置
@@ -7,7 +7,7 @@ export const settingButton: ButtonConfig = {
id: 'setting',
groupId: 'group-2',
type: 'button',
- label: '设置',
+ label: 'toolbar.setting',
icon: ' ',
keepActive: false,
onClick: (button) => {
diff --git a/src/toolbar/buttons/walk/walk-bird/index.ts b/src/components/button-group/toolbar/buttons/walk/walk-bird/index.ts
similarity index 82%
rename from src/toolbar/buttons/walk/walk-bird/index.ts
rename to src/components/button-group/toolbar/buttons/walk/walk-bird/index.ts
index 6ead661..fc397db 100644
--- a/src/toolbar/buttons/walk/walk-bird/index.ts
+++ b/src/components/button-group/toolbar/buttons/walk/walk-bird/index.ts
@@ -1,11 +1,12 @@
-import type { ButtonConfig } from '../../../index.type';
+import type { ButtonConfig } from '../../../../index.type';
export const walkBirdButton: ButtonConfig = {
id: 'walk-bird',
groupId: 'group-1',
parentId: 'walk',
+ align: 'vertical',
type: 'button',
- label: '鸟瞰漫游',
+ label: 'toolbar.walkBird',
icon: ' ',
onClick: (button) => {
console.log('鸟瞰漫游被点击:', button.id);
diff --git a/src/toolbar/buttons/walk/walk-menu/index.ts b/src/components/button-group/toolbar/buttons/walk/walk-menu/index.ts
similarity index 83%
rename from src/toolbar/buttons/walk/walk-menu/index.ts
rename to src/components/button-group/toolbar/buttons/walk/walk-menu/index.ts
index 5c97cee..f4f3a94 100644
--- a/src/toolbar/buttons/walk/walk-menu/index.ts
+++ b/src/components/button-group/toolbar/buttons/walk/walk-menu/index.ts
@@ -1,4 +1,4 @@
-import type { ButtonConfig } from '../../../index.type';
+import type { ButtonConfig } from '../../../../index.type';
/**
* 漫游菜单按钮配置
@@ -7,7 +7,8 @@ export const walkMenuButton: ButtonConfig = {
id: 'walk',
groupId: 'group-1',
type: 'menu',
- label: '漫游',
+ label: 'toolbar.walk',
+ align: 'vertical',
icon: ' ',
keepActive: true,
onClick: (button) => {
diff --git a/src/toolbar/buttons/walk/walk-person/index.ts b/src/components/button-group/toolbar/buttons/walk/walk-person/index.ts
similarity index 82%
rename from src/toolbar/buttons/walk/walk-person/index.ts
rename to src/components/button-group/toolbar/buttons/walk/walk-person/index.ts
index 65290ab..07c0324 100644
--- a/src/toolbar/buttons/walk/walk-person/index.ts
+++ b/src/components/button-group/toolbar/buttons/walk/walk-person/index.ts
@@ -1,11 +1,12 @@
-import type { ButtonConfig } from '../../../index.type';
+import type { ButtonConfig } from '../../../../index.type';
export const walkPersonButton: ButtonConfig = {
id: 'walk-person',
groupId: 'group-1',
parentId: 'walk',
type: 'button',
- label: '人视漫游',
+ align: 'vertical',
+ label: 'toolbar.walkPerson',
icon: ' ',
onClick: (button) => {
console.log('人视漫游被点击:', button.id);
diff --git a/src/components/button-group/toolbar/index.ts b/src/components/button-group/toolbar/index.ts
new file mode 100644
index 0000000..58164ec
--- /dev/null
+++ b/src/components/button-group/toolbar/index.ts
@@ -0,0 +1,35 @@
+import { BimButtonGroup } from '../index';
+
+/**
+ * 底部工具栏 (Toolbar)
+ * BimButtonGroup 的子类,专门用于加载工具栏默认按钮。
+ */
+export class Toolbar extends BimButtonGroup {
+ /**
+ * 重写初始化,加载默认按钮
+ */
+ public async init(): Promise {
+ await super.init();
+
+ // 动态加载默认按钮配置
+ const { homeButton } = await import('./buttons/home');
+ const { locationButton } = await import('./buttons/location');
+ const { walkMenuButton } = await import('./buttons/walk/walk-menu');
+ const { walkPersonButton } = await import('./buttons/walk/walk-person');
+ const { walkBirdButton } = await import('./buttons/walk/walk-bird');
+ const { settingButton } = await import('./buttons/setting');
+ const { infoButton } = await import('./buttons/info');
+
+ this.addGroup('group-1');
+ this.addButton(homeButton);
+ this.addButton(walkMenuButton);
+ this.addButton(walkPersonButton);
+ this.addButton(walkBirdButton);
+ this.addButton(locationButton);
+ this.addGroup('group-2');
+ this.addButton(settingButton);
+ this.addButton(infoButton);
+
+ this.render();
+ }
+}
diff --git a/src/dialog/bimInfoDialog/index.css b/src/components/dialog/bimInfoDialog/index.css
similarity index 100%
rename from src/dialog/bimInfoDialog/index.css
rename to src/components/dialog/bimInfoDialog/index.css
diff --git a/src/dialog/bimInfoDialog/index.ts b/src/components/dialog/bimInfoDialog/index.ts
similarity index 59%
rename from src/dialog/bimInfoDialog/index.ts
rename to src/components/dialog/bimInfoDialog/index.ts
index 04796f6..7931d4d 100644
--- a/src/dialog/bimInfoDialog/index.ts
+++ b/src/components/dialog/bimInfoDialog/index.ts
@@ -2,18 +2,16 @@ import './index.css';
import { BimDialog } from '../index';
/**
- * BimInfoDialog (二次封装示例)
- * 这是一个展示项目信息的业务弹窗组件,内部封装了 BimDialog。
+ * BimInfoDialog (继承版)
+ * 这是一个展示项目信息的业务弹窗组件,直接继承自 BimDialog。
*/
-export class BimInfoDialog {
- private dialog: BimDialog;
-
+export class BimInfoDialog extends BimDialog {
/**
* 构造函数
* @param container 父容器
*/
constructor(container: HTMLElement) {
- // 创建自定义的 DOM 内容
+ // 1. 准备内容 DOM
const contentEl = document.createElement('div');
contentEl.className = 'bim-info-dialog-content';
@@ -39,23 +37,29 @@ export class BimInfoDialog {
contentEl.appendChild(infoList);
contentEl.appendChild(actionBtn);
- // 初始化 BimDialog,直接传入构建好的 HTMLElement
- this.dialog = new BimDialog({
+ // 2. 调用父类构造函数,传入特定的配置
+ super({
container: container,
- title: 'Project Info (Wrapped)',
- content: contentEl,
+ title: 'dialog.testTitle',
+ content: contentEl,
width: 320,
height: 'auto',
position: 'center',
resizable: true,
- draggable: true
+ draggable: true,
+ // 可以在这里添加特定的 onClose 逻辑
+ onClose: () => {
+ console.log('Info dialog closed');
+ },
+ onOpen: () => {
+ console.log('Info dialog opened');
+ }
});
+
+ // 3. 如果有特定于子类的初始化逻辑,可以在 super() 之后执行
+ // 例如:this.element.classList.add('my-special-class');
}
- /**
- * 关闭弹窗
- */
- public close() {
- this.dialog.close();
- }
+ // 不需要再手动实现 setTheme, destroy, close, init
+ // 它们都已从 BimDialog 继承
}
\ No newline at end of file
diff --git a/src/dialog/index.css b/src/components/dialog/index.css
similarity index 100%
rename from src/dialog/index.css
rename to src/components/dialog/index.css
diff --git a/src/dialog/index.ts b/src/components/dialog/index.ts
similarity index 79%
rename from src/dialog/index.ts
rename to src/components/dialog/index.ts
index e551ad6..52fc0b9 100644
--- a/src/dialog/index.ts
+++ b/src/components/dialog/index.ts
@@ -1,17 +1,24 @@
import './index.css';
import type { DialogOptions } from './index.type';
+import type { ThemeConfig } from '../../themes/types';
+import { IBimComponent } from '../../types/component';
+import { themeManager } from '../../services/theme';
+import { t, localeManager } from '../../services/locale';
/**
* 通用弹窗组件类
* 支持拖拽、缩放、自定义内容和位置。
*/
-export class BimDialog {
+export class BimDialog implements IBimComponent {
private element: HTMLElement;
private options: DialogOptions;
private container: HTMLElement;
private header: HTMLElement;
private contentArea: HTMLElement;
private _isDestroyed = false;
+ private _isInitialized = false;
+ private unsubscribeTheme: (() => void) | null = null;
+ private unsubscribeLocale: (() => void) | null = null;
/**
* 构造函数
@@ -37,10 +44,69 @@ export class BimDialog {
this.header = this.element.querySelector('.bim-dialog-header') as HTMLElement;
this.contentArea = this.element.querySelector('.bim-dialog-content') as HTMLElement;
- // 初始化
+ // 自动初始化 (为了兼容现有逻辑)
this.init();
}
+ /**
+ * 设置主题
+ * @param theme 全局主题配置
+ */
+ public setTheme(theme: ThemeConfig) {
+ const style = this.element.style;
+ if (!this.options.backgroundColor) style.setProperty('--bim-dialog-bg', theme.panelBackground);
+ if (!this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', theme.componentHover);
+ if (!this.options.titleColor) style.setProperty('--bim-dialog-title-color', theme.textPrimary);
+ if (!this.options.textColor) style.setProperty('--bim-dialog-text-color', theme.textPrimary);
+ if (!this.options.borderColor) style.setProperty('--bim-dialog-border-color', theme.border);
+ }
+
+ /**
+ * 初始化组件功能 (接口实现)
+ */
+ public init() {
+ if (this._isInitialized) return;
+
+ this.container.appendChild(this.element);
+
+ // 必须先挂载才能计算尺寸进行定位
+ this.initPosition();
+
+ if (this.options.draggable) {
+ this.initDrag();
+ }
+
+ if (this.options.resizable) {
+ this.initResize();
+ }
+
+ this._isInitialized = true;
+
+ // 调用弹窗开启后回调
+ if (this.options.onOpen) {
+ this.options.onOpen();
+ }
+
+ // 自动订阅主题变更
+ this.unsubscribeTheme = themeManager.subscribe((theme) => {
+ this.setTheme(theme);
+ });
+
+ // 自动订阅语言变更
+ this.unsubscribeLocale = localeManager.subscribe(() => {
+ this.setLocales();
+ });
+ }
+
+ public setLocales(): void {
+ if (this.options.title) {
+ const titleEl = this.header.querySelector('.bim-dialog-title');
+ if (titleEl) {
+ titleEl.textContent = t(this.options.title);
+ }
+ }
+ }
+
/**
* 创建弹窗的 DOM 结构
*/
@@ -49,7 +115,7 @@ export class BimDialog {
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);
@@ -68,7 +134,7 @@ export class BimDialog {
const title = document.createElement('span');
title.className = 'bim-dialog-title';
- title.textContent = this.options.title || '';
+ title.textContent = this.options.title ? t(this.options.title) : '';
const closeBtn = document.createElement('span');
closeBtn.className = 'bim-dialog-close';
@@ -112,24 +178,6 @@ export class BimDialog {
}
}
- /**
- * 初始化组件功能
- */
- private init() {
- this.container.appendChild(this.element);
-
- // 必须先挂载才能计算尺寸进行定位
- this.initPosition();
-
- if (this.options.draggable) {
- this.initDrag();
- }
-
- if (this.options.resizable) {
- this.initResize();
- }
- }
-
/**
* 初始化弹窗位置
*/
@@ -137,7 +185,7 @@ export class BimDialog {
const pos = this.options.position;
const elRect = this.element.getBoundingClientRect();
-
+
// 计算相对父容器的定位
let left = 0;
let top = 0;
@@ -285,10 +333,25 @@ export class BimDialog {
*/
public close() {
if (this._isDestroyed) return;
+ if (this.unsubscribeTheme) {
+ this.unsubscribeTheme();
+ this.unsubscribeTheme = null;
+ }
+ if (this.unsubscribeLocale) {
+ this.unsubscribeLocale();
+ this.unsubscribeLocale = null;
+ }
this.element.remove();
this._isDestroyed = true;
if (this.options.onClose) {
this.options.onClose();
}
}
+
+ /**
+ * 销毁组件 (接口实现)
+ */
+ public destroy() {
+ this.close();
+ }
}
diff --git a/src/dialog/index.type.ts b/src/components/dialog/index.type.ts
similarity index 89%
rename from src/dialog/index.type.ts
rename to src/components/dialog/index.type.ts
index 825b203..c3da4f6 100644
--- a/src/dialog/index.type.ts
+++ b/src/components/dialog/index.type.ts
@@ -3,10 +3,10 @@
* 可以是预设的字符串位置(如 'center', 'top-left' 等),
* 也可以是具体的坐标对象 { x, y }
*/
-export type DialogPosition =
- | 'center'
- | 'top-left' | 'top-center' | 'top-right'
- | 'left-center' | 'right-center'
+export type DialogPosition =
+ | 'center'
+ | 'top-left' | 'top-center' | 'top-right'
+ | 'left-center' | 'right-center'
| 'bottom-left' | 'bottom-center' | 'bottom-right'
| { x: number; y: number };
@@ -52,6 +52,8 @@ export interface DialogOptions extends DialogColors {
minHeight?: number;
/** 关闭时的回调函数 */
onClose?: () => void;
+ /** 打开时的回调函数 */
+ onOpen?: () => void;
/** 弹窗唯一标识 ID (可选) */
id?: string;
}
diff --git a/src/index.ts b/src/index.ts
index d6b5c90..3794c4f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,10 +1,11 @@
import { BimEngine } from './bim-engine';
-// 导出 OptBtnGroups 组件,用于工具栏操作
-export { OptBtnGroups } from './toolbar';
+// 导出通用组件
+export { BimButtonGroup } from './components/button-group';
+export { Toolbar } from './components/button-group/toolbar';
// 导出相关类型定义
-export type { OptButton, ButtonGroup, OptBtnGroupsOptions, ClickPayload } from './toolbar/index.type';
+export type { OptButton, ButtonGroup, ButtonGroupOptions, ClickPayload } from './components/button-group/index.type';
// 导出主引擎类
export { BimEngine };
\ No newline at end of file
diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts
new file mode 100644
index 0000000..cc18c8c
--- /dev/null
+++ b/src/locales/en-US.ts
@@ -0,0 +1,24 @@
+import { TranslationDictionary } from './types';
+
+export const enUS: TranslationDictionary = {
+ common: {
+ title: 'BimEngine',
+ description: 'This is a BIM-ENGINE demo.',
+ openTestDialog: 'Open Test Dialog',
+ openInfoDialog: 'Open Info Dialog (Wrapped)',
+ },
+ toolbar: {
+ home: 'Home',
+ info: 'Info',
+ location: 'Location',
+ setting: 'Settings',
+ walk: 'Walk',
+ walkPerson: 'Person',
+ walkBird: 'Bird Eye',
+ walkMenu: 'Menu',
+ },
+ dialog: {
+ testTitle: 'Test Dialog',
+ testContent: 'This is a draggable and resizable dialog. Try dragging the title bar or resizing from the bottom-right corner.
',
+ },
+};
diff --git a/src/locales/types.ts b/src/locales/types.ts
new file mode 100644
index 0000000..73e8bbf
--- /dev/null
+++ b/src/locales/types.ts
@@ -0,0 +1,31 @@
+/**
+ * 翻译字典接口
+ * 定义所有可用的翻译键值对结构,保证类型安全
+ */
+export interface TranslationDictionary {
+ common: {
+ title: string;
+ description: string;
+ openTestDialog: string;
+ openInfoDialog: string;
+ };
+ toolbar: {
+ home: string;
+ info: string;
+ location: string;
+ setting: string;
+ walk: string;
+ walkPerson: string;
+ walkBird: string;
+ walkMenu: string;
+ };
+ dialog: {
+ testTitle: string;
+ testContent: string;
+ };
+}
+
+/**
+ * 语言代码类型
+ */
+export type LocaleType = 'zh-CN' | 'en-US';
diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts
new file mode 100644
index 0000000..13b9841
--- /dev/null
+++ b/src/locales/zh-CN.ts
@@ -0,0 +1,24 @@
+import { TranslationDictionary } from './types';
+
+export const zhCN: TranslationDictionary = {
+ common: {
+ title: 'BimEngine',
+ description: '这是一个使用 BIM-ENGINE。',
+ openTestDialog: '打开测试弹窗',
+ openInfoDialog: '打开信息弹窗 (封装版)',
+ },
+ toolbar: {
+ home: '首页',
+ info: '信息',
+ location: '定位',
+ setting: '设置',
+ walk: '漫游',
+ walkPerson: '人视',
+ walkBird: '鸟瞰',
+ walkMenu: '菜单',
+ },
+ dialog: {
+ testTitle: '测试弹窗',
+ testContent: '这是一个 可拖拽 且 可缩放 的弹窗。 你可以尝试拖动标题栏,或者拖动右下角改变大小。
',
+ },
+};
diff --git a/src/managers/button-group-manager.ts b/src/managers/button-group-manager.ts
new file mode 100644
index 0000000..64d7cf6
--- /dev/null
+++ b/src/managers/button-group-manager.ts
@@ -0,0 +1,48 @@
+import { BimButtonGroup } from '../components/button-group';
+import type { ButtonGroupOptions } from '../components/button-group/index.type';
+import type { ThemeConfig } from '../themes/types';
+
+/**
+ * 通用按钮组管理器
+ * 负责创建和管理除底部工具栏以外的其他按钮组。
+ */
+export class ButtonGroupManager {
+ private activeGroups: BimButtonGroup[] = [];
+ private container: HTMLElement;
+
+ constructor(container: HTMLElement) {
+ this.container = container;
+ }
+
+ /**
+ * 创建一个新的按钮组
+ */
+ public create(options: Omit): BimButtonGroup {
+ // 自动创建一个 div 作为容器
+ const groupContainer = document.createElement('div');
+ this.container.appendChild(groupContainer);
+
+ const group = new BimButtonGroup({
+ container: groupContainer,
+ ...options
+ });
+
+ // 立即初始化
+ group.init();
+ this.activeGroups.push(group);
+ return group;
+ }
+
+ public updateTheme(theme: ThemeConfig) {
+ this.activeGroups.forEach(g => g.setTheme(theme));
+ }
+
+ public refresh() {
+ this.activeGroups.forEach(g => g.render());
+ }
+
+ public destroy() {
+ this.activeGroups.forEach(g => g.destroy());
+ this.activeGroups = [];
+ }
+}
diff --git a/src/managers/dialog-manager.ts b/src/managers/dialog-manager.ts
new file mode 100644
index 0000000..a44c0a6
--- /dev/null
+++ b/src/managers/dialog-manager.ts
@@ -0,0 +1,69 @@
+import { BimDialog } from '../components/dialog';
+import { BimInfoDialog } from '../components/dialog/bimInfoDialog';
+import type { DialogOptions } from '../components/dialog/index.type';
+import type { ThemeConfig } from '../themes/types';
+import { themeManager } from '../services/theme'; // 修正路径
+
+/**
+ * 弹窗管理器
+ * 负责创建和管理应用中的各类弹窗。
+ */
+export class DialogManager {
+ /** 弹窗挂载的父容器 */
+ private container: HTMLElement;
+ /** 活跃的弹窗实例列表 */
+ private activeDialogs: BimDialog[] = [];
+
+ /**
+ * 构造函数
+ * @param container 弹窗挂载的目标容器
+ */
+ constructor(container: HTMLElement) {
+ this.container = container;
+ }
+
+ /**
+ * 创建一个通用弹窗
+ * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器)
+ * @returns BimDialog 实例
+ */
+ public create(options: Omit): BimDialog {
+ const dialog = new BimDialog({
+ container: this.container,
+ ...options,
+ onClose: () => {
+ // 从活跃列表中移除
+ this.activeDialogs = this.activeDialogs.filter(d => d !== dialog);
+ if (options.onClose) options.onClose();
+ }
+ });
+
+ // 应用当前主题
+ dialog.setTheme(themeManager.getTheme());
+
+ this.activeDialogs.push(dialog);
+ return dialog;
+ }
+
+ /**
+ * 显示二次封装的模型信息弹窗
+ * 演示如何调用特定的业务弹窗组件
+ */
+ public showInfoDialog() {
+ // 最佳实践:所有弹窗应通过 create 统一管理,或者手动加入管理。
+ new BimInfoDialog(this.container);
+ // 暂时不做主题追踪,作为遗留逻辑保留
+ }
+
+ /**
+ * 响应全局主题变更
+ * @param theme 全局主题配置
+ */
+ public updateTheme(theme: ThemeConfig) {
+ this.activeDialogs.forEach(dialog => {
+ if (dialog.setTheme) {
+ dialog.setTheme(theme);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/managers/toolbar-manager.ts b/src/managers/toolbar-manager.ts
new file mode 100644
index 0000000..849afe8
--- /dev/null
+++ b/src/managers/toolbar-manager.ts
@@ -0,0 +1,63 @@
+import type { ButtonGroupColors, ButtonConfig } from '../components/button-group/index.type';
+import { Toolbar } from '../components/button-group/toolbar';
+import type { ThemeConfig } from '../themes/types';
+
+/**
+ * 底部工具栏管理器 (ToolbarManager)
+ * 仅负责管理底部工具栏实例。
+ */
+export class ToolbarManager {
+ private toolbar: Toolbar | null = null;
+ private toolbarContainer: HTMLElement | null = null;
+ private container: HTMLElement;
+
+ constructor(container: HTMLElement) {
+ this.container = container;
+ this.init();
+ }
+
+ private init() {
+ // 创建底部工具栏专用容器
+ this.toolbarContainer = document.createElement('div');
+ this.toolbarContainer.id = 'opt-btn-groups';
+ this.toolbarContainer.className = 'bim-engine-opt-btn-container is-bottom-toolbar';
+ this.container.appendChild(this.toolbarContainer);
+
+ this.toolbar = new Toolbar({
+ container: this.toolbarContainer,
+ showLabel: true,
+ direction: 'row',
+ position: 'bottom-center', // 底部居中
+ align: 'vertical', // 图标在上
+ expand: 'up' // 向上展开
+ });
+
+ this.toolbar.init();
+ }
+
+ public updateTheme(theme: ThemeConfig) {
+ this.toolbar?.setTheme(theme);
+ }
+
+ public refresh() {
+ this.toolbar?.render();
+ }
+
+ public destroy() {
+ this.toolbar?.destroy();
+ this.toolbar = null;
+ }
+
+ // --- 转发 API ---
+ public addGroup(groupId: string, beforeGroupId?: string) { this.toolbar?.addGroup(groupId, beforeGroupId); this.toolbar?.render(); }
+ public addButton(config: ButtonConfig) { this.toolbar?.addButton(config); this.toolbar?.render(); }
+ public setButtonVisibility(id: string, v: boolean) { this.toolbar?.updateButtonVisibility(id, v); }
+ public setShowLabel(show: boolean) { this.toolbar?.setShowLabel(show); }
+ public setVisible(visible: boolean) {
+ if (this.toolbarContainer) {
+ this.toolbarContainer.style.visibility = visible ? 'visible' : 'hidden';
+ }
+ }
+ public setBackgroundColor(color: string) { this.toolbar?.setBackgroundColor(color); }
+ public setColors(colors: ButtonGroupColors) { this.toolbar?.setColors(colors); }
+}
diff --git a/src/modules/dialog-manager.ts b/src/modules/dialog-manager.ts
deleted file mode 100644
index bbe178f..0000000
--- a/src/modules/dialog-manager.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-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): BimDialog {
- return new BimDialog({
- container: this.container,
- ...options
- });
- }
-
- /**
- * 显示二次封装的模型信息弹窗
- * 演示如何调用特定的业务弹窗组件
- */
- public showInfoDialog() {
- new BimInfoDialog(this.container);
- }
-}
\ No newline at end of file
diff --git a/src/modules/toolbar-manager.ts b/src/modules/toolbar-manager.ts
deleted file mode 100644
index 6c70a14..0000000
--- a/src/modules/toolbar-manager.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-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;
- }
- }
-}
diff --git a/src/services/locale.ts b/src/services/locale.ts
new file mode 100644
index 0000000..d8e9828
--- /dev/null
+++ b/src/services/locale.ts
@@ -0,0 +1,80 @@
+import { LocaleType, TranslationDictionary } from '../locales/types';
+import { zhCN } from '../locales/zh-CN';
+import { enUS } from '../locales/en-US';
+
+type LocaleChangeListener = (locale: LocaleType) => void;
+
+/**
+ * 语言管理器类
+ */
+export class LocaleManager {
+ private currentLocale: LocaleType = 'zh-CN';
+ private messages: Record = {
+ 'zh-CN': zhCN,
+ 'en-US': enUS,
+ };
+ private listeners: LocaleChangeListener[] = [];
+
+ constructor() {
+ // 默认初始化
+ }
+
+ /**
+ * 获取当前语言
+ */
+ public getLocale(): LocaleType {
+ return this.currentLocale;
+ }
+
+ /**
+ * 切换语言
+ */
+ public setLocale(locale: LocaleType) {
+ if (this.currentLocale === locale) return;
+ this.currentLocale = locale;
+ this.notifyListeners();
+ }
+
+ /**
+ * 翻译核心方法
+ */
+ public t(key: string): string {
+ if (!key) return '';
+
+ const keys = key.split('.');
+ let value: any = this.messages[this.currentLocale];
+
+ for (const k of keys) {
+ if (value && typeof value === 'object' && k in value) {
+ value = value[k];
+ } else {
+ return key;
+ }
+ }
+ return value as string;
+ }
+
+ /**
+ * 订阅变更
+ */
+ public subscribe(listener: LocaleChangeListener): () => void {
+ this.listeners.push(listener);
+ return () => {
+ this.listeners = this.listeners.filter(l => l !== listener);
+ };
+ }
+
+ private notifyListeners() {
+ this.listeners.forEach(listener => listener(this.currentLocale));
+ }
+}
+
+// --- 导出单例 ---
+export const localeManager = new LocaleManager();
+
+// --- 导出便捷方法 ---
+/**
+ * 全局翻译函数
+ * @param key 键路径 (如 'toolbar.home')
+ */
+export const t = (key: string): string => localeManager.t(key);
diff --git a/src/services/theme.ts b/src/services/theme.ts
new file mode 100644
index 0000000..28a0f9d
--- /dev/null
+++ b/src/services/theme.ts
@@ -0,0 +1,70 @@
+import { ThemeConfig } from '../themes/types';
+import { darkTheme, lightTheme } from '../themes/presets';
+
+type ThemeChangeListener = (theme: ThemeConfig) => void;
+
+/**
+ * 主题管理器 (单例)
+ */
+export class ThemeManager {
+ private currentTheme: ThemeConfig = darkTheme;
+ private listeners: ThemeChangeListener[] = [];
+
+ constructor() {
+ // 默认初始化
+ }
+
+ /**
+ * 获取当前主题配置
+ */
+ public getTheme(): ThemeConfig {
+ return this.currentTheme;
+ }
+
+ /**
+ * 切换预设主题
+ * @param themeName 'dark' | 'light'
+ */
+ public setTheme(themeName: 'dark' | 'light') {
+ if (themeName === 'light') {
+ this.applyTheme(lightTheme);
+ } else {
+ this.applyTheme(darkTheme);
+ }
+ }
+
+ /**
+ * 应用自定义主题配置
+ * @param theme 配置对象
+ */
+ public setCustomTheme(theme: ThemeConfig) {
+ this.applyTheme(theme);
+ }
+
+ /**
+ * 内部应用主题逻辑
+ */
+ private applyTheme(theme: ThemeConfig) {
+ this.currentTheme = theme;
+ this.notifyListeners();
+ }
+
+ /**
+ * 订阅主题变更
+ */
+ public subscribe(listener: ThemeChangeListener): () => void {
+ this.listeners.push(listener);
+ // 立即回调一次当前状态
+ listener(this.currentTheme);
+ return () => {
+ this.listeners = this.listeners.filter(l => l !== listener);
+ };
+ }
+
+ private notifyListeners() {
+ this.listeners.forEach(listener => listener(this.currentTheme));
+ }
+}
+
+// 导出单例
+export const themeManager = new ThemeManager();
\ No newline at end of file
diff --git a/src/themes/presets.ts b/src/themes/presets.ts
new file mode 100644
index 0000000..5737c20
--- /dev/null
+++ b/src/themes/presets.ts
@@ -0,0 +1,95 @@
+import { ThemeConfig } from './types';
+
+/**
+ * 深色主题 (默认)
+ */
+export const darkTheme: ThemeConfig = {
+ name: 'dark',
+ primary: '#0078d4',
+ primaryHover: '#0063b1',
+
+ // 修改:背景色统一为浅灰,不再跟随深色模式变黑
+ background: '#f5f5f5',
+ panelBackground: 'rgba(30, 30, 30, 0.9)',
+
+ // 注意:如果背景是浅色,主文字颜色通常需要是深色才能看清
+ // 但这里的 textPrimary 主要是用于 UI 组件内部的。
+ // 如果 BimEngine wrapper 上的文字直接显示在 background 上,
+ // 我们可能需要区分 "UI文字" 和 "页面文字"。
+ // 目前架构中:
+ // theme.textPrimary 会应用到 wrapper.style.color (BimEngine.ts)
+ // 以及 Toolbar/Dialog 的文字颜色。
+
+ // 如果背景是浅灰,而 wrapper 文字设置为白色 (#ffffff),那就看不清了。
+ // 这是一个语义冲突:
+ // 1. Panel (Toolbar/Dialog) 是黑底,需要白字。
+ // 2. Background (Wrapper) 是白底,需要黑字。
+
+ // 既然您要求背景统一浅灰,那么 Wrapper 上的“直接子文本”应该是深色。
+ // 但 Toolbar/Dialog 仍然是深色模式(黑底),它们需要白字。
+
+ // 妥协方案:
+ // 保持 textPrimary 为白色(为了适配黑���的 Toolbar/Dialog)。
+ // 但是在 BimEngine 中,如果背景强制改为浅色,Wrapper 的默认文字颜色可能需要单独处理,
+ // 或者我们可以认为 "Wrapper" 主要是承载 UI 组件的,直接写在 Wrapper 上的文字(标题/描述)
+ // 应该有自己的样式,而不是直接继承 theme.textPrimary。
+
+ // 在之前的 BimEngine.ts 中:
+ // this.wrapper.style.color = theme.textPrimary;
+
+ // 如果背景变浅灰,这里 textPrimary 还是白色的话,标题就看不见了。
+ // 所以,深色模式下:
+ // 背景:浅灰
+ // 组件:深黑
+ // 组件文字:白
+ // 页面文字:黑 (问题点)
+
+ // 让我们先按您的要求改背景。通常这种情况下,ThemeConfig 可能需要区分
+ // contentText (页面内容文字) 和 uiText (组件文字)。
+ // 但为了不破坏现有结构,我将假定 textPrimary 主要服务于 UI 组件。
+ // 为了让 Wrapper 上的标题可见,我们可能需要在 BimEngine 中移除对 wrapper.style.color 的强制设置,
+ // 或者在 presets 里把 textPrimary 改回来?不对,改回来 Toolbar 就看不清了。
+
+ // 方案:我将仅修改 background。
+ // 至于 Wrapper 上的标题(BimEngine 标题),由于在最新的 BimEngine.ts 中
+ // ���们已经移除了 titleEl 和 descEl(在之前的重构中),
+ // 所以现在 Wrapper 里主要是 Toolbar 和 Dialog,它们有自己的 panelBackground。
+ // 只要 Toolbar/Dialog 内部正常即可。
+
+ textPrimary: '#ffffff',
+ textSecondary: '#cccccc',
+
+ border: '#444444',
+
+ icon: '#cccccc',
+ iconActive: '#ffffff',
+
+ componentBackground: 'transparent',
+ componentHover: '#333333',
+ componentActive: 'rgba(255, 255, 255, 0.1)'
+};
+
+/**
+ * 浅色主题
+ */
+export const lightTheme: ThemeConfig = {
+ name: 'light',
+ primary: '#0078d4',
+ primaryHover: '#106ebe',
+
+ // 统一为浅灰
+ background: '#f5f5f5',
+ panelBackground: '#ffffff',
+
+ textPrimary: '#333333',
+ textSecondary: '#666666',
+
+ border: '#e0e0e0',
+
+ icon: '#555555',
+ iconActive: '#0078d4',
+
+ componentBackground: 'transparent',
+ componentHover: '#f0f0f0',
+ componentActive: '#e0e0e0'
+};
\ No newline at end of file
diff --git a/src/themes/types.ts b/src/themes/types.ts
new file mode 100644
index 0000000..120a007
--- /dev/null
+++ b/src/themes/types.ts
@@ -0,0 +1,43 @@
+/**
+ * 全局主题配置接口
+ * 定义系统通用的语义化颜色
+ */
+export interface ThemeConfig {
+ /** 主题名称 */
+ name: string;
+
+ /** 品牌色/主色 */
+ primary: string;
+ /** 主色悬停/激活态 */
+ primaryHover: string;
+
+ /** 基础背景色 (应用整体背景) */
+ background: string;
+ /** 面板背景色 (工具栏、弹窗背景) */
+ panelBackground: string;
+
+ /** 主要文字颜色 */
+ textPrimary: string;
+ /** 次要文字颜色 */
+ textSecondary: string;
+
+ /** 边框/分割线颜色 */
+ border: string;
+
+ /** 图标默认颜色 */
+ icon: string;
+ /** 图标激活颜色 */
+ iconActive: string;
+
+ /** 交互组件背景 (如按钮默认背景) */
+ componentBackground: string;
+ /** 交互组件悬停背景 */
+ componentHover: string;
+ /** 交互组件激活背景 */
+ componentActive: string;
+}
+
+/**
+ * 主题类型定义
+ */
+export type ThemeType = 'dark' | 'light' | 'custom';
diff --git a/src/toolbar/index.css b/src/toolbar/index.css
deleted file mode 100644
index e256477..0000000
--- a/src/toolbar/index.css
+++ /dev/null
@@ -1,179 +0,0 @@
-: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 {
- display: flex;
- align-items: center;
- max-width: 100%;
- overflow-x: auto;
- scrollbar-width: none;
- -ms-overflow-style: none;
-}
-
-.toolbar-container::-webkit-scrollbar {
- display: none;
-}
-
-/* 按钮组样式 */
-.opt-btn-group {
- border-radius: 4px;
- overflow: hidden;
- display: flex;
- align-items: center;
- flex-shrink: 0;
- background-color: var(--bim-toolbar-bg);
- border-radius: 4px;
- padding: 4px 8px;
-}
-
-.has-divider {
- margin-right: 16px;
-}
-
-/* 按钮包装器 */
-.opt-btn-wrapper {
- position: relative;
-}
-
-/* 按钮样式 */
-.opt-btn {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 50px;
- min-height: 50px;
- padding: 4px;
- cursor: pointer;
- background-color: var(--bim-btn-bg);
- color: var(--bim-icon-color);
- transition: all 0.2s;
- border-bottom: 2px solid transparent;
-}
-
-.opt-btn:hover {
- background-color: var(--bim-btn-hover-bg);
- color: var(--bim-icon-active-color);
-}
-
-.opt-btn.active {
- background-color: var(--bim-btn-active-bg);
- color: var(--bim-icon-active-color);
- border-bottom: 2px solid #fff;
-}
-
-.opt-btn.disabled {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-/* 图标样式 */
-.opt-btn-icon {
- width: 32px;
- height: 32px;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.opt-btn-icon svg {
- width: 100%;
- height: 100%;
-}
-
-/* 文字标签样式 */
-.opt-btn-label {
- font-size: 10px;
- 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 {
- font-size: 8px;
- position: absolute;
- top: 2px;
- right: 2px;
- opacity: 0.6;
- transition: transform 0.2s ease;
-}
-
-.opt-btn-arrow.rotated {
- transform: rotate(180deg);
-}
-
-.opt-btn.no-label .opt-btn-arrow {
- top: 2px;
- right: 2px;
-}
-
-/* 下拉菜单样式 */
-.opt-btn-dropdown {
- position: fixed;
- transform: translate(-50%, -100%);
- background-color: var(--bim-toolbar-bg);
- border-radius: 4px;
- overflow: hidden;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
- min-width: 50px;
- z-index: 9999;
- display: flex;
- flex-direction: column;
-}
-
-/* 下拉菜单项样式 */
-.opt-btn-dropdown-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- color: var(--bim-icon-color);
- cursor: pointer;
- transition: background 0.2s;
- white-space: nowrap;
- min-width: 50px;
- min-height: 50px;
- padding: 4px;
- background-color: var(--bim-btn-bg);
-}
-
-.opt-btn-dropdown-item:hover {
- background-color: var(--bim-btn-hover-bg);
- color: var(--bim-icon-active-color);
-}
-
-.opt-btn-dropdown-item .opt-btn-icon.small {
- width: 30px;
- height: 30px;
- margin-right: 0;
- margin-bottom: 4px;
-}
-
-.opt-btn-dropdown-item span {
- 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 {
- width: 32px;
- height: 32px;
-}
diff --git a/src/toolbar/index.ts b/src/toolbar/index.ts
deleted file mode 100644
index b5168c6..0000000
--- a/src/toolbar/index.ts
+++ /dev/null
@@ -1,536 +0,0 @@
-import './index.css';
-import type {
- OptButton,
- ButtonGroup,
- OptBtnGroupsOptions,
- ButtonConfig,
- ToolbarColors
-} from './index.type';
-
-/**
- * 底部操作按钮组组件
- * 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。
- */
-export class OptBtnGroups {
- /** 挂载容器 */
- private container: HTMLElement;
- /** 组件配置选项 */
- private options: OptBtnGroupsOptions;
- /** 按钮组列表,按顺序存储 */
- private groups: ButtonGroup[] = [];
- /** 当前处于激活状态的按钮 ID 集合 */
- private activeBtnIds: Set = new Set();
- /** 按钮 DOM 元素的引用映射,方便快速查找 */
- private btnRefs: Map = new Map();
- /** 当��显示的下拉菜单元素 */
- private dropdownElement: HTMLElement | null = null;
- /** 鼠标悬停计时器,用于处理菜单显示的防抖 */
- private hoverTimeout: number | null = null;
-
- /** 默认图标 SVG */
- private readonly DEFAULT_ICON = ' ';
-
- /**
- * 构造函数
- * @param options 配置选项
- */
- constructor(options: OptBtnGroupsOptions) {
- const el = typeof options.container === 'string'
- ? document.getElementById(options.container)
- : options.container;
-
- if (!el) throw new Error('Container not found');
-
- this.container = el;
- this.options = {
- showLabel: true,
- visibility: {},
- ...options
- };
-
- this.initContainer();
- this.applyStyles(); // 应用初始样式配置
- }
-
- /**
- * 初始化容器
- */
- private initContainer(): void {
- 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 beforeGroupId 在哪个组之前插入(可选���,不传则插入到最后
- */
- public addGroup(groupId: string, beforeGroupId?: string): void {
- if (this.groups.some(g => g.id === groupId)) {
- console.warn('Group ' + groupId + ' already exists');
- return;
- }
-
- const newGroup: ButtonGroup = { id: groupId, buttons: [] };
-
- if (beforeGroupId) {
- const index = this.groups.findIndex(g => g.id === beforeGroupId);
- if (index !== -1) {
- this.groups.splice(index, 0, newGroup);
- } else {
- console.warn(`Target group ${beforeGroupId} not found, appending ${groupId} to end.`);
- this.groups.push(newGroup);
- }
- } else {
- this.groups.push(newGroup);
- }
- }
-
- /**
- * 添加按钮到指定组
- * @param config 按钮配置(必须包含 groupId,可选包含 parentId)
- */
- public addButton(config: ButtonConfig): void {
- const { groupId, parentId } = config;
-
- if (!groupId) {
- throw new Error(`Button ${config.id} config must contain 'groupId'`);
- }
-
- const group = this.groups.find(g => g.id === groupId);
- if (!group) {
- throw new Error(`Group ${groupId} not found. Please call addGroup first.`);
- }
-
- const button: OptButton = {
- ...config,
- children: config.children || []
- };
-
- if (parentId) {
- // 添加为子按钮(菜单项)
- const parentBtn = this.findButton(group.buttons, parentId);
- if (!parentBtn) {
- throw new Error(`Parent button ${parentId} not found in group ${groupId}`);
- }
- if (!parentBtn.children) {
- parentBtn.children = [];
- }
- parentBtn.children.push(button);
- } else {
- // 添加为主按钮
- group.buttons.push(button);
- }
- }
-
- /**
- * 递归查找按钮
- */
- private findButton(buttons: OptButton[], id: string): OptButton | undefined {
- for (const btn of buttons) {
- if (btn.id === id) return btn;
- if (btn.children) {
- const found = this.findButton(btn.children, id);
- if (found) return found;
- }
- }
- return undefined;
- }
-
- /**
- * 初始化组件,加载默认按钮配置
- */
- public async init(): Promise {
- // 动态导入默认按钮配置
- const { homeButton } = await import('./buttons/home');
- const { locationButton } = await import('./buttons/location');
- const { walkMenuButton } = await import('./buttons/walk/walk-menu');
- const { walkPersonButton } = await import('./buttons/walk/walk-person');
- const { walkBirdButton } = await import('./buttons/walk/walk-bird');
- const { settingButton } = await import('./buttons/setting');
- const { infoButton } = await import('./buttons/info');
-
- // 配置默认组和按钮
- this.addGroup('group-1');
- this.addButton(homeButton);
- this.addButton(walkMenuButton);
- this.addButton(walkPersonButton);
- this.addButton(walkBirdButton);
- this.addButton(locationButton);
- this.addGroup('group-2');
- this.addButton(settingButton);
- this.addButton(infoButton);
- this.render();
- }
-
- /**
- * 渲染整个工具栏
- */
- public render(): void {
- this.container.innerHTML = '';
- this.btnRefs.clear();
-
- const wrapper = document.createElement('div');
- wrapper.className = 'toolbar-container';
-
- // 渲染所有组
- this.groups.forEach((group, index) => {
- const groupElement = this.renderGroup(group, index, this.groups.length);
- wrapper.appendChild(groupElement);
- });
-
- this.container.appendChild(wrapper);
- }
-
- /**
- * 渲染单个按钮组
- */
- private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement {
- const groupEl = document.createElement('div');
- groupEl.className = 'opt-btn-group';
-
- if (index < total - 1) {
- groupEl.classList.add('has-divider');
- }
-
- group.buttons.forEach(button => {
- if (this.isVisible(button.id)) {
- const btnWrapper = this.renderButton(button);
- groupEl.appendChild(btnWrapper);
- }
- });
-
- return groupEl;
- }
-
- /**
- * 渲染单个按钮
- */
- private renderButton(button: OptButton): HTMLElement {
- const wrapper = document.createElement('div');
- wrapper.className = 'opt-btn-wrapper';
-
- const btnEl = document.createElement('div');
- btnEl.className = 'opt-btn';
-
- // 设置激活状态
- if (this.activeBtnIds.has(button.id)) {
- btnEl.classList.add('active');
- }
-
- if (button.disabled) {
- btnEl.classList.add('disabled');
- }
-
- if (!this.options.showLabel) {
- btnEl.classList.add('no-label');
- if (button.label) {
- btnEl.title = button.label;
- }
- }
-
- // 渲染图标
- const icon = document.createElement('div');
- icon.className = 'opt-btn-icon';
- icon.innerHTML = this.getIcon(button.icon);
- btnEl.appendChild(icon);
-
- // 渲染标签
- if (this.options.showLabel && button.label) {
- const label = document.createElement('span');
- label.className = 'opt-btn-label';
- label.textContent = button.label;
- btnEl.appendChild(label);
- }
-
- // 如果有子菜单,渲染箭头
- if (button.children && button.children.length > 0) {
- const arrow = document.createElement('span');
- arrow.className = 'opt-btn-arrow';
- arrow.textContent = '▼';
- btnEl.appendChild(arrow);
- }
-
- // 绑定事件
- btnEl.addEventListener('click', () => this.handleClick(button));
- btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl));
- btnEl.addEventListener('mouseleave', () => this.handleMouseLeave());
-
- this.btnRefs.set(button.id, btnEl);
-
- wrapper.appendChild(btnEl);
- return wrapper;
- }
-
- /**
- * 处理按钮点击事件
- */
- private handleClick(button: OptButton): void {
- if (button.disabled) return;
-
- // 如果没有子菜单,直接触发
- if (!button.children || button.children.length === 0) {
- if (button.keepActive) {
- const wasActive = this.activeBtnIds.has(button.id);
- if (wasActive) {
- this.activeBtnIds.delete(button.id);
- } else {
- this.activeBtnIds.add(button.id);
- }
- this.updateButtonState(button.id);
- }
-
- this.closeDropdown();
-
- if (button.onClick) {
- button.onClick(button);
- }
- }
- }
-
- /**
- * 处理子菜单项点击事件
- */
- private handleSubClick(button: OptButton): void {
- if (button.keepActive) {
- const wasActive = this.activeBtnIds.has(button.id);
- if (wasActive) {
- this.activeBtnIds.delete(button.id);
- } else {
- this.activeBtnIds.add(button.id);
- }
- this.updateButtonState(button.id);
- }
-
- this.closeDropdown();
-
- if (button.onClick) {
- button.onClick(button);
- }
- }
-
- /**
- * 处理鼠标移入事件(显示菜单)
- */
- private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void {
- if (this.hoverTimeout) {
- clearTimeout(this.hoverTimeout);
- this.hoverTimeout = null;
- }
-
- if (button.children && button.children.length > 0) {
- this.showDropdown(button, btnEl);
-
- const arrow = btnEl.querySelector('.opt-btn-arrow');
- if (arrow) {
- arrow.classList.add('rotated');
- }
- } else {
- this.closeDropdown();
- }
- }
-
- /**
- * 处理鼠标移出事件(隐藏菜单)
- */
- private handleMouseLeave(): void {
- this.hoverTimeout = window.setTimeout(() => {
- this.closeDropdown();
- }, 200);
- }
-
- /**
- * 显示下拉菜单
- */
- private showDropdown(button: OptButton, btnEl: HTMLElement): void {
- this.closeDropdown();
-
- if (!button.children) return;
-
- const dropdown = document.createElement('div');
- 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 centerX = rect.left + rect.width / 2;
-
- dropdown.style.top = rect.top - 8 + 'px';
- dropdown.style.left = centerX + 'px';
-
- button.children.forEach(subBtn => {
- if (this.isVisible(subBtn.id)) {
- const item = this.renderDropdownItem(subBtn);
- dropdown.appendChild(item);
- }
- });
-
- // 保持菜单显���
- dropdown.addEventListener('mouseenter', () => {
- if (this.hoverTimeout) {
- clearTimeout(this.hoverTimeout);
- this.hoverTimeout = null;
- }
- });
-
- dropdown.addEventListener('mouseleave', () => this.handleMouseLeave());
-
- document.body.appendChild(dropdown);
- this.dropdownElement = dropdown;
- }
-
- /**
- * 渲染下拉菜单项
- */
- private renderDropdownItem(button: OptButton): HTMLElement {
- const item = document.createElement('div');
- item.className = 'opt-btn-dropdown-item';
-
- const icon = document.createElement('div');
- icon.className = 'opt-btn-icon small';
- icon.innerHTML = this.getIcon(button.icon);
- item.appendChild(icon);
-
- if (this.options.showLabel) {
- const label = document.createElement('span');
- label.textContent = button.label;
- item.appendChild(label);
- }
-
- item.addEventListener('click', (e) => {
- e.stopPropagation();
- this.handleSubClick(button);
- });
-
- return item;
- }
-
- /**
- * 关闭所有下拉菜单
- */
- private closeDropdown(): void {
- if (this.dropdownElement) {
- this.dropdownElement.remove();
- this.dropdownElement = null;
- }
-
- this.btnRefs.forEach(btnEl => {
- const arrow = btnEl.querySelector('.opt-btn-arrow');
- if (arrow) {
- arrow.classList.remove('rotated');
- }
- });
- }
-
- /**
- * 更新按钮的激活状态样式
- */
- private updateButtonState(buttonId: string): void {
- const btnEl = this.btnRefs.get(buttonId);
- if (btnEl) {
- if (this.activeBtnIds.has(buttonId)) {
- btnEl.classList.add('active');
- } else {
- btnEl.classList.remove('active');
- }
- }
- }
-
- /**
- * 获取图标 SVG 字符串
- */
- private getIcon(icon?: string): string {
- 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 {
- return this.options.visibility?.[id] !== false;
- }
-
- /**
- * 销毁组件,清理资源
- */
- public destroy(): void {
- this.closeDropdown();
- if (this.hoverTimeout) {
- clearTimeout(this.hoverTimeout);
- }
- this.container.innerHTML = '';
- this.btnRefs.clear();
- this.activeBtnIds.clear();
- this.groups = [];
- }
-}
\ No newline at end of file
diff --git a/src/toolbar/index.type.ts b/src/toolbar/index.type.ts
deleted file mode 100644
index fbd6ac3..0000000
--- a/src/toolbar/index.type.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-export type ButtonType = 'button' | 'menu';
-
-/**
- * 按钮配置接口(用于外部定义按钮)
- */
-export interface ButtonConfig {
- /** 唯一标识 */
- id: string;
- /** 按钮类型:普通按钮或菜单按钮 */
- type: ButtonType;
- /** 按钮显示文字 */
- label: string;
- /** SVG 图标(内联 SVG 字符串) */
- icon?: string;
- /** 是否保持激活状态(默认 false) */
- keepActive?: boolean;
- /** 是否禁用 */
- disabled?: boolean;
- /** 点击回调函数 */
- onClick?: (button: OptButton) => void;
- /** 子按钮配置(可选,用于菜单按钮) */
- children?: ButtonConfig[];
-
- /** 所属组ID */
- groupId?: string;
- /** 父按钮ID(如果是子按钮,则必填) */
- parentId?: string;
-}
-
-/**
- * 操作按钮接口(内部使用,继承配置)
- */
-export interface OptButton extends ButtonConfig {
- /** 内部使用的子按钮列表 */
- children?: OptButton[];
-}
-
-/**
- * 按钮组接口
- */
-export interface ButtonGroup {
- /** 组 ID */
- id: string;
- /** 组内按钮列表 */
- 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 配置选项
- */
-export interface OptBtnGroupsOptions extends ToolbarColors {
- /** 容器元素或 ID */
- container: HTMLElement | string;
- /** 是否显示标签 */
- showLabel?: boolean;
- /** 按钮可见性配置 Map */
- visibility?: Record;
-}
-
-/**
- * 点击事件载荷
- */
-export interface ClickPayload {
- /** 被点击的按钮对象 */
- button: OptButton;
- /** 触发的动作类型 */
- action: 'activate' | 'deactivate' | 'trigger';
- /** 当前激活状态 */
- isActive?: boolean;
-}
diff --git a/src/types/component.ts b/src/types/component.ts
new file mode 100644
index 0000000..51d3210
--- /dev/null
+++ b/src/types/component.ts
@@ -0,0 +1,32 @@
+import { ThemeConfig } from '../themes/types';
+
+/**
+ * BIM 引擎组件通用接口
+ * 所有受引擎管理的 UI 组件都必须实现此接口
+ */
+export interface IBimComponent {
+ /**
+ * 初始化组件
+ * 用于创建 DOM、绑定事件、加载资源等
+ * 支持同步或异步操作
+ */
+ init(): void | Promise;
+
+ /**
+ * 设置主题
+ * 组件应在此方法中将 ThemeConfig 映射为自身的 CSS 变量或样式
+ */
+ setTheme(theme: ThemeConfig): void;
+
+
+ /**
+ * 设置语言
+ */
+ setLocales(): void;
+
+ /**
+ * 销毁组件
+ * 清理 DOM 事件监听、定时器和引用
+ */
+ destroy(): void;
+}
\ No newline at end of file