import './index.css'; import { CollapseOptions, CollapseItemConfig } from './types'; import { IBimComponent } from '../../types/component'; import { t, localeManager } from '../../services/locale'; import { themeManager } from '../../services/theme'; import type { ThemeConfig } from '../../themes/types'; /** * 单个折叠面板项 */ class BimCollapseItem { public element: HTMLElement; public headerEl!: HTMLElement; public contentEl!: HTMLElement; public contentBoxEl!: HTMLElement; public arrowEl!: HTMLElement; public titleEl!: HTMLElement; private config: CollapseItemConfig; private parent: BimCollapse; constructor(config: CollapseItemConfig, parent: BimCollapse) { this.config = config; this.parent = parent; this.element = this.createDom(); } private createDom(): HTMLElement { const itemEl = document.createElement('div'); itemEl.className = `bim-collapse-item ${this.config.className || ''}`; if (this.config.disabled) itemEl.classList.add('is-disabled'); itemEl.dataset.id = this.config.id; // ��部区域 this.headerEl = document.createElement('div'); this.headerEl.className = 'bim-collapse-header'; // 箭头图标 this.arrowEl = document.createElement('span'); this.arrowEl.className = 'bim-collapse-arrow'; this.arrowEl.innerHTML = ``; this.headerEl.appendChild(this.arrowEl); // 自定义图标 (可选) if (this.config.icon) { const iconEl = document.createElement('span'); iconEl.className = 'bim-collapse-icon'; iconEl.innerHTML = this.config.icon; this.headerEl.appendChild(iconEl); } // 标题文本 this.titleEl = document.createElement('span'); this.titleEl.className = 'bim-collapse-title'; this.titleEl.textContent = t(this.config.title); // 初始翻译 this.headerEl.appendChild(this.titleEl); // 额外内容 (可选,如右侧标签) if (this.config.extra) { const extraEl = document.createElement('div'); extraEl.className = 'bim-collapse-extra'; if (typeof this.config.extra === 'string') { extraEl.innerHTML = this.config.extra; } else { extraEl.appendChild(this.config.extra); } this.headerEl.appendChild(extraEl); } // 点击事件 this.headerEl.addEventListener('click', () => { if (this.config.disabled) return; this.parent.toggleItem(this.config.id); }); itemEl.appendChild(this.headerEl); // 内容区域 this.contentEl = document.createElement('div'); this.contentEl.className = 'bim-collapse-content is-hidden'; this.contentBoxEl = document.createElement('div'); this.contentBoxEl.className = 'bim-collapse-content-box'; if (typeof this.config.content === 'string') { this.contentBoxEl.innerHTML = this.config.content; } else { this.contentBoxEl.appendChild(this.config.content); } this.contentEl.appendChild(this.contentBoxEl); itemEl.appendChild(this.contentEl); return itemEl; } public updateLocale() { if (this.titleEl) { this.titleEl.textContent = t(this.config.title); } } public setActive(isActive: boolean) { if (isActive) { this.element.classList.add('is-active'); this.contentEl.classList.remove('is-hidden'); // 简单的动画处理:设置 height // 实际生产中可能需要更复杂的 JS 动画库或 transitionend 事件处理 // 这里依赖 CSS transition } else { this.element.classList.remove('is-active'); this.contentEl.classList.add('is-hidden'); } } } /** * 折叠面板组件 */ export class BimCollapse implements IBimComponent { private element: HTMLElement; private options: CollapseOptions; private items: Map = new Map(); private activeIds: Set = new Set(); private unsubscribeLocale: (() => void) | null = null; private unsubscribeTheme: (() => void) | null = null; constructor(options: CollapseOptions) { this.options = { bordered: true, accordion: false, ...options }; this.element = document.createElement('div'); this.element.className = `bim-collapse ${this.options.className || ''}`; if (!this.options.bordered) this.element.style.border = 'none'; if (this.options.ghost) this.element.classList.add('is-ghost'); const container = typeof this.options.container === 'string' ? document.getElementById(this.options.container) : this.options.container; if (container) { container.appendChild(this.element); } // 初始化激活的 ID if (this.options.activeIds) { this.options.activeIds.forEach(id => this.activeIds.add(id)); } this.init(); } public init() { // 创建子项 this.options.items.forEach(itemConfig => { const item = new BimCollapseItem(itemConfig, this); this.items.set(itemConfig.id, item); this.element.appendChild(item.element); // 设置初始状态 if (this.activeIds.has(itemConfig.id)) { item.setActive(true); } }); // 订阅语言变更 this.unsubscribeLocale = localeManager.subscribe(() => { this.setLocales(); }); // 订阅主题变更 this.unsubscribeTheme = themeManager.subscribe((theme) => { this.setTheme(theme); }); // 初始应用主题 this.setTheme(themeManager.getTheme()); } public toggleItem(id: string) { const isActive = this.activeIds.has(id); if (this.options.accordion) { // 手风琴模式:关闭其他所有,只展开目标 this.activeIds.clear(); if (!isActive) { this.activeIds.add(id); } } else { // 普通模式:切换目标状态 if (isActive) { this.activeIds.delete(id); } else { this.activeIds.add(id); } } this.refreshState(); if (this.options.onChange) { this.options.onChange(Array.from(this.activeIds)); } } private refreshState() { this.items.forEach((item, id) => { item.setActive(this.activeIds.has(id)); }); } public setTheme(theme: ThemeConfig): void { const style = this.element.style; style.setProperty('--bim-bg-color', theme.bgElevated); style.setProperty('--bim-border-color', theme.borderDefault); style.setProperty('--bim-text-color', theme.textPrimary); style.setProperty('--bim-header-bg-color', theme.componentBgHover); style.setProperty('--bim-header-hover-bg-color', theme.componentBgActive); style.setProperty('--bim-content-bg-color', theme.bgElevated); style.setProperty('--bim-disabled-color', theme.textDisabled); } public setLocales(): void { this.items.forEach(item => item.updateLocale()); } public destroy(): void { if (this.unsubscribeLocale) { this.unsubscribeLocale(); this.unsubscribeLocale = null; } if (this.unsubscribeTheme) { this.unsubscribeTheme(); this.unsubscribeTheme = null; } this.element.remove(); this.items.clear(); } }