2026-01-02 21:40:49 +00:00
# DeesAppui
2025-12-19 13:54:37 +00:00
2026-01-03 02:09:13 +00:00
A comprehensive application shell component providing a complete UI framework with navigation, menus, activity logging, and view management. 🚀
2025-12-19 13:54:37 +00:00
## Quick Start
``` typescript
import { html , DeesElement , customElement } from '@design.estate/dees-element' ;
2026-01-02 21:40:49 +00:00
import { DeesAppui } from '@design.estate/dees-catalog' ;
2025-12-19 13:54:37 +00:00
@customElement ( 'my-app' )
class MyApp extends DeesElement {
2026-01-02 21:40:49 +00:00
private appui : DeesAppui ;
2025-12-19 13:54:37 +00:00
async firstUpdated() {
2026-01-02 21:40:49 +00:00
this . appui = this . shadowRoot . querySelector ( 'dees-appui' ) ;
2025-12-19 13:54:37 +00:00
// Configure with views and menu
this . appui . configure ( {
branding : { logoIcon : 'lucide:box' , logoText : 'My App' } ,
views : [
{ id : 'dashboard' , name : 'Dashboard' , iconName : 'lucide:home' , content : 'my-dashboard' } ,
{ id : 'settings' , name : 'Settings' , iconName : 'lucide:settings' , content : 'my-settings' } ,
] ,
mainMenu : {
sections : [ { name : 'Main' , views : [ 'dashboard' , 'settings' ] } ]
} ,
defaultView : 'dashboard'
} ) ;
}
render() {
2026-01-02 21:40:49 +00:00
return html ` <dees-appui></dees-appui> ` ;
2025-12-19 13:54:37 +00:00
}
}
```
2026-01-03 02:09:13 +00:00
## Architecture Overview
The DeesAppui shell consists of several interconnected components:
```
┌─────────────────────────────────────────────────────────────────────┐
│ AppBar (dees-appui-appbar) │
│ ├── Menus (File, Edit, View...) │
│ ├── Breadcrumbs │
│ ├── User Profile + Dropdown │
│ └── Activity Log Toggle │
├─────────────┬───────────────────────────────────┬───────────────────┤
│ Main Menu │ Content Area │ Activity Log │
│ (collapsed/ │ ├── Content Tabs │ (slide panel) │
│ expanded) │ │ (closable, from tables/lists)│ │
│ │ └── View Container │ │
│ ┌─────────┐ │ └── Active View │ │
│ │ 🏠 Home │ ├─────────────────────────────────┐ │ │
│ │ 📁 Files│ │ Secondary Menu │ │ │
│ │ ⚙ Settings ├── Collapsible Groups │ │ │
│ │ │ │ ├── Item 1 │ │ │
│ └─────────┘ │ ├── Item 2 (with badge) │ │ │
│ │ └── Item 3 │ │ │
└─────────────┴─────────────────────────────────┴───────────────────────┘
```
---
2025-12-19 13:54:37 +00:00
## Configuration API
### `configure(config: IAppConfig)`
Configure the entire application shell with a single configuration object.
``` typescript
interface IAppConfig {
branding? : IBrandingConfig ;
appBar? : IAppBarConfig ;
views : IViewDefinition [ ] ;
mainMenu? : IMainMenuConfig ;
defaultView? : string ;
activityLog? : IActivityLogConfig ;
onViewChange ? : ( viewId : string , view : IViewDefinition ) = > void ;
onSearch ? : ( query : string ) = > void ;
}
```
### View Definition
``` typescript
interface IViewDefinition {
id : string ; // Unique identifier
name : string ; // Display name
iconName? : string ; // Icon (e.g., 'lucide:home')
content : // View content
| string // Tag name ('my-component')
| ( new ( ) = > HTMLElement ) // Class constructor
| ( ( ) = > TemplateResult ) // Template function
| ( ( ) = > Promise < ... > ) ; // Async for lazy loading
secondaryMenu? : ISecondaryMenuGroup [ ] ;
contentTabs? : ITab [ ] ;
route? : string ; // URL route (default: id)
badge? : string | number ;
cache? : boolean ; // Cache view instance (default: true)
}
```
---
## Programmatic APIs
### App Bar API
Control the top application bar.
``` typescript
// Set menu items (File, Edit, View, etc.)
appui . setAppBarMenus ( [
{
name : 'File' ,
submenu : [
{ name : 'New' , shortcut : 'Cmd+N' , action : ( ) = > { } } ,
{ name : 'Save' , shortcut : 'Cmd+S' , action : ( ) = > { } } ,
]
}
] ) ;
// Update single menu
appui . updateAppBarMenu ( 'File' , { submenu : [ . . . newItems ] } ) ;
// Breadcrumbs
appui . setBreadcrumbs ( 'Dashboard > Settings > Profile' ) ;
appui . setBreadcrumbs ( [ 'Dashboard' , 'Settings' , 'Profile' ] ) ;
// User profile
appui . setUser ( {
name : 'John Doe' ,
email : 'john@example.com' ,
avatar : '/avatars/john.png' ,
status : 'online' // 'online' | 'offline' | 'busy' | 'away'
} ) ;
appui . setProfileMenuItems ( [
{ name : 'Profile' , iconName : 'lucide:user' , action : ( ) = > { } } ,
{ divider : true } ,
{ name : 'Sign Out' , iconName : 'lucide:log-out' , action : ( ) = > { } }
] ) ;
// Search
appui . setSearchVisible ( true ) ;
appui . onSearch ( ( query ) = > console . log ( 'Search:' , query ) ) ;
// Window controls (for Electron/Tauri apps)
appui . setWindowControlsVisible ( false ) ;
```
### Main Menu API (Left Sidebar)
Control the main navigation menu.
``` typescript
// Set entire menu
appui . setMainMenu ( {
logoIcon : 'lucide:box' ,
logoText : 'My App' ,
groups : [
{
name : 'Main' ,
tabs : [
{ key : 'dashboard' , iconName : 'lucide:home' , action : ( ) = > { } } ,
{ key : 'inbox' , iconName : 'lucide:inbox' , badge : 5 , action : ( ) = > { } } ,
]
}
] ,
bottomTabs : [
{ key : 'settings' , iconName : 'lucide:settings' , action : ( ) = > { } }
]
} ) ;
// Update specific group
appui . updateMainMenuGroup ( 'Main' , { tabs : [ . . . newTabs ] } ) ;
// Add/remove items
appui . addMainMenuItem ( 'Main' , { key : 'tasks' , iconName : 'lucide:check' , action : ( ) = > { } } ) ;
appui . removeMainMenuItem ( 'Main' , 'tasks' ) ;
// Selection
appui . setMainMenuSelection ( 'dashboard' ) ;
2026-01-03 02:09:13 +00:00
// Visibility control
appui . setMainMenuCollapsed ( true ) ; // Collapse to icon-only sidebar
appui . setMainMenuVisible ( false ) ; // Hide completely
2025-12-19 13:54:37 +00:00
// Badges
appui . setMainMenuBadge ( 'inbox' , 12 ) ;
appui . clearMainMenuBadge ( 'inbox' ) ;
```
2026-01-03 02:09:13 +00:00
---
## Secondary Menu API 📋
The secondary menu is a contextual sidebar that appears next to the main content area. It supports **collapsible groups ** with icons and badges, making it perfect for:
2025-12-19 13:54:37 +00:00
2026-01-03 02:09:13 +00:00
- **Settings pages** (grouped settings categories)
- **File browsers** (folder trees)
- **Project navigation** (grouped by category)
- **Documentation** (chapters/sections)
### Collapsible Groups
Groups can be collapsed/expanded by clicking the group header. The state is visually indicated with an icon rotation.
2025-12-19 13:54:37 +00:00
``` typescript
2026-01-03 02:09:13 +00:00
// Set secondary menu with collapsible groups
2025-12-19 13:54:37 +00:00
appui . setSecondaryMenu ( {
heading : 'Settings' ,
groups : [
{
name : 'Account' ,
2026-01-03 02:09:13 +00:00
iconName : 'lucide:user' , // Group icon
collapsed : false , // Initial state (default: false)
2025-12-19 13:54:37 +00:00
items : [
2026-01-03 02:09:13 +00:00
{ key : 'profile' , iconName : 'lucide:user' , action : ( ) = > showProfile ( ) } ,
{ key : 'security' , iconName : 'lucide:shield' , badge : '!' , badgeVariant : 'warning' , action : ( ) = > showSecurity ( ) } ,
{ key : 'billing' , iconName : 'lucide:credit-card' , action : ( ) = > showBilling ( ) }
]
} ,
{
name : 'Preferences' ,
iconName : 'lucide:settings' ,
collapsed : true , // Start collapsed
items : [
{ key : 'notifications' , iconName : 'lucide:bell' , action : ( ) = > { } } ,
{ key : 'appearance' , iconName : 'lucide:palette' , action : ( ) = > { } } ,
{ key : 'language' , iconName : 'lucide:globe' , action : ( ) = > { } }
2025-12-19 13:54:37 +00:00
]
}
]
} ) ;
2026-01-03 02:09:13 +00:00
```
### Secondary Menu Item Properties
``` typescript
interface ISecondaryMenuItem {
key : string ; // Unique identifier
iconName? : string ; // Icon (e.g., 'lucide:user')
action : ( ) = > void ; // Click handler
badge? : string | number ; // Badge text/count
badgeVariant ? : 'default' | 'success' | 'warning' | 'error' ;
}
2025-12-19 13:54:37 +00:00
2026-01-03 02:09:13 +00:00
interface ISecondaryMenuGroup {
name : string ; // Group name (shown in header)
iconName? : string ; // Group icon
collapsed? : boolean ; // Initial collapsed state
items : ISecondaryMenuItem [ ] ; // Items in this group
}
```
### Updating Secondary Menu
``` typescript
// Update a specific group
appui . updateSecondaryMenuGroup ( 'Account' , {
items : [ . . . newItems ]
} ) ;
2025-12-19 13:54:37 +00:00
2026-01-03 02:09:13 +00:00
// Add item to a group
2025-12-19 13:54:37 +00:00
appui . addSecondaryMenuItem ( 'Account' , {
2026-01-03 02:09:13 +00:00
key : 'api-keys' ,
iconName : 'lucide:key' ,
action : ( ) = > showApiKeys ( )
2025-12-19 13:54:37 +00:00
} ) ;
2026-01-03 02:09:13 +00:00
// Selection (highlights the item)
2025-12-19 13:54:37 +00:00
appui . setSecondaryMenuSelection ( 'profile' ) ;
2026-01-03 02:09:13 +00:00
// Visibility control
appui . setSecondaryMenuCollapsed ( true ) ; // Collapse panel
appui . setSecondaryMenuVisible ( false ) ; // Hide completely
2025-12-19 13:54:37 +00:00
// Clear
appui . clearSecondaryMenu ( ) ;
```
2026-01-03 02:09:13 +00:00
### View-Specific Secondary Menus
Each view can define its own secondary menu that appears when the view is activated:
``` typescript
// In view definition
{
id : 'settings' ,
name : 'Settings' ,
content : 'my-settings-view' ,
secondaryMenu : [
{
name : 'General' ,
items : [
{ key : 'account' , iconName : 'lucide:user' , action : ( ) = > { } } ,
{ key : 'security' , iconName : 'lucide:shield' , action : ( ) = > { } }
]
}
]
}
// Or set dynamically in view's onActivate hook
onActivate ( context : IViewActivationContext ) {
context . appui . setSecondaryMenu ( {
heading : 'Project Files' ,
groups : [ . . . ]
} ) ;
}
```
---
## Content Tabs API 📑
Content tabs appear above the main view content. They're designed for **opening multiple items ** from tables, lists, or other data sources—similar to browser tabs or IDE editor tabs.
2025-12-19 13:54:37 +00:00
2026-01-03 02:09:13 +00:00
### Common Use Cases
- **Table row details** - Click a row to open it as a tab
- **Document editing** - Open multiple documents
- **Entity inspection** - View customer, order, product details
- **Multi-file editing** - Edit multiple configuration files
### Closable Tabs
Tabs can be closable, allowing users to open items, work with them, and close when done:
2025-12-19 13:54:37 +00:00
``` typescript
2026-01-03 02:09:13 +00:00
// Set initial tabs
2025-12-19 13:54:37 +00:00
appui . setContentTabs ( [
2026-01-03 02:09:13 +00:00
{ key : 'overview' , iconName : 'lucide:home' , action : ( ) = > showOverview ( ) } ,
{ key : 'activity' , iconName : 'lucide:activity' , action : ( ) = > showActivity ( ) }
2025-12-19 13:54:37 +00:00
] ) ;
2026-01-03 02:09:13 +00:00
// Add a closable tab when user clicks a table row
table . addEventListener ( 'row-click' , ( e ) = > {
const item = e . detail . item ;
appui . addContentTab ( {
key : ` item- ${ item . id } ` ,
label : item.name , // Display label
iconName : 'lucide:file' ,
closable : true , // Allow closing
action : ( ) = > showItemDetails ( item )
} ) ;
// Select the new tab
appui . selectContentTab ( ` item- ${ item . id } ` ) ;
} ) ;
// Handle tab close
appui . addEventListener ( 'tab-close' , ( e ) = > {
const tabKey = e . detail . key ;
// Cleanup resources if needed
console . log ( ` Tab ${ tabKey } closed ` ) ;
} ) ;
```
### Tab Management
``` typescript
// Add/remove tabs
appui . addContentTab ( {
key : 'debug' ,
iconName : 'lucide:bug' ,
closable : true ,
action : ( ) = > { }
} ) ;
2025-12-19 13:54:37 +00:00
appui . removeContentTab ( 'debug' ) ;
2026-01-03 02:09:13 +00:00
// Select tab
2025-12-19 13:54:37 +00:00
appui . selectContentTab ( 'preview' ) ;
2026-01-03 02:09:13 +00:00
// Get current tab
2025-12-19 13:54:37 +00:00
const current = appui . getSelectedContentTab ( ) ;
2026-01-03 02:09:13 +00:00
// Visibility control
appui . setContentTabsVisible ( false ) ; // Hide tab bar
// Auto-hide when only one tab
appui . setContentTabsAutoHide ( true , 1 ) ; // Hide when ≤ 1 tab
```
### Opening Items from Tables/Lists
A common pattern is opening table rows as closable tabs:
``` typescript
@customElement ( 'my-customers-view' )
class MyCustomersView extends DeesElement {
private appui : DeesAppui ;
onActivate ( context : IViewActivationContext ) {
this . appui = context . appui ;
// Set base tabs
this . appui . setContentTabs ( [
{ key : 'list' , label : 'All Customers' , iconName : 'lucide:users' , action : ( ) = > this . showList ( ) }
] ) ;
}
render() {
return html `
<dees-table
.data= ${ this . customers }
@row-dblclick= ${ this . openCustomerTab }
></dees-table>
` ;
}
openCustomerTab ( e : CustomEvent ) {
const customer = e . detail . item ;
const tabKey = ` customer- ${ customer . id } ` ;
// Check if tab already exists
const existingTab = this . appui . getSelectedContentTab ( ) ;
if ( existingTab ? . key === tabKey ) {
return ; // Already viewing this customer
}
// Add new closable tab
this . appui . addContentTab ( {
key : tabKey ,
label : customer.name ,
iconName : 'lucide:user' ,
closable : true ,
action : ( ) = > this . showCustomerDetails ( customer )
} ) ;
this . appui . selectContentTab ( tabKey ) ;
}
showCustomerDetails ( customer : Customer ) {
// Render customer details
this . currentView = html ` <customer-details .customer= ${ customer } ></customer-details> ` ;
}
showList() {
this . currentView = html ` <dees-table ...></dees-table> ` ;
}
}
2025-12-19 13:54:37 +00:00
```
2026-01-03 02:09:13 +00:00
---
## Activity Log API 📊
The activity log is a slide-out panel on the right side showing user actions and system events.
2025-12-19 13:54:37 +00:00
2026-01-03 02:09:13 +00:00
### Activity Log Toggle
The appbar includes a toggle button with a badge showing the entry count:
``` typescript
// Control visibility
appui . setActivityLogVisible ( true ) ; // Show panel
appui . toggleActivityLog ( ) ; // Toggle state
const isVisible = appui . getActivityLogVisible ( ) ;
// The toggle button automatically shows entry count
// Add entries and the badge updates automatically
```
### Adding Entries
2025-12-19 13:54:37 +00:00
``` typescript
// Add single entry
appui . activityLog . add ( {
type : 'create' , // 'login' | 'logout' | 'view' | 'create' | 'update' | 'delete' | 'custom'
user : 'John Doe' ,
message : 'created a new invoice' ,
iconName : 'lucide:file-plus' , // Optional custom icon
data : { invoiceId : '123' } // Optional metadata
} ) ;
2026-01-03 02:09:13 +00:00
// Add multiple entries (e.g., from backend)
2025-12-19 13:54:37 +00:00
appui . activityLog . addMany ( [ . . . entries ] ) ;
2026-01-03 02:09:13 +00:00
// Clear all entries
2025-12-19 13:54:37 +00:00
appui . activityLog . clear ( ) ;
2026-01-03 02:09:13 +00:00
// Query entries
2025-12-19 13:54:37 +00:00
const entries = appui . activityLog . getEntries ( ) ;
const filtered = appui . activityLog . filter ( { user : 'John' , type : 'create' } ) ;
const searched = appui . activityLog . search ( 'invoice' ) ;
```
2026-01-03 02:09:13 +00:00
### Activity Entry Types
Each type has a default icon that can be overridden:
| Type | Default Icon | Use Case |
|------|--------------|----------|
| `login` | `lucide:log-in` | User sign-in |
| `logout` | `lucide:log-out` | User sign-out |
| `view` | `lucide:eye` | Page/item viewed |
| `create` | `lucide:plus` | New item created |
| `update` | `lucide:pencil` | Item modified |
| `delete` | `lucide:trash` | Item deleted |
| `custom` | `lucide:activity` | Custom events |
---
## Navigation API
2025-12-19 13:54:37 +00:00
Navigate between views programmatically.
``` typescript
// Navigate to view
await appui . navigateToView ( 'settings' ) ;
await appui . navigateToView ( 'settings' , { section : 'profile' } ) ;
// Get current view
const current = appui . getCurrentView ( ) ;
// Subscribe to view changes
appui . viewChanged $ . subscribe ( ( event ) = > {
console . log ( ` Navigated to: ${ event . viewId } ` ) ;
} ) ;
// Subscribe to lifecycle events
appui . viewLifecycle $ . subscribe ( ( event ) = > {
if ( event . type === 'activated' ) {
console . log ( ` View ${ event . viewId } activated ` ) ;
}
} ) ;
```
---
## View Lifecycle Hooks
Views can implement lifecycle hooks to respond to activation/deactivation.
``` typescript
import { DeesElement , customElement } from '@design.estate/dees-element' ;
import type { IViewActivationContext , IViewLifecycle } from '@design.estate/dees-catalog' ;
@customElement ( 'my-settings-view' )
class MySettingsView extends DeesElement implements IViewLifecycle {
/**
* Called when view is activated (displayed)
* Receives typed context with appui reference
*/
async onActivate ( context : IViewActivationContext ) {
const { appui , viewId , params } = context ;
// Set view-specific secondary menu
appui . setSecondaryMenu ( {
heading : 'Settings' ,
groups : [ { name : 'Options' , items : [ . . . ] } ]
} ) ;
// Set view-specific tabs
appui . setContentTabs ( [ . . . ] ) ;
// Load data based on route params
if ( params ? . section ) {
await this . loadSection ( params . section ) ;
}
}
/**
* Called when view is deactivated (hidden)
*/
onDeactivate() {
this . cleanup ( ) ;
}
/**
* Called before navigation away
* Return false or a message string to block navigation
*/
canDeactivate ( ) : boolean | string {
if ( this . hasUnsavedChanges ) {
return 'You have unsaved changes. Leave anyway?' ;
}
return true ;
}
}
```
### IViewActivationContext
``` typescript
interface IViewActivationContext {
2026-01-02 21:40:49 +00:00
appui : DeesAppui ; // Reference to the app shell
2025-12-19 13:54:37 +00:00
viewId : string ; // The view ID being activated
params? : Record < string , string > ; // Route parameters
}
```
---
## Routing
Routes are automatically registered from view definitions using `domtools.router` .
``` typescript
const views = [
{ id : 'dashboard' , route : 'dashboard' , . . . } ,
{ id : 'settings' , route : 'settings/:section?' , . . . } , // Parameterized
{ id : 'user' , route : 'users/:id' , . . . } ,
] ;
// URL: #dashboard → navigates to dashboard view
// URL: #settings/profile → navigates to settings with params.section = 'profile'
// URL: #users/123 → navigates to user with params.id = '123'
```
### Hash-based Routing
The router uses hash-based routing by default (`#viewId` ). URLs are automatically synchronized when navigating via `navigateToView()` .
---
## View Caching
Views are cached by default. When navigating away and back, the same DOM element is reused (hidden/shown) rather than destroyed and recreated.
``` typescript
// Disable caching for a specific view
{
id : 'reports' ,
name : 'Reports' ,
content : 'my-reports-view' ,
cache : false // Always recreate this view
}
```
---
## Lazy Loading
Use async content functions for lazy loading views.
``` typescript
{
id : 'analytics' ,
name : 'Analytics' ,
content : async ( ) = > {
const module = await import ( './views/analytics.js' ) ;
return module . AnalyticsView ;
}
}
```
---
## RxJS Observables
The component exposes RxJS Subjects for reactive programming.
``` typescript
// View lifecycle events
appui . viewLifecycle $ . subscribe ( ( event ) = > {
// event.type: 'loading' | 'activated' | 'deactivated' | 'loaded' | 'loadError'
// event.viewId: string
// event.element?: HTMLElement
// event.params?: Record<string, string>
// event.error?: unknown
} ) ;
// View change events
appui . viewChanged $ . subscribe ( ( event ) = > {
// event.viewId: string
// event.view: IViewDefinition
// event.previousView?: IViewDefinition
// event.params?: Record<string, string>
} ) ;
```
---
## Complete Example
``` typescript
import { html , DeesElement , customElement } from '@design.estate/dees-element' ;
2026-01-02 21:40:49 +00:00
import { DeesAppui , IViewActivationContext } from '@design.estate/dees-catalog' ;
2025-12-19 13:54:37 +00:00
@customElement ( 'my-app' )
class MyApp extends DeesElement {
2026-01-02 21:40:49 +00:00
private appui : DeesAppui ;
2025-12-19 13:54:37 +00:00
async firstUpdated() {
2026-01-02 21:40:49 +00:00
this . appui = this . shadowRoot . querySelector ( 'dees-appui' ) ;
2025-12-19 13:54:37 +00:00
this . appui . configure ( {
branding : {
logoIcon : 'lucide:briefcase' ,
logoText : 'CRM Pro'
} ,
appBar : {
menuItems : [
{ name : 'File' , submenu : [ . . . ] } ,
{ name : 'Edit' , submenu : [ . . . ] }
] ,
showSearch : true ,
user : { name : 'Jane Smith' , status : 'online' }
} ,
views : [
{
id : 'dashboard' ,
name : 'Dashboard' ,
iconName : 'lucide:home' ,
content : 'crm-dashboard' ,
route : 'dashboard'
} ,
{
id : 'contacts' ,
name : 'Contacts' ,
iconName : 'lucide:users' ,
content : 'crm-contacts' ,
route : 'contacts' ,
badge : 42
} ,
{
id : 'settings' ,
name : 'Settings' ,
iconName : 'lucide:settings' ,
content : 'crm-settings' ,
route : 'settings/:section?'
}
] ,
mainMenu : {
sections : [
{ name : 'Main' , views : [ 'dashboard' , 'contacts' ] }
] ,
bottomItems : [ 'settings' ]
} ,
defaultView : 'dashboard' ,
onViewChange : ( viewId , view ) = > {
console . log ( ` Navigated to: ${ view . name } ` ) ;
} ,
onSearch : ( query ) = > {
console . log ( ` Search: ${ query } ` ) ;
}
} ) ;
// Load activity from backend
const activities = await fetch ( '/api/activities' ) . then ( r = > r . json ( ) ) ;
this . appui . activityLog . addMany ( activities ) ;
}
render() {
2026-01-02 21:40:49 +00:00
return html ` <dees-appui></dees-appui> ` ;
2025-12-19 13:54:37 +00:00
}
}
// View with lifecycle hooks
@customElement ( 'crm-settings' )
class CrmSettings extends DeesElement {
2026-01-02 21:40:49 +00:00
private appui : DeesAppui ;
2025-12-19 13:54:37 +00:00
onActivate ( context : IViewActivationContext ) {
this . appui = context . appui ;
// Set secondary menu for settings
this . appui . setSecondaryMenu ( {
heading : 'Settings' ,
groups : [
{
name : 'Account' ,
2026-01-03 02:09:13 +00:00
iconName : 'lucide:user' ,
2025-12-19 13:54:37 +00:00
items : [
{ key : 'profile' , iconName : 'lucide:user' , action : ( ) = > this . showSection ( 'profile' ) } ,
{ key : 'security' , iconName : 'lucide:shield' , action : ( ) = > this . showSection ( 'security' ) }
]
} ,
{
name : 'Preferences' ,
2026-01-03 02:09:13 +00:00
iconName : 'lucide:settings' ,
collapsed : true ,
2025-12-19 13:54:37 +00:00
items : [
{ key : 'notifications' , iconName : 'lucide:bell' , action : ( ) = > this . showSection ( 'notifications' ) }
]
}
]
} ) ;
// Navigate to section from URL params
if ( context . params ? . section ) {
this . showSection ( context . params . section ) ;
}
}
showSection ( section : string ) {
this . appui . setSecondaryMenuSelection ( section ) ;
// ... load section content
}
}
```
---
## TypeScript Types
All interfaces are exported from `@design.estate/dees-catalog` :
- `IAppConfig` - Main configuration
- `IViewDefinition` - View definition
- `IViewActivationContext` - Context passed to `onActivate`
- `IViewLifecycle` - Lifecycle hooks interface
- `IViewLifecycleEvent` - Lifecycle event for rxjs Subject
- `IViewChangeEvent` - View change event
- `IAppUser` - User configuration
- `IActivityEntry` - Activity log entry
- `IActivityLogAPI` - Activity log methods
- `IAppBarMenuItem` - App bar menu item
- `IMainMenuConfig` - Main menu configuration
- `ISecondaryMenuGroup` - Secondary menu group
2026-01-03 02:09:13 +00:00
- `ISecondaryMenuItem` - Secondary menu item
- `IMenuItem` - Tab/menu item definition