feat(dees-appui-base): overhaul AppUI core: replace simple view rendering with a full-featured ViewRegistry (caching, hide/show lifecycle, async lazy-loading), introduce view lifecycle hooks and activation context, add activity log API/component, remove built-in router and state manager, and update configuration interfaces and demos
This commit is contained in:
@@ -2,6 +2,39 @@ import type { TemplateResult } from '@design.estate/dees-element';
|
||||
import type { IAppBarMenuItem } from './appbarmenuitem.js';
|
||||
import type { ITab } from './tab.js';
|
||||
import type { ISecondaryMenuGroup } from './secondarymenu.js';
|
||||
import type { IMenuGroup } from './menugroup.js';
|
||||
|
||||
// Forward declaration for circular reference
|
||||
export type TDeesAppuiBase = HTMLElement & {
|
||||
setAppBarMenus: (menus: IAppBarMenuItem[]) => void;
|
||||
updateAppBarMenu: (name: string, update: Partial<IAppBarMenuItem>) => void;
|
||||
setBreadcrumbs: (breadcrumbs: string | string[]) => void;
|
||||
setUser: (user: IAppUser | undefined) => void;
|
||||
setProfileMenuItems: (items: IAppBarMenuItem[]) => void;
|
||||
setSearchVisible: (visible: boolean) => void;
|
||||
setWindowControlsVisible: (visible: boolean) => void;
|
||||
setMainMenu: (config: IMainMenuConfig) => void;
|
||||
updateMainMenuGroup: (groupName: string, update: Partial<IMenuGroup>) => void;
|
||||
addMainMenuItem: (groupName: string, tab: ITab) => void;
|
||||
removeMainMenuItem: (groupName: string, tabKey: string) => void;
|
||||
setMainMenuSelection: (tabKey: string) => void;
|
||||
setMainMenuCollapsed: (collapsed: boolean) => void;
|
||||
setMainMenuBadge: (tabKey: string, badge: string | number) => void;
|
||||
clearMainMenuBadge: (tabKey: string) => void;
|
||||
setSecondaryMenu: (config: { heading?: string; groups: ISecondaryMenuGroup[] }) => void;
|
||||
updateSecondaryMenuGroup: (groupName: string, update: Partial<ISecondaryMenuGroup>) => void;
|
||||
addSecondaryMenuItem: (groupName: string, item: ISecondaryMenuGroup['items'][0]) => void;
|
||||
setSecondaryMenuSelection: (itemKey: string) => void;
|
||||
clearSecondaryMenu: () => void;
|
||||
setContentTabs: (tabs: ITab[]) => void;
|
||||
addContentTab: (tab: ITab) => void;
|
||||
removeContentTab: (tabKey: string) => void;
|
||||
selectContentTab: (tabKey: string) => void;
|
||||
getSelectedContentTab: () => ITab | undefined;
|
||||
activityLog: IActivityLogAPI;
|
||||
navigateToView: (viewId: string, params?: Record<string, string>) => Promise<boolean>;
|
||||
getCurrentView: () => IViewDefinition | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* User configuration for the app bar
|
||||
@@ -13,6 +46,69 @@ export interface IAppUser {
|
||||
status?: 'online' | 'offline' | 'busy' | 'away';
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity entry for the activity log
|
||||
*/
|
||||
export interface IActivityEntry {
|
||||
/** Unique identifier (auto-generated if not provided) */
|
||||
id?: string;
|
||||
/** Timestamp (auto-set to now if not provided) */
|
||||
timestamp?: Date;
|
||||
/** Activity type for icon styling */
|
||||
type: 'login' | 'logout' | 'view' | 'create' | 'update' | 'delete' | 'custom';
|
||||
/** User who performed the action */
|
||||
user: string;
|
||||
/** Activity message */
|
||||
message: string;
|
||||
/** Optional custom icon (overrides type-based icon) */
|
||||
iconName?: string;
|
||||
/** Optional additional data */
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity log programmatic API
|
||||
*/
|
||||
export interface IActivityLogAPI {
|
||||
/** Add a single activity entry */
|
||||
add: (entry: IActivityEntry) => void;
|
||||
/** Add multiple activity entries */
|
||||
addMany: (entries: IActivityEntry[]) => void;
|
||||
/** Clear all entries */
|
||||
clear: () => void;
|
||||
/** Get all entries */
|
||||
getEntries: () => IActivityEntry[];
|
||||
/** Filter entries */
|
||||
filter: (criteria: { user?: string; type?: IActivityEntry['type'] }) => IActivityEntry[];
|
||||
/** Search entries by message */
|
||||
search: (query: string) => IActivityEntry[];
|
||||
}
|
||||
|
||||
/**
|
||||
* View activation context passed to onActivate lifecycle hook
|
||||
*/
|
||||
export interface IViewActivationContext {
|
||||
/** Reference to the DeesAppuiBase instance */
|
||||
appui: TDeesAppuiBase;
|
||||
/** The view ID being activated */
|
||||
viewId: string;
|
||||
/** Route parameters if any */
|
||||
params?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* View lifecycle hooks interface
|
||||
* Views can implement these methods to receive lifecycle notifications
|
||||
*/
|
||||
export interface IViewLifecycle {
|
||||
/** Called when view is activated (displayed) */
|
||||
onActivate?: (context: IViewActivationContext) => void | Promise<void>;
|
||||
/** Called when view is deactivated (hidden) */
|
||||
onDeactivate?: () => void | Promise<void>;
|
||||
/** Called before navigation away - return false or message to block */
|
||||
canDeactivate?: () => boolean | string | Promise<boolean | string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* View definition for the view registry
|
||||
*/
|
||||
@@ -23,17 +119,29 @@ export interface IViewDefinition {
|
||||
name: string;
|
||||
/** Optional icon */
|
||||
iconName?: string;
|
||||
/** The view content - can be a tag name, element class, or template function */
|
||||
content: string | (new () => HTMLElement) | (() => TemplateResult);
|
||||
/**
|
||||
* The view content - can be:
|
||||
* - Tag name string (e.g., 'my-dashboard')
|
||||
* - Element class constructor
|
||||
* - Template function returning TemplateResult
|
||||
* - Async function returning any of the above (for lazy loading)
|
||||
*/
|
||||
content:
|
||||
| string
|
||||
| (new () => HTMLElement)
|
||||
| (() => TemplateResult)
|
||||
| (() => Promise<string | (new () => HTMLElement) | (() => TemplateResult)>);
|
||||
/** Secondary menu items specific to this view */
|
||||
secondaryMenu?: ISecondaryMenuGroup[];
|
||||
/** Content tabs specific to this view */
|
||||
contentTabs?: ITab[];
|
||||
/** Optional route path (defaults to id) */
|
||||
/** Optional route path (defaults to id). Supports params like 'settings/:section' */
|
||||
route?: string;
|
||||
/** Badge to show on menu item */
|
||||
badge?: string | number;
|
||||
badgeVariant?: 'default' | 'success' | 'warning' | 'error';
|
||||
/** Whether to cache this view instance (default: true) */
|
||||
cache?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,10 +158,18 @@ export interface IMainMenuSection {
|
||||
* Main menu configuration
|
||||
*/
|
||||
export interface IMainMenuConfig {
|
||||
/** Menu sections with view references */
|
||||
/** Logo icon */
|
||||
logoIcon?: string;
|
||||
/** Logo text */
|
||||
logoText?: string;
|
||||
/** Menu groups with tabs */
|
||||
groups?: IMenuGroup[];
|
||||
/** Menu sections with view references (alternative to groups) */
|
||||
sections?: IMainMenuSection[];
|
||||
/** Bottom pinned items (view IDs) */
|
||||
/** Bottom pinned items (view IDs or tabs) */
|
||||
bottomItems?: string[];
|
||||
/** Bottom tabs */
|
||||
bottomTabs?: ITab[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,47 +193,13 @@ export interface IBrandingConfig {
|
||||
logoText?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing configuration
|
||||
*/
|
||||
export interface IRoutingConfig {
|
||||
/** Routing mode */
|
||||
mode: 'hash' | 'history' | 'external' | 'none';
|
||||
/** Base path for history mode */
|
||||
basePath?: string;
|
||||
/** Default view ID to show on startup */
|
||||
defaultView?: string;
|
||||
/** Sync URL on view change */
|
||||
syncUrl?: boolean;
|
||||
/** Handle 404s - show view ID or callback */
|
||||
notFound?: string | (() => void);
|
||||
}
|
||||
|
||||
/**
|
||||
* State persistence configuration
|
||||
*/
|
||||
export interface IStatePersistenceConfig {
|
||||
/** Enable state persistence */
|
||||
enabled: boolean;
|
||||
/** Storage key prefix */
|
||||
storageKey?: string;
|
||||
/** Storage type */
|
||||
storage?: 'localStorage' | 'sessionStorage' | 'memory';
|
||||
/** What to persist */
|
||||
persist?: {
|
||||
mainMenuCollapsed?: boolean;
|
||||
secondaryMenuCollapsed?: boolean;
|
||||
selectedView?: boolean;
|
||||
secondaryMenuSelection?: boolean;
|
||||
collapsedGroups?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity log configuration
|
||||
*/
|
||||
export interface IActivityLogConfig {
|
||||
enabled?: boolean;
|
||||
/** Whether activity log is visible */
|
||||
visible?: boolean;
|
||||
/** Width of activity log panel */
|
||||
width?: number;
|
||||
}
|
||||
|
||||
@@ -137,46 +219,15 @@ export interface IAppConfig {
|
||||
/** Main menu structure */
|
||||
mainMenu?: IMainMenuConfig;
|
||||
|
||||
/** Routing configuration */
|
||||
routing?: IRoutingConfig;
|
||||
|
||||
/** State persistence configuration */
|
||||
statePersistence?: IStatePersistenceConfig;
|
||||
/** Default view ID to show on startup */
|
||||
defaultView?: string;
|
||||
|
||||
/** Activity log configuration */
|
||||
activityLog?: IActivityLogConfig;
|
||||
|
||||
/** Event callbacks (optional shorthand) */
|
||||
/** Event callbacks */
|
||||
onViewChange?: (viewId: string, view: IViewDefinition) => void;
|
||||
onSearch?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialized UI state for persistence
|
||||
*/
|
||||
export interface IAppUIState {
|
||||
/** Current view ID */
|
||||
currentViewId?: string;
|
||||
/** Main menu collapsed state */
|
||||
mainMenuCollapsed?: boolean;
|
||||
/** Secondary menu collapsed state */
|
||||
secondaryMenuCollapsed?: boolean;
|
||||
/** Selected secondary menu item key */
|
||||
secondaryMenuSelectedKey?: string;
|
||||
/** Collapsed group names in secondary menu */
|
||||
collapsedGroups?: string[];
|
||||
/** Timestamp of last save */
|
||||
timestamp?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route change event detail
|
||||
*/
|
||||
export interface IRouteChangeEvent {
|
||||
viewId: string;
|
||||
previousViewId: string | null;
|
||||
params?: Record<string, string>;
|
||||
source: 'navigation' | 'popstate' | 'initial' | 'programmatic';
|
||||
onSearch?: (query: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,4 +237,16 @@ export interface IViewChangeEvent {
|
||||
viewId: string;
|
||||
view: IViewDefinition;
|
||||
previousView?: IViewDefinition;
|
||||
params?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* View lifecycle event (for rxjs Subject)
|
||||
*/
|
||||
export interface IViewLifecycleEvent {
|
||||
type: 'activated' | 'deactivated' | 'loading' | 'loaded' | 'loadError';
|
||||
viewId: string;
|
||||
element?: HTMLElement;
|
||||
params?: Record<string, string>;
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user