@flusys/ng-layout 4.0.2 → 4.1.1

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,745 +1,518 @@
1
- # @flusys/ng-layout Package Guide
1
+ # @flusys/ng-layout
2
2
 
3
- ## Overview
4
-
5
- `@flusys/ng-layout` provides the application shell, menu system, theme management, and layout components for FLUSYS applications.
6
-
7
- **Key Principle:** ng-layout depends on ng-core and ng-shared, but remains independent of feature packages like ng-auth via injection tokens.
3
+ > Application shell and layout system for the FLUSYS Angular platform — topbar, sidebar, menu, configurator, and layout state management.
8
4
 
9
- ## Package Information
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)
10
10
 
11
- | Property | Value |
12
- |----------|-------|
13
- | Package | `@flusys/ng-layout` |
14
- | Version | 4.0.2 |
15
- | Dependencies | ng-core, ng-shared |
16
- | Build | `npm run build:ng-layout` |
17
-
18
- ## Architecture
11
+ ---
19
12
 
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
- ```
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)
48
39
 
49
40
  ---
50
41
 
51
- ## Components
42
+ ## Overview
52
43
 
53
- ### AppLayout
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.
54
45
 
55
- Main layout wrapper with signal-based state management.
46
+ The layout integrates with auth, notification, and localization packages through injection token interfaces, maintaining clean package independence via the Provider Interface Pattern.
56
47
 
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
- ```
48
+ ---
70
49
 
71
- **Includes:** AppTopbar, AppSidebar, main content area, AppFooter, layout mask overlay
50
+ ## Features
72
51
 
73
- **Layout Modes:**
74
- - **Static** - Sidebar always visible on desktop, overlay on mobile
75
- - **Overlay** - Sidebar overlays content when opened
76
- - **Topbar** - Horizontal menu bar at the top for desktop, sidebar on mobile
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
77
62
 
78
- **Dynamic CSS Classes:**
79
- | Class | Condition |
80
- |-------|-----------|
81
- | `layout-static` | Static menu mode |
82
- | `layout-overlay` | Overlay menu mode |
83
- | `layout-static-inactive` | Static menu collapsed on desktop |
84
- | `layout-overlay-active` | Overlay menu open |
85
- | `layout-mobile-active` | Mobile menu open |
63
+ ---
86
64
 
87
- **Behavior:**
88
- - Subscribes to `layoutService.overlayOpen$` for outside click handling
89
- - Auto-hides menu on route navigation
90
- - Blocks body scroll when mobile menu is active
65
+ ## Compatibility
91
66
 
92
- ### AppTopbar
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+ |
93
74
 
94
- Application header with branding and user actions. Supports different layouts via `menuMode`.
75
+ ---
95
76
 
96
- **Static/Overlay Modes:**
97
- ```
98
- ┌─────────────────────────────────────────────────────────────────┐
99
- │ [≡] CompanyName [☀/🌙] [🎨] [...] [Apps] [🏢] [👤] │
100
- └─────────────────────────────────────────────────────────────────┘
101
- ```
77
+ ## Installation
102
78
 
