diff --git a/demo.html b/demo.html deleted file mode 100644 index 71575dc..0000000 --- a/demo.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - BIM Engine SDK Demo - - - - -
- - - - \ No newline at end of file diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..5915251 --- /dev/null +++ b/demo/index.html @@ -0,0 +1,171 @@ + + + + + + + BIM Engine SDK Demo + + + + +
+ +
+ + + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/dist/bim-engine-sdk.es.js b/dist/bim-engine-sdk.es.js index cf7c8aa..d6c1d92 100644 --- a/dist/bim-engine-sdk.es.js +++ b/dist/bim-engine-sdk.es.js @@ -1,210 +1,631 @@ -(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}.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:#111111e0;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;color:#ccc;transition:all .2s;border-bottom:2px solid transparent}.opt-btn:hover{background-color:#444;color:#fff}.opt-btn.active{background-color:#ffffff26;color:#fff;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}.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:#111111e0;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:#b3b4b4;cursor:pointer;transition:background .2s;white-space:nowrap;min-width:50px;min-height:50px;padding:4px}.opt-btn-dropdown-item:last-child{border-bottom:none}.opt-btn-dropdown-item:hover{background-color:#444;color:#fff}.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}.opt-btn.no-label .opt-btn-icon{width:32px;height:32px}")),document.head.appendChild(o)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})(); -class a { +(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; - // 改用 Array 存储 Group,方便控制顺序 + /** 按钮组列表,按顺序存储 */ groups = []; + /** 当前处于激活状态的按钮 ID 集合 */ activeBtnIds = /* @__PURE__ */ new Set(); + /** 按钮 DOM 元素的引用映射,方便快速查找 */ btnRefs = /* @__PURE__ */ new Map(); + /** 当��显示的下拉菜单元素 */ dropdownElement = null; + /** 鼠标悬停计时器,用于处理菜单显示的防抖 */ hoverTimeout = null; + /** 默认图标 SVG */ DEFAULT_ICON = ''; + /** + * 构造函数 + * @param options 配置选项 + */ 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 = { + 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.initContainer(), this.applyStyles(); } + /** + * 初始化容器 + */ initContainer() { - this.container.innerHTML = ""; + this.container.innerHTML = "", this.container.classList.add("toolbar-root"); + } + /** + * 应用样式配置到 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); + } + /** + * 更新颜色配置 + * @param colors 颜色配置对象 + */ + setColors(t) { + this.options = { ...this.options, ...t }, this.applyStyles(); } /** * 添加按钮组 * @param groupId 组ID - * @param beforeGroupId 在哪个组之前插入(可选),不传则插入到最后 + * @param beforeGroupId 在哪个组之前插入(可选���,不传则插入到最后 */ - addGroup(t, e) { - if (this.groups.some((n) => n.id === t)) { + addGroup(t, o) { + if (this.groups.some((i) => i.id === t)) { console.warn("Group " + t + " already exists"); return; } - const i = { id: t, buttons: [] }; - if (e) { - const n = this.groups.findIndex((s) => s.id === e); - n !== -1 ? this.groups.splice(n, 0, i) : (console.warn(`Target group ${e} not found, appending ${t} to end.`), this.groups.push(i)); + 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(i); + this.groups.push(e); } /** - * 添加按钮 + * 添加按钮到指定组 * @param config 按钮配置(必须包含 groupId,可选包含 parentId) */ addButton(t) { - const { groupId: e, parentId: i } = t; - if (!e) + const { groupId: o, parentId: e } = t; + if (!o) throw new Error(`Button ${t.id} config must contain 'groupId'`); - const n = this.groups.find((o) => o.id === e); - if (!n) - throw new Error(`Group ${e} not found. Please call addGroup first.`); - const s = { + 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 (i) { - const o = this.findButton(n.buttons, i); - if (!o) - throw new Error(`Parent button ${i} not found in group ${e}`); - o.children || (o.children = []), o.children.push(s); + 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 - n.buttons.push(s); + i.buttons.push(n); } - findButton(t, e) { - for (const i of t) { - if (i.id === e) return i; - if (i.children) { - const n = this.findButton(i.children, e); - if (n) return 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 import("./index-CAJWny5G.mjs"), { locationButton: e } = await import("./index-C12x1apF.mjs"), { walkMenuButton: i } = await import("./index-Wpi9Br9A.mjs"), { walkPersonButton: n } = await import("./index-BXbORK0j.mjs"), { walkBirdButton: s } = await import("./index-Djlk5GIH.mjs"), { settingButton: o } = await import("./index-DsRG5l_h.mjs"), { infoButton: r } = await import("./index-DvZ5eiUH.mjs"); - this.addGroup("group-1"), this.addButton(t), this.addButton(i), this.addButton(n), this.addButton(s), this.addButton(e), this.addGroup("group-2"), this.addButton(o), this.addButton(r), this.render(); + 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((e, i) => { - const n = this.renderGroup(e, i, this.groups.length); - t.appendChild(n); + 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, e, i) { - const n = document.createElement("div"); - return n.className = "opt-btn-group", e < i - 1 && n.classList.add("has-divider"), t.buttons.forEach((s) => { - if (this.isVisible(s.id)) { - const o = this.renderButton(s); - n.appendChild(o); - } - }), n; - } - renderButton(t) { - const e = document.createElement("div"); - e.className = "opt-btn-wrapper"; + /** + * 渲染单个按钮组 + */ + renderGroup(t, o, e) { const i = document.createElement("div"); - i.className = "opt-btn", this.activeBtnIds.has(t.id) && i.classList.add("active"), t.disabled && i.classList.add("disabled"), this.options.showLabel || (i.classList.add("no-label"), t.label && (i.title = t.label)); - const n = document.createElement("div"); - if (n.className = "opt-btn-icon", n.innerHTML = this.getIcon(t.icon), i.appendChild(n), this.options.showLabel && t.label) { - const s = document.createElement("span"); - s.className = "opt-btn-label", s.textContent = t.label, i.appendChild(s); + 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); + } + }), 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 s = document.createElement("span"); - s.className = "opt-btn-arrow", s.textContent = "▼", i.appendChild(s); + const n = document.createElement("span"); + n.className = "opt-btn-arrow", n.textContent = "▼", e.appendChild(n); } - return i.addEventListener("click", () => this.handleClick(t)), i.addEventListener("mouseenter", () => this.handleMouseEnter(t, i)), i.addEventListener("mouseleave", () => this.handleMouseLeave()), this.btnRefs.set(t.id, i), e.appendChild(i), e; + 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, e) { + /** + * 处理鼠标移入事件(显示菜单) + */ + handleMouseEnter(t, o) { if (this.hoverTimeout && (clearTimeout(this.hoverTimeout), this.hoverTimeout = null), t.children && t.children.length > 0) { - this.showDropdown(t, e); - const i = e.querySelector(".opt-btn-arrow"); - i && i.classList.add("rotated"); + 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, e) { + /** + * 显示下拉菜单 + */ + showDropdown(t, o) { if (this.closeDropdown(), !t.children) return; - const i = document.createElement("div"); - i.className = "opt-btn-dropdown"; - const n = e.getBoundingClientRect(), s = n.left + n.width / 2; - i.style.top = n.top - 8 + "px", i.style.left = s + "px", t.children.forEach((o) => { - if (this.isVisible(o.id)) { - const r = this.renderDropdownItem(o); - i.appendChild(r); - } - }), i.addEventListener("mouseenter", () => { - this.hoverTimeout && (clearTimeout(this.hoverTimeout), this.hoverTimeout = null); - }), i.addEventListener("mouseleave", () => this.handleMouseLeave()), document.body.appendChild(i), this.dropdownElement = i; - } - renderDropdownItem(t) { const e = document.createElement("div"); - e.className = "opt-btn-dropdown-item"; - const i = document.createElement("div"); - if (i.className = "opt-btn-icon small", i.innerHTML = this.getIcon(t.icon), e.appendChild(i), this.options.showLabel) { - const n = document.createElement("span"); - n.textContent = t.label, e.appendChild(n); - } - return e.addEventListener("click", (n) => { - n.stopPropagation(), this.handleSubClick(t); - }), e; + 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 a = this.renderDropdownItem(r); + e.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; } + /** + * 渲染下拉菜单项 + */ + 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 e = t.querySelector(".opt-btn-arrow"); - e && e.classList.remove("rotated"); + const o = t.querySelector(".opt-btn-arrow"); + o && o.classList.remove("rotated"); }); } + /** + * 更新按钮的激活状态样式 + */ updateButtonState(t) { - const e = this.btnRefs.get(t); - e && (this.activeBtnIds.has(t) ? e.classList.add("active") : e.classList.remove("active")); + const o = this.btnRefs.get(t); + o && (this.activeBtnIds.has(t) ? o.classList.add("active") : o.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(); + } + /** + * 设置是否显示标签 + * @param show 是否显示 + */ + setShowLabel(t) { + this.options.showLabel = t, this.render(); + } + /** + * 设置背景颜色 (兼容旧接口) + * @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 = []; } } -class c { - container; +class g { + /** 内部工具栏组件实例 */ optBtnGroups = null; + /** 工具栏挂载的容器 */ + container; + /** + * 构造函数 + * @param container 工具栏挂载的容器元素 + */ constructor(t) { - const e = typeof t == "string" ? document.getElementById(t) : t; - if (!e) throw new Error("Container not found"); - this.container = e, this.init(); + this.container = t, this.init(); } + /** + * 初始化工具栏 + */ init() { - this.container.innerHTML = ""; - const t = document.createElement("div"); - t.className = "bim-engine-wrapper"; - const e = document.createElement("h1"); - e.textContent = "BimEngine", e.className = "bim-engine-title"; - const i = document.createElement("p"); - i.textContent = "这是一个使用BIM-ENGINE。", i.className = "bim-engine-desc"; - const n = document.createElement("div"); - n.id = "opt-btn-groups", n.className = "bim-engine-opt-btn-container", t.appendChild(e), t.appendChild(i), t.appendChild(n), this.container.appendChild(t), this.initOptBtnGroups(n); - } - initOptBtnGroups(t) { - this.optBtnGroups = new a({ - container: t, + this.optBtnGroups = new f({ + container: this.container, showLabel: !0 - }), this.optBtnGroups.init().catch((e) => { - console.error("Failed to initialize OptBtnGroups:", e); + }), this.optBtnGroups.init().catch((t) => { + console.error("Failed to initialize OptBtnGroups:", t); }); } + /** + * 添加一个工具栏按钮组 + * @param groupId 新组的 ID + * @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后 + */ + addGroup(t, o) { + this.optBtnGroups ? (this.optBtnGroups.addGroup(t, o), this.optBtnGroups.render()) : console.warn("Toolbar not initialized yet."); + } + /** + * 添加一个工具栏按钮 + * @param config 按钮配置对象 + */ + addButton(t) { + this.optBtnGroups ? (this.optBtnGroups.addButton(t), this.optBtnGroups.render()) : console.warn("Toolbar not initialized yet."); + } + /** + * 设置按钮的可见性 + * @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.container.innerHTML = ""; + this.optBtnGroups && (this.optBtnGroups.destroy(), this.optBtnGroups = null); + } +} +class u { + element; + options; + container; + header; + contentArea; + _isDestroyed = !1; + /** + * 构造函数 + * @param options 弹窗配置选项 + */ + 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(); + } + /** + * 创建弹窗的 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 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, a = 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 - a) / 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 - a) / 2; + break; + case "right-center": + e = n - r, i = (s - a) / 2; + break; + case "bottom-left": + e = 0, i = s - a; + break; + case "bottom-center": + e = (n - r) / 2, i = s - a; + break; + case "bottom-right": + e = n - r, i = s - a; + break; + default: + e = (n - r) / 2, i = (s - 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`; + } + /** + * 初始化拖拽功能 + */ + 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`; + }, 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 = (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); + }, 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`; + }, a = () => { + document.removeEventListener("mousemove", r), document.removeEventListener("mouseup", a); + }; + t.addEventListener("mousedown", s); + } + /** + * 动态设置内容 + * @param content 内容元素或 HTML 字符串 + */ + 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; + /** + * 构造函数 + * @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 i = document.createElement("ul"); + i.innerHTML = ` +
  • Name: Sample Project
  • +
  • Version: 1.0.0
  • +
  • Date: ${(/* @__PURE__ */ 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 u({ + 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; + /** + * 构造函数 + * @param container 弹窗挂载的目标容器 + */ + constructor(t) { + this.container = t; + } + /** + * 创建一个通用弹窗 + * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器) + * @returns BimDialog 实例 + */ + create(t) { + return new u({ + container: this.container, + ...t + }); + } + /** + * 显示二次封装的模型信息弹窗 + * 演示如何调用特定的业务弹窗组件 + */ + showInfoDialog() { + new v(this.container); + } +} +class C { + /** 主容器元素 */ + container; + /** 内部包装器元素,用于承载所有 UI 组件 */ + wrapper = null; + /** 工具栏管理器实例 */ + toolbar = null; + /** 弹窗管理器实例 */ + dialog = null; + /** + * 构造函数 + * @param container 容器元素或容器 ID + */ + constructor(t) { + const o = typeof t == "string" ? document.getElementById(t) : t; + if (!o) throw new Error("Container not found"); + this.container = o, this.init(); + } + /** + * 初始化方法 + * 创建 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); + } + /** + * 销毁实例 + * 清理所有资源和 DOM 元素 + */ + destroy() { + this.toolbar && (this.toolbar.destroy(), this.toolbar = null), this.dialog = null, this.container.innerHTML = ""; } } export { - c as BimEngine, - a as OptBtnGroups + C as BimEngine, + f as OptBtnGroups }; //# 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 1f6acae..1938650 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/bim-engine.ts"],"sourcesContent":["import './index.css';\nimport type {\n OptButton,\n ButtonGroup,\n OptBtnGroupsOptions,\n ButtonConfig\n} from './index.type';\n\nexport class OptBtnGroups {\n private container: HTMLElement;\n private options: OptBtnGroupsOptions;\n // 改用 Array 存储 Group,方便控制顺序\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\n private readonly DEFAULT_ICON = '';\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 }\n\n private initContainer(): void {\n this.container.innerHTML = '';\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 // Add as sub-button\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 // Add as main button\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 async init(): Promise {\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 // 添加组1\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 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 // 直接遍历数组,顺序由 addGroup 控制\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 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 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 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 const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.innerHTML = this.getIcon(button.icon);\n btnEl.appendChild(icon);\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 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 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 private handleClick(button: OptButton): void {\n if (button.disabled) return;\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 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 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 private handleMouseLeave(): void {\n this.hoverTimeout = window.setTimeout(() => {\n this.closeDropdown();\n }, 200);\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 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 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 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 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 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 private getIcon(icon?: string): string {\n return icon || this.DEFAULT_ICON;\n }\n\n private isVisible(id: string): boolean {\n return this.options.visibility?.[id] !== false;\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 './bim-engine.css';\nimport { OptBtnGroups } from './toolbar';\n\nexport class BimEngine {\n private container: HTMLElement;\n private optBtnGroups: OptBtnGroups | null = null;\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 private init() {\n // 1. 清空容器可能存在的旧内容\n this.container.innerHTML = '';\n\n // 2. 创建外层容器 div\n const wrapper = document.createElement('div');\n 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 wrapper.appendChild(title);\n wrapper.appendChild(desc);\n wrapper.appendChild(btnGroupContainer); // 将按钮组放入 wrapper 中\n\n // 8. 挂载到主容器\n this.container.appendChild(wrapper);\n\n // 9. 初始化操作按钮组\n this.initOptBtnGroups(btnGroupContainer);\n }\n\n private initOptBtnGroups(container: HTMLElement) {\n this.optBtnGroups = new OptBtnGroups({\n 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 public destroy() {\n if (this.optBtnGroups) {\n this.optBtnGroups.destroy();\n this.optBtnGroups = null;\n }\n this.container.innerHTML = '';\n }\n}"],"names":["OptBtnGroups","options","el","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","BimEngine","container","title","desc","btnGroupContainer","err"],"mappings":"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"} \ No newline at end of file +{"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":"AAaO,MAAMA,EAAa;AAAA;AAAA,EAEd;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA,SAAwB,CAAA;AAAA;AAAA,EAExB,mCAAgC,IAAA;AAAA;AAAA,EAEhC,8BAAwC,IAAA;AAAA;AAAA,EAExC,kBAAsC;AAAA;AAAA,EAEtC,eAA8B;AAAA;AAAA,EAGrB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhC,YAAYC,GAA8B;AACtC,UAAMC,IAAK,OAAOD,EAAQ,aAAc,WAClC,SAAS,eAAeA,EAAQ,SAAS,IACzCA,EAAQ;AAEd,QAAI,CAACC,EAAI,OAAM,IAAI,MAAM,qBAAqB;AAE9C,SAAK,YAAYA,GACjB,KAAK,UAAU;AAAA,MACX,WAAW;AAAA,MACX,YAAY,CAAA;AAAA,MACZ,GAAGD;AAAA,IAAA,GAGP,KAAK,cAAA,GACL,KAAK,YAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC1B,SAAK,UAAU,YAAY,IAC3B,KAAK,UAAU,UAAU,IAAI,cAAc;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AACxB,UAAME,IAAQ,KAAK,UAAU;AAC7B,IAAI,KAAK,QAAQ,mBAAiBA,EAAM,YAAY,oBAAoB,KAAK,QAAQ,eAAe,GAChG,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,UAAUC,GAA6B;AAE1C,SAAK,UAAU,EAAE,GAAG,KAAK,SAAS,GAAGA,EAAA,GAErC,KAAK,YAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,SAASC,GAAiBC,GAA8B;AAC3D,QAAI,KAAK,OAAO,KAAK,OAAKC,EAAE,OAAOF,CAAO,GAAG;AACzC,cAAQ,KAAK,WAAWA,IAAU,iBAAiB;AACnD;AAAA,IACJ;AAEA,UAAMG,IAAwB,EAAE,IAAIH,GAAS,SAAS,CAAA,EAAC;AAEvD,QAAIC,GAAe;AACf,YAAMG,IAAQ,KAAK,OAAO,UAAU,CAAAF,MAAKA,EAAE,OAAOD,CAAa;AAC/D,MAAIG,MAAU,KACV,KAAK,OAAO,OAAOA,GAAO,GAAGD,CAAQ,KAErC,QAAQ,KAAK,gBAAgBF,CAAa,yBAAyBD,CAAO,UAAU,GACpF,KAAK,OAAO,KAAKG,CAAQ;AAAA,IAEjC;AACI,WAAK,OAAO,KAAKA,CAAQ;AAAA,EAEjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAUE,GAA4B;AACzC,UAAM,EAAE,SAAAL,GAAS,UAAAM,EAAA,IAAaD;AAE9B,QAAI,CAACL;AACD,YAAM,IAAI,MAAM,UAAUK,EAAO,EAAE,gCAAgC;AAGvE,UAAME,IAAQ,KAAK,OAAO,KAAK,CAAAL,MAAKA,EAAE,OAAOF,CAAO;AACpD,QAAI,CAACO;AACD,YAAM,IAAI,MAAM,SAASP,CAAO,yCAAyC;AAG7E,UAAMQ,IAAoB;AAAA,MACtB,GAAGH;AAAA,MACH,UAAUA,EAAO,YAAY,CAAA;AAAA,IAAC;AAGlC,QAAIC,GAAU;AAEV,YAAMG,IAAY,KAAK,WAAWF,EAAM,SAASD,CAAQ;AACzD,UAAI,CAACG;AACD,cAAM,IAAI,MAAM,iBAAiBH,CAAQ,uBAAuBN,CAAO,EAAE;AAE7E,MAAKS,EAAU,aACXA,EAAU,WAAW,CAAA,IAEzBA,EAAU,SAAS,KAAKD,CAAM;AAAA,IAClC;AAEI,MAAAD,EAAM,QAAQ,KAAKC,CAAM;AAAA,EAEjC;AAAA;AAAA;AAAA;AAAA,EAKQ,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;AAAA;AAAA;AAAA,EAKA,MAAa,OAAsB;AAE/B,UAAM,EAAE,YAAAC,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;AAGpD,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,GACzB,KAAK,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKO,SAAe;AAClB,SAAK,UAAU,YAAY,IAC3B,KAAK,QAAQ,MAAA;AAEb,UAAMC,IAAU,SAAS,cAAc,KAAK;AAC5C,IAAAA,EAAQ,YAAY,qBAGpB,KAAK,OAAO,QAAQ,CAACd,GAAOH,MAAU;AAClC,YAAMkB,IAAe,KAAK,YAAYf,GAAOH,GAAO,KAAK,OAAO,MAAM;AACtE,MAAAiB,EAAQ,YAAYC,CAAY;AAAA,IACpC,CAAC,GAED,KAAK,UAAU,YAAYD,CAAO;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAYd,GAAoBH,GAAemB,GAA4B;AAC/E,UAAMC,IAAU,SAAS,cAAc,KAAK;AAC5C,WAAAA,EAAQ,YAAY,iBAEhBpB,IAAQmB,IAAQ,KAChBC,EAAQ,UAAU,IAAI,aAAa,GAGvCjB,EAAM,QAAQ,QAAQ,CAAAC,MAAU;AAC5B,UAAI,KAAK,UAAUA,EAAO,EAAE,GAAG;AAC3B,cAAMiB,IAAa,KAAK,aAAajB,CAAM;AAC3C,QAAAgB,EAAQ,YAAYC,CAAU;AAAA,MAClC;AAAA,IACJ,CAAC,GAEMD;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAahB,GAAgC;AACjD,UAAMa,IAAU,SAAS,cAAc,KAAK;AAC5C,IAAAA,EAAQ,YAAY;AAEpB,UAAMK,IAAQ,SAAS,cAAc,KAAK;AAC1C,IAAAA,EAAM,YAAY,WAGd,KAAK,aAAa,IAAIlB,EAAO,EAAE,KAC/BkB,EAAM,UAAU,IAAI,QAAQ,GAG5BlB,EAAO,YACPkB,EAAM,UAAU,IAAI,UAAU,GAG7B,KAAK,QAAQ,cACdA,EAAM,UAAU,IAAI,UAAU,GAC1BlB,EAAO,UACPkB,EAAM,QAAQlB,EAAO;AAK7B,UAAMmB,IAAO,SAAS,cAAc,KAAK;AAMzC,QALAA,EAAK,YAAY,gBACjBA,EAAK,YAAY,KAAK,QAAQnB,EAAO,IAAI,GACzCkB,EAAM,YAAYC,CAAI,GAGlB,KAAK,QAAQ,aAAanB,EAAO,OAAO;AACxC,YAAMoB,IAAQ,SAAS,cAAc,MAAM;AAC3C,MAAAA,EAAM,YAAY,iBAClBA,EAAM,cAAcpB,EAAO,OAC3BkB,EAAM,YAAYE,CAAK;AAAA,IAC3B;AAGA,QAAIpB,EAAO,YAAYA,EAAO,SAAS,SAAS,GAAG;AAC/C,YAAMqB,IAAQ,SAAS,cAAc,MAAM;AAC3C,MAAAA,EAAM,YAAY,iBAClBA,EAAM,cAAc,KACpBH,EAAM,YAAYG,CAAK;AAAA,IAC3B;AAGA,WAAAH,EAAM,iBAAiB,SAAS,MAAM,KAAK,YAAYlB,CAAM,CAAC,GAC9DkB,EAAM,iBAAiB,cAAc,MAAM,KAAK,iBAAiBlB,GAAQkB,CAAK,CAAC,GAC/EA,EAAM,iBAAiB,cAAc,MAAM,KAAK,kBAAkB,GAElE,KAAK,QAAQ,IAAIlB,EAAO,IAAIkB,CAAK,GAEjCL,EAAQ,YAAYK,CAAK,GAClBL;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAYb,GAAyB;AACzC,IAAIA,EAAO,aAGP,CAACA,EAAO,YAAYA,EAAO,SAAS,WAAW,OAC3CA,EAAO,eACW,KAAK,aAAa,IAAIA,EAAO,EAAE,IAE7C,KAAK,aAAa,OAAOA,EAAO,EAAE,IAElC,KAAK,aAAa,IAAIA,EAAO,EAAE,GAEnC,KAAK,kBAAkBA,EAAO,EAAE,IAGpC,KAAK,cAAA,GAEDA,EAAO,WACPA,EAAO,QAAQA,CAAM;AAAA,EAGjC;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAeA,GAAyB;AAC5C,IAAIA,EAAO,eACW,KAAK,aAAa,IAAIA,EAAO,EAAE,IAE7C,KAAK,aAAa,OAAOA,EAAO,EAAE,IAElC,KAAK,aAAa,IAAIA,EAAO,EAAE,GAEnC,KAAK,kBAAkBA,EAAO,EAAE,IAGpC,KAAK,cAAA,GAEDA,EAAO,WACPA,EAAO,QAAQA,CAAM;AAAA,EAE7B;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiBA,GAAmBkB,GAA0B;AAMlE,QALI,KAAK,iBACL,aAAa,KAAK,YAAY,GAC9B,KAAK,eAAe,OAGpBlB,EAAO,YAAYA,EAAO,SAAS,SAAS,GAAG;AAC/C,WAAK,aAAaA,GAAQkB,CAAK;AAE/B,YAAMG,IAAQH,EAAM,cAAc,gBAAgB;AAClD,MAAIG,KACAA,EAAM,UAAU,IAAI,SAAS;AAAA,IAErC;AACI,WAAK,cAAA;AAAA,EAEb;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC7B,SAAK,eAAe,OAAO,WAAW,MAAM;AACxC,WAAK,cAAA;AAAA,IACT,GAAG,GAAG;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAarB,GAAmBkB,GAA0B;AAG9D,QAFA,KAAK,cAAA,GAED,CAAClB,EAAO,SAAU;AAEtB,UAAMsB,IAAW,SAAS,cAAc,KAAK;AAC7C,IAAAA,EAAS,YAAY;AAQrB,UAAMhC,IAAQgC,EAAS;AACvB,IAAI,KAAK,QAAQ,mBAAiBhC,EAAM,YAAY,oBAAoB,KAAK,QAAQ,eAAe,GAChG,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;AAE/G,UAAMiC,IAAOL,EAAM,sBAAA,GACbM,IAAUD,EAAK,OAAOA,EAAK,QAAQ;AAEzC,IAAAD,EAAS,MAAM,MAAMC,EAAK,MAAM,IAAI,MACpCD,EAAS,MAAM,OAAOE,IAAU,MAEhCxB,EAAO,SAAS,QAAQ,CAAAyB,MAAU;AAC9B,UAAI,KAAK,UAAUA,EAAO,EAAE,GAAG;AAC3B,cAAMC,IAAO,KAAK,mBAAmBD,CAAM;AAC3C,QAAAH,EAAS,YAAYI,CAAI;AAAA,MAC7B;AAAA,IACJ,CAAC,GAGDJ,EAAS,iBAAiB,cAAc,MAAM;AAC1C,MAAI,KAAK,iBACL,aAAa,KAAK,YAAY,GAC9B,KAAK,eAAe;AAAA,IAE5B,CAAC,GAEDA,EAAS,iBAAiB,cAAc,MAAM,KAAK,kBAAkB,GAErE,SAAS,KAAK,YAAYA,CAAQ,GAClC,KAAK,kBAAkBA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmBtB,GAAgC;AACvD,UAAM0B,IAAO,SAAS,cAAc,KAAK;AACzC,IAAAA,EAAK,YAAY;AAEjB,UAAMP,IAAO,SAAS,cAAc,KAAK;AAKzC,QAJAA,EAAK,YAAY,sBACjBA,EAAK,YAAY,KAAK,QAAQnB,EAAO,IAAI,GACzC0B,EAAK,YAAYP,CAAI,GAEjB,KAAK,QAAQ,WAAW;AACxB,YAAMC,IAAQ,SAAS,cAAc,MAAM;AAC3C,MAAAA,EAAM,cAAcpB,EAAO,OAC3B0B,EAAK,YAAYN,CAAK;AAAA,IAC1B;AAEA,WAAAM,EAAK,iBAAiB,SAAS,CAACC,MAAM;AAClC,MAAAA,EAAE,gBAAA,GACF,KAAK,eAAe3B,CAAM;AAAA,IAC9B,CAAC,GAEM0B;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC1B,IAAI,KAAK,oBACL,KAAK,gBAAgB,OAAA,GACrB,KAAK,kBAAkB,OAG3B,KAAK,QAAQ,QAAQ,CAAAR,MAAS;AAC1B,YAAMG,IAAQH,EAAM,cAAc,gBAAgB;AAClD,MAAIG,KACAA,EAAM,UAAU,OAAO,SAAS;AAAA,IAExC,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkBO,GAAwB;AAC9C,UAAMV,IAAQ,KAAK,QAAQ,IAAIU,CAAQ;AACvC,IAAIV,MACI,KAAK,aAAa,IAAIU,CAAQ,IAC9BV,EAAM,UAAU,IAAI,QAAQ,IAE5BA,EAAM,UAAU,OAAO,QAAQ;AAAA,EAG3C;AAAA;AAAA;AAAA;AAAA,EAKQ,QAAQC,GAAuB;AACnC,WAAOA,KAAQ,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,uBAAuBS,GAAkBC,GAAwB;AACpE,IAAK,KAAK,QAAQ,eACd,KAAK,QAAQ,aAAa,CAAA,IAE9B,KAAK,QAAQ,WAAWD,CAAQ,IAAIC,GACpC,KAAK,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,aAAaC,GAAqB;AACrC,SAAK,QAAQ,YAAYA,GACzB,KAAK,OAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,mBAAmBC,GAAqB;AAC3C,SAAK,UAAU,EAAE,iBAAiBA,EAAA,CAAO;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAU5B,GAAqB;AACnC,WAAO,KAAK,QAAQ,aAAaA,CAAE,MAAM;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKO,UAAgB;AACnB,SAAK,cAAA,GACD,KAAK,gBACL,aAAa,KAAK,YAAY,GAElC,KAAK,UAAU,YAAY,IAC3B,KAAK,QAAQ,MAAA,GACb,KAAK,aAAa,MAAA,GAClB,KAAK,SAAS,CAAA;AAAA,EAClB;AACJ;AC/gBO,MAAM6B,EAAe;AAAA;AAAA,EAEhB,eAAoC;AAAA;AAAA,EAEpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,YAAYC,GAAwB;AAChC,SAAK,YAAYA,GACjB,KAAK,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAO;AACX,SAAK,eAAe,IAAI9C,EAAa;AAAA,MACjC,WAAW,KAAK;AAAA,MAChB,WAAW;AAAA,IAAA,CACd,GAGD,KAAK,aAAa,KAAA,EAAO,MAAM,CAAA+C,MAAO;AAClC,cAAQ,MAAM,sCAAsCA,CAAG;AAAA,IAC3D,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,SAAS1C,GAAiBC,GAAwB;AACrD,IAAI,KAAK,gBACL,KAAK,aAAa,SAASD,GAASC,CAAa,GACjD,KAAK,aAAa,OAAA,KAElB,QAAQ,KAAK,8BAA8B;AAAA,EAEnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAUI,GAAsB;AACnC,IAAI,KAAK,gBACL,KAAK,aAAa,UAAUA,CAAM,GAClC,KAAK,aAAa,OAAA,KAElB,QAAQ,KAAK,8BAA8B;AAAA,EAEnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,oBAAoB+B,GAAkBC,GAAkB;AAC3D,IAAI,KAAK,eACL,KAAK,aAAa,uBAAuBD,GAAUC,CAAO,IAE1D,QAAQ,KAAK,8BAA8B;AAAA,EAEnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,aAAaC,GAAe;AAC/B,IAAI,KAAK,eACL,KAAK,aAAa,aAAaA,CAAI,IAEnC,QAAQ,KAAK,8BAA8B;AAAA,EAEnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,WAAWD,GAAkB;AAChC,SAAK,UAAU,MAAM,UAAUA,IAAU,UAAU;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,mBAAmBE,GAAe;AACrC,IAAI,KAAK,eACL,KAAK,aAAa,mBAAmBA,CAAK,IAE1C,QAAQ,KAAK,8BAA8B;AAAA,EAEnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAUxC,GAAuB;AACpC,IAAI,KAAK,eACL,KAAK,aAAa,UAAUA,CAAM,IAElC,QAAQ,KAAK,8BAA8B;AAAA,EAEnD;AAAA;AAAA;AAAA;AAAA,EAKO,UAAU;AACb,IAAI,KAAK,iBACL,KAAK,aAAa,QAAA,GAClB,KAAK,eAAe;AAAA,EAE5B;AACJ;AC5HO,MAAM4C,EAAU;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvB,YAAY/C,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,EAKQ,YAAyB;AAC7B,UAAMC,IAAK,SAAS,cAAc,KAAK;AACvC,IAAAA,EAAG,YAAY,cAEX,KAAK,QAAQ,OAAIA,EAAG,KAAK,KAAK,QAAQ;AAG1C,UAAMC,IAAQD,EAAG;AACjB,IAAI,KAAK,QAAQ,mBAAiBC,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,QAAQD,GAAI,KAAK,QAAQ,OAAO,KAAK,QAAQ,MAAM;AAGxD,UAAM+C,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,SAAS;AAE1C,UAAMC,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,GAG5ClD,EAAG,YAAY+C,CAAM,GACrB/C,EAAG,YAAYkD,CAAO,GAGlB,KAAK,QAAQ,WAAW;AACxB,YAAMC,IAAe,SAAS,cAAc,KAAK;AACjD,MAAAA,EAAa,YAAY,4BACzBnD,EAAG,YAAYmD,CAAY;AAAA,IAC/B;AAEA,WAAOnD;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,QAAQA,GAAiBoD,GAAyBC,GAA0B;AAChF,IAAID,MAAU,WACVpD,EAAG,MAAM,QAAQ,OAAOoD,KAAU,WAAW,GAAGA,CAAK,OAAOA,IAE5DC,MAAW,WACXrD,EAAG,MAAM,SAAS,OAAOqD,KAAW,WAAW,GAAGA,CAAM,OAAOA;AAAA,EAEvE;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAO;AACX,SAAK,UAAU,YAAY,KAAK,OAAO,GAGvC,KAAK,aAAA,GAED,KAAK,QAAQ,aACb,KAAK,SAAA,GAGL,KAAK,QAAQ,aACb,KAAK,WAAA;AAAA,EAEb;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe;AACnB,UAAMC,IAAM,KAAK,QAAQ,UAEnBC,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,OAAOD,KAAQ,YAAY,OAAOA;AAClC,MAAAE,IAAOF,EAAI,GACXG,IAAMH,EAAI;AAAA;AAEV,cAAQA,GAAA;AAAA,QACJ,KAAK;AACD,UAAAE,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,CAAC5B,MAAkB;AACnC,MAAAA,EAAE,eAAA,GACFwB,IAASxB,EAAE,SACXyB,IAASzB,EAAE,SACX0B,IAAY,KAAK,QAAQ,YACzBC,IAAW,KAAK,QAAQ,WAExB,SAAS,iBAAiB,aAAaE,CAAW,GAClD,SAAS,iBAAiB,WAAWC,CAAS;AAAA,IAClD,GAEMD,IAAc,CAAC7B,MAAkB;AACnC,YAAM+B,IAAK/B,EAAE,UAAUwB,GACjBQ,IAAKhC,EAAE,UAAUyB;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,CAAC5B,MAAkB;AACnC,MAAAA,EAAE,eAAA,GACFA,EAAE,gBAAA,GACFwB,IAASxB,EAAE,SACXyB,IAASzB,EAAE,SACXsC,IAAS,KAAK,QAAQ,aACtBC,IAAS,KAAK,QAAQ,cAEtB,SAAS,iBAAiB,aAAaV,CAAW,GAClD,SAAS,iBAAiB,WAAWC,CAAS;AAAA,IAClD,GAEMD,IAAc,CAAC7B,MAAkB;AACnC,YAAM+B,IAAK/B,EAAE,UAAUwB,GACjBQ,IAAKhC,EAAE,UAAUyB,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,WAAWhB,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,iBACT,KAAK,QAAQ,OAAA,GACb,KAAK,eAAe,IAChB,KAAK,QAAQ,WACb,KAAK,QAAQ,QAAA;AAAA,EAErB;AACJ;AC9RO,MAAM8B,EAAc;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,YAAYpC,GAAwB;AAEhC,UAAMqC,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,KAAK,SAAS,IAAItC,EAAU;AAAA,MACxB,WAAAF;AAAA,MACA,OAAO;AAAA,MACP,SAASqC;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,IAAA,CACd;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKO,QAAQ;AACX,SAAK,OAAO,MAAA;AAAA,EAChB;AACJ;ACpDO,MAAMI,EAAc;AAAA;AAAA,EAEf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,YAAYzC,GAAwB;AAChC,SAAK,YAAYA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,OAAO7C,GAAsD;AAChE,WAAO,IAAI+C,EAAU;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,GAAG/C;AAAA,IAAA,CACN;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,iBAAiB;AACpB,QAAIiF,EAAc,KAAK,SAAS;AAAA,EACpC;AACJ;AC/BO,MAAMM,EAAU;AAAA;AAAA,EAEX;AAAA;AAAA,EAEA,UAA8B;AAAA;AAAA,EAG/B,UAAiC;AAAA;AAAA,EAEjC,SAA+B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,YAAY1C,GAAiC;AACzC,UAAM5C,IAAK,OAAO4C,KAAc,WAAW,SAAS,eAAeA,CAAS,IAAIA;AAChF,QAAI,CAAC5C,EAAI,OAAM,IAAI,MAAM,qBAAqB;AAC9C,SAAK,YAAYA,GACjB,KAAK,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,OAAO;AAEX,SAAK,UAAU,YAAY,IAG3B,KAAK,UAAU,SAAS,cAAc,KAAK,GAC3C,KAAK,QAAQ,YAAY;AAGzB,UAAMgD,IAAQ,SAAS,cAAc,IAAI;AACzC,IAAAA,EAAM,cAAc,aACpBA,EAAM,YAAY;AAGlB,UAAMuC,IAAO,SAAS,cAAc,GAAG;AACvC,IAAAA,EAAK,cAAc,qBACnBA,EAAK,YAAY;AAGjB,UAAMC,IAAoB,SAAS,cAAc,KAAK;AACtD,IAAAA,EAAkB,KAAK,kBACvBA,EAAkB,YAAY,gCAG9B,KAAK,QAAQ,YAAYxC,CAAK,GAC9B,KAAK,QAAQ,YAAYuC,CAAI,GAG7B,KAAK,SAAS,IAAIF,EAAc,KAAK,OAAO,GAC5C,KAAK,UAAU,IAAI1C,EAAe6C,CAAiB;AAInD,UAAMC,IAAY,SAAS,cAAc,QAAQ;AACjD,IAAAA,EAAU,cAAc,UACxBA,EAAU,YAAY,kBACtBA,EAAU,UAAU,MAAM;AACtB,WAAK,QAAQ,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,WAAW;AAAA,QACX,WAAW;AAAA,MAAA,CACd;AAAA,IACL;AAGA,UAAMC,IAAgB,SAAS,cAAc,QAAQ;AACrD,IAAAA,EAAc,cAAc,gBAC5BA,EAAc,YAAY,kBAC1BA,EAAc,MAAM,aAAa,QACjCA,EAAc,UAAU,MAAM;AAC1B,WAAK,QAAQ,eAAA;AAAA,IACjB,GAGA,KAAK,QAAQ,YAAYD,CAAS,GAClC,KAAK,QAAQ,YAAYC,CAAa,GACtC,KAAK,QAAQ,YAAYF,CAAiB,GAG1C,KAAK,UAAU,YAAY,KAAK,OAAO;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAU;AACb,IAAI,KAAK,YACL,KAAK,QAAQ,QAAA,GACb,KAAK,UAAU,OAEnB,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 a924607..309d8c8 100644 --- a/dist/bim-engine-sdk.umd.js +++ b/dist/bim-engine-sdk.umd.js @@ -1,3 +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}.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:#111111e0;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;color:#ccc;transition:all .2s;border-bottom:2px solid transparent}.opt-btn:hover{background-color:#444;color:#fff}.opt-btn.active{background-color:#ffffff26;color:#fff;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}.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:#111111e0;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:#b3b4b4;cursor:pointer;transition:background .2s;white-space:nowrap;min-width:50px;min-height:50px;padding:4px}.opt-btn-dropdown-item:last-child{border-bottom:none}.opt-btn-dropdown-item:hover{background-color:#444;color:#fff}.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}.opt-btn.no-label .opt-btn-icon{width:32px;height:32px}")),document.head.appendChild(o)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})(); -(function(l,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(l=typeof globalThis<"u"?globalThis:l||self,d(l.LyzBimEngineSDK={}))})(this,(function(l){"use strict";class d{container;options;groups=[];activeBtnIds=new Set;btnRefs=new Map;dropdownElement=null;hoverTimeout=null;DEFAULT_ICON='';constructor(e){const t=typeof e.container=="string"?document.getElementById(e.container):e.container;if(!t)throw new Error("Container not found");this.container=t,this.options={showLabel:!0,visibility:{},...e},this.initContainer()}initContainer(){this.container.innerHTML=""}addGroup(e,t){if(this.groups.some(o=>o.id===e)){console.warn("Group "+e+" already exists");return}const n={id:e,buttons:[]};if(t){const o=this.groups.findIndex(i=>i.id===t);o!==-1?this.groups.splice(o,0,n):(console.warn(`Target group ${t} not found, appending ${e} to end.`),this.groups.push(n))}else this.groups.push(n)}addButton(e){const{groupId:t,parentId:n}=e;if(!t)throw new Error(`Button ${e.id} config must contain 'groupId'`);const o=this.groups.find(r=>r.id===t);if(!o)throw new Error(`Group ${t} not found. Please call addGroup first.`);const i={...e,children:e.children||[]};if(n){const r=this.findButton(o.buttons,n);if(!r)throw new Error(`Parent button ${n} not found in group ${t}`);r.children||(r.children=[]),r.children.push(i)}else o.buttons.push(i)}findButton(e,t){for(const n of e){if(n.id===t)return n;if(n.children){const o=this.findButton(n.children,t);if(o)return o}}}async init(){const{homeButton:e}=await Promise.resolve().then(()=>h),{locationButton:t}=await Promise.resolve().then(()=>p),{walkMenuButton:n}=await Promise.resolve().then(()=>u),{walkPersonButton:o}=await Promise.resolve().then(()=>m),{walkBirdButton:i}=await Promise.resolve().then(()=>v),{settingButton:r}=await Promise.resolve().then(()=>f),{infoButton:a}=await Promise.resolve().then(()=>g);this.addGroup("group-1"),this.addButton(e),this.addButton(n),this.addButton(o),this.addButton(i),this.addButton(t),this.addGroup("group-2"),this.addButton(r),this.addButton(a),this.render()}render(){this.container.innerHTML="",this.btnRefs.clear();const e=document.createElement("div");e.className="toolbar-container",this.groups.forEach((t,n)=>{const o=this.renderGroup(t,n,this.groups.length);e.appendChild(o)}),this.container.appendChild(e)}renderGroup(e,t,n){const o=document.createElement("div");return o.className="opt-btn-group",t{if(this.isVisible(i.id)){const r=this.renderButton(i);o.appendChild(r)}}),o}renderButton(e){const t=document.createElement("div");t.className="opt-btn-wrapper";const n=document.createElement("div");n.className="opt-btn",this.activeBtnIds.has(e.id)&&n.classList.add("active"),e.disabled&&n.classList.add("disabled"),this.options.showLabel||(n.classList.add("no-label"),e.label&&(n.title=e.label));const o=document.createElement("div");if(o.className="opt-btn-icon",o.innerHTML=this.getIcon(e.icon),n.appendChild(o),this.options.showLabel&&e.label){const i=document.createElement("span");i.className="opt-btn-label",i.textContent=e.label,n.appendChild(i)}if(e.children&&e.children.length>0){const i=document.createElement("span");i.className="opt-btn-arrow",i.textContent="▼",n.appendChild(i)}return n.addEventListener("click",()=>this.handleClick(e)),n.addEventListener("mouseenter",()=>this.handleMouseEnter(e,n)),n.addEventListener("mouseleave",()=>this.handleMouseLeave()),this.btnRefs.set(e.id,n),t.appendChild(n),t}handleClick(e){e.disabled||(!e.children||e.children.length===0)&&(e.keepActive&&(this.activeBtnIds.has(e.id)?this.activeBtnIds.delete(e.id):this.activeBtnIds.add(e.id),this.updateButtonState(e.id)),this.closeDropdown(),e.onClick&&e.onClick(e))}handleSubClick(e){e.keepActive&&(this.activeBtnIds.has(e.id)?this.activeBtnIds.delete(e.id):this.activeBtnIds.add(e.id),this.updateButtonState(e.id)),this.closeDropdown(),e.onClick&&e.onClick(e)}handleMouseEnter(e,t){if(this.hoverTimeout&&(clearTimeout(this.hoverTimeout),this.hoverTimeout=null),e.children&&e.children.length>0){this.showDropdown(e,t);const n=t.querySelector(".opt-btn-arrow");n&&n.classList.add("rotated")}else this.closeDropdown()}handleMouseLeave(){this.hoverTimeout=window.setTimeout(()=>{this.closeDropdown()},200)}showDropdown(e,t){if(this.closeDropdown(),!e.children)return;const n=document.createElement("div");n.className="opt-btn-dropdown";const o=t.getBoundingClientRect(),i=o.left+o.width/2;n.style.top=o.top-8+"px",n.style.left=i+"px",e.children.forEach(r=>{if(this.isVisible(r.id)){const a=this.renderDropdownItem(r);n.appendChild(a)}}),n.addEventListener("mouseenter",()=>{this.hoverTimeout&&(clearTimeout(this.hoverTimeout),this.hoverTimeout=null)}),n.addEventListener("mouseleave",()=>this.handleMouseLeave()),document.body.appendChild(n),this.dropdownElement=n}renderDropdownItem(e){const t=document.createElement("div");t.className="opt-btn-dropdown-item";const n=document.createElement("div");if(n.className="opt-btn-icon small",n.innerHTML=this.getIcon(e.icon),t.appendChild(n),this.options.showLabel){const o=document.createElement("span");o.textContent=e.label,t.appendChild(o)}return t.addEventListener("click",o=>{o.stopPropagation(),this.handleSubClick(e)}),t}closeDropdown(){this.dropdownElement&&(this.dropdownElement.remove(),this.dropdownElement=null),this.btnRefs.forEach(e=>{const t=e.querySelector(".opt-btn-arrow");t&&t.classList.remove("rotated")})}updateButtonState(e){const t=this.btnRefs.get(e);t&&(this.activeBtnIds.has(e)?t.classList.add("active"):t.classList.remove("active"))}getIcon(e){return e||this.DEFAULT_ICON}isVisible(e){return this.options.visibility?.[e]!==!1}destroy(){this.closeDropdown(),this.hoverTimeout&&clearTimeout(this.hoverTimeout),this.container.innerHTML="",this.btnRefs.clear(),this.activeBtnIds.clear(),this.groups=[]}}class c{container;optBtnGroups=null;constructor(e){const t=typeof e=="string"?document.getElementById(e):e;if(!t)throw new Error("Container not found");this.container=t,this.init()}init(){this.container.innerHTML="";const e=document.createElement("div");e.className="bim-engine-wrapper";const t=document.createElement("h1");t.textContent="BimEngine",t.className="bim-engine-title";const n=document.createElement("p");n.textContent="这是一个使用BIM-ENGINE。",n.className="bim-engine-desc";const o=document.createElement("div");o.id="opt-btn-groups",o.className="bim-engine-opt-btn-container",e.appendChild(t),e.appendChild(n),e.appendChild(o),this.container.appendChild(e),this.initOptBtnGroups(o)}initOptBtnGroups(e){this.optBtnGroups=new d({container:e,showLabel:!0}),this.optBtnGroups.init().catch(t=>{console.error("Failed to initialize OptBtnGroups:",t)})}destroy(){this.optBtnGroups&&(this.optBtnGroups.destroy(),this.optBtnGroups=null),this.container.innerHTML=""}}const h=Object.freeze(Object.defineProperty({__proto__:null,homeButton:{id:"home",groupId:"group-1",type:"button",label:"首页",icon:'',keepActive:!0,onClick:s=>{console.log("首页按钮被点击:",s.id)}}},Symbol.toStringTag,{value:"Module"})),p=Object.freeze(Object.defineProperty({__proto__:null,locationButton:{id:"location",groupId:"group-1",type:"button",label:"定位",icon:'',keepActive:!1,onClick:s=>{console.log("定位按钮被点击:",s.id)}}},Symbol.toStringTag,{value:"Module"})),u=Object.freeze(Object.defineProperty({__proto__:null,walkMenuButton:{id:"walk",groupId:"group-1",type:"menu",label:"漫游",icon:'',keepActive:!0,onClick:s=>{console.log("漫游按钮被点击:",s.id)}}},Symbol.toStringTag,{value:"Module"})),m=Object.freeze(Object.defineProperty({__proto__:null,walkPersonButton:{id:"walk-person",groupId:"group-1",parentId:"walk",type:"button",label:"人视漫游",icon:'',onClick:s=>{console.log("人视漫游被点击:",s.id)}}},Symbol.toStringTag,{value:"Module"})),v=Object.freeze(Object.defineProperty({__proto__:null,walkBirdButton:{id:"walk-bird",groupId:"group-1",parentId:"walk",type:"button",label:"鸟瞰漫游",icon:'',onClick:s=>{console.log("鸟瞰漫游被点击:",s.id)}}},Symbol.toStringTag,{value:"Module"})),f=Object.freeze(Object.defineProperty({__proto__:null,settingButton:{id:"setting",groupId:"group-2",type:"button",label:"设置",icon:'',keepActive:!1,onClick:s=>{console.log("设置按钮被点击:",s.id)}}},Symbol.toStringTag,{value:"Module"})),g=Object.freeze(Object.defineProperty({__proto__:null,infoButton:{id:"info",groupId:"group-2",type:"button",label:"信息",icon:'',keepActive:!1,onClick:s=>{console.log("信息按钮被点击:",s.id)}}},Symbol.toStringTag,{value:"Module"}));l.BimEngine=c,l.OptBtnGroups=d,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})})); +(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=` +
  • 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"})})); //# 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 2980017..0a12d8e 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/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} from './index.type';\n\nexport class OptBtnGroups {\n private container: HTMLElement;\n private options: OptBtnGroupsOptions;\n // 改用 Array 存储 Group,方便控制顺序\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\n private readonly DEFAULT_ICON = '';\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 }\n\n private initContainer(): void {\n this.container.innerHTML = '';\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 // Add as sub-button\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 // Add as main button\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 async init(): Promise {\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 // 添加组1\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 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 // 直接遍历数组,顺序由 addGroup 控制\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 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 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 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 const icon = document.createElement('div');\n icon.className = 'opt-btn-icon';\n icon.innerHTML = this.getIcon(button.icon);\n btnEl.appendChild(icon);\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 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 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 private handleClick(button: OptButton): void {\n if (button.disabled) return;\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 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 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 private handleMouseLeave(): void {\n this.hoverTimeout = window.setTimeout(() => {\n this.closeDropdown();\n }, 200);\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 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 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 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 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 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 private getIcon(icon?: string): string {\n return icon || this.DEFAULT_ICON;\n }\n\n private isVisible(id: string): boolean {\n return this.options.visibility?.[id] !== false;\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 './bim-engine.css';\nimport { OptBtnGroups } from './toolbar';\n\nexport class BimEngine {\n private container: HTMLElement;\n private optBtnGroups: OptBtnGroups | null = null;\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 private init() {\n // 1. 清空容器可能存在的旧内容\n this.container.innerHTML = '';\n\n // 2. 创建外层容器 div\n const wrapper = document.createElement('div');\n 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 wrapper.appendChild(title);\n wrapper.appendChild(desc);\n wrapper.appendChild(btnGroupContainer); // 将按钮组放入 wrapper 中\n\n // 8. 挂载到主容器\n this.container.appendChild(wrapper);\n\n // 9. 初始化操作按钮组\n this.initOptBtnGroups(btnGroupContainer);\n }\n\n private initOptBtnGroups(container: HTMLElement) {\n this.optBtnGroups = new OptBtnGroups({\n 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 public destroy() {\n if (this.optBtnGroups) {\n this.optBtnGroups.destroy();\n this.optBtnGroups = null;\n }\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","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","BimEngine","container","title","desc","btnGroupContainer","err"],"mappings":"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"} \ No newline at end of file +{"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 diff --git a/dist/index.d.ts b/dist/index.d.ts index 60dda3f..47f5956 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,9 +1,81 @@ -export declare class BimEngine { +/** + * 通用弹窗组件类 + * 支持拖拽、缩放、自定义内容和位置。 + */ +declare class BimDialog { + private element; + private options; private container; - private optBtnGroups; - constructor(container: HTMLElement | string); + private header; + private contentArea; + private _isDestroyed; + /** + * 构造函数 + * @param options 弹窗配置选项 + */ + constructor(options: DialogOptions); + /** + * 创建弹窗的 DOM 结构 + */ + private createDom; + /** + * 设置元素尺寸 + */ + private setSize; + /** + * 初始化组件功能 + */ private init; - private initOptBtnGroups; + /** + * 初始化弹窗位置 + */ + private initPosition; + /** + * 初始化拖拽功能 + */ + private initDrag; + /** + * 初始化缩放功能 + */ + private initResize; + /** + * 动态设置内容 + * @param content 内容元素或 HTML 字符串 + */ + setContent(content: HTMLElement | string): void; + /** + * 关闭弹窗并销毁 + */ + close(): void; +} + +/** + * BimEngine 主类 + * 负责初始化整个应用界面,协调各个子模块(如工具栏、弹窗等)。 + */ +export declare class BimEngine { + /** 主容器元素 */ + private container; + /** 内部包装器元素,用于承载所有 UI 组件 */ + private wrapper; + /** 工具栏管理器实例 */ + toolbar: ToolbarManager | null; + /** 弹窗管理器实例 */ + dialog: DialogManager | null; + /** + * 构造函数 + * @param container 容器元素或容器 ID + */ + constructor(container: HTMLElement | string); + /** + * 初始化方法 + * 创建 DOM 结构并初始化各子模块 + */ + private init; + /** + * 销毁实例 + * 清理所有资源和 DOM 元素 + */ destroy(): void; } @@ -11,15 +83,25 @@ export declare class BimEngine { * 按钮配置接口(用于外部定义按钮) */ 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; } @@ -27,7 +109,9 @@ declare interface ButtonConfig { * 按钮组接口 */ export declare interface ButtonGroup { + /** 组 ID */ id: string; + /** 组内按钮列表 */ buttons: OptButton[]; } @@ -37,57 +121,236 @@ declare type ButtonType = 'button' | 'menu'; * 点击事件载荷 */ export declare interface ClickPayload { + /** 被点击的按钮对象 */ button: OptButton; + /** 触发的动作类型 */ action: 'activate' | 'deactivate' | 'trigger'; + /** 当前激活状态 */ isActive?: boolean; } -export declare class OptBtnGroups { +/** + * 弹窗颜色配置 + */ +declare interface DialogColors { + /** 窗体背景颜色,默认 rgba(17, 17, 17, 0.95) */ + backgroundColor?: string; + /** 标题栏背景颜色,默认 #2a2a2a */ + headerBackgroundColor?: string; + /** 标题文字颜色,默认 #fff */ + titleColor?: string; + /** 内容文字颜色,默认 #ccc */ + textColor?: string; + /** 边框颜色,默认 #444 */ + borderColor?: string; +} + +/** + * 弹窗管理器 + * 负责创建和管理应用中的各类弹窗。 + */ +declare class DialogManager { + /** 弹窗挂载的父容器 */ private container; + /** + * 构造函数 + * @param container 弹窗挂载的目标容器 + */ + constructor(container: HTMLElement); + /** + * 创建一个通用弹窗 + * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器) + * @returns BimDialog 实例 + */ + create(options: Omit): BimDialog; + /** + * 显示二次封装的模型信息弹窗 + * 演示如何调用特定的业务弹窗组件 + */ + showInfoDialog(): void; +} + +/** + * 弹窗配置选项接口 + */ +declare interface DialogOptions extends DialogColors { + /** 弹窗挂载的父容器 */ + container: HTMLElement; + /** 弹窗标题 */ + title?: string; + /** 弹窗内容,支持 HTML 字符串或 HTMLElement */ + content?: HTMLElement | string; + /** 弹窗宽度,数字(像素)或字符串(如 '50%') */ + width?: number | string; + /** 弹窗高度 */ + height?: number | string; + /** 弹窗位置 */ + position?: DialogPosition; + /** 是否可拖拽 */ + draggable?: boolean; + /** 是否可调整大小 */ + resizable?: boolean; + /** 最小宽度限制 */ + minWidth?: number; + /** 最小高度限制 */ + minHeight?: number; + /** 关闭时的回调函数 */ + onClose?: () => void; + /** 弹窗唯一标识 ID (可选) */ + id?: string; +} + +/** + * 弹窗位置类型定义 + * 可以是预设的字符串位置(如 'center', 'top-left' 等), + * 也可以是具体的坐标对象 { x, y } + */ +declare type DialogPosition = 'center' | 'top-left' | 'top-center' | 'top-right' | 'left-center' | 'right-center' | 'bottom-left' | 'bottom-center' | 'bottom-right' | { + x: number; + y: number; +}; + +/** + * 底部操作按钮组组件 + * 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。 + */ +export declare class OptBtnGroups { + /** 挂载容器 */ + private container; + /** 组件配置选项 */ private options; + /** 按钮组列表,按顺序存储 */ private groups; + /** 当前处于激活状态的按钮 ID 集合 */ private activeBtnIds; + /** 按钮 DOM 元素的引用映射,方便快速查找 */ private btnRefs; + /** 当��显示的下拉菜单元素 */ private dropdownElement; + /** 鼠标悬停计时器,用于处理菜单显示的防抖 */ private hoverTimeout; + /** 默认图标 SVG */ private readonly DEFAULT_ICON; + /** + * 构造函数 + * @param options 配置选项 + */ constructor(options: OptBtnGroupsOptions); + /** + * 初始化容器 + */ private initContainer; + /** + * 应用样式配置到 CSS 变量 + */ + private applyStyles; + /** + * 更新颜色配置 + * @param colors 颜色配置对象 + */ + setColors(colors: ToolbarColors): void; /** * 添加按钮组 * @param groupId 组ID - * @param beforeGroupId 在哪个组之前插入(可选),不传则插入到最后 + * @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; + /** + * 销毁组件,清理资源 + */ destroy(): void; } /** * OptBtnGroups 配置选项 */ -export declare interface OptBtnGroupsOptions { +export declare interface OptBtnGroupsOptions extends ToolbarColors { + /** 容器元素或 ID */ container: HTMLElement | string; + /** 是否显示标签 */ showLabel?: boolean; + /** 按钮可见性配置 Map */ visibility?: Record; } @@ -95,7 +358,91 @@ export declare interface OptBtnGroupsOptions { * 操作按钮接口(内部使用,继承配置) */ export declare interface OptButton extends ButtonConfig { + /** 内部使用的子按钮列表 */ children?: OptButton[]; } +/** + * 工具栏颜色配置接口 + */ +declare interface ToolbarColors { + /** 工具栏背景颜色 */ + backgroundColor?: string; + /** 按钮默认背景颜色 */ + btnBackgroundColor?: string; + /** 按钮 Hover 背景颜色 */ + btnHoverColor?: string; + /** 按钮激活状态背景颜色 */ + btnActiveColor?: string; + /** 图标默认颜色 */ + iconColor?: string; + /** 图标激活/Hover 颜色 */ + iconActiveColor?: string; + /** 文字默认颜色 */ + textColor?: string; + /** 文字激活/Hover 颜色 */ + textActiveColor?: string; +} + +/** + * 工具栏管理器 + * 负责管理底部操作栏的按钮组、按钮及其可见性等状态。 + */ +declare class ToolbarManager { + /** 内部工具栏组件实例 */ + private optBtnGroups; + /** 工具栏挂载的容器 */ + private container; + /** + * 构造函数 + * @param container 工具栏挂载的容器元素 + */ + constructor(container: HTMLElement); + /** + * 初始化工具栏 + */ + private init; + /** + * 添加一个工具栏按钮组 + * @param groupId 新组的 ID + * @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后 + */ + addGroup(groupId: string, beforeGroupId?: string): void; + /** + * 添加一个工具栏按钮 + * @param config 按钮配置对象 + */ + addButton(config: ButtonConfig): void; + /** + * 设置按钮的可见性 + * @param buttonId 按钮 ID + * @param visible 是否可见 + */ + setButtonVisibility(buttonId: string, visible: boolean): void; + /** + * 设置是否显示按钮下方的文字标签 + * @param show 是否显示 + */ + setShowLabel(show: boolean): void; + /** + * 设置整个工具栏的可见性 + * @param visible 是否可见 + */ + setVisible(visible: boolean): void; + /** + * 设置工具栏背景颜色 + * @param color CSS 颜色值 + */ + setBackgroundColor(color: string): void; + /** + * 设置工具栏详细颜色配置 + * @param colors 颜色配置对象 + */ + setColors(colors: ToolbarColors): void; + /** + * 销毁工具栏管理器 + */ + destroy(): void; +} + export { } diff --git a/docs/Dialog.md b/docs/Dialog.md new file mode 100644 index 0000000..1a18066 --- /dev/null +++ b/docs/Dialog.md @@ -0,0 +1,95 @@ +# 弹窗组件 (Dialog) + +BimEngine SDK 提供了可拖拽、可缩放的通用弹窗组件 `DialogManager` (底层基于 `BimDialog`),支持自定义内容和高度定制的样式。 + +## 1. 组件作用 + +* 提供浮动的交互窗口。 +* 支持任意 HTML 内容挂载。 +* 内置拖拽移动和拖拽缩放功能。 +* 支持丰富的样式定制接口。 + +## 2. 初始化与使用 + +通过 `BimEngine` 实例访问: + +```typescript +const engine = new BimEngine('container-id'); +// engine.dialog 即为 DialogManager 实例 +``` + +## 3. 配置项 (DialogOptions) + +`engine.dialog.create(options)` 方法接受以下配置: + +| 属性 | 类型 | 默认值 | 说明 | +| :--- | :--- | :--- | :--- | +| `title` | `string` | `'Dialog'` | 弹窗标题 | +| `content` | `string \| HTMLElement` | - | 弹窗内容 | +| `width` | `number \| string` | `300` | 宽度 | +| `height` | `number \| string` | `'auto'` | 高度 | +| `position` | `DialogPosition` | `'center'` | 初始位置 (支持 'center', 'top-left' 等或坐标对象) | +| `draggable` | `boolean` | `true` | 是否可拖拽 | +| `resizable` | `boolean` | `false` | 是否可调整大小 | +| `minWidth` | `number` | `200` | 最小宽度 | +| `minHeight` | `number` | `100` | 最小高度 | +| `onClose` | `Function` | - | 关闭时的回调 | +| `backgroundColor` | `string` | `rgba(17, 17, 17, 0.95)` | 窗体背景色 | +| `headerBackgroundColor` | `string` | `#2a2a2a` | 标题栏背景色 | +| `titleColor` | `string` | `#fff` | 标题文字颜色 | +| `textColor` | `string` | `#ccc` | 内容默认文字颜色 | +| `borderColor` | `string` | `#444` | 边框颜色 | + +## 4. 使用案例 + +### 案例 1: 创建基本弹窗 + +```typescript +engine.dialog.create({ + title: '欢迎', + content: 'Hello World!', + width: 400 +}); +``` + +### 案例 2: 创建包含复杂 DOM 的弹窗 + +```typescript +const div = document.createElement('div'); +div.innerHTML = ''; +div.querySelector('button').onclick = () => alert('Clicked'); + +engine.dialog.create({ + title: '交互组件', + content: div, + resizable: true +}); +``` + +### 案例 3: 定制样式 (红色主题) + +```typescript +engine.dialog.create({ + title: '警告', + content: '这是一个红色主题的警告弹窗。', + backgroundColor: 'rgba(100, 0, 0, 0.9)', // 深红背景 + headerBackgroundColor: '#ff0000', // 鲜红标题栏 + titleColor: '#ffffff', + borderColor: '#ff4444' +}); +``` + +### 案例 4: 监听关闭事件 + +```typescript +const dlg = engine.dialog.create({ + title: '任务', + content: '处理中...', + onClose: () => { + console.log('弹窗已关闭'); + } +}); + +// 手动关闭 +// dlg.close(); +``` diff --git a/docs/Toolbar.md b/docs/Toolbar.md new file mode 100644 index 0000000..d830029 --- /dev/null +++ b/docs/Toolbar.md @@ -0,0 +1,130 @@ +# 工具栏组件 (Toolbar) + +BimEngine SDK 提供了功能强大的工具栏组件 `ToolbarManager` (底层基于 `OptBtnGroups`),支持多级菜单、按钮分组、动态显隐和样式高度定制。 + +## 1. 组件作用 + +* 提供一个统一的底部操作栏。 +* 支持按钮分组管理。 +* 支持层级下拉菜单。 +* 提供标准的交互反馈(Hover、Active、Disabled)。 +* 支持完全的样式定制(背景色、图标色、文字色等)。 + +## 2. 初始化与使用 + +通常通过 `BimEngine` 实例访问: + +```typescript +const engine = new BimEngine('container-id'); +// engine.toolbar 即为 ToolbarManager 实例 +``` + +或者单独使用(不推荐,除非只需工具栏): + +```typescript +import { ToolbarManager } from 'bim-engine-sdk'; +const toolbar = new ToolbarManager(document.getElementById('toolbar-container')); +``` + +## 3. 配置项 + +`ToolbarManager` 没有直接的配置项,但它管理着底层的 `OptBtnGroups`。主要配置在于添加按钮时的 `ButtonConfig` 和样式设置。 + +### ButtonConfig (按钮配置) + +| 属性 | 类型 | 说明 | +| :--- | :--- | :--- | +| `id` | `string` | 按钮唯一标识 | +| `type` | `'button' \| 'menu'` | 按钮类型 | +| `label` | `string` | 按钮显示文字 | +| `icon` | `string` | SVG 图标字符串 | +| `groupId` | `string` | 所属组 ID (必需) | +| `parentId` | `string` | 父按钮 ID (可选,用于子菜单) | +| `keepActive` | `boolean` | 是否保持激活状态 (Toggle 模式) | +| `disabled` | `boolean` | 是否禁用 | +| `onClick` | `Function` | 点击回调 | +| `children` | `ButtonConfig[]` | 子按钮数组 | + +### ToolbarColors (颜色配置) + +用于 `setColors` 方法。 + +| 属性 | 说明 | 默认值 | +| :--- | :--- | :--- | +| `backgroundColor` | 工具栏背景色 | `rgba(17, 17, 17, 0.88)` | +| `btnBackgroundColor` | 按钮默认背景 | `transparent` | +| `btnHoverColor` | 按钮 Hover 背景 | `#444` | +| `btnActiveColor` | 按钮激活背景 | `rgba(255, 255, 255, 0.15)` | +| `iconColor` | 图标默认颜色 | `#ccc` | +| `iconActiveColor` | 图标激活颜色 | `#fff` | +| `textColor` | 文字默认颜色 | `#ccc` | +| `textActiveColor` | 文字激活颜色 | `#fff` | + +## 4. 使用案例 + +### 案例 1: 添加自定义按钮组和按钮 + +```typescript +// 1. 添加一个新组 +engine.toolbar.addGroup('my-group'); + +// 2. 在该组添加按钮 +engine.toolbar.addButton({ + id: 'my-btn', + groupId: 'my-group', + type: 'button', + label: '自定义功能', + icon: '...', // 填入 SVG 内容 + onClick: (btn) => { + console.log('Clicked!', btn); + } +}); +``` + +### 案例 2: 添加带下拉菜单的按钮 + +```typescript +engine.toolbar.addButton({ + id: 'menu-btn', + groupId: 'my-group', + type: 'menu', + label: '更多选项', + children: [ + { + id: 'sub-1', + type: 'button', + label: '选项 A', + onClick: () => console.log('A') + }, + { + id: 'sub-2', + type: 'button', + label: '选项 B', + onClick: () => console.log('B') + } + ] +}); +``` + +### 案例 3: 修改工具栏样式 + +```typescript +engine.toolbar.setColors({ + backgroundColor: 'rgba(0, 122, 255, 0.9)', // 蓝色背景 + iconColor: '#ffffff', // 白色图标 + btnHoverColor: 'rgba(255, 255, 255, 0.2)' +}); +``` + +### 案例 4: 控制显隐 + +```typescript +// 隐藏整个工具栏 +engine.toolbar.setVisible(false); + +// 隐藏特定按钮 +engine.toolbar.setButtonVisibility('my-btn', false); + +// 隐藏文字标签 +engine.toolbar.setShowLabel(false); +``` diff --git a/src/bim-engine.ts b/src/bim-engine.ts index d0765ef..31b2e2a 100644 --- a/src/bim-engine.ts +++ b/src/bim-engine.ts @@ -1,10 +1,26 @@ import './bim-engine.css'; -import { OptBtnGroups } from './toolbar'; +import { ToolbarManager } from './modules/toolbar-manager'; +import { DialogManager } from './modules/dialog-manager'; +/** + * BimEngine 主类 + * 负责初始化整个应用界面,协调各个子模块(如工具栏、弹窗等)。 + */ export class BimEngine { + /** 主容器元素 */ private container: HTMLElement; - private optBtnGroups: OptBtnGroups | null = null; + /** 内部包装器元素,用于承载所有 UI 组件 */ + private wrapper: HTMLElement | null = null; + + /** 工具栏管理器实例 */ + public toolbar: ToolbarManager | null = null; + /** 弹窗管理器实例 */ + public dialog: DialogManager | null = null; + /** + * 构造函数 + * @param container 容器元素或容器 ID + */ constructor(container: HTMLElement | string) { const el = typeof container === 'string' ? document.getElementById(container) : container; if (!el) throw new Error('Container not found'); @@ -12,20 +28,24 @@ export class BimEngine { this.init(); } + /** + * 初始化方法 + * 创建 DOM 结构并初始化各子模块 + */ private init() { // 1. 清空容器可能存在的旧内容 this.container.innerHTML = ''; // 2. 创建外层容器 div - const wrapper = document.createElement('div'); - wrapper.className = 'bim-engine-wrapper'; + this.wrapper = document.createElement('div'); + this.wrapper.className = 'bim-engine-wrapper'; // 3. 创建标题 h1 const title = document.createElement('h1'); title.textContent = 'BimEngine'; title.className = 'bim-engine-title'; - // 4. 创建段落 p + // 4. 创建描述段落 p const desc = document.createElement('p'); desc.textContent = '这是一个使用BIM-ENGINE。'; desc.className = 'bim-engine-desc'; @@ -36,34 +56,58 @@ export class BimEngine { btnGroupContainer.className = 'bim-engine-opt-btn-container'; // 7. 组装元素 - wrapper.appendChild(title); - wrapper.appendChild(desc); - wrapper.appendChild(btnGroupContainer); // 将按钮组放入 wrapper 中 + this.wrapper.appendChild(title); + this.wrapper.appendChild(desc); + + // 初始化管理器 + this.dialog = new DialogManager(this.wrapper); + this.toolbar = new ToolbarManager(btnGroupContainer); - // 8. 挂载到主容器 - this.container.appendChild(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 + }); + }; - // 9. 初始化操作按钮组 - this.initOptBtnGroups(btnGroupContainer); - } - - private initOptBtnGroups(container: HTMLElement) { - this.optBtnGroups = new OptBtnGroups({ - container, - showLabel: true - }); - - // 初始化并加载默认按钮 - this.optBtnGroups.init().catch(err => { - console.error('Failed to initialize OptBtnGroups:', err); - }); + // 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); } + /** + * 销毁实例 + * 清理所有资源和 DOM 元素 + */ public destroy() { - if (this.optBtnGroups) { - this.optBtnGroups.destroy(); - this.optBtnGroups = null; + if (this.toolbar) { + this.toolbar.destroy(); + this.toolbar = null; } + this.dialog = null; this.container.innerHTML = ''; } } \ No newline at end of file diff --git a/src/dialog/bimInfoDialog/index.css b/src/dialog/bimInfoDialog/index.css new file mode 100644 index 0000000..f15c3cd --- /dev/null +++ b/src/dialog/bimInfoDialog/index.css @@ -0,0 +1,30 @@ +.bim-info-dialog-content { + padding: 16px; + font-family: sans-serif; + color: #333; +} + +.bim-info-dialog-content h3 { + margin-top: 0; + margin-bottom: 12px; + border-bottom: 1px solid #eee; + padding-bottom: 8px; + color: #0078d4; +} + +.bim-info-dialog-content ul { + list-style: none; + padding: 0; + margin: 0; +} + +.bim-info-dialog-content li { + margin-bottom: 8px; + font-size: 14px; + display: flex; +} + +.bim-info-dialog-content li strong { + width: 80px; + color: #555; +} diff --git a/src/dialog/bimInfoDialog/index.ts b/src/dialog/bimInfoDialog/index.ts new file mode 100644 index 0000000..04796f6 --- /dev/null +++ b/src/dialog/bimInfoDialog/index.ts @@ -0,0 +1,61 @@ +import './index.css'; +import { BimDialog } from '../index'; + +/** + * BimInfoDialog (二次封装示例) + * 这是一个展示项目信息的业务弹窗组件,内部封装了 BimDialog。 + */ +export class BimInfoDialog { + private dialog: BimDialog; + + /** + * 构造函数 + * @param container 父容器 + */ + constructor(container: HTMLElement) { + // 创建自定义的 DOM 内容 + const contentEl = document.createElement('div'); + contentEl.className = 'bim-info-dialog-content'; + + const infoTitle = document.createElement('h3'); + infoTitle.textContent = 'Model Information'; + + const infoList = document.createElement('ul'); + infoList.innerHTML = ` +
  • Name: Sample Project
  • +
  • Version: 1.0.0
  • +
  • Date: ${new Date().toLocaleDateString()}
  • +
  • Status: Active
  • + `; + + const actionBtn = document.createElement('button'); + actionBtn.textContent = 'Update Status'; + actionBtn.style.marginTop = '10px'; + actionBtn.onclick = () => { + alert('Status updated!'); + }; + + contentEl.appendChild(infoTitle); + contentEl.appendChild(infoList); + contentEl.appendChild(actionBtn); + + // 初始化 BimDialog,直接传入构建好的 HTMLElement + this.dialog = new BimDialog({ + container: container, + title: 'Project Info (Wrapped)', + content: contentEl, + width: 320, + height: 'auto', + position: 'center', + resizable: true, + draggable: true + }); + } + + /** + * 关闭弹窗 + */ + public close() { + this.dialog.close(); + } +} \ No newline at end of file diff --git a/src/dialog/index.css b/src/dialog/index.css new file mode 100644 index 0000000..26e8441 --- /dev/null +++ b/src/dialog/index.css @@ -0,0 +1,95 @@ +:root { + --bim-dialog-bg: rgba(17, 17, 17, 0.95); + --bim-dialog-header-bg: #2a2a2a; + --bim-dialog-title-color: #fff; + --bim-dialog-text-color: #ccc; + --bim-dialog-border-color: #444; +} + +.bim-dialog { + position: absolute; + background-color: var(--bim-dialog-bg); + border: 1px solid var(--bim-dialog-border-color); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + display: flex; + flex-direction: column; + z-index: 1000; + color: var(--bim-dialog-title-color); + overflow: hidden; + min-width: 200px; + min-height: 100px; +} + +.bim-dialog-header { + height: 32px; + background-color: var(--bim-dialog-header-bg); + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 10px; + cursor: default; + user-select: none; + border-bottom: 1px solid var(--bim-dialog-border-color); + flex-shrink: 0; +} + +.bim-dialog-header.draggable { + cursor: move; +} + +.bim-dialog-title { + font-size: 14px; + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--bim-dialog-title-color); +} + +.bim-dialog-close { + cursor: pointer; + font-size: 18px; + color: #999; + line-height: 1; + margin-left: 8px; +} + +.bim-dialog-close:hover { + color: #fff; +} + +.bim-dialog-content { + flex: 1; + padding: 10px; + overflow: auto; + font-size: 14px; + color: var(--bim-dialog-text-color); +} + +/* 缩放句柄 */ +.bim-dialog-resize-handle { + position: absolute; + width: 10px; + height: 10px; + bottom: 0; + right: 0; + cursor: se-resize; + z-index: 10; +} + +/* 右下角装饰,类似斜线 */ +.bim-dialog-resize-handle::after { + content: ''; + position: absolute; + bottom: 3px; + right: 3px; + width: 6px; + height: 6px; + border-right: 2px solid #666; + border-bottom: 2px solid #666; +} + +.bim-dialog-resize-handle:hover::after { + border-color: #fff; +} \ No newline at end of file diff --git a/src/dialog/index.ts b/src/dialog/index.ts new file mode 100644 index 0000000..e551ad6 --- /dev/null +++ b/src/dialog/index.ts @@ -0,0 +1,294 @@ +import './index.css'; +import type { DialogOptions } from './index.type'; + +/** + * 通用弹窗组件类 + * 支持拖拽、缩放、自定义内容和位置。 + */ +export class BimDialog { + private element: HTMLElement; + private options: DialogOptions; + private container: HTMLElement; + private header: HTMLElement; + private contentArea: HTMLElement; + private _isDestroyed = false; + + /** + * 构造函数 + * @param options 弹窗配置选项 + */ + constructor(options: DialogOptions) { + // 合并默认配置 + this.options = { + title: 'Dialog', + width: 300, + height: 'auto', + position: 'center', + draggable: true, + resizable: false, + minWidth: 200, + minHeight: 100, + ...options + }; + this.container = options.container; + + // 创建 DOM 结构 + this.element = this.createDom(); + this.header = this.element.querySelector('.bim-dialog-header') as HTMLElement; + this.contentArea = this.element.querySelector('.bim-dialog-content') as HTMLElement; + + // 初始化 + this.init(); + } + + /** + * 创建弹窗的 DOM 结构 + */ + private createDom(): HTMLElement { + const el = document.createElement('div'); + el.className = 'bim-dialog'; + + if (this.options.id) el.id = this.options.id; + + // 应用颜色配置到 CSS 变量 (局部作用域) + const style = el.style; + if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor); + if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor); + if (this.options.titleColor) style.setProperty('--bim-dialog-title-color', this.options.titleColor); + if (this.options.textColor) style.setProperty('--bim-dialog-text-color', this.options.textColor); + if (this.options.borderColor) style.setProperty('--bim-dialog-border-color', this.options.borderColor); + + // 设置初始尺寸 + this.setSize(el, this.options.width, this.options.height); + + // 创建标题栏 (Header) + const header = document.createElement('div'); + header.className = 'bim-dialog-header'; + if (this.options.draggable) header.classList.add('draggable'); + + const title = document.createElement('span'); + title.className = 'bim-dialog-title'; + title.textContent = this.options.title || ''; + + const closeBtn = document.createElement('span'); + closeBtn.className = 'bim-dialog-close'; + closeBtn.innerHTML = '×'; + closeBtn.onclick = () => this.close(); + + header.appendChild(title); + header.appendChild(closeBtn); + + // 创建内容区域 (Content) + const content = document.createElement('div'); + content.className = 'bim-dialog-content'; + if (typeof this.options.content === 'string') { + content.innerHTML = this.options.content; + } else if (this.options.content instanceof HTMLElement) { + content.appendChild(this.options.content); + } + + el.appendChild(header); + el.appendChild(content); + + // 如果允许缩放,创建缩放手柄 + if (this.options.resizable) { + const resizeHandle = document.createElement('div'); + resizeHandle.className = 'bim-dialog-resize-handle'; + el.appendChild(resizeHandle); + } + + return el; + } + + /** + * 设置元素尺寸 + */ + private setSize(el: HTMLElement, width?: number | string, height?: number | string) { + if (width !== undefined) { + el.style.width = typeof width === 'number' ? `${width}px` : width; + } + if (height !== undefined) { + el.style.height = typeof height === 'number' ? `${height}px` : height; + } + } + + /** + * 初始化组件功能 + */ + private init() { + this.container.appendChild(this.element); + + // 必须先挂载才能计算尺寸进行定位 + this.initPosition(); + + if (this.options.draggable) { + this.initDrag(); + } + + if (this.options.resizable) { + this.initResize(); + } + } + + /** + * 初始化弹窗位置 + */ + private initPosition() { + const pos = this.options.position; + + const elRect = this.element.getBoundingClientRect(); + + // 计算相对父容器的定位 + let left = 0; + let top = 0; + + const pW = this.container.clientWidth; + const pH = this.container.clientHeight; + const elW = elRect.width; + const elH = elRect.height; + + if (typeof pos === 'object' && 'x' in pos) { + left = pos.x; + top = pos.y; + } else { + switch (pos) { + case 'center': + left = (pW - elW) / 2; + top = (pH - elH) / 2; + break; + case 'top-left': left = 0; top = 0; break; + case 'top-center': left = (pW - elW) / 2; top = 0; break; + case 'top-right': left = pW - elW; top = 0; break; + case 'left-center': left = 0; top = (pH - elH) / 2; break; + case 'right-center': left = pW - elW; top = (pH - elH) / 2; break; + case 'bottom-left': left = 0; top = pH - elH; break; + case 'bottom-center': left = (pW - elW) / 2; top = pH - elH; break; + case 'bottom-right': left = pW - elW; top = pH - elH; break; + default: + left = (pW - elW) / 2; + top = (pH - elH) / 2; + } + } + + // 简单的边界检查,防止初始位置溢出 + left = Math.max(0, Math.min(left, pW - elW)); + top = Math.max(0, Math.min(top, pH - elH)); + + this.element.style.left = `${left}px`; + this.element.style.top = `${top}px`; + } + + /** + * 初始化拖拽功能 + */ + private initDrag() { + let startX = 0; + let startY = 0; + let startLeft = 0; + let startTop = 0; + + const onMouseDown = (e: MouseEvent) => { + e.preventDefault(); + startX = e.clientX; + startY = e.clientY; + startLeft = this.element.offsetLeft; + startTop = this.element.offsetTop; + + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + }; + + const onMouseMove = (e: MouseEvent) => { + const dx = e.clientX - startX; + const dy = e.clientY - startY; + + let newLeft = startLeft + dx; + let newTop = startTop + dy; + + // 边界限制,防止拖出容器 + const maxLeft = this.container.clientWidth - this.element.offsetWidth; + const maxTop = this.container.clientHeight - this.element.offsetHeight; + + newLeft = Math.max(0, Math.min(newLeft, maxLeft)); + newTop = Math.max(0, Math.min(newTop, maxTop)); + + this.element.style.left = `${newLeft}px`; + this.element.style.top = `${newTop}px`; + }; + + const onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + + this.header.addEventListener('mousedown', onMouseDown); + } + + /** + * 初始化缩放功能 + */ + private initResize() { + const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement; + if (!handle) return; + + let startX = 0; + let startY = 0; + let startW = 0; + let startH = 0; + + const onMouseDown = (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + startX = e.clientX; + startY = e.clientY; + startW = this.element.offsetWidth; + startH = this.element.offsetHeight; + + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + }; + + const onMouseMove = (e: MouseEvent) => { + const dx = e.clientX - startX; + const dy = e.clientY - startY; + + const newW = Math.max(this.options.minWidth || 100, startW + dx); + const newH = Math.max(this.options.minHeight || 50, startH + dy); + + this.element.style.width = `${newW}px`; + this.element.style.height = `${newH}px`; + }; + + const onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + + handle.addEventListener('mousedown', onMouseDown); + } + + /** + * 动态设置内容 + * @param content 内容元素或 HTML 字符串 + */ + public setContent(content: HTMLElement | string) { + this.contentArea.innerHTML = ''; + if (typeof content === 'string') { + this.contentArea.innerHTML = content; + } else { + this.contentArea.appendChild(content); + } + } + + /** + * 关闭弹窗并销毁 + */ + public close() { + if (this._isDestroyed) return; + this.element.remove(); + this._isDestroyed = true; + if (this.options.onClose) { + this.options.onClose(); + } + } +} diff --git a/src/dialog/index.type.ts b/src/dialog/index.type.ts new file mode 100644 index 0000000..825b203 --- /dev/null +++ b/src/dialog/index.type.ts @@ -0,0 +1,57 @@ +/** + * 弹窗位置类型定义 + * 可以是预设的字符串位置(如 'center', 'top-left' 等), + * 也可以是具体的坐标对象 { x, y } + */ +export type DialogPosition = + | 'center' + | 'top-left' | 'top-center' | 'top-right' + | 'left-center' | 'right-center' + | 'bottom-left' | 'bottom-center' | 'bottom-right' + | { x: number; y: number }; + +/** + * 弹窗颜色配置 + */ +export interface DialogColors { + /** 窗体背景颜色,默认 rgba(17, 17, 17, 0.95) */ + backgroundColor?: string; + /** 标题栏背景颜色,默认 #2a2a2a */ + headerBackgroundColor?: string; + /** 标题文字颜色,默认 #fff */ + titleColor?: string; + /** 内容文字颜色,默认 #ccc */ + textColor?: string; + /** 边框颜色,默认 #444 */ + borderColor?: string; +} + +/** + * 弹窗配置选项接口 + */ +export interface DialogOptions extends DialogColors { + /** 弹窗挂载的父容器 */ + container: HTMLElement; + /** 弹窗标题 */ + title?: string; + /** 弹窗内容,支持 HTML 字符串或 HTMLElement */ + content?: HTMLElement | string; + /** 弹窗宽度,数字(像素)或字符串(如 '50%') */ + width?: number | string; + /** 弹窗高度 */ + height?: number | string; + /** 弹窗位置 */ + position?: DialogPosition; + /** 是否可拖拽 */ + draggable?: boolean; + /** 是否可调整大小 */ + resizable?: boolean; + /** 最小宽度限制 */ + minWidth?: number; + /** 最小高度限制 */ + minHeight?: number; + /** 关闭时的回调函数 */ + onClose?: () => void; + /** 弹窗唯一标识 ID (可选) */ + id?: string; +} diff --git a/src/index.ts b/src/index.ts index 17ef664..d6b5c90 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,10 @@ import { BimEngine } from './bim-engine'; + +// 导出 OptBtnGroups 组件,用于工具栏操作 export { OptBtnGroups } from './toolbar'; + +// 导出相关类型定义 export type { OptButton, ButtonGroup, OptBtnGroupsOptions, ClickPayload } from './toolbar/index.type'; -export { BimEngine }; + +// 导出主引擎类 +export { BimEngine }; \ No newline at end of file diff --git a/src/modules/dialog-manager.ts b/src/modules/dialog-manager.ts new file mode 100644 index 0000000..bbe178f --- /dev/null +++ b/src/modules/dialog-manager.ts @@ -0,0 +1,40 @@ +import { BimDialog } from '../dialog'; +import { BimInfoDialog } from '../dialog/bimInfoDialog'; +import type { DialogOptions } from '../dialog/index.type'; + +/** + * 弹窗管理器 + * 负责创建和管理应用中的各类弹窗。 + */ +export class DialogManager { + /** 弹窗挂载的父容器 */ + private container: HTMLElement; + + /** + * 构造函数 + * @param container 弹窗挂载的目标容器 + */ + constructor(container: HTMLElement) { + this.container = container; + } + + /** + * 创建一个通用弹窗 + * @param options 弹窗配置选项(不需要传 container,自动使用管理器绑定的容器) + * @returns BimDialog 实例 + */ + public create(options: Omit): 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 new file mode 100644 index 0000000..6c70a14 --- /dev/null +++ b/src/modules/toolbar-manager.ts @@ -0,0 +1,132 @@ +import type { ToolbarColors } from '../toolbar/index.type'; +import { OptBtnGroups } from '../toolbar'; +import type { ButtonConfig } from '../toolbar/index.type'; + +/** + * 工具栏管理器 + * 负责管理底部操作栏的按钮组、按钮及其可见性等状态。 + */ +export class ToolbarManager { + /** 内部工具栏组件实例 */ + private optBtnGroups: OptBtnGroups | null = null; + /** 工具栏挂载的容器 */ + private container: HTMLElement; + + /** + * 构造函数 + * @param container 工具栏挂载的容器元素 + */ + constructor(container: HTMLElement) { + this.container = container; + this.init(); + } + + /** + * 初始化工具栏 + */ + private init() { + this.optBtnGroups = new OptBtnGroups({ + container: this.container, + showLabel: true + }); + + // 初始化并加载默认按钮配置 + this.optBtnGroups.init().catch(err => { + console.error('Failed to initialize OptBtnGroups:', err); + }); + } + + /** + * 添加一个工具栏按钮组 + * @param groupId 新组的 ID + * @param beforeGroupId (可选) 插入到哪个组之前,不传则追加到最后 + */ + public addGroup(groupId: string, beforeGroupId?: string) { + if (this.optBtnGroups) { + this.optBtnGroups.addGroup(groupId, beforeGroupId); + this.optBtnGroups.render(); // 重新渲染以更新 UI + } else { + console.warn('Toolbar not initialized yet.'); + } + } + + /** + * 添加一个工具栏按钮 + * @param config 按钮配置对象 + */ + public addButton(config: ButtonConfig) { + if (this.optBtnGroups) { + this.optBtnGroups.addButton(config); + this.optBtnGroups.render(); // 重新渲染以更新 UI + } else { + console.warn('Toolbar not initialized yet.'); + } + } + + /** + * 设置按钮的可见性 + * @param buttonId 按钮 ID + * @param visible 是否可见 + */ + public setButtonVisibility(buttonId: string, visible: boolean) { + if (this.optBtnGroups) { + this.optBtnGroups.updateButtonVisibility(buttonId, visible); + } else { + console.warn('Toolbar not initialized yet.'); + } + } + + /** + * 设置是否显示按钮下方的文字标签 + * @param show 是否显示 + */ + public setShowLabel(show: boolean) { + if (this.optBtnGroups) { + this.optBtnGroups.setShowLabel(show); + } else { + console.warn('Toolbar not initialized yet.'); + } + } + + /** + * 设置整个工具栏的可见性 + * @param visible 是否可见 + */ + public setVisible(visible: boolean) { + this.container.style.display = visible ? 'block' : 'none'; + } + + /** + * 设置工具栏背景颜色 + * @param color CSS 颜色值 + */ + public setBackgroundColor(color: string) { + if (this.optBtnGroups) { + this.optBtnGroups.setBackgroundColor(color); + } else { + console.warn('Toolbar not initialized yet.'); + } + } + + /** + * 设置工具栏详细颜色配置 + * @param colors 颜色配置对象 + */ + public setColors(colors: ToolbarColors) { + if (this.optBtnGroups) { + this.optBtnGroups.setColors(colors); + } else { + console.warn('Toolbar not initialized yet.'); + } + } + + /** + * 销毁工具栏管理器 + */ + public destroy() { + if (this.optBtnGroups) { + this.optBtnGroups.destroy(); + this.optBtnGroups = null; + } + } +} diff --git a/src/toolbar/index.css b/src/toolbar/index.css index 1010599..e256477 100644 --- a/src/toolbar/index.css +++ b/src/toolbar/index.css @@ -1,19 +1,26 @@ +:root { + --bim-toolbar-bg: rgba(17, 17, 17, 0.88); + --bim-btn-bg: transparent; + --bim-btn-hover-bg: #444; + --bim-btn-active-bg: rgba(255, 255, 255, 0.15); + --bim-icon-color: #ccc; + --bim-icon-active-color: #fff; + --bim-btn-text-color: #ccc; + --bim-btn-text-active-color: #fff; +} + /* 容器样式 */ .toolbar-container { display: flex; align-items: center; - /* 容器本身不需要背景 */ max-width: 100%; overflow-x: auto; scrollbar-width: none; - /* Firefox 隐藏滚动条 */ -ms-overflow-style: none; - /* IE 10+ 隐藏滚动条 */ } .toolbar-container::-webkit-scrollbar { display: none; - /* Chrome/Safari 隐藏滚动条 */ } /* 按钮组样式 */ @@ -23,15 +30,13 @@ display: flex; align-items: center; flex-shrink: 0; - background-color: rgba(17, 17, 17, 0.88); - /* 每个组独立的背景 */ + background-color: var(--bim-toolbar-bg); border-radius: 4px; padding: 4px 8px; } .has-divider { margin-right: 16px; - /* 增加右边距来分隔组 */ } /* 按钮包装器 */ @@ -49,23 +54,21 @@ min-height: 50px; padding: 4px; cursor: pointer; - color: #ccc; + 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: #444; - color: #fff; + background-color: var(--bim-btn-hover-bg); + color: var(--bim-icon-active-color); } .opt-btn.active { - background-color: rgba(255, 255, 255, 0.15); - /* 白色半透明背景 */ - color: #fff; + background-color: var(--bim-btn-active-bg); + color: var(--bim-icon-active-color); border-bottom: 2px solid #fff; - /* 纯白色底部横条 */ } .opt-btn.disabled { @@ -81,7 +84,6 @@ align-items: center; justify-content: center; flex-shrink: 0; - /* 防止图标被压缩 */ } .opt-btn-icon svg { @@ -93,7 +95,13 @@ .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 { @@ -105,12 +113,10 @@ transition: transform 0.2s ease; } -/* 箭头旋转 (菜单展开时) */ .opt-btn-arrow.rotated { transform: rotate(180deg); } -/* 无标签模式调整 */ .opt-btn.no-label .opt-btn-arrow { top: 2px; right: 2px; @@ -119,16 +125,13 @@ /* 下拉菜单样式 */ .opt-btn-dropdown { position: fixed; - /* 固定定位 */ transform: translate(-50%, -100%); - /* 水平居中并向上移动 */ - background-color: rgba(17, 17, 17, 0.88); + 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; } @@ -137,45 +140,40 @@ .opt-btn-dropdown-item { display: flex; flex-direction: column; - /* 垂直布局 */ align-items: center; justify-content: center; - color: #b3b4b4; + color: var(--bim-icon-color); cursor: pointer; transition: background 0.2s; white-space: nowrap; min-width: 50px; min-height: 50px; padding: 4px; -} - -.opt-btn-dropdown-item:last-child { - border-bottom: none; - /* 移除最后一项的分隔线 */ + background-color: var(--bim-btn-bg); } .opt-btn-dropdown-item:hover { - background-color: #444; - color: #fff; + 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; -} \ No newline at end of file +} diff --git a/src/toolbar/index.ts b/src/toolbar/index.ts index c278ae0..b5168c6 100644 --- a/src/toolbar/index.ts +++ b/src/toolbar/index.ts @@ -3,21 +3,37 @@ import type { OptButton, ButtonGroup, OptBtnGroupsOptions, - ButtonConfig + ButtonConfig, + ToolbarColors } from './index.type'; +/** + * 底部操作按钮组组件 + * 负责渲染和管理底部工具栏的按钮、下拉菜单及相关交互。 + */ export class OptBtnGroups { + /** 挂载容器 */ private container: HTMLElement; + /** 组件配置选项 */ private options: OptBtnGroupsOptions; - // 改用 Array 存储 Group,方便控制顺序 + /** 按钮组列表,按顺序存储 */ 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) @@ -33,16 +49,47 @@ export class OptBtnGroups { }; 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 在哪个组之前插入(可选),不传则插入到最后 + * @param beforeGroupId 在哪个组之前插入(可选���,不传则插入到最后 */ public addGroup(groupId: string, beforeGroupId?: string): void { if (this.groups.some(g => g.id === groupId)) { @@ -66,7 +113,7 @@ export class OptBtnGroups { } /** - * 添加按钮 + * 添加按钮到指定组 * @param config 按钮配置(必须包含 groupId,可选包含 parentId) */ public addButton(config: ButtonConfig): void { @@ -87,7 +134,7 @@ export class OptBtnGroups { }; if (parentId) { - // Add as sub-button + // 添加为子按钮(菜单项) const parentBtn = this.findButton(group.buttons, parentId); if (!parentBtn) { throw new Error(`Parent button ${parentId} not found in group ${groupId}`); @@ -97,11 +144,14 @@ export class OptBtnGroups { } parentBtn.children.push(button); } else { - // Add as main button + // 添加为主按钮 group.buttons.push(button); } } + /** + * 递归查找按钮 + */ private findButton(buttons: OptButton[], id: string): OptButton | undefined { for (const btn of buttons) { if (btn.id === id) return btn; @@ -113,7 +163,11 @@ export class OptBtnGroups { 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'); @@ -122,7 +176,7 @@ export class OptBtnGroups { const { settingButton } = await import('./buttons/setting'); const { infoButton } = await import('./buttons/info'); - // 添加组1 + // 配置默认组和按钮 this.addGroup('group-1'); this.addButton(homeButton); this.addButton(walkMenuButton); @@ -135,6 +189,9 @@ export class OptBtnGroups { this.render(); } + /** + * 渲染整个工具栏 + */ public render(): void { this.container.innerHTML = ''; this.btnRefs.clear(); @@ -142,7 +199,7 @@ export class OptBtnGroups { const wrapper = document.createElement('div'); wrapper.className = 'toolbar-container'; - // 直接遍历数组,顺序由 addGroup 控制 + // 渲染所有组 this.groups.forEach((group, index) => { const groupElement = this.renderGroup(group, index, this.groups.length); wrapper.appendChild(groupElement); @@ -151,6 +208,9 @@ export class OptBtnGroups { this.container.appendChild(wrapper); } + /** + * 渲染单个按钮组 + */ private renderGroup(group: ButtonGroup, index: number, total: number): HTMLElement { const groupEl = document.createElement('div'); groupEl.className = 'opt-btn-group'; @@ -169,6 +229,9 @@ export class OptBtnGroups { return groupEl; } + /** + * 渲染单个按钮 + */ private renderButton(button: OptButton): HTMLElement { const wrapper = document.createElement('div'); wrapper.className = 'opt-btn-wrapper'; @@ -176,6 +239,7 @@ export class OptBtnGroups { const btnEl = document.createElement('div'); btnEl.className = 'opt-btn'; + // 设置激活状态 if (this.activeBtnIds.has(button.id)) { btnEl.classList.add('active'); } @@ -191,11 +255,13 @@ export class OptBtnGroups { } } + // 渲染图标 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'; @@ -203,6 +269,7 @@ export class OptBtnGroups { btnEl.appendChild(label); } + // 如果有子菜单,渲染箭头 if (button.children && button.children.length > 0) { const arrow = document.createElement('span'); arrow.className = 'opt-btn-arrow'; @@ -210,6 +277,7 @@ export class OptBtnGroups { btnEl.appendChild(arrow); } + // 绑定事件 btnEl.addEventListener('click', () => this.handleClick(button)); btnEl.addEventListener('mouseenter', () => this.handleMouseEnter(button, btnEl)); btnEl.addEventListener('mouseleave', () => this.handleMouseLeave()); @@ -220,9 +288,13 @@ export class OptBtnGroups { 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); @@ -242,6 +314,9 @@ export class OptBtnGroups { } } + /** + * 处理子菜单项点击事件 + */ private handleSubClick(button: OptButton): void { if (button.keepActive) { const wasActive = this.activeBtnIds.has(button.id); @@ -260,6 +335,9 @@ export class OptBtnGroups { } } + /** + * 处理鼠标移入事件(显示菜单) + */ private handleMouseEnter(button: OptButton, btnEl: HTMLElement): void { if (this.hoverTimeout) { clearTimeout(this.hoverTimeout); @@ -278,12 +356,18 @@ export class OptBtnGroups { } } + /** + * 处理鼠标移出事件(隐藏菜单) + */ private handleMouseLeave(): void { this.hoverTimeout = window.setTimeout(() => { this.closeDropdown(); }, 200); } + /** + * 显示下拉菜单 + */ private showDropdown(button: OptButton, btnEl: HTMLElement): void { this.closeDropdown(); @@ -291,6 +375,22 @@ export class OptBtnGroups { 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; @@ -305,6 +405,7 @@ export class OptBtnGroups { } }); + // 保持菜单显��� dropdown.addEventListener('mouseenter', () => { if (this.hoverTimeout) { clearTimeout(this.hoverTimeout); @@ -318,6 +419,9 @@ export class OptBtnGroups { this.dropdownElement = dropdown; } + /** + * 渲染下拉菜单项 + */ private renderDropdownItem(button: OptButton): HTMLElement { const item = document.createElement('div'); item.className = 'opt-btn-dropdown-item'; @@ -341,6 +445,9 @@ export class OptBtnGroups { return item; } + /** + * 关闭所有下拉菜单 + */ private closeDropdown(): void { if (this.dropdownElement) { this.dropdownElement.remove(); @@ -355,6 +462,9 @@ export class OptBtnGroups { }); } + /** + * 更新按钮的激活状态样式 + */ private updateButtonState(buttonId: string): void { const btnEl = this.btnRefs.get(buttonId); if (btnEl) { @@ -366,14 +476,53 @@ export class OptBtnGroups { } } + /** + * 获取图标 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) { @@ -382,6 +531,6 @@ export class OptBtnGroups { this.container.innerHTML = ''; this.btnRefs.clear(); this.activeBtnIds.clear(); - this.groups = []; // 清空数组 + this.groups = []; } } \ No newline at end of file diff --git a/src/toolbar/index.type.ts b/src/toolbar/index.type.ts index 74352dd..fbd6ac3 100644 --- a/src/toolbar/index.type.ts +++ b/src/toolbar/index.type.ts @@ -4,40 +4,78 @@ export type ButtonType = 'button' | 'menu'; * 按钮配置接口(用于外部定义按钮) */ export interface ButtonConfig { - id: string; // 唯一标识 - type: ButtonType; // 按钮类型 - label: string; // 按钮文字 - icon?: string; // SVG 图标(内联 SVG 字符串) - keepActive?: boolean; // 是否保持激活状态(默认 false) - disabled?: boolean; // 是否禁用 - onClick?: (button: OptButton) => void; // 点击回调 - children?: ButtonConfig[]; // 子按钮配置(可选,用于菜单按钮) + /** 唯一标识 */ + id: string; + /** 按钮类型:普通按钮或菜单按钮 */ + type: ButtonType; + /** 按钮显示文字 */ + label: string; + /** SVG 图标(内联 SVG 字符串) */ + icon?: string; + /** 是否保持激活状态(默认 false) */ + keepActive?: boolean; + /** 是否禁用 */ + disabled?: boolean; + /** 点击回调函数 */ + onClick?: (button: OptButton) => void; + /** 子按钮配置(可选,用于菜单按钮) */ + children?: ButtonConfig[]; - groupId?: string; // 所属组ID - parentId?: string; // 父按钮ID(如果是子按钮) + /** 所属组ID */ + groupId?: string; + /** 父按钮ID(如果是子按钮,则必填) */ + parentId?: string; } /** * 操作按钮接口(内部使用,继承配置) */ export interface OptButton extends ButtonConfig { - children?: OptButton[]; // 内部使用的子按钮列表 + /** 内部使用的子按钮列表 */ + 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 { +export interface OptBtnGroupsOptions extends ToolbarColors { + /** 容器元素或 ID */ container: HTMLElement | string; + /** 是否显示标签 */ showLabel?: boolean; + /** 按钮可见性配置 Map */ visibility?: Record; } @@ -45,7 +83,10 @@ export interface OptBtnGroupsOptions { * 点击事件载荷 */ export interface ClickPayload { + /** 被点击的按钮对象 */ button: OptButton; + /** 触发的动作类型 */ action: 'activate' | 'deactivate' | 'trigger'; + /** 当前激活状态 */ isActive?: boolean; }