feat(dees-appui-base): Add unified App UI API to dees-appui-base with ViewRegistry, AppRouter and StateManager
This commit is contained in:
185
ts_web/elements/00group-appui/dees-appui-base/state.manager.ts
Normal file
185
ts_web/elements/00group-appui/dees-appui-base/state.manager.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import type { IStatePersistenceConfig, IAppUIState } from '../../interfaces/appconfig.js';
|
||||
|
||||
/**
|
||||
* Manager for persisting and restoring UI state
|
||||
*/
|
||||
export class StateManager {
|
||||
private config: Required<IStatePersistenceConfig>;
|
||||
private memoryStorage: Map<string, string> = new Map();
|
||||
|
||||
constructor(config: IStatePersistenceConfig = { enabled: false }) {
|
||||
this.config = {
|
||||
enabled: config.enabled,
|
||||
storageKey: config.storageKey || 'dees-appui-state',
|
||||
storage: config.storage || 'localStorage',
|
||||
persist: {
|
||||
mainMenuCollapsed: true,
|
||||
secondaryMenuCollapsed: true,
|
||||
selectedView: true,
|
||||
secondaryMenuSelection: true,
|
||||
collapsedGroups: true,
|
||||
...config.persist,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if state persistence is enabled
|
||||
*/
|
||||
public isEnabled(): boolean {
|
||||
return this.config.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current UI state
|
||||
*/
|
||||
public save(state: Partial<IAppUIState>): void {
|
||||
if (!this.config.enabled) return;
|
||||
|
||||
const existingState = this.load() || {};
|
||||
const newState: IAppUIState = {
|
||||
...existingState,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
// Only save what's configured
|
||||
if (this.config.persist.selectedView && state.currentViewId !== undefined) {
|
||||
newState.currentViewId = state.currentViewId;
|
||||
}
|
||||
if (this.config.persist.mainMenuCollapsed && state.mainMenuCollapsed !== undefined) {
|
||||
newState.mainMenuCollapsed = state.mainMenuCollapsed;
|
||||
}
|
||||
if (this.config.persist.secondaryMenuCollapsed && state.secondaryMenuCollapsed !== undefined) {
|
||||
newState.secondaryMenuCollapsed = state.secondaryMenuCollapsed;
|
||||
}
|
||||
if (this.config.persist.secondaryMenuSelection && state.secondaryMenuSelectedKey !== undefined) {
|
||||
newState.secondaryMenuSelectedKey = state.secondaryMenuSelectedKey;
|
||||
}
|
||||
if (this.config.persist.collapsedGroups && state.collapsedGroups !== undefined) {
|
||||
newState.collapsedGroups = state.collapsedGroups;
|
||||
}
|
||||
|
||||
this.setItem(this.config.storageKey, JSON.stringify(newState));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load persisted UI state
|
||||
*/
|
||||
public load(): IAppUIState | null {
|
||||
if (!this.config.enabled) return null;
|
||||
|
||||
try {
|
||||
const data = this.getItem(this.config.storageKey);
|
||||
if (!data) return null;
|
||||
return JSON.parse(data) as IAppUIState;
|
||||
} catch (e) {
|
||||
console.warn('Failed to load UI state:', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear persisted state
|
||||
*/
|
||||
public clear(): void {
|
||||
this.removeItem(this.config.storageKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if state exists
|
||||
*/
|
||||
public hasState(): boolean {
|
||||
return this.getItem(this.config.storageKey) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state age in milliseconds
|
||||
*/
|
||||
public getStateAge(): number | null {
|
||||
const state = this.load();
|
||||
if (!state?.timestamp) return null;
|
||||
return Date.now() - state.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update specific state properties
|
||||
*/
|
||||
public update(updates: Partial<IAppUIState>): void {
|
||||
const currentState = this.load() || {};
|
||||
this.save({ ...currentState, ...updates });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the storage key being used
|
||||
*/
|
||||
public getStorageKey(): string {
|
||||
return this.config.storageKey;
|
||||
}
|
||||
|
||||
// Storage abstraction methods
|
||||
|
||||
private getItem(key: string): string | null {
|
||||
switch (this.config.storage) {
|
||||
case 'localStorage':
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
case 'sessionStorage':
|
||||
try {
|
||||
return sessionStorage.getItem(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
case 'memory':
|
||||
return this.memoryStorage.get(key) || null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private setItem(key: string, value: string): void {
|
||||
switch (this.config.storage) {
|
||||
case 'localStorage':
|
||||
try {
|
||||
localStorage.setItem(key, value);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save to localStorage:', e);
|
||||
}
|
||||
break;
|
||||
case 'sessionStorage':
|
||||
try {
|
||||
sessionStorage.setItem(key, value);
|
||||
} catch (e) {
|
||||
console.warn('Failed to save to sessionStorage:', e);
|
||||
}
|
||||
break;
|
||||
case 'memory':
|
||||
this.memoryStorage.set(key, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private removeItem(key: string): void {
|
||||
switch (this.config.storage) {
|
||||
case 'localStorage':
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
break;
|
||||
case 'sessionStorage':
|
||||
try {
|
||||
sessionStorage.removeItem(key);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
break;
|
||||
case 'memory':
|
||||
this.memoryStorage.delete(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user