103
- **Topbar Mode (Desktop):**
104
- ```
105
- ┌───────────────────────────────────────────────────────────────────────────────┐
106
- │ CompanyName [Dashboard] [Admin] [IAM] [Storage] [☀/🌙] [🎨] [Apps] [🏢] [👤]│
107
- └───────────────────────────────────────────────────────────────────────────────┘
79
+ ```bash
80
+ npm install @flusys/ng-layout @flusys/ng-core @flusys/ng-shared
108
81
  ```
109
82
 
110
- | Element | Description |
111
- |---------|-------------|
112
- | `[≡]` | Menu toggle (Static/Overlay on mobile, Topbar on desktop) |
113
- | CompanyName | From `layoutService.companyName` → links to `/` |
114
- | Menu Items | In topbar mode, rendered horizontally on desktop |
115
- | `[☀/🌙]` | Dark mode toggle |
116
- | `[🎨]` | AppConfigurator panel |
117
- | `[...]` | Mobile menu (hidden on desktop) |
118
- | `[Apps]` | AppLauncher (if `layoutService.hasApps()`) |
119
- | `[🏢]` | AppCompanyBranchSelector (if company feature enabled) |
120
- | `[👤]` | AppProfile dropdown |
121
-
122
- **Behavior:**
123
- - **Topbar on desktop:** Renders menu items horizontally with hover-triggered submenus
124
- - **Topbar on mobile:** Falls back to vertical sidebar menu
125
- - **Static/Overlay:** Standard sidebar behavior (unchanged)
126
-
127
- ### AppMenu & AppMenuitem
128
-
129
- Menu container renders permission-filtered items from `layoutService.menu`.
130
-
131
- **AppMenuitem Signal Inputs:**
132
- | Input | Type | Description |
133
- |-------|------|-------------|
134
- | `item` | `IMenuItem` | Required menu item data |
135
- | `index` | `number` | Required index in parent |
136
- | `parentKey` | `string` | Optional parent key (default: `''`) |
137
-
138
- **Router Link Active Options:**
139
- - Root path (`/`) uses `'exact'` matching
140
- - Other paths use `'subset'` matching for child route support
141
-
142
- **CSS Transitions (no @angular/animations):**
143
- ```css
144
- :host ul {
145
- transition: max-height 400ms cubic-bezier(0.86, 0, 0.07, 1);
146
- }
147
- :host ul.submenu-collapsed { max-height: 0; }
148
- :host ul.submenu-expanded { max-height: 1000px; }
149
- ```
150
-
151
- **Topbar Mode Behavior:**
152
- - `@if (layoutService.isTopbar())` - Conditional rendering for topbar-specific UI
153
- - `onMouseEnter()` - Triggers submenu display on hover (topbar only)
154
- - Parent link positioning tracked via `@viewChild('parentLink')` for submenu alignment
155
- - Submenu visibility controlled by `submenu-expanded` / `submenu-collapsed` classes
156
- - Submenus only expand/collapse in sidebar mode (`!layoutService.isTopbar()`)
157
-
158
83
  ---
159
84
 
160
- ## Topbar Mode (v4.0.1)
85
+ ## Quick Start
161
86
 
162
- Horizontal navigation bar at the top of the page, replacing traditional sidebar on desktop.
87
+ ### 1. Set Up App Layout Route
163
88
 
164
- ### Features
89
+ ```typescript
90
+ // app.routes.ts
91
+ import { Routes } from '@angular/router';
92
+ import { AppLayoutComponent } from '@flusys/ng-layout';
165
93
 
166
- - **Desktop:** Horizontal menu items with hover-triggered submenus
167
- - **Mobile:** Falls back to traditional vertical sidebar
168
- - **Responsive:** Auto-switches between topbar and sidebar based on viewport
169
- - **Submenus:** Positioned below parent items, auto-adjusted to viewport
170
- - **Persistence:** Menu visibility state persists in `layoutService.layoutState().topbarMenuVisible`
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
+ ];
114
+ ```
171
115
 
172
- ### Layout Behavior
116
+ ### 2. Define Menu Configuration
173
117
 
174
- **Desktop (Topbar Mode):**
175
- - Menu items rendered horizontally next to company name
176
- - Hovering over items with children shows submenu
177
- - No sidebar visible
178
- - Menu toggle only appears on mobile
118
+ ```typescript
119
+ // app-menu.config.ts
120
+ import { IMenuItem } from '@flusys/ng-layout';
179
121
 
180
- **Mobile (Topbar Mode):**
181
- - Menu items rendered in vertical sidebar (same as overlay mode)
182
- - Topbar shows menu toggle button
183
- - Sidebar overlays content when open
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
+ ```
184
148
 
185
- ### Configuration
149
+ ### 3. Provide Layout Configuration
186
150
 
187
151
  ```typescript
