169 lines
4.8 KiB
TypeScript
169 lines
4.8 KiB
TypeScript
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('漫游面板占位');
|
|
}
|
|
});
|
|
}
|
|
}
|