@flusys/ng-layout 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,622 +1,518 @@
1
- # @flusys/ng-layout Package Guide
1
+ # @flusys/ng-layout
2
2
 
3
- ## Overview
3
+ > Application shell and layout system for the FLUSYS Angular platform — topbar, sidebar, menu, configurator, and layout state management.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-layout.svg)](https://www.npmjs.com/package/@flusys/ng-layout)
6
+ [![Angular](https://img.shields.io/badge/Angular-21-red.svg)](https://angular.io)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)
8
+ [![PrimeNG](https://img.shields.io/badge/PrimeNG-18+-blue.svg)](https://primeng.org)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
4
10
 
5
- `@flusys/ng-layout` provides the application shell, menu system, theme management, and layout components for FLUSYS applications.
11
+ ---
6
12
 
7
- **Key Principle:** ng-layout depends on ng-core and ng-shared, but remains independent of feature packages like ng-auth via injection tokens.
13
+ ## Table of Contents
14
+
15
+ - [Overview](#overview)
16
+ - [Features](#features)
17
+ - [Compatibility](#compatibility)
18
+ - [Installation](#installation)
19
+ - [Quick Start](#quick-start)
20
+ - [Layout Configuration](#layout-configuration)
21
+ - [LayoutConfig](#layoutconfig)
22
+ - [Menu Modes](#menu-modes)
23
+ - [Themes & Colors](#themes--colors)
24
+ - [Injection Tokens](#injection-tokens)
25
+ - [LayoutService](#layoutservice)
26
+ - [LayoutState](#layoutstate)
27
+ - [Components](#components)
28
+ - [AppLayoutComponent](#applayoutcomponent)
29
+ - [AppTopbarComponent](#apptopbarcomponent)
30
+ - [AppSidebarComponent](#appsidebarcomponent)
31
+ - [AppMenuComponent](#appmenucomponent)
32
+ - [AppMenuitemComponent](#appmenuitemcomponent)
33
+ - [AppCompanyBranchSelectorComponent](#appcompanybranchselectorcomponent)
34
+ - [AppConfiguratorComponent](#appconfiguratorscomponent)
35
+ - [Configuration Persistence](#configuration-persistence)
36
+ - [Integration Tokens](#integration-tokens)
37
+ - [Troubleshooting](#troubleshooting)
38
+ - [License](#license)
8
39
 
9
- ## Package Information
40
+ ---
10
41
 
11
- | Property | Value |
12
- |----------|-------|
13
- | Package | `@flusys/ng-layout` |
14
- | Version | 4.0.1 |
15
- | Dependencies | ng-core, ng-shared |
16
- | Build | `npm run build:ng-layout` |
42
+ ## Overview
17
43
 
18
- ## Architecture
44
+ `@flusys/ng-layout` provides the complete application shell for FLUSYS apps. It includes the topbar, sidebar, menu system, and layout configurator panel — all powered by Angular 21 signals and PrimeNG.
19
45
 
20
- ```
21
- ng-layout/
22
- ├── components/
23
- │ ├── app.layout.ts # Main layout wrapper
24
- │ ├── app.topbar.ts # Application header
25
- │ ├── app.sidebar.ts # Sidebar wrapper
26
- │ ├── app.menu.ts # Menu container
27
- │ ├── app.menuitem.ts # Recursive menu item
28
- │ ├── app.footer.ts # Footer component
29
- │ ├── app.configurator.ts # Theme customizer
30
- │ ├── app.floatingconfigurator.ts
31
- │ └── top-bar/
32
- │ ├── app.profile.ts
33
- │ ├── app.launcher.ts
34
- │ └── app.company-branch-selector.ts
35
- ├── interfaces/
36
- │ ├── layout-auth.interface.ts # Auth integration tokens
37
- │ └── view-transitions.ts
38
- ├── services/
39
- │ ├── layout.service.ts # Layout state management
40
- │ └── layout-persistence.service.ts
41
- ├── theme/
42
- │ ├── green-theme.ts
43
- │ └── navy-blue-theme.ts
44
- └── utils/
45
- ├── menu-filter.util.ts
46
- └── apps-filter.util.ts
47
- ```
46
+ The layout integrates with auth, notification, and localization packages through injection token interfaces, maintaining clean package independence via the Provider Interface Pattern.
48
47
 
49
48
  ---
50
49
 
51
- ## Components
50
+ ## Features
52
51
 
53
- ### AppLayout
52
+ - ✅ Three menu modes: `static`, `overlay`, `topbar`
53
+ - ✅ Signal-based layout state management (`LayoutService`)
54
+ - ✅ Layout persistence (theme, color, mode saved to localStorage)
55
+ - ✅ Company/branch selector in sidebar
56
+ - ✅ Topbar user profile dropdown with avatar
57
+ - ✅ Notification bell integration via injection token
58
+ - ✅ Language selector integration via injection token
59
+ - ✅ Responsive mobile layout
60
+ - ✅ Fully configurable color palettes and themes
61
+ - ✅ Zoneless-compatible
54
62
 
55
- Main layout wrapper with signal-based state management.
63
+ ---
56
64
 
57
- ```typescript
58
- import { AppLayout, LayoutService } from '@flusys/ng-layout';
59
-
60
- @Component({
61
- selector: 'app-root',
62
- imports: [AppLayout],
63
- template: `<app-layout><router-outlet /></app-layout>`,
64
- host: { '[class.app-dark]': 'layoutService.isDarkTheme()' },
65
- })
66
- export class AppComponent {
67
- readonly layoutService = inject(LayoutService);
68
- }
69
- ```
65
+ ## Compatibility
66
+
67
+ | Package | Version |
68
+ |---------|---------|
69
+ | Angular | 21+ |
70
+ | @flusys/ng-core | 4.x |
71
+ | @flusys/ng-shared | 4.x |
72
+ | PrimeNG | 18+ |
73
+ | Tailwind CSS | 3+ |
74
+
75
+ ---
70
76
 
71
- **Includes:** AppTopbar, AppSidebar, main content area, AppFooter, layout mask overlay
77
+ ## Installation
72
78
 
73
- **Layout Modes:**
74
- - **Static** - Sidebar always visible on desktop, overlay on mobile
75
- - **Overlay** - Sidebar overlays content when opened
79
+ ```bash
80
+ npm install @flusys/ng-layout @flusys/ng-core @flusys/ng-shared
81
+ ```
76
82
 
77
- **Dynamic CSS Classes:**
78
- | Class | Condition |
79
- |-------|-----------|
80
- | `layout-static` | Static menu mode |
81
- | `layout-overlay` | Overlay menu mode |
82
- | `layout-static-inactive` | Static menu collapsed on desktop |
83
- | `layout-overlay-active` | Overlay menu open |
84
- | `layout-mobile-active` | Mobile menu open |
83
+ ---
85
84
 
86
- **Behavior:**
87
- - Subscribes to `layoutService.overlayOpen$` for outside click handling
88
- - Auto-hides menu on route navigation
89
- - Blocks body scroll when mobile menu is active
85
+ ## Quick Start
90
86
 
91
- ### AppTopbar
87
+ ### 1. Set Up App Layout Route
92
88
 
93
- Application header with branding and user actions.
89
+ ```typescript
90
+ // app.routes.ts
91
+ import { Routes } from '@angular/router';
92
+ import { AppLayoutComponent } from '@flusys/ng-layout';
94
93
 
95
- ```
96
- ┌─────────────────────────────────────────────────────────────────┐
97
- [≡] CompanyName [☀/🌙] [🎨] [...] [Apps] [🏢] [👤] │
98
- └─────────────────────────────────────────────────────────────────┘
94
+ export const routes: Routes = [
95
+ {
96
+ path: '',
97
+ component: AppLayoutComponent,
98
+ children: [
99
+ {
100
+ path: 'dashboard',
101
+ loadComponent: () => import('./pages/dashboard/dashboard.component'),
102
+ },
103
+ {
104
+ path: 'products',
105
+ loadComponent: () => import('./pages/products/product-list.component'),
106
+ },
107
+ ],
108
+ },
109
+ {
110
+ path: 'auth',
111
+ loadChildren: () => import('./pages/auth/auth.routes'),
112
+ },
113
+ ];
99
114
  ```
100
115
 
101
- | Element | Description |
102
- |---------|-------------|
103
- | `[≡]` | Menu toggle → `layoutService.onMenuToggle()` |
104
- | CompanyName | From `layoutService.companyName` → links to `/` |
105
- | `[☀/🌙]` | Dark mode toggle |
106
- | `[🎨]` | AppConfigurator panel |
107
- | `[...]` | Mobile menu (hidden on desktop) |
108
- | `[Apps]` | AppLauncher (if `layoutService.hasApps()`) |
109
- | `[🏢]` | AppCompanyBranchSelector (if company feature enabled) |
110
- | `[👤]` | AppProfile dropdown |
116
+ ### 2. Define Menu Configuration
111
117
 
112
- ### AppMenu & AppMenuitem
118
+ ```typescript
119
+ // app-menu.config.ts
120
+ import { IMenuItem } from '@flusys/ng-layout';
121
+
122
+ export const APP_MENU: IMenuItem[] = [
123
+ {
124
+ labelKey: 'menu.dashboard',
125
+ label: 'Dashboard',
126
+ icon: 'pi pi-home',
127
+ routerLink: ['/dashboard'],
128
+ },
129
+ {
130
+ labelKey: 'menu.products',
131
+ label: 'Products',
132
+ icon: 'pi pi-box',
133
+ items: [
134
+ {
135
+ labelKey: 'menu.products.list',
136
+ label: 'All Products',
137
+ routerLink: ['/products'],
138
+ },
139
+ {
140
+ labelKey: 'menu.products.create',
141
+ label: 'Add Product',
142
+ routerLink: ['/products/create'],
143
+ },
144
+ ],
145
+ },
146
+ ];
147
+ ```
113
148
 
114
- Menu container renders permission-filtered items from `layoutService.menu`.
149
+ ### 3. Provide Layout Configuration
115
150
 
116
- **AppMenuitem Signal Inputs:**
117
- | Input | Type | Description |
118
- |-------|------|-------------|
119
- | `item` | `IMenuItem` | Required menu item data |
120
- | `index` | `number` | Required index in parent |
121
- | `parentKey` | `string` | Optional parent key (default: `''`) |
122
-
123
- **Router Link Active Options:**
124
- - Root path (`/`) uses `'exact'` matching
125
- - Other paths use `'subset'` matching for child route support
126
-
127
- **CSS Transitions (no @angular/animations):**
128
- ```css
129
- :host ul {
130
- transition: max-height 400ms cubic-bezier(0.86, 0, 0.07, 1);
131
- }
132
- :host ul.submenu-collapsed { max-height: 0; }
133
- :host ul.submenu-expanded { max-height: 1000px; }
151
+ ```typescript
152
+ // app.config.ts
153
+ import { ApplicationConfig } from '@angular/core';
154
+ import { APP_CONFIG } from '@flusys/ng-core';
155
+ import { LAYOUT_MENU, LAYOUT_CONFIG } from '@flusys/ng-layout';
156
+ import { APP_MENU } from './app-menu.config';
157
+ import { environment } from './environments/environment';
158
+
159
+ export const appConfig: ApplicationConfig = {
160
+ providers: [
161
+ { provide: APP_CONFIG, useValue: environment },
162
+ { provide: LAYOUT_MENU, useValue: APP_MENU },
163
+ {
164
+ provide: LAYOUT_CONFIG,
165
+ useValue: {
166
+ menuMode: 'static',
167
+ theme: 'lara-light-blue',
168
+ colorScheme: 'light',
169
+ },
170
+ },
171
+ ],
172
+ };
134
173
  ```
135
174
 
136
- ### AppFooter
175
+ ---
137
176
 
138
- Displays branding from `LayoutService`: `appName`, `authorName`, `authorUrl`
177
+ ## Layout Configuration
139
178
 
140
- ### AppProfile
179
+ ### LayoutConfig
141
180
 
142
- User profile dropdown in topbar.
181
+ ```typescript
182
+ interface LayoutConfig {
183
+ /** Menu display mode */
184
+ menuMode: 'static' | 'overlay' | 'topbar';
185
+
186
+ /** PrimeNG theme name */
187
+ theme: string;
143
188
 
144
- **Features:**
145
- - User info (picture, name, email) with text truncation
146
- - Profile link → `/profile`
147
- - Copy SignUp Link → `{origin}/auth/register?companySlug={slug}`
148
- - Logout with toast feedback
189
+ /** Color scheme */
190
+ colorScheme: 'light' | 'dark' | 'dim';
149
191
 
150
- **Computed Signals:**
151
- | Signal | Source | Fallback |
152
- |--------|--------|----------|
153
- | `userName` | `authState.loginUserData()?.name` | `'Guest'` |
154
- | `userEmail` | `authState.loginUserData()?.email` | `''` |
155
- | `profilePicture` | `layoutService.userProfilePictureUrl()` | `''` |
192
+ /** Ripple effect */
193
+ ripple?: boolean;
156
194
 
157
- ### AppCompanyBranchSelector
195
+ /** Scale factor (12-16) */
196
+ scale?: number;
158
197
 
159
- Company/branch switcher in topbar. Shown when company feature is enabled.
198
+ /** Menu theme */
199
+ menuTheme?: 'light' | 'dark' | 'colored';
160
200
 
161
- **State Signals:**
162
- | Signal | Description |
163
- |--------|-------------|
164
- | `companies` | Available companies list |
165
- | `branches` | Branches for selected company |
166
- | `selectedCompanyId` / `selectedBranchId` | Current selection |
167
- | `isLoadingCompanies` / `isLoadingBranches` | Loading states |
168
- | `isSwitching` | Switch operation in progress |
169
- | `canSwitch` | Computed: enabled when selection differs from current |
201
+ /** Card border style */
202
+ inputStyle?: 'outlined' | 'filled';
203
+ }
204
+ ```
170
205
 
171
- **Workflow:**
172
- 1. Click button → Panel opens, companies load
173
- 2. Select company → Branches load (auto-select if single branch)
174
- 3. Select branch → Click "Switch" → API call → JWT updated → Redirect
206
+ ### Menu Modes
175
207
 
176
- ### AppLauncher
208
+ #### Static Mode (Default)
177
209
 
178
- App launcher grid for quick access to external applications.
210
+ Sidebar is always visible on desktop. Collapses to icons on tablet.
179
211
 
180
212
  ```typescript
181
- const apps: ILauncherApp[] = [
182
- {
183
- id: 'docs',
184
- name: 'Docs',
185
- iconType: IconTypeEnum.PrimeNg,
186
- icon: 'pi pi-book',
187
- url: 'https://docs.example.com',
188
- permissionLogic: { id: 'docs', type: 'action', actionId: 'docs.view' }, // optional
189
- },
190
- ];
191
- layoutService.setApps(apps);
213
+ { provide: LAYOUT_CONFIG, useValue: { menuMode: 'static' } }
192
214
  ```
193
215
 
194
- **Features:**
195
- - Responsive grid: `grid-cols-2 sm:grid-cols-3`
196
- - External links (new tab)
197
- - Permission filtering via `permissionLogic`
198
- - Only visible when `layoutService.hasApps()` is true
216
+ #### Overlay Mode
199
217
 
200
- ### AppConfigurator
218
+ Sidebar slides over content. Click outside to close.
201
219
 
202
- Theme customizer dropdown.
203
-
204
- **Options:**
205
- | Setting | Values |
206
- |---------|--------|
207
- | Primary colors | `emerald`, `green`, `lime`, `orange`, `amber`, `yellow`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`, `noir` |
208
- | Surface colors | `slate`, `gray`, `zinc`, `neutral`, `stone`, `soho`, `viva`, `ocean` |
209
- | Presets | `Aura` (default), `Lara`, `Nora` |
210
- | Menu mode | `static`, `overlay` |
211
-
212
- Uses `$t()`, `updatePreset()`, `updateSurfacePalette()` from `@primeuix/themes`.
220
+ ```typescript
221
+ { provide: LAYOUT_CONFIG, useValue: { menuMode: 'overlay' } }
222
+ ```
213
223
 
214
- ### AppFloatingConfigurator
224
+ #### Topbar Mode (v4.0.1+)
215
225
 
216
- Fixed-position floating buttons for pages without AppLayout (e.g., auth pages).
226
+ Desktop renders horizontal navigation bar. Mobile falls back to vertical sidebar.
217
227
 
218
228
  ```typescript
219
- @Component({
220
- imports: [AppFloatingConfigurator],
221
- template: `<app-floating-configurator /><div class="login-form">...</div>`
222
- })
223
- export class LoginComponent {}
229
+ { provide: LAYOUT_CONFIG, useValue: { menuMode: 'topbar' } }
224
230
  ```
225
231
 
226
- Position: `top-4 md:top-8 right-2 md:right-8 z-50`
232
+ Submenu items appear on hover in topbar mode with automatic positioning.
227
233
 
228
- ---
234
+ ### Themes & Colors
229
235
 
230
- ## Services
236
+ Built-in themes (PrimeNG):
231
237
 
232
- ### LayoutService
238
+ | Theme | Description |
239
+ |-------|-------------|
240
+ | `lara-light-blue` | Default — light blue |
241
+ | `lara-dark-blue` | Dark blue |
242
+ | `lara-light-indigo` | Light indigo |
243
+ | `lara-dark-indigo` | Dark indigo |
244
+ | `lara-light-purple` | Light purple |
245
+ | `lara-dark-purple` | Dark purple |
246
+ | `lara-light-teal` | Light teal |
247
+ | `lara-dark-teal` | Dark teal |
233
248
 
234
- Signal-based singleton service for layout state management.
249
+ ---
235
250
 
236
- #### Interfaces
251
+ ## Injection Tokens
237
252
 
238
- ```typescript
239
- interface LayoutConfig {
240
- preset?: string; // "Aura" | "Lara" | "Nora"
241
- primary?: string; // "emerald" | "green" | etc.
242
- surface?: string | null; // "slate" | "gray" | etc.
243
- darkTheme?: boolean;
244
- menuMode?: 'static' | 'overlay';
245
- }
253
+ | Token | Type | Description |
254
+ |-------|------|-------------|
255
+ | `LAYOUT_CONFIG` | `InjectionToken<LayoutConfig>` | Initial layout configuration |
256
+ | `LAYOUT_MENU` | `InjectionToken<IMenuItem[]>` | Application menu items |
257
+ | `LAYOUT_AUTH_STATE` | `InjectionToken<ILayoutAuthState>` | Auth state bridge (from ng-auth) |
258
+ | `LAYOUT_AUTH_API` | `InjectionToken<ILayoutAuthApi>` | Auth API bridge (from ng-auth) |
259
+ | `LAYOUT_NOTIFICATION_BELL` | `InjectionToken<INotificationBellProvider>` | Notification bell (from ng-notification) |
260
+ | `LAYOUT_LANGUAGE_SELECTOR` | `InjectionToken<ILanguageSelectorProvider>` | Language selector (from ng-localization) |
246
261
 
247
- interface LayoutState {
248
- staticMenuDesktopInactive?: boolean;
249
- overlayMenuActive?: boolean;
250
- staticMenuMobileActive?: boolean;
251
- menuHoverActive?: boolean;
252
- }
262
+ ---
253
263
 
254
- interface UserProfile { id: string; name: string; email: string; profilePictureUrl?: string | null; }
255
- interface CompanyProfile { id: string; name: string; slug: string; logoUrl?: string | null; }
256
- ```
264
+ ## LayoutService
265
+
266
+ Central signal-based service for layout state management.
257
267
 
258
- #### Signals
259
-
260
- **State Signals (readonly):**
261
- | Signal | Type |
262
- |--------|------|
263
- | `layoutConfig` | `Signal<LayoutConfig>` |
264
- | `layoutState` | `Signal<LayoutState>` |
265
- | `transitionComplete` | `Signal<boolean>` |
266
- | `userProfile` | `Signal<UserProfile \| null>` |
267
- | `companyProfile` | `Signal<CompanyProfile \| null>` |
268
-
269
- **Static Values (from APP_CONFIG):**
270
- | Property | Fallback |
271
- |----------|----------|
272
- | `appName` | `DEFAULT_APP_NAME` |
273
- | `authorName` | `DEFAULT_AUTHOR.name` |
274
- | `authorUrl` | `DEFAULT_AUTHOR.url` |
275
-
276
- **Computed Signals:**
277
- | Signal | Description |
278
- |--------|-------------|
279
- | `menu` | Permission-filtered menu items |
280
- | `apps` | Permission-filtered launcher apps |
281
- | `hasApps` | Whether apps exist |
282
- | `isDarkTheme` | Dark theme active |
283
- | `isSidebarActive` | Sidebar visible |
284
- | `isOverlay` | Overlay menu mode |
285
- | `getPrimary` / `getSurface` | Current colors |
286
- | `userName` / `userEmail` | User info (with fallbacks) |
287
- | `userProfilePictureUrl` / `companyLogoUrl` | Image URLs |
288
- | `companyName` | Company name or appName fallback |
289
- | `isAuthenticated` | User profile exists |
290
-
291
- #### Methods
292
-
293
- | Method | Description |
294
- |--------|-------------|
295
- | `onMenuToggle()` | Toggle menu (handles static/overlay/mobile) |
296
- | `onMenuStateChange(event)` | Emit menu state change |
297
- | `reset()` | Reset menu state |
298
- | `isDesktop()` / `isMobile()` | Viewport checks (SSR-safe) |
299
- | `updateLayoutConfig(config)` | Partial config update |
300
- | `updateLayoutState(state)` | Partial state update |
301
- | `toggleDarkMode(config?)` | Toggle `app-dark` class |
302
- | `setUserProfile(profile)` | Set user profile |
303
- | `setCompanyProfile(profile)` | Set company profile |
304
- | `setMenu(items)` / `clearMenu()` | Menu management |
305
- | `setApps(apps)` / `clearApps()` | Apps management |
306
-
307
- #### RxJS Observables
308
-
309
- | Observable | Purpose |
310
- |------------|---------|
311
- | `menuSource$` | Menu state change events |
312
- | `resetSource$` | Menu reset events |
313
- | `configUpdate$` | Config change events |
314
- | `overlayOpen$` | Menu overlay opened |
315
-
316
- #### Dark Mode Transition
317
-
318
- Uses View Transitions API with fallback:
319
268
  ```typescript
320
- toggleDarkMode(config?: LayoutConfig): void {
321
- const isDark = (config ?? this._layoutConfig()).darkTheme;
322
- this.document.documentElement.classList.toggle('app-dark', isDark);
269
+ import { LayoutService } from '@flusys/ng-layout';
270
+
271
+ @Component({ ... })
272
+ export class MyComponent {
273
+ private layoutService = inject(LayoutService);
274
+
275
+ // Computed signals (read-only)
276
+ isStatic = this.layoutService.isStatic(); // boolean
277
+ isOverlay = this.layoutService.isOverlay(); // boolean
278
+ isTopbar = this.layoutService.isTopbar(); // boolean
279
+ isMobile = this.layoutService.isMobile(); // boolean
280
+ isMenuOpen = this.layoutService.isMenuOpen(); // boolean
281
+
282
+ // Actions
283
+ toggleMenu(): void {
284
+ this.layoutService.toggleMenu();
285
+ }
286
+
287
+ setTheme(theme: string): void {
288
+ this.layoutService.setTheme(theme);
289
+ }
290
+
291
+ setMenuMode(mode: 'static' | 'overlay' | 'topbar'): void {
292
+ this.layoutService.setMenuMode(mode);
293
+ }
323
294
  }
324
295
  ```
325
296
 
326
- ### LayoutPersistenceService
297
+ **LayoutService API:**
298
+
299
+ | Member | Type | Description |
300
+ |--------|------|-------------|
301
+ | `config` | `Signal<LayoutConfig>` | Current layout configuration |
302
+ | `state` | `Signal<LayoutState>` | Current layout state |
303
+ | `isStatic()` | `Signal<boolean>` | True if menu mode is static |
304
+ | `isOverlay()` | `Signal<boolean>` | True if menu mode is overlay |
305
+ | `isTopbar()` | `Signal<boolean>` | True if menu mode is topbar |
306
+ | `isMobile()` | `Signal<boolean>` | True on mobile viewport |
307
+ | `isMenuOpen()` | `Signal<boolean>` | True if sidebar is open |
308
+ | `toggleMenu()` | `void` | Toggle sidebar open/close |
309
+ | `hideMenu()` | `void` | Force sidebar closed |
310
+ | `setTheme(theme)` | `void` | Change PrimeNG theme |
311
+ | `setMenuMode(mode)` | `void` | Change menu mode |
312
+ | `setColorScheme(scheme)` | `void` | Change color scheme |
327
313
 
328
- Persists layout configuration to localStorage.
329
-
330
- | Method | Description |
331
- |--------|-------------|
332
- | `load()` | Load and validate saved config |
333
- | `save(config)` | Save config with version |
334
- | `clear()` | Remove saved config |
314
+ ---
335
315
 
336
- **localStorage Key:** `flusys.layout.config`
316
+ ## LayoutState
337
317
 
338
- **Validation:**
339
- - Invalid preset → `'Aura'`
340
- - Invalid menuMode → `'static'`
341
- - Non-boolean darkTheme → `false`
342
- - Version mismatch or invalid JSON → Clear and return null
318
+ Reactive state object managed by `LayoutService`:
343
319
 
344
- ---
320
+ ```typescript
321
+ interface LayoutState {
322
+ /** Sidebar open on mobile */
323
+ staticMenuMobileActive: boolean;
345
324
 
346
- ## Auth Integration
325
+ /** Overlay sidebar open */
326
+ overlayMenuActive: boolean;
347
327
 
348
- Layout components use injection tokens for auth data without direct ng-auth dependency.
328
+ /** Desktop sidebar collapsed */
329
+ staticMenuDesktopInactive: boolean;
349
330
 
350
- ### Tokens
331
+ /** Right-click menu visible */
332
+ menuHoverActive: boolean;
351
333
 
352
- ```typescript
353
- // LAYOUT_AUTH_STATE - Provides current user/company/branch state
354
- interface ILayoutAuthState {
355
- currentCompanyInfo: Signal<ICompanyInfo | null>;
356
- currentBranchInfo: Signal<IBranchInfo | null>;
357
- loginUserData: Signal<IUserInfo | null>;
358
- }
334
+ /** Config panel open */
335
+ configSidebarVisible: boolean;
359
336
 
360
- // LAYOUT_AUTH_API - Provides auth actions
361
- interface ILayoutAuthApi {
362
- logOut(): Observable<any>;
363
- navigateLogin(withUrl?: boolean): void;
364
- switchCompany(companyId: string, branchId: string): Observable<any>;
365
- getUserCompanies(): Observable<ICompanyInfo[]>;
366
- getCompanyBranches(companyId: string): Observable<IBranchInfo[]>;
337
+ /** Topbar nav visible (topbar mode) */
338
+ topbarMenuVisible: boolean;
367
339
  }
368
340
  ```
369
341
 
370
- **Related:** `ICompanyInfo` (id, name, slug, imageId), `IBranchInfo` (id, name, slug, imageId?), `IUserInfo` (id, name, email, profilePicture?)
342
+ ---
343
+
344
+ ## Components
345
+
346
+ ### AppLayoutComponent
371
347
 
372
- ### Configuration
348
+ Root layout component. Use this as the parent route component.
373
349
 
374
350
  ```typescript
375
- // app.config.ts
376
- import { LAYOUT_AUTH_STATE, LAYOUT_AUTH_API } from '@flusys/ng-layout';
377
- import { AuthLayoutStateAdapter, AuthLayoutApiAdapter } from '@flusys/ng-auth';
351
+ import { AppLayoutComponent } from '@flusys/ng-layout';
378
352
 
379
- providers: [
380
- { provide: LAYOUT_AUTH_STATE, useExisting: AuthLayoutStateAdapter },
381
- { provide: LAYOUT_AUTH_API, useExisting: AuthLayoutApiAdapter },
382
- ]
353
+ // In routes:
354
+ { path: '', component: AppLayoutComponent, children: [...] }
383
355
  ```
384
356
 
385
- Components inject with `{ optional: true }` for graceful degradation without auth.
357
+ Renders:
358
+ - `AppTopbarComponent` (header)
359
+ - `AppSidebarComponent` (left navigation)
360
+ - `<router-outlet>` (page content)
361
+ - `AppConfiguratorComponent` (floating settings panel)
386
362
 
387
- ---
363
+ ### AppTopbarComponent
388
364
 
389
- ## Permission-Based Filtering
365
+ Application header bar with:
366
+ - Logo / app name
367
+ - Menu toggle button (mobile)
368
+ - User profile dropdown (avatar, name, logout)
369
+ - Sign-up link (conditionally shown based on `isSignUpEnabled()`)
370
+ - Notification bell slot (via `LAYOUT_NOTIFICATION_BELL` token)
371
+ - Language selector slot (via `LAYOUT_LANGUAGE_SELECTOR` token)
390
372
 
391
- Menu items and launcher apps are automatically filtered via `permissionLogic`.
373
+ ### AppSidebarComponent
392
374
 
393
- ### How It Works
375
+ Left navigation panel containing:
376
+ - `AppMenuComponent` (navigation items)
377
+ - `AppCompanyBranchSelectorComponent` (if company selection enabled)
394
378
 
395
- 1. Set raw items via `layoutService.setMenu(items)` or `setApps(apps)`
396
- 2. `LayoutService.menu` / `apps` are computed signals that filter via utilities
397
- 3. Uses `evaluateLogicNode()` from ng-shared
379
+ ### AppMenuComponent
398
380
 
399
- ### Interfaces
381
+ Renders the menu tree from `LAYOUT_MENU` token. Supports:
382
+ - Nested submenus (unlimited depth)
383
+ - Active route highlighting
384
+ - Translation keys via `labelKey`
385
+ - Icons via PrimeIcons
400
386
 
401
- ```typescript
402
- interface IMenuItem {
403
- id?: string; label?: string; icon?: string; iconType?: number;
404
- routerLink?: string[]; children?: IMenuItem[]; separator?: boolean;
405
- permissionLogic?: ILogicNode | null;
406
- }
387
+ ### AppMenuitemComponent
407
388
 
408
- interface ILogicNode {
409
- id: string;
410
- type: 'group' | 'action' | 'role';
411
- operator?: 'AND' | 'OR'; // For 'group'
412
- actionId?: string; // For 'action'
413
- roleId?: string; // For 'role'
414
- children?: ILogicNode[]; // For 'group'
415
- }
416
- ```
389
+ Individual menu item component. Handles:
390
+ - Route navigation
391
+ - Submenu expand/collapse
392
+ - Topbar hover positioning
393
+ - `routerLinkActiveOptions`
417
394
 
418
- ### Examples
395
+ ### AppCompanyBranchSelectorComponent
419
396
 
420
- ```typescript
421
- // Single action check
422
- permissionLogic: { id: '1', type: 'action', actionId: 'admin.access' }
423
-
424
- // OR - user needs ANY permission
425
- permissionLogic: {
426
- id: 'check', type: 'group', operator: 'OR',
427
- children: [
428
- { id: '1', type: 'action', actionId: 'reports.view' },
429
- { id: '2', type: 'role', roleId: 'manager-role-id' },
430
- ],
431
- }
432
-
433
- // AND - user needs ALL permissions
434
- permissionLogic: {
435
- id: 'check', type: 'group', operator: 'AND',
436
- children: [
437
- { id: '1', type: 'action', actionId: 'admin.access' },
438
- { id: '2', type: 'role', roleId: 'superuser-role-id' },
439
- ],
440
- }
441
- ```
397
+ Dropdown selectors for company and branch. Shown in sidebar when `services.auth.features.companySelection` is enabled.
442
398
 
443
- ### Filtering Behavior
399
+ ### AppConfiguratorComponent
444
400
 
445
- - **Separators** pass through without permission checks
446
- - **Parent items** with no visible children are hidden
447
- - **Children** filtered recursively
401
+ Floating right-side panel for live layout customization:
402
+ - Theme picker
403
+ - Color scheme toggle (light/dark/dim)
404
+ - Menu mode selector (static/overlay/topbar)
405
+ - Scale slider
448
406
 
449
407
  ---
450
408
 
451
- ## Pre-defined Themes
409
+ ## Configuration Persistence
452
410
 
453
- Custom Material-based themes using `definePreset`:
411
+ `LayoutPersistenceService` automatically saves and restores layout preferences to `localStorage`:
454
412
 
455
- | Theme | Primary Color | Hover Color |
456
- |-------|---------------|-------------|
457
- | `GreenTheme` | `#01712c` | `#119744` |
458
- | `NavyBlueTheme` | `#3535cd` | `#0707a9` |
413
+ ```typescript
414
+ // Automatically persisted keys:
415
+ // - flusys.layout.theme
416
+ // - flusys.layout.colorScheme
417
+ // - flusys.layout.menuMode
418
+ // - flusys.layout.scale
419
+ // - flusys.layout.ripple
420
+ ```
421
+
422
+ No manual setup required — persistence is active whenever `LayoutService` is injected. To clear persisted settings:
459
423
 
460
424
  ```typescript
461
- import { GreenTheme, NavyBlueTheme } from '@flusys/ng-layout';
425
+ import { LayoutPersistenceService } from '@flusys/ng-layout';
426
+
427
+ this.persistenceService.clear();
462
428
  ```
463
429
 
464
430
  ---
465
431
 
466
- ## Usage Examples
432
+ ## Integration Tokens
467
433
 
468
- ### Basic Setup
434
+ ### Auth Integration (from ng-auth)
469
435
 
470
436
  ```typescript
471
- @Component({
472
- imports: [AppLayout],
473
- template: `<app-layout><router-outlet /></app-layout>`,
474
- host: { '[class.app-dark]': 'layoutService.isDarkTheme()' },
475
- })
476
- export class AppComponent {
477
- readonly layoutService = inject(LayoutService);
478
- }
479
- ```
437
+ import { provideAuthLayoutIntegration } from '@flusys/ng-auth';
480
438
 
481
- ### Menu Configuration
439
+ // app.config.ts providers:
440
+ ...provideAuthLayoutIntegration()
441
+ // Provides: LAYOUT_AUTH_STATE, LAYOUT_AUTH_API
442
+ ```
482
443
 
444
+ **`LAYOUT_AUTH_STATE`** interface:
483
445
  ```typescript
484
- const menuItems: IMenuItem[] = [
485
- { id: 'dashboard', label: 'Dashboard', icon: 'pi pi-home', routerLink: ['/dashboard'] },
486
- {
487
- id: 'admin', label: 'Admin', icon: 'pi pi-cog',
488
- permissionLogic: { id: 'admin', type: 'action', actionId: 'admin.access' },
489
- children: [
490
- { id: 'users', label: 'Users', icon: 'pi pi-users', routerLink: ['/admin/users'] },
491
- { id: 'roles', label: 'Roles', icon: 'pi pi-shield', routerLink: ['/admin/roles'] },
492
- ],
493
- },
494
- { separator: true },
495
- { id: 'settings', label: 'Settings', icon: 'pi pi-cog', routerLink: ['/settings'] },
496
- ];
497
- layoutService.setMenu(menuItems);
446
+ interface ILayoutAuthState {
447
+ user: Signal<ICurrentUser | null>;
448
+ company: Signal<ICompany | null>;
449
+ branch: Signal<IBranch | null>;
450
+ isAuthenticated: Signal<boolean>;
451
+ profilePictureUrl: Signal<string | null>;
452
+ companyLogoUrl: Signal<string | null>;
453
+ }
498
454
  ```
499
455
 
500
- ### Profile Setup
501
-
456
+ **`LAYOUT_AUTH_API`** interface:
502
457
  ```typescript
503
- // After login
504
- layoutService.setUserProfile({
505
- id: 'user-123',
506
- name: 'John Doe',
507
- email: 'john@example.com',
508
- profilePictureUrl: 'https://example.com/avatar.jpg',
509
- });
510
-
511
- layoutService.setCompanyProfile({
512
- id: 'company-456',
513
- name: 'Acme Corp',
514
- slug: 'acme-corp',
515
- logoUrl: 'https://example.com/logo.png',
516
- });
517
-
518
- // On logout
519
- layoutService.setUserProfile(null);
520
- layoutService.setCompanyProfile(null);
458
+ interface ILayoutAuthApi {
459
+ logout(): Observable<void>;
460
+ selectCompany(companyId: string, branchId: string): Observable<void>;
461
+ }
521
462
  ```
522
463
 
523
- ---
524
-
525
- ## Best Practices
464
+ ### Notification Bell Integration (from ng-notification)
526
465
 
527
- ### Signal Patterns
528
-
529
- Use private writable + public readonly pattern:
530
466
  ```typescript
531
- private readonly _isActive = signal(false);
532
- readonly isActive = this._isActive.asReadonly();
467
+ import { provideNotificationProviders } from '@flusys/ng-notification';
533
468
 
534
- // Update via private signal
535
- this._isActive.set(true);
536
- this._isActive.update(v => !v);
469
+ ...provideNotificationProviders()
470
+ // Provides: LAYOUT_NOTIFICATION_BELL
537
471
  ```
538
472
 
539
- ### Responsive Widths
540
-
541
- | Component | Pattern |
542
- |-----------|---------|
543
- | AppConfigurator | `w-[calc(100vw-2rem)] sm:w-72 max-w-72` |
544
- | AppProfile | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]` |
545
- | AppCompanyBranchSelector | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[300px] max-w-[360px]` |
546
- | AppLauncher | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]` |
473
+ ### Language Selector Integration (from ng-localization)
547
474
 
548
- ### Dark Mode CSS Variables
475
+ ```typescript
476
+ import { provideLocalization } from '@flusys/ng-localization';
549
477
 
550
- ```css
551
- background-color: var(--surface-overlay); /* Panels */
552
- bg-emphasis /* Hover states */
553
- text-color / text-muted-color /* Text */
554
- border-surface /* Borders */
555
- bg-primary / text-primary-contrast /* Primary */
478
+ ...provideLocalization()
479
+ // Provides: LAYOUT_LANGUAGE_SELECTOR
556
480
  ```
557
481
 
558
482
  ---
559
483
 
560
- ## Common Issues
484
+ ## Troubleshooting
561
485
 
562
- | Issue | Solution |
563
- |-------|----------|
564
- | Menu not showing | Call `layoutService.setMenu([...])` |
565
- | Auth info missing | Provide `LAYOUT_AUTH_STATE` token |
566
- | Theme not applying | Add `host: { '[class.app-dark]': 'layoutService.isDarkTheme()' }` |
567
- | Menu items hidden | Check `permissionLogic` and user permissions |
568
- | Profile picture missing | Include `profilePictureUrl` in `setUserProfile()` |
569
- | Company feature missing | Enable `services.auth.enabled: true` and provide both auth tokens |
486
+ **Sidebar doesn't open on mobile**
570
487
 
571
- ---
488
+ Ensure `AppLayoutComponent` is the parent route component, not just imported as a standalone component in a non-route context.
572
489
 
573
- ## API Reference
490
+ **Menu items don't highlight active route**
574
491
 
575
- ### Components
492
+ Add `routerLinkActiveOptions: { exact: true }` to leaf menu items and `{ exact: false }` to parent items:
576
493
 
577
- | Component | Selector |
578
- |-----------|----------|
579
- | `AppLayout` | `app-layout` |
580
- | `AppTopbar` | `app-topbar` |
581
- | `AppSidebar` | `app-sidebar` |
582
- | `AppMenu` | `app-menu` |
583
- | `AppMenuitem` | `[app-menuitem]` |
584
- | `AppFooter` | `app-footer` |
585
- | `AppProfile` | `app-profile` |
586
- | `AppCompanyBranchSelector` | `app-company-branch-selector` |
587
- | `AppLauncher` | `app-launcher` |
588
- | `AppConfigurator` | `app-configurator` |
589
- | `AppFloatingConfigurator` | `app-floating-configurator` |
494
+ ```typescript
495
+ {
496
+ labelKey: 'menu.dashboard',
497
+ routerLink: ['/dashboard'],
498
+ routerLinkActiveOptions: { exact: true },
499
+ }
500
+ ```
590
501
 
591
- ### Services
502
+ **Topbar submenus appear behind other elements**
592
503
 
593
- | Service | Description |
594
- |---------|-------------|
595
- | `LayoutService` | Signal-based layout state management |
596
- | `LayoutPersistenceService` | localStorage persistence |
504
+ Set a higher `z-index` on `.topbar-submenu` in your global styles, or ensure no parent has `overflow: hidden`.
597
505
 
598
- ### Tokens
506
+ **Theme not changing**
599
507
 
600
- | Token | Interface |
601
- |-------|-----------|
602
- | `LAYOUT_AUTH_STATE` | `ILayoutAuthState` |
603
- | `LAYOUT_AUTH_API` | `ILayoutAuthApi` |
508
+ Check that `LayoutPersistenceService` is provided (it's automatic when `LayoutService` is used). If the theme is cached, call `persistenceService.clear()` once.
604
509
 
605
- ### Utilities
510
+ **`No provider for LAYOUT_AUTH_STATE`**
606
511
 
607
- | Function | Description |
608
- |----------|-------------|
609
- | `filterMenuByPermissions()` | Filter menu items by permission logic |
610
- | `filterAppsByPermissions()` | Filter launcher apps by permission logic |
512
+ You must call `provideAuthLayoutIntegration()` in `app.config.ts` after enabling `ng-auth`.
611
513
 
612
514
  ---
613
515
 
614
- ## See Also
615
-
616
- - [CORE-GUIDE.md](./CORE-GUIDE.md) - Configuration, interceptors
617
- - [SHARED-GUIDE.md](./SHARED-GUIDE.md) - Shared components, provider interfaces
618
- - [AUTH-GUIDE.md](./AUTH-GUIDE.md) - Authentication integration, adapters
619
-
620
- ---
516
+ ## License
621
517
 
622
- **Last Updated:** 2026-02-25 | **Version:** 3.0.1 | **Angular:** 21
518
+ MIT © FLUSYS