188
- // Set menu mode to topbar
189
- layoutService.updateLayoutConfig({ menuMode: 'topbar' });
190
-
191
- // Or persist via LayoutPersistenceService
192
- layoutPersistenceService.save({
193
- preset: 'Aura',
194
- primary: 'blue',
195
- menuMode: 'topbar',
196
- darkTheme: false,
197
- });
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
+ };
198
173
  ```
199
174
 
200
- ### Styling
201
-
202
- Topbar styles are defined in `assets/layout/_topbar_nav.scss`:
203
-
204
- - `.layout-topbar-nav-horizontal` - Main horizontal nav container
205
- - `.layout-topbar-nav-hidden` - Hide nav on mobile (Tailwind: `hidden`)
206
- - Submenu positioning with `position: absolute`
207
- - Chevron icon for topbar (vs angle-down for sidebar)
208
- - Hover effects with opacity and visibility transitions
209
-
210
- ### AppMenuitem Changes for Topbar
211
-
212
- When in topbar mode:
213
- - Submenu toggler icon changes to `pi-chevron-right` (vs `pi-angle-down`)
214
- - Parent links show `cursor: default` instead of pointer
215
- - Submenus positioned absolutely with auto-adjustment logic
216
- - Hover-triggered submenu display (vs click-triggered in sidebar)
217
-
218
175
  ---
219
176
 
220
- ### AppFooter
177
+ ## Layout Configuration
221
178
 
222
- Displays branding from `LayoutService`: `appName`, `authorName`, `authorUrl`
179
+ ### LayoutConfig
223
180
 
224
- ### AppProfile
225
-
226
- User profile dropdown in topbar.
181
+ ```typescript
182
+ interface LayoutConfig {
183
+ /** Menu display mode */
184
+ menuMode: 'static' | 'overlay' | 'topbar';
227
185
 
228
- **Features:**
229
- - User info (picture, name, email) with text truncation
230
- - Profile link → `/profile`
231
- - Copy SignUp Link → `{origin}/auth/register?companySlug={slug}` (hidden if sign-up disabled)
232
- - Logout with toast feedback
186
+ /** PrimeNG theme name */
187
+ theme: string;
233
188
 
234
- **Computed Signals:**
235
- | Signal | Source | Fallback |
236
- |--------|--------|----------|
237
- | `userName` | `authState.loginUserData()?.name` | `'Guest'` |
238
- | `userEmail` | `authState.loginUserData()?.email` | `''` |
239
- | `profilePicture` | `layoutService.userProfilePictureUrl()` | `''` |
240
- | `signUpEnabled` | `APP_CONFIG` via `isSignUpEnabled()` helper | `false` |
189
+ /** Color scheme */
190
+ colorScheme: 'light' | 'dark' | 'dim';
241
191
 
242
- **Sign-up Link Visibility:**
243
- - Only shown when `APP_CONFIG.services.auth.features.signUp === true`
244
- - Controlled via `signUpEnabled()` computed signal
245
- - Uses `isSignUpEnabled()` helper from `@flusys/ng-core`
192
+ /** Ripple effect */
193
+ ripple?: boolean;
246
194
 
247
- ### AppCompanyBranchSelector
195
+ /** Scale factor (12-16) */
196
+ scale?: number;
248
197
 
249
- Company/branch switcher in topbar. Shown when company feature is enabled.
198
+ /** Menu theme */
199
+ menuTheme?: 'light' | 'dark' | 'colored';
250
200
 
251
- **State Signals:**
252
- | Signal | Description |
253
- |--------|-------------|
254
- | `companies` | Available companies list |
255
- | `branches` | Branches for selected company |
256
- | `selectedCompanyId` / `selectedBranchId` | Current selection |
257
- | `isLoadingCompanies` / `isLoadingBranches` | Loading states |
258
- | `isSwitching` | Switch operation in progress |
259
- | `canSwitch` | Computed: enabled when selection differs from current |
201
+ /** Card border style */
202
+ inputStyle?: 'outlined' | 'filled';
203
+ }
204
+ ```
260
205
 
261
- **Workflow:**
262
- 1. Click button → Panel opens, companies load
263
- 2. Select company → Branches load (auto-select if single branch)
264
- 3. Select branch → Click "Switch" → API call → JWT updated → Redirect
206
+ ### Menu Modes
265
207
 
266
- ### AppLauncher
208
+ #### Static Mode (Default)
267
209
 
268
- App launcher grid for quick access to external applications.
210
+ Sidebar is always visible on desktop. Collapses to icons on tablet.
269
211
 
270
212
  ```typescript
271
- const apps: ILauncherApp[] = [
272
- {
273
- id: 'docs',
274
- name: 'Docs',
275
- iconType: IconTypeEnum.PrimeNg,
276
- icon: 'pi pi-book',
277
- url: 'https://docs.example.com',
278
- permissionLogic: { id: 'docs', type: 'action', actionId: 'docs.view' }, // optional
279
- },
280
- ];
281
- layoutService.setApps(apps);
213
+ { provide: LAYOUT_CONFIG, useValue: { menuMode: 'static' } }
282
214
  ```
283
215
 
284
- **Features:**
285
- - Responsive grid: `grid-cols-2 sm:grid-cols-3`
286
- - External links (new tab)
287
- - Permission filtering via `permissionLogic`
288
- - Only visible when `layoutService.hasApps()` is true
289
-
290
- ### AppConfigurator
291
-
292
- Theme customizer dropdown.
216
+ #### Overlay Mode
293
217
 
294
- **Options:**
295
- | Setting | Values |
296
- |---------|--------|
297
- | Primary colors | `emerald`, `green`, `lime`, `orange`, `amber`, `yellow`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`, `noir` |
298
- | Surface colors | `slate`, `gray`, `zinc`, `neutral`, `stone`, `soho`, `viva`, `ocean` |
299
- | Presets | `Aura` (default), `Lara`, `Nora` |
300
- | Menu mode | `static`, `overlay`, `topbar` |
218
+ Sidebar slides over content. Click outside to close.
301
219
 
