Files
bim_engine/src/managers/bottom-dock-manager.ts

169 lines
4.8 KiB
TypeScript
Raw Normal View History

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<string, BottomDockPanelDefinition> = new Map();
private openStates: Map<string, boolean> = new Map();
private listeners: Set<BottomDockStateListener> = 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('漫游面板占位');
}
});
}
}