2025-12-09 08:26:24 +00:00
|
|
|
import type { TemplateResult } from '@design.estate/dees-element';
|
|
|
|
|
import type { IAppBarMenuItem } from './appbarmenuitem.js';
|
2025-12-29 01:20:24 +00:00
|
|
|
import type { IMenuItem } from './tab.js';
|
2025-12-19 13:54:37 +00:00
|
|
|
import type { IMenuGroup } from './menugroup.js';
|
2026-01-03 01:24:36 +00:00
|
|
|
import type { ISecondaryMenuGroup, ISecondaryMenuItem } from './secondarymenu.js';
|
2025-12-19 13:54:37 +00:00
|
|
|
|
2026-01-03 12:40:11 +00:00
|
|
|
// ==========================================
|
|
|
|
|
// BOTTOM BAR INTERFACES
|
|
|
|
|
// ==========================================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bottom bar widget status for styling
|
|
|
|
|
*/
|
|
|
|
|
export type TBottomBarWidgetStatus = 'idle' | 'active' | 'success' | 'warning' | 'error';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generic status widget for the bottom bar
|
|
|
|
|
*/
|
|
|
|
|
export interface IBottomBarWidget {
|
|
|
|
|
/** Unique identifier for the widget */
|
|
|
|
|
id: string;
|
|
|
|
|
/** Icon to display (lucide icon name) */
|
|
|
|
|
iconName?: string;
|
|
|
|
|
/** Text label to display */
|
|
|
|
|
label?: string;
|
|
|
|
|
/** Status affects styling (colors) */
|
|
|
|
|
status?: TBottomBarWidgetStatus;
|
|
|
|
|
/** Tooltip text */
|
|
|
|
|
tooltip?: string;
|
|
|
|
|
/** Whether the widget shows a loading spinner */
|
|
|
|
|
loading?: boolean;
|
|
|
|
|
/** Click handler for the widget */
|
|
|
|
|
onClick?: () => void;
|
|
|
|
|
/** Optional context menu items on right-click */
|
|
|
|
|
contextMenuItems?: IBottomBarContextMenuItem[];
|
|
|
|
|
/** Position: 'left' (default) or 'right' */
|
|
|
|
|
position?: 'left' | 'right';
|
|
|
|
|
/** Order within position group (lower = earlier) */
|
|
|
|
|
order?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Context menu item for bottom bar widgets
|
|
|
|
|
*/
|
|
|
|
|
export interface IBottomBarContextMenuItem {
|
|
|
|
|
name: string;
|
|
|
|
|
iconName?: string;
|
|
|
|
|
action: () => void | Promise<void>;
|
|
|
|
|
disabled?: boolean;
|
|
|
|
|
divider?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bottom bar action (quick action button)
|
|
|
|
|
*/
|
|
|
|
|
export interface IBottomBarAction {
|
|
|
|
|
/** Unique identifier */
|
|
|
|
|
id: string;
|
|
|
|
|
/** Icon to display */
|
|
|
|
|
iconName: string;
|
|
|
|
|
/** Tooltip */
|
|
|
|
|
tooltip?: string;
|
|
|
|
|
/** Click handler */
|
|
|
|
|
onClick: () => void | Promise<void>;
|
|
|
|
|
/** Whether action is disabled */
|
|
|
|
|
disabled?: boolean;
|
|
|
|
|
/** Position: 'left' or 'right' (default) */
|
|
|
|
|
position?: 'left' | 'right';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bottom bar configuration
|
|
|
|
|
*/
|
|
|
|
|
export interface IBottomBarConfig {
|
|
|
|
|
/** Whether bottom bar is visible */
|
|
|
|
|
visible?: boolean;
|
|
|
|
|
/** Initial widgets */
|
|
|
|
|
widgets?: IBottomBarWidget[];
|
|
|
|
|
/** Initial actions */
|
|
|
|
|
actions?: IBottomBarAction[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bottom bar programmatic API
|
|
|
|
|
*/
|
|
|
|
|
export interface IBottomBarAPI {
|
|
|
|
|
/** Add a widget */
|
|
|
|
|
addWidget: (widget: IBottomBarWidget) => void;
|
|
|
|
|
/** Update an existing widget by ID */
|
|
|
|
|
updateWidget: (id: string, update: Partial<IBottomBarWidget>) => void;
|
|
|
|
|
/** Remove a widget by ID */
|
|
|
|
|
removeWidget: (id: string) => void;
|
|
|
|
|
/** Get a widget by ID */
|
|
|
|
|
getWidget: (id: string) => IBottomBarWidget | undefined;
|
|
|
|
|
/** Clear all widgets */
|
|
|
|
|
clearWidgets: () => void;
|
|
|
|
|
/** Add an action button */
|
|
|
|
|
addAction: (action: IBottomBarAction) => void;
|
|
|
|
|
/** Remove an action by ID */
|
|
|
|
|
removeAction: (id: string) => void;
|
|
|
|
|
/** Clear all actions */
|
|
|
|
|
clearActions: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 13:54:37 +00:00
|
|
|
// Forward declaration for circular reference
|
2026-01-02 21:40:49 +00:00
|
|
|
export type TDeesAppui = HTMLElement & {
|
2025-12-19 13:54:37 +00:00
|
|
|
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;
|
2025-12-29 01:20:24 +00:00
|
|
|
addMainMenuItem: (groupName: string, tab: IMenuItem) => void;
|
2025-12-19 13:54:37 +00:00
|
|
|
removeMainMenuItem: (groupName: string, tabKey: string) => void;
|
|
|
|
|
setMainMenuSelection: (tabKey: string) => void;
|
|
|
|
|
setMainMenuCollapsed: (collapsed: boolean) => void;
|
2025-12-29 02:55:03 +00:00
|
|
|
setMainMenuVisible: (visible: boolean) => void;
|
|
|
|
|
setSecondaryMenuCollapsed: (collapsed: boolean) => void;
|
|
|
|
|
setSecondaryMenuVisible: (visible: boolean) => void;
|
|
|
|
|
setContentTabsVisible: (visible: boolean) => void;
|
2025-12-29 23:33:38 +00:00
|
|
|
setContentTabsAutoHide: (enabled: boolean, threshold?: number) => void;
|
2025-12-19 13:54:37 +00:00
|
|
|
setMainMenuBadge: (tabKey: string, badge: string | number) => void;
|
|
|
|
|
clearMainMenuBadge: (tabKey: string) => void;
|
2026-01-03 01:24:36 +00:00
|
|
|
setSecondaryMenu: (config: { heading?: string; groups: ISecondaryMenuGroup[] }) => void;
|
|
|
|
|
updateSecondaryMenuGroup: (groupName: string, update: Partial<ISecondaryMenuGroup>) => void;
|
|
|
|
|
addSecondaryMenuItem: (groupName: string, item: ISecondaryMenuItem) => void;
|
2025-12-19 13:54:37 +00:00
|
|
|
setSecondaryMenuSelection: (itemKey: string) => void;
|
|
|
|
|
clearSecondaryMenu: () => void;
|
2025-12-29 01:20:24 +00:00
|
|
|
setContentTabs: (tabs: IMenuItem[]) => void;
|
|
|
|
|
addContentTab: (tab: IMenuItem) => void;
|
2025-12-19 13:54:37 +00:00
|
|
|
removeContentTab: (tabKey: string) => void;
|
|
|
|
|
selectContentTab: (tabKey: string) => void;
|
2025-12-29 01:20:24 +00:00
|
|
|
getSelectedContentTab: () => IMenuItem | undefined;
|
2025-12-19 13:54:37 +00:00
|
|
|
activityLog: IActivityLogAPI;
|
2026-01-03 01:24:36 +00:00
|
|
|
setActivityLogVisible: (visible: boolean) => void;
|
|
|
|
|
toggleActivityLog: () => void;
|
|
|
|
|
getActivityLogVisible: () => boolean;
|
2025-12-19 13:54:37 +00:00
|
|
|
navigateToView: (viewId: string, params?: Record<string, string>) => Promise<boolean>;
|
|
|
|
|
getCurrentView: () => IViewDefinition | undefined;
|
2026-01-03 12:40:11 +00:00
|
|
|
// Bottom bar
|
|
|
|
|
bottomBar: IBottomBarAPI;
|
|
|
|
|
setBottomBarVisible: (visible: boolean) => void;
|
|
|
|
|
getBottomBarVisible: () => boolean;
|
2025-12-19 13:54:37 +00:00
|
|
|
};
|
2025-12-09 08:26:24 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* User configuration for the app bar
|
|
|
|
|
*/
|
|
|
|
|
export interface IAppUser {
|
|
|
|
|
name: string;
|
|
|
|
|
email?: string;
|
|
|
|
|
avatar?: string;
|
|
|
|
|
status?: 'online' | 'offline' | 'busy' | 'away';
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 13:54:37 +00:00
|
|
|
/**
|
|
|
|
|
* 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 {
|
2026-01-02 21:40:49 +00:00
|
|
|
/** Reference to the DeesAppui instance */
|
|
|
|
|
appui: TDeesAppui;
|
2025-12-19 13:54:37 +00:00
|
|
|
/** 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>;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-09 08:26:24 +00:00
|
|
|
/**
|
|
|
|
|
* View definition for the view registry
|
|
|
|
|
*/
|
|
|
|
|
export interface IViewDefinition {
|
|
|
|
|
/** Unique identifier for routing */
|
|
|
|
|
id: string;
|
|
|
|
|
/** Display name */
|
|
|
|
|
name: string;
|
|
|
|
|
/** Optional icon */
|
|
|
|
|
iconName?: string;
|
2025-12-19 13:54:37 +00:00
|
|
|
/**
|
|
|
|
|
* 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)>);
|
2025-12-09 08:26:24 +00:00
|
|
|
/** Secondary menu items specific to this view */
|
2026-01-03 01:24:36 +00:00
|
|
|
secondaryMenu?: ISecondaryMenuGroup[];
|
2025-12-09 08:26:24 +00:00
|
|
|
/** Content tabs specific to this view */
|
2025-12-29 01:20:24 +00:00
|
|
|
contentTabs?: IMenuItem[];
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Optional route path (defaults to id). Supports params like 'settings/:section' */
|
2025-12-09 08:26:24 +00:00
|
|
|
route?: string;
|
|
|
|
|
/** Badge to show on menu item */
|
|
|
|
|
badge?: string | number;
|
|
|
|
|
badgeVariant?: 'default' | 'success' | 'warning' | 'error';
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Whether to cache this view instance (default: true) */
|
|
|
|
|
cache?: boolean;
|
2025-12-09 08:26:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Main menu section with view references
|
|
|
|
|
*/
|
|
|
|
|
export interface IMainMenuSection {
|
|
|
|
|
/** Section name (optional for ungrouped) */
|
|
|
|
|
name?: string;
|
|
|
|
|
/** Views in this section (by ID reference) */
|
|
|
|
|
views: string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Main menu configuration
|
|
|
|
|
*/
|
|
|
|
|
export interface IMainMenuConfig {
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Logo icon */
|
|
|
|
|
logoIcon?: string;
|
|
|
|
|
/** Logo text */
|
|
|
|
|
logoText?: string;
|
|
|
|
|
/** Menu groups with tabs */
|
|
|
|
|
groups?: IMenuGroup[];
|
|
|
|
|
/** Menu sections with view references (alternative to groups) */
|
2025-12-09 08:26:24 +00:00
|
|
|
sections?: IMainMenuSection[];
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Bottom pinned items (view IDs or tabs) */
|
2025-12-09 08:26:24 +00:00
|
|
|
bottomItems?: string[];
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Bottom tabs */
|
2025-12-29 01:20:24 +00:00
|
|
|
bottomTabs?: IMenuItem[];
|
2025-12-09 08:26:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* App bar configuration
|
|
|
|
|
*/
|
|
|
|
|
export interface IAppBarConfig {
|
|
|
|
|
menuItems?: IAppBarMenuItem[];
|
|
|
|
|
breadcrumbs?: string;
|
|
|
|
|
breadcrumbSeparator?: string;
|
|
|
|
|
showWindowControls?: boolean;
|
|
|
|
|
showSearch?: boolean;
|
|
|
|
|
user?: IAppUser;
|
|
|
|
|
profileMenuItems?: IAppBarMenuItem[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Branding configuration
|
|
|
|
|
*/
|
|
|
|
|
export interface IBrandingConfig {
|
|
|
|
|
logoIcon?: string;
|
|
|
|
|
logoText?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Activity log configuration
|
|
|
|
|
*/
|
|
|
|
|
export interface IActivityLogConfig {
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Whether activity log is visible */
|
|
|
|
|
visible?: boolean;
|
|
|
|
|
/** Width of activity log panel */
|
2025-12-09 08:26:24 +00:00
|
|
|
width?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-02 21:40:49 +00:00
|
|
|
* Main unified configuration interface for dees-appui
|
2025-12-09 08:26:24 +00:00
|
|
|
*/
|
|
|
|
|
export interface IAppConfig {
|
|
|
|
|
/** Application branding */
|
|
|
|
|
branding?: IBrandingConfig;
|
|
|
|
|
|
|
|
|
|
/** App bar configuration */
|
|
|
|
|
appBar?: IAppBarConfig;
|
|
|
|
|
|
|
|
|
|
/** View definitions (the registry) */
|
|
|
|
|
views: IViewDefinition[];
|
|
|
|
|
|
|
|
|
|
/** Main menu structure */
|
|
|
|
|
mainMenu?: IMainMenuConfig;
|
|
|
|
|
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Default view ID to show on startup */
|
|
|
|
|
defaultView?: string;
|
2025-12-09 08:26:24 +00:00
|
|
|
|
|
|
|
|
/** Activity log configuration */
|
|
|
|
|
activityLog?: IActivityLogConfig;
|
|
|
|
|
|
2026-01-03 12:40:11 +00:00
|
|
|
/** Bottom bar configuration */
|
|
|
|
|
bottomBar?: IBottomBarConfig;
|
|
|
|
|
|
2025-12-19 13:54:37 +00:00
|
|
|
/** Event callbacks */
|
2025-12-09 08:26:24 +00:00
|
|
|
onViewChange?: (viewId: string, view: IViewDefinition) => void;
|
2025-12-19 13:54:37 +00:00
|
|
|
onSearch?: (query: string) => void;
|
2025-12-09 08:26:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-19 13:54:37 +00:00
|
|
|
* View change event detail
|
2025-12-09 08:26:24 +00:00
|
|
|
*/
|
2025-12-19 13:54:37 +00:00
|
|
|
export interface IViewChangeEvent {
|
2025-12-09 08:26:24 +00:00
|
|
|
viewId: string;
|
2025-12-19 13:54:37 +00:00
|
|
|
view: IViewDefinition;
|
|
|
|
|
previousView?: IViewDefinition;
|
2025-12-09 08:26:24 +00:00
|
|
|
params?: Record<string, string>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-19 13:54:37 +00:00
|
|
|
* View lifecycle event (for rxjs Subject)
|
2025-12-09 08:26:24 +00:00
|
|
|
*/
|
2025-12-19 13:54:37 +00:00
|
|
|
export interface IViewLifecycleEvent {
|
|
|
|
|
type: 'activated' | 'deactivated' | 'loading' | 'loaded' | 'loadError';
|
2025-12-09 08:26:24 +00:00
|
|
|
viewId: string;
|
2025-12-19 13:54:37 +00:00
|
|
|
element?: HTMLElement;
|
|
|
|
|
params?: Record<string, string>;
|
|
|
|
|
error?: unknown;
|
2025-12-09 08:26:24 +00:00
|
|
|
}
|