302
- Uses `$t()`, `updatePreset()`, `updateSurfacePalette()` from `@primeuix/themes`.
220
+ ```typescript
221
+ { provide: LAYOUT_CONFIG, useValue: { menuMode: 'overlay' } }
222
+ ```
303
223
 
304
- ### AppFloatingConfigurator
224
+ #### Topbar Mode (v4.0.1+)
305
225
 
306
- Fixed-position floating buttons for pages without AppLayout (e.g., auth pages).
226
+ Desktop renders horizontal navigation bar. Mobile falls back to vertical sidebar.
307
227
 
308
228
  ```typescript
309
- @Component({
310
- imports: [AppFloatingConfigurator],
311
- template: `<app-floating-configurator /><div class="login-form">...</div>`
312
- })
313
- export class LoginComponent {}
229
+ { provide: LAYOUT_CONFIG, useValue: { menuMode: 'topbar' } }
314
230
  ```
315
231
 
316
- 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.
317
233
 
318
- ---
234
+ ### Themes & Colors
319
235
 
320
- ## Services
236
+ Built-in themes (PrimeNG):
321
237
 
322
- ### 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 |
323
248
 
324
- Signal-based singleton service for layout state management.
249
+ ---
325
250
 
326
- #### Interfaces
251
+ ## Injection Tokens
327
252
 
328
- ```typescript
329
- interface LayoutConfig {
330
- preset?: string; // "Aura" | "Lara" | "Nora"
331
- primary?: string; // "emerald" | "green" | etc.
332
- surface?: string | null; // "slate" | "gray" | etc.
333
- darkTheme?: boolean;
334
- menuMode?: 'static' | 'overlay' | 'topbar';
335
- }
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) |
336
261
 
337
- interface LayoutState {
338
- staticMenuDesktopInactive?: boolean;
339
- overlayMenuActive?: boolean;
340
- staticMenuMobileActive?: boolean;
341
- menuHoverActive?: boolean;
342
- topbarMenuVisible?: boolean; // For topbar mode on desktop
343
- }
262
+ ---
344
263
 
345
- interface UserProfile { id: string; name: string; email: string; profilePictureUrl?: string | null; }
346
- interface CompanyProfile { id: string; name: string; slug: string; logoUrl?: string | null; }
347
- ```
264
+ ## LayoutService
265
+
266
+ Central signal-based service for layout state management.
348
267
 
349
- #### Signals
350
-
351
- **State Signals (readonly):**
352
- | Signal | Type |
353
- |--------|------|
354
- | `layoutConfig` | `Signal<LayoutConfig>` |
355
- | `layoutState` | `Signal<LayoutState>` |
356
- | `transitionComplete` | `Signal<boolean>` |
357
- | `userProfile` | `Signal<UserProfile \| null>` |
358
- | `companyProfile` | `Signal<CompanyProfile \| null>` |
359
-
360
- **Static Values (from APP_CONFIG):**
361
- | Property | Fallback |
362
- |----------|----------|
363
- | `appName` | `DEFAULT_APP_NAME` |
364
- | `authorName` | `DEFAULT_AUTHOR.name` |
365
- | `authorUrl` | `DEFAULT_AUTHOR.url` |
366
-
367
- **Computed Signals:**
368
- | Signal | Description |
369
- |--------|-------------|
370
- | `menu` | Permission-filtered menu items |
371
- | `apps` | Permission-filtered launcher apps |
372
- | `hasApps` | Whether apps exist |
373
- | `isDarkTheme` | Dark theme active |
374
- | `isSidebarActive` | Sidebar visible |
375
- | `isStatic` | Static menu mode |
376
- | `isOverlay` | Overlay menu mode |
377
- | `isTopbar` | Topbar menu mode (horizontal nav on desktop) |
378
- | `getPrimary` / `getSurface` | Current colors |
379
- | `userName` / `userEmail` | User info (with fallbacks) |
380
- | `userProfilePictureUrl` / `companyLogoUrl` | Image URLs |
381
- | `companyName` | Company name or appName fallback |
382
- | `isAuthenticated` | User profile exists |
383
-
384
- #### Methods
385
-
386
- | Method | Description |
387
- |--------|-------------|
388
- | `onMenuToggle()` | Toggle menu (handles static/overlay/mobile) |
389
- | `onMenuStateChange(event)` | Emit menu state change |
390
- | `reset()` | Reset menu state |
391
- | `isDesktop()` / `isMobile()` | Viewport checks (SSR-safe) |
392
- | `updateLayoutConfig(config)` | Partial config update |
393
- | `updateLayoutState(state)` | Partial state update |
394
- | `toggleDarkMode(config?)` | Toggle `app-dark` class |
395
- | `setUserProfile(profile)` | Set user profile |
396
- | `setCompanyProfile(profile)` | Set company profile |
397
- | `setMenu(items)` / `clearMenu()` | Menu management |
398
- | `setApps(apps)` / `clearApps()` | Apps management |
399
-
400
- #### RxJS Observables
401
-
402
- | Observable | Purpose |
403
- |------------|---------|
404
- | `menuSource$` | Menu state change events |
405
- | `resetSource$` | Menu reset events |
406
- | `configUpdate$` | Config change events |
407
- | `overlayOpen$` | Menu overlay opened |
408
-
409
- #### Dark Mode Transition
410
-
411
- Uses View Transitions API with fallback:
412
268
  ```typescript
413
- toggleDarkMode(config?: LayoutConfig): void {
414
- const isDark = (config ?? this._layoutConfig()).darkTheme;
415
- 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
+ }
416
294
  }
417
295
  ```
