import { BaseManager } from '../core/base-manager'; import { ManagerRegistry } from '../core/manager-registry'; import { BottomDockStack } from '../components/bottom-dock-stack'; import { themeManager } from '../services/theme'; export interface BottomDockPanelDefinition { id: string; title: string; closable?: boolean; createContent?: () => HTMLElement; } export interface BottomDockStateChange { id: string; open: boolean; } type BottomDockStateListener = (state: BottomDockStateChange) => void; export class BottomDockManager extends BaseManager { private stack: BottomDockStack; private definitions: Map = new Map(); private openStates: Map = new Map(); private listeners: Set = new Set(); private unsubscribeTheme: (() => void) | null = null; constructor(container: HTMLElement, registry: ManagerRegistry) { super(registry); this.stack = new BottomDockStack(container); this.stack.setTheme(themeManager.getTheme()); this.unsubscribeTheme = themeManager.subscribe((theme) => { this.stack.setTheme(theme); }); this.registerDefaultPanels(); } public register(definition: BottomDockPanelDefinition): void { this.definitions.set(definition.id, definition); if (!this.openStates.has(definition.id)) { this.openStates.set(definition.id, false); } } public unregister(id: string): void { if (this.isOpen(id)) { this.close(id); } this.definitions.delete(id); this.openStates.delete(id); } public toggle(id: string): void { if (this.isOpen(id)) { this.close(id); return; } this.open(id); } public open(id: string): void { const definition = this.definitions.get(id); if (!definition) { console.warn(`[BottomDock] Unknown panel id: ${id}`); return; } if (this.isOpen(id)) { return; } const content = definition.createContent ? definition.createContent() : this.stack.createPlaceholderContent(`${definition.title} 面板内容占位`); this.stack.addPanel({ id, content, closable: definition.closable !== false, onClose: () => { this.close(id); } }); this.openStates.set(id, true); this.emitState({ id, open: true }); } public close(id: string): void { if (!this.isOpen(id)) { return; } this.stack.removePanel(id); this.openStates.set(id, false); this.emitState({ id, open: false }); } public isOpen(id: string): boolean { return this.openStates.get(id) ?? false; } public createPlaceholderContent(text: string): HTMLElement { return this.stack.createPlaceholderContent(text); } public onStateChange(listener: BottomDockStateListener): () => void { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; } public destroy(): void { if (this.unsubscribeTheme) { this.unsubscribeTheme(); this.unsubscribeTheme = null; } this.listeners.clear(); this.definitions.clear(); this.openStates.clear(); this.stack.destroy(); super.destroy(); } private emitState(state: BottomDockStateChange): void { this.listeners.forEach((listener) => { listener(state); }); } private registerDefaultPanels(): void { this.register({ id: 'measure', title: '测量', createContent: () => { const measurePanel = this.registry.measureDock?.getPanelElement(); if (measurePanel) { return measurePanel; } return this.stack.createPlaceholderContent('测量面板占位'); } }); this.register({ id: 'section', title: '剖切', createContent: () => { const sectionPanel = this.registry.sectionDock?.getPanelElement(); if (sectionPanel) { return sectionPanel; } return this.stack.createPlaceholderContent('剖切面板占位'); } }); this.register({ id: 'walk', title: '漫游', createContent: () => { const walkPanel = this.registry.walkDock?.getPanelElement(); if (walkPanel) { return walkPanel; } return this.stack.createPlaceholderContent('漫游面板占位'); } }); } }