418
296
 
419
- ### LayoutPersistenceService
420
-
421
- Persists layout configuration to localStorage.
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 |
422
313
 
423
- | Method | Description |
424
- |--------|-------------|
425
- | `load()` | Load and validate saved config |
426
- | `save(config)` | Save config with version |
427
- | `clear()` | Remove saved config |
314
+ ---
428
315
 
429
- **localStorage Key:** `flusys.layout.config`
316
+ ## LayoutState
430
317
 
431
- **Validation:**
432
- - Invalid preset → `'Aura'`
433
- - Invalid menuMode → `'static'` (valid: `'static'`, `'overlay'`, `'topbar'`)
434
- - Non-boolean darkTheme → `false`
435
- - Version mismatch or invalid JSON → Clear and return null
318
+ Reactive state object managed by `LayoutService`:
436
319
 
437
- ---
320
+ ```typescript
321
+ interface LayoutState {
322
+ /** Sidebar open on mobile */
323
+ staticMenuMobileActive: boolean;
438
324
 
439
- ## Auth Integration
325
+ /** Overlay sidebar open */
326
+ overlayMenuActive: boolean;
440
327
 
441
- Layout components use injection tokens for auth data without direct ng-auth dependency.
328
+ /** Desktop sidebar collapsed */
329
+ staticMenuDesktopInactive: boolean;
442
330
 
443
- ### Tokens
331
+ /** Right-click menu visible */
332
+ menuHoverActive: boolean;
444
333
 
445
- ```typescript
446
- // LAYOUT_AUTH_STATE - Provides current user/company/branch state
447
- interface ILayoutAuthState {
448
- currentCompanyInfo: Signal<ICompanyInfo | null>;
449
- currentBranchInfo: Signal<IBranchInfo | null>;
450
- loginUserData: Signal<IUserInfo | null>;
451
- }
334
+ /** Config panel open */
335
+ configSidebarVisible: boolean;
452
336
 
453
- // LAYOUT_AUTH_API - Provides auth actions
454
- interface ILayoutAuthApi {
455
- logOut(): Observable<any>;
456
- navigateLogin(withUrl?: boolean): void;
457
- switchCompany(companyId: string, branchId: string): Observable<any>;
458
- getUserCompanies(): Observable<ICompanyInfo[]>;
459
- getCompanyBranches(companyId: string): Observable<IBranchInfo[]>;
337
+ /** Topbar nav visible (topbar mode) */
338
+ topbarMenuVisible: boolean;
460
339
  }
461
340
  ```
462
341
 
463
- **Related:** `ICompanyInfo` (id, name, slug, imageId), `IBranchInfo` (id, name, slug, imageId?), `IUserInfo` (id, name, email, profilePicture?)
342
+ ---
464
343
 
465
- ### Configuration
344
+ ## Components
345
+
346
+ ### AppLayoutComponent
347
+
348
+ Root layout component. Use this as the parent route component.
466
349
 
467
350
  ```typescript
468
- // app.config.ts
469
- import { LAYOUT_AUTH_STATE, LAYOUT_AUTH_API } from '@flusys/ng-layout';
470
- import { AuthLayoutStateAdapter, AuthLayoutApiAdapter } from '@flusys/ng-auth';
351
+ import { AppLayoutComponent } from '@flusys/ng-layout';
471
352
 
472
- providers: [
473
- { provide: LAYOUT_AUTH_STATE, useExisting: AuthLayoutStateAdapter },
474
- { provide: LAYOUT_AUTH_API, useExisting: AuthLayoutApiAdapter },
475
- ]
353
+ // In routes:
354
+ { path: '', component: AppLayoutComponent, children: [...] }
476
355
  ```
477
356
 
478
- 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)
479
362
 
480
- ---
363
+ ### AppTopbarComponent
481
364
 
482
- ## 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)
483
372
 
484
- Menu items and launcher apps are automatically filtered via `permissionLogic`.
373
+ ### AppSidebarComponent
485
374
 
486
- ### How It Works
375
+ Left navigation panel containing:
376
+ - `AppMenuComponent` (navigation items)
377
+ - `AppCompanyBranchSelectorComponent` (if company selection enabled)
487
378
 
488
- 1. Set raw items via `layoutService.setMenu(items)` or `setApps(apps)`
489
- 2. `LayoutService.menu` / `apps` are computed signals that filter via utilities
490
- 3. Uses `evaluateLogicNode()` from ng-shared
379
+ ### AppMenuComponent
491
380
 
492
- ### 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
493
386
 
494
- ```typescript
495
- interface IMenuItem {
496
- id?: string; label?: string; icon?: string; iconType?: number;
497
- routerLink?: string[]; children?: IMenuItem[]; separator?: boolean;
498
- permissionLogic?: ILogicNode | null;
499
- }
387
+ ### AppMenuitemComponent
500
388
 
501
- interface ILogicNode {
502
- id: string;
503
- type: 'group' | 'action' | 'role';
504
- operator?: 'AND' | 'OR'; // For 'group'
505
- actionId?: string; // For 'action'
506
- roleId?: string; // For 'role'
507
- children?: ILogicNode[]; // For 'group'
508
- }
509
- ```
389
+ Individual menu item component. Handles:
390
+ - Route navigation
391
+ - Submenu expand/collapse
392
+ - Topbar hover positioning
393
+ - `routerLinkActiveOptions`
510
394
 
511
- ### Examples
395
+ ### AppCompanyBranchSelectorComponent
512
396
 
513
- ```typescript
514
- // Single action check
515
- permissionLogic: { id: '1', type: 'action', actionId: 'admin.access' }
516
-
517
- // OR - user needs ANY permission
518
- permissionLogic: {
519
- id: 'check', type: 'group', operator: 'OR',
520
- children: [
521
- { id: '1', type: 'action', actionId: 'reports.view' },
522
- { id: '2', type: 'role', roleId: 'manager-role-id' },
523
- ],
524
- }
525
-
526
- // AND - user needs ALL permissions
527
- permissionLogic: {
528
- id: 'check', type: 'group', operator: 'AND',
529
- children: [
530
- { id: '1', type: 'action', actionId: 'admin.access' },
531
- { id: '2', type: 'role', roleId: 'superuser-role-id' },
532
- ],
533
- }
534
- ```
397
+ Dropdown selectors for company and branch. Shown in sidebar when `services.auth.features.companySelection` is enabled.
535
398
 
536
- ### Filtering Behavior
399
+ ### AppConfiguratorComponent
537
400
 
538
- - **Separators** pass through without permission checks
539
- - **Parent items** with no visible children are hidden
540
- - **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
541
406
 
542
407
  ---
543
408
 
544
- ## Pre-defined Themes
409
+ ## Configuration Persistence
545
410
 
546
- Custom Material-based themes using `definePreset`:
547
-
548
- | Theme | Primary Color | Hover Color |
549
- |-------|---------------|-------------|
550
- | `GreenTheme` | `#01712c` | `#119744` |
551
- | `NavyBlueTheme` | `#3535cd` | `#0707a9` |
411
+ `LayoutPersistenceService` automatically saves and restores layout preferences to `localStorage`:
552
412
 
553
413
  ```typescript
554
- import { GreenTheme, NavyBlueTheme } from '@flusys/ng-layout';
414
+ // Automatically persisted keys:
415
+ // - flusys.layout.theme
416
+ // - flusys.layout.colorScheme
417
+ // - flusys.layout.menuMode
418
+ // - flusys.layout.scale
419
+ // - flusys.layout.ripple
555
420
  ```
556
421
 
557
- ---
558
-
559
- ## Usage Examples
560
-
561
- ### Basic Setup
422
+ No manual setup required — persistence is active whenever `LayoutService` is injected. To clear persisted settings:
562
423
 
563
424
  ```typescript
564
- @Component({
565
- imports: [AppLayout],
566
- template: `<app-layout><router-outlet /></app-layout>`,
567
- host: { '[class.app-dark]': 'layoutService.isDarkTheme()' },
568
- })
569
- export class AppComponent {
570
- readonly layoutService = inject(LayoutService);
571
- }
425
+ import { LayoutPersistenceService } from '@flusys/ng-layout';
426
+
427
+ this.persistenceService.clear();
572
428
  ```
573
429
 
574
- ### Menu Configuration
430
+ ---
575
431
 
576
- **Centralized Config File (v4.0.1):**
432
+ ## Integration Tokens
433
+
434
+ ### Auth Integration (from ng-auth)
577
435
 
578
436
  ```typescript
579
- // app-menu.config.ts
580
- import { IMenuItem } from '@flusys/ng-layout';
437
+ import { provideAuthLayoutIntegration } from '@flusys/ng-auth';
581
438
 
582
- export const APP_MENU: IMenuItem[] = [
583
- {
584
- id: 'dashboard',
585
- labelKey: 'menu.dashboard',
586
- icon: 'pi pi-home',
587
- iconType: 1,
588
- routerLink: ['/'],
589
- },
590
- {
591
- id: 'admin',
592
- labelKey: 'menu.administration',
593
- icon: 'pi pi-cog',
594
- iconType: 1,
595
- permissionLogic: { id: 'admin', type: 'action', actionId: 'admin.access' },
596
- children: [
597
- {
598
- id: 'users',
599
- labelKey: 'menu.users',
600
- icon: 'pi pi-users',
601
- iconType: 1,
602
- routerLink: ['/admin/users'],
603
- },
604
- ],
605
- },
606
- ];
439
+ // app.config.ts providers:
440
+ ...provideAuthLayoutIntegration()
441
+ // Provides: LAYOUT_AUTH_STATE, LAYOUT_AUTH_API
607
442
  ```
608
443
 
609
- **Benefits:**
610
- - Centralized menu definition in config file
611
- - Uses `labelKey` for localization (not hardcoded labels)
612
- - Easier to maintain and share across multiple layouts
613
- - Supports nested children and permission logic
614
-
615
- **Usage in App:**
444
+ **`LAYOUT_AUTH_STATE`** interface:
616
445
  ```typescript
617
- import { APP_MENU } from './config/app-menu.config.ts';
618
- import { LayoutService } from '@flusys/ng-layout';
619
-
620
- layoutService.setMenu(APP_MENU);
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
+ }
621
454
  ```
622
455
 
623
- ### Profile Setup
624
-
456
+ **`LAYOUT_AUTH_API`** interface:
625
457
  ```typescript
626
- // After login
627
- layoutService.setUserProfile({
628
- id: 'user-123',
629
- name: 'John Doe',
630
- email: 'john@example.com',
631
- profilePictureUrl: 'https://example.com/avatar.jpg',
632
- });
633
-
634
- layoutService.setCompanyProfile({
635
- id: 'company-456',
636
- name: 'Acme Corp',
637
- slug: 'acme-corp',
638
- logoUrl: 'https://example.com/logo.png',
639
- });
640
-
641
- // On logout
642
- layoutService.setUserProfile(null);
643
- layoutService.setCompanyProfile(null);
458
+ interface ILayoutAuthApi {
459
+ logout(): Observable<void>;
460
+ selectCompany(companyId: string, branchId: string): Observable<void>;
461
+ }
644
462
  ```
645
463
 
646
- ---
647
-
648
- ## Best Practices
649
-
650
- ### Signal Patterns
464
+ ### Notification Bell Integration (from ng-notification)
651
465
 
652
- Use private writable + public readonly pattern:
653
466
  ```typescript
654
- private readonly _isActive = signal(false);
655
- readonly isActive = this._isActive.asReadonly();
467
+ import { provideNotificationProviders } from '@flusys/ng-notification';
656
468
 
657
- // Update via private signal
658
- this._isActive.set(true);
659
- this._isActive.update(v => !v);
469
+ ...provideNotificationProviders()
470
+ // Provides: LAYOUT_NOTIFICATION_BELL
660
471
  ```
661
472
 
662
- ### Responsive Widths
473
+ ### Language Selector Integration (from ng-localization)
663
474
 
664
- | Component | Pattern |
665
- |-----------|---------|
666
- | AppConfigurator | `w-[calc(100vw-2rem)] sm:w-72 max-w-72` |
667
- | AppProfile | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]` |
668
- | AppCompanyBranchSelector | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[300px] max-w-[360px]` |
669
- | AppLauncher | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]` |
670
-
671
- ### Dark Mode CSS Variables
475
+ ```typescript
476
+ import { provideLocalization } from '@flusys/ng-localization';
672
477
 
673
- ```css
674
- background-color: var(--surface-overlay); /* Panels */
675
- bg-emphasis /* Hover states */
676
- text-color / text-muted-color /* Text */
677
- border-surface /* Borders */
678
- bg-primary / text-primary-contrast /* Primary */
478
+ ...provideLocalization()
479
+ // Provides: LAYOUT_LANGUAGE_SELECTOR
679
480
  ```
680
481
 
681
482
  ---
682
483
 
683
- ## Common Issues
484
+ ## Troubleshooting
684
485
 
685
- | Issue | Solution |
686
- |-------|----------|
687
- | Menu not showing | Call `layoutService.setMenu([...])` |
688
- | Auth info missing | Provide `LAYOUT_AUTH_STATE` token |
689
- | Theme not applying | Add `host: { '[class.app-dark]': 'layoutService.isDarkTheme()' }` |
690
- | Menu items hidden | Check `permissionLogic` and user permissions |
691
- | Profile picture missing | Include `profilePictureUrl` in `setUserProfile()` |
692
- | Company feature missing | Enable `services.auth.enabled: true` and provide both auth tokens |
486
+ **Sidebar doesn't open on mobile**
693
487
 
694
- ---
488
+ Ensure `AppLayoutComponent` is the parent route component, not just imported as a standalone component in a non-route context.
695
489
 
696
- ## API Reference
490
+ **Menu items don't highlight active route**
697
491
 
698
- ### Components
492
+ Add `routerLinkActiveOptions: { exact: true }` to leaf menu items and `{ exact: false }` to parent items:
699
493
 
700
- | Component | Selector |
701
- |-----------|----------|
702
- | `AppLayout` | `app-layout` |
703
- | `AppTopbar` | `app-topbar` |
704
- | `AppSidebar` | `app-sidebar` |
705
- | `AppMenu` | `app-menu` |
706
- | `AppMenuitem` | `[app-menuitem]` |
707
- | `AppFooter` | `app-footer` |
708
- | `AppProfile` | `app-profile` |
709
- | `AppCompanyBranchSelector` | `app-company-branch-selector` |
710
- | `AppLauncher` | `app-launcher` |
711
- | `AppConfigurator` | `app-configurator` |
712
- | `AppFloatingConfigurator` | `app-floating-configurator` |
494
+ ```typescript
495
+ {
496
+ labelKey: 'menu.dashboard',
497
+ routerLink: ['/dashboard'],
498
+ routerLinkActiveOptions: { exact: true },
499
+ }
500
+ ```
713
501
 
714
- ### Services
502
+ **Topbar submenus appear behind other elements**
715
503
 
716
- | Service | Description |
717
- |---------|-------------|
718
- | `LayoutService` | Signal-based layout state management |
719
- | `LayoutPersistenceService` | localStorage persistence |
504
+ Set a higher `z-index` on `.topbar-submenu` in your global styles, or ensure no parent has `overflow: hidden`.
720
505
 
721
- ### Tokens
506
+ **Theme not changing**
722
507
 
723
- | Token | Interface |
724
- |-------|-----------|
725
- | `LAYOUT_AUTH_STATE` | `ILayoutAuthState` |
726
- | `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.
727
509
 
728
- ### Utilities
510
+ **`No provider for LAYOUT_AUTH_STATE`**
729
511
 
730
- | Function | Description |
731
- |----------|-------------|
732
- | `filterMenuByPermissions()` | Filter menu items by permission logic |
733
- | `filterAppsByPermissions()` | Filter launcher apps by permission logic |
512
+ You must call `provideAuthLayoutIntegration()` in `app.config.ts` after enabling `ng-auth`.
734
513
 
735
514
  ---
736
515
 
737
- ## See Also
738
-
739
- - [CORE-GUIDE.md](./CORE-GUIDE.md) - Configuration, interceptors
740
- - [SHARED-GUIDE.md](./SHARED-GUIDE.md) - Shared components, provider interfaces
741
- - [AUTH-GUIDE.md](./AUTH-GUIDE.md) - Authentication integration, adapters
742
-
743
- ---
516
+ ## License
744
517
 
745
- **Last Updated:** 2026-02-25 | **Version:** 3.0.1 | **Angular:** 21
518
+ MIT © FLUSYS