@flusys/ng-layout 1.1.0 → 1.2.0-rc.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,714 +1,101 @@
1
- # @flusys/ng-layout Package Guide
1
+ # @flusys/ng-layout
2
2
 
3
- ## Overview
3
+ Application shell for the FLUSYS Angular platform — topbar, sidebar, menu, and signal-based layout state management.
4
4
 
5
- `@flusys/ng-layout` provides the application shell, menu system, theme management, and layout components for FLUSYS applications. This package creates the visual structure and navigation framework.
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-layout.svg)](https://www.npmjs.com/package/@flusys/ng-layout)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
 
7
- **Key Principle:** ng-layout depends on ng-core and ng-shared, but remains independent of feature packages like ng-auth.
8
-
9
- ## Package Information
10
-
11
- - **Package:** `@flusys/ng-layout`
12
- - **Version:** 1.1.0
13
- - **Dependencies:** ng-core, ng-shared
14
- - **Dependents:** flusysng
15
- - **Build Command:** `npm run build:ng-layout`
16
-
17
- ## Features
18
-
19
- ### 1. App Layout Components
20
-
21
- #### AppLayout
22
-
23
- Main layout wrapper with signal-based state management.
8
+ ---
24
9
 
25
- ```typescript
26
- import { AppLayout } from '@flusys/ng-layout';
10
+ ## Installation
27
11
 
28
- @Component({
29
- selector: 'app-root',
30
- standalone: true,
31
- imports: [AppLayout],
32
- template: `
33
- <app-layout>
34
- <router-outlet />
35
- </app-layout>
36
- `
37
- })
38
- export class AppComponent {}
12
+ ```bash
13
+ npm install @flusys/ng-layout @flusys/ng-core @flusys/ng-shared
39
14
  ```
40
15
 
41
- **What it includes:**
42
- - AppTopbar (header with logo, user menu)
43
- - AppSidebar (collapsible navigation menu)
44
- - Main content area with `<router-outlet>`
45
- - AppFooter
46
-
47
- **Layout modes:**
48
- - **Static** - Sidebar always visible on desktop, overlay on mobile
49
- - **Overlay** - Sidebar overlays content when opened
50
-
51
- #### AppTopbar
52
-
53
- Application header with branding and user actions. Used automatically by AppLayout.
54
-
55
- **Features:**
56
- - Company name display (from `LAYOUT_AUTH_STATE` or fallback to "Flusys")
57
- - Menu toggle button (calls `layoutService.onMenuToggle()`)
58
- - Dark mode toggle button
59
- - Theme/color palette configurator (AppConfigurator via pStyleClass)
60
- - App launcher grid (if apps configured via `LayoutService.setApps()`)
61
- - Company/Branch selector (if `enableCompanyFeature` is true)
62
- - User profile dropdown (AppProfile)
63
-
64
- #### AppSidebar
65
-
66
- Collapsible navigation sidebar. Contains AppMenu.
67
-
68
- **Features:**
69
- - Multi-level menu via AppMenu
70
- - Collapsible/expandable
71
- - Active route highlighting
72
- - Icons with labels
73
- - Smooth CSS animations
74
-
75
- #### AppMenu
76
-
77
- Menu container that renders `LayoutService.menu` signal (permission-filtered).
78
-
79
- #### AppMenuitem
80
-
81
- Recursive menu item component using attribute selector `[app-menuitem]`.
82
-
83
- **Features:**
84
- - Nested children with toggle animation
85
- - Router link active detection with smart path matching
86
- - Menu state synchronization via `layoutService.menuSource$`
87
- - Smooth submenu collapse/expand via CSS transitions (no `@angular/animations`)
88
-
89
- #### AppFooter
90
-
91
- Displays product branding information from `LayoutService`:
92
- - **appName** - Application name from `APP_CONFIG` or default
93
- - **authorName** - Author/company name with link
94
- - **authorUrl** - Link to author website
95
-
96
- #### AppProfile
97
-
98
- User profile dropdown in topbar.
99
-
100
- **Features:**
101
- - User info display (picture, name, email) with text truncation for long values
102
- - Profile route link
103
- - Copy SignUp Link button (generates link with company slug)
104
- - Logout button with toast feedback
105
- - Responsive dropdown width (`w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]`)
106
- - Uses `LAYOUT_AUTH_API` and `LAYOUT_AUTH_STATE` tokens (optional injection)
107
-
108
- #### AppCompanyBranchSelector
109
-
110
- Company and branch switcher in topbar. Shown when company feature is enabled.
111
-
112
- **Features:**
113
- - Button shows current company name and branch name
114
- - Popover with company/branch dropdowns
115
- - Auto-selects branch if only one available
116
- - Loading states and error handling
117
- - Uses `LAYOUT_AUTH_API` for data fetching and switching
118
- - Signal-based reactivity for button enable/disable state
119
-
120
- **User Workflow:**
121
- 1. Click company/branch button in topbar
122
- 2. Popover opens with company dropdown
123
- 3. Select company → Branches load
124
- 4. Select branch (required if branches exist)
125
- 5. Click "Switch" → Backend API call → JWT updated → Navigate to dashboard
126
-
127
- **Configuration requires:**
128
- 1. Auth service enabled in environment (`services.auth.enabled: true`)
129
- 2. Auth adapters provided in `app.config.ts`
130
-
131
- #### AppLauncher
16
+ ---
132
17
 
133
- App launcher grid in topbar for quick access to related applications.
18
+ ## 1. Set Up the Layout Route
134
19
 
135
- **Features:**
136
- - Responsive grid: 2 columns on mobile, 3 columns on desktop (`grid-cols-2 sm:grid-cols-3`)
137
- - External links (open in new tabs)
138
- - Icon support: PrimeNG, Material, or image URLs
139
- - Permission filtering via `permissionLogic`
140
- - Only visible when `layoutService.hasApps()` is true
141
- - App names truncated to prevent overflow
20
+ Use `AppLayout` as the parent route component. All authenticated pages become children.
142
21
 
143
- **Configuration:**
144
22
  ```typescript
145
- import { ILauncherApp } from '@flusys/ng-layout';
23
+ // app.routes.ts
24
+ import { Routes } from '@angular/router';
25
+ import { AppLayout } from '@flusys/ng-layout';
146
26
 
147
- const apps: ILauncherApp[] = [
27
+ export const routes: Routes = [
148
28
  {
149
- id: 'docs',
150
- name: 'Docs',
151
- iconType: 1, // 1=primeng, 2=material, 3=image
152
- icon: 'pi pi-book',
153
- url: 'https://docs.example.com',
29
+ path: '',
30
+ component: AppLayout,
31
+ children: [
32
+ { path: 'dashboard', loadComponent: () => import('./pages/dashboard.component') },
33
+ { path: 'products', loadComponent: () => import('./pages/product-list.component') },
34
+ ],
154
35
  },
155
36
  {
156
- id: 'analytics',
157
- name: 'Analytics',
158
- iconType: 1,
159
- icon: 'pi pi-chart-bar',
160
- url: 'https://analytics.example.com',
161
- permissionLogic: { type: 'action', actionId: 'analytics.view' },
37
+ path: 'auth',
38
+ loadChildren: () => import('./pages/auth/auth.routes'),
162
39
  },
163
40
  ];
164
-
165
- // In app initialization:
166
- layoutService.setApps(apps);
167
41
  ```
168
42
 
169
- #### AppConfigurator
170
-
171
- Theme customizer dropdown. Opened from topbar via pStyleClass.
172
-
173
- **Features:**
174
- - Primary color selector (16 colors + noir)
175
- - Surface color selector (9 colors)
176
- - Preset selector (Aura, Lara, Nora)
177
- - Menu mode selector (static/overlay)
178
- - Dynamic theme application via PrimeUI themes API
179
-
180
- #### AppFloatingConfigurator
181
-
182
- Fixed-position floating button for theme customization. Provides quick access to dark mode toggle and color palette.
183
-
184
- ### 2. LayoutService
43
+ ---
185
44
 
186
- Signal-based service for managing layout state. Singleton (`providedIn: 'root'`).
45
+ ## 2. LayoutService — Reading and Updating Layout State
187
46
 
188
47
  ```typescript
189
48
  import { LayoutService } from '@flusys/ng-layout';
190
49
 
191
- @Component({...})
192
- export class AppComponent {
193
- private readonly layoutService = inject(LayoutService);
194
- }
195
- ```
196
-
197
- **Signals:**
198
-
199
- | Signal | Type | Description |
200
- |--------|------|-------------|
201
- | `layoutConfig` | `Signal<LayoutConfig>` | Theme/preset/menuMode configuration |
202
- | `layoutState` | `Signal<LayoutState>` | UI state (menu active, sidebar visible) |
203
- | `transitionComplete` | `Signal<boolean>` | Dark mode transition state |
204
- | `userProfile` | `Signal<UserProfile \| null>` | Current user display data |
205
- | `companyProfile` | `Signal<CompanyProfile \| null>` | Current company display data |
206
- | `appName` | `Signal<string>` | App name for fallback display |
207
- | `authorName` | `Signal<string>` | Author/company name for footer |
208
- | `authorUrl` | `Signal<string>` | Author website URL for footer |
209
- | `menu` | `Computed<IMenuItem[]>` | Permission-filtered menu items |
210
- | `apps` | `Computed<ILauncherApp[]>` | Permission-filtered launcher apps |
211
-
212
- **Computed Signals:**
50
+ @Component({ ... })
51
+ export class MyComponent {
52
+ private readonly layout = inject(LayoutService);
213
53
 
214
- | Signal | Type | Description |
215
- |--------|------|-------------|
216
- | `isDarkTheme` | `Computed<boolean>` | Whether dark theme is active |
217
- | `isSidebarActive` | `Computed<boolean>` | Whether sidebar/menu is visible |
218
- | `isOverlay` | `Computed<boolean>` | Whether menu mode is overlay |
219
- | `getPrimary` | `Computed<string>` | Current primary color |
220
- | `getSurface` | `Computed<string \| null>` | Current surface color |
221
- | `userName` | `Computed<string>` | User name or "User" fallback |
222
- | `userEmail` | `Computed<string>` | User email |
223
- | `userProfilePictureUrl` | `Computed<string \| null>` | Profile picture URL |
224
- | `companyName` | `Computed<string>` | Company name or appName fallback |
225
- | `companyLogoUrl` | `Computed<string \| null>` | Company logo URL |
226
- | `isAuthenticated` | `Computed<boolean>` | Whether user profile exists |
227
- | `hasApps` | `Computed<boolean>` | Whether filtered apps exist |
54
+ // Read signals
55
+ readonly isDark = this.layout.isDarkTheme; // Signal<boolean>
56
+ readonly isTopbar = this.layout.isTopbar; // Signal<boolean>
57
+ readonly isOverlay = this.layout.isOverlay; // Signal<boolean>
58
+ readonly menu = this.layout.menu; // Signal<IMenuItem[]> — permission-filtered
228
59
 
229
- **Methods:**
230
-
231
- | Method | Description |
232
- |--------|-------------|
233
- | `onMenuToggle()` | Toggle menu visibility (handles static/overlay/mobile) |
234
- | `onConfigUpdate()` | Emit config change event |
235
- | `onMenuStateChange(event)` | Emit menu item state change |
236
- | `reset()` | Reset menu state |
237
- | `setUserProfile(profile)` | Set user profile for layout display |
238
- | `setCompanyProfile(profile)` | Set company profile for layout display |
239
- | `setMenu(items)` | Set raw menu items (auto-filtered by permissions) |
240
- | `clearMenu()` | Clear menu items |
241
- | `setApps(apps)` | Set launcher apps (auto-filtered by permissions) |
242
- | `clearApps()` | Clear launcher apps |
243
- | `toggleDarkMode(config?)` | Toggle dark mode CSS class |
244
- | `isDesktop()` | Check if viewport > 991px |
245
- | `isMobile()` | Check if viewport <= 991px |
246
-
247
- **RxJS Observables:**
248
-
249
- | Observable | Description |
250
- |------------|-------------|
251
- | `menuSource$` | Menu state change events |
252
- | `resetSource$` | Menu reset events |
253
- | `configUpdate$` | Config change events |
254
- | `overlayOpen$` | Menu overlay opened |
255
-
256
- #### Configuration Persistence
257
-
258
- Layout configuration is automatically persisted to localStorage and restored on app bootstrap.
259
-
260
- **localStorage Key:** `flusys.layout.config`
261
-
262
- **Persisted Configuration (LayoutConfig):**
263
- ```typescript
264
- interface LayoutConfig {
265
- preset?: string; // "Aura" | "Lara" | "Nora"
266
- primary?: string; // "emerald" | "green" | "blue" | etc.
267
- surface?: string | null; // "slate" | "gray" | "zinc" | etc. | null
268
- darkTheme?: boolean;
269
- menuMode?: 'static' | 'overlay';
270
60
  }
271
61
  ```
272
62
 
273
- **Behavior:**
274
- - Changes saved automatically via Angular `effect()` when config changes
275
- - Configuration restored on page refresh (merged with defaults)
276
- - SSR-safe (`isPlatformBrowser` check)
277
- - Invalid preset falls back to "Aura", invalid menuMode falls back to "static"
278
- - Corrupted JSON is cleared and defaults are used
279
- - Version mismatches trigger automatic clearing
280
-
281
- **Clearing Saved Configuration:**
282
- ```typescript
283
- import { LayoutPersistenceService } from '@flusys/ng-layout';
284
-
285
- inject(LayoutPersistenceService).clear();
286
- ```
287
-
288
- ### 3. Theme Management
289
-
290
- Built-in theme support with light/dark modes using PrimeUI themes.
291
-
292
- **Dark mode toggle** uses the View Transition API (with fallback):
293
- ```typescript
294
- // AppTopbar toggles dark mode by updating layoutConfig signal:
295
- this.layoutService.layoutConfig.update((state) => ({
296
- ...state,
297
- darkTheme: !state.darkTheme,
298
- }));
299
- ```
300
-
301
- **Host binding** for root component:
302
- ```typescript
303
- @Component({
304
- host: { '[class.app-dark]': 'layoutService.isDarkTheme()' }
305
- })
306
- export class AppComponent {
307
- readonly layoutService = inject(LayoutService);
308
- }
309
- ```
310
-
311
- **Pre-defined Themes:**
312
-
313
- | Theme | Base | Primary Color |
314
- |-------|------|---------------|
315
- | `GreenTheme` | Material | `#01712c` |
316
- | `NavyBlueTheme` | Material | `#3535cd` |
317
-
318
- ```typescript
319
- import { GreenTheme, NavyBlueTheme } from '@flusys/ng-layout';
320
- ```
321
-
322
- ### 4. Auth Integration
323
-
324
- Layout components use injection tokens to access auth data without depending on ng-auth.
325
-
326
- #### LAYOUT_AUTH_STATE Token
327
-
328
- Provides current user/company/branch state to layout components.
329
-
330
- ```typescript
331
- interface ILayoutAuthState {
332
- readonly currentCompanyInfo: Signal<ICompanyInfo | null>;
333
- readonly currentBranchInfo: Signal<IBranchInfo | null>;
334
- readonly loginUserData: Signal<IUserInfo | null>;
335
- }
336
- ```
337
-
338
- #### LAYOUT_AUTH_API Token
339
-
340
- Provides auth actions (logout, company switching) to layout components.
341
-
342
- ```typescript
343
- interface ILayoutAuthApi {
344
- logOut(): Observable<any>;
345
- navigateLogin(withUrl?: boolean): void;
346
- switchCompany(companyId: string, branchId: string): Observable<any>;
347
- getUserCompanies(): Observable<ICompanyInfo[]>;
348
- getCompanyBranches(companyId: string): Observable<IBranchInfo[]>;
349
- }
350
- ```
351
-
352
- #### Providing Auth Tokens
353
-
354
- ```typescript
355
- // app.config.ts
356
- import { LAYOUT_AUTH_STATE, LAYOUT_AUTH_API } from '@flusys/ng-layout';
357
- import { AuthLayoutStateAdapter, AuthLayoutApiAdapter } from '@flusys/ng-auth';
358
-
359
- providers: [
360
- { provide: LAYOUT_AUTH_STATE, useExisting: AuthLayoutStateAdapter },
361
- { provide: LAYOUT_AUTH_API, useExisting: AuthLayoutApiAdapter },
362
- ]
363
- ```
364
-
365
- Components inject tokens with `{ optional: true }` and degrade gracefully when auth is not provided.
366
-
367
- ### 5. Permission-Based Menu Filtering
368
-
369
- Menu items and launcher apps are automatically filtered based on user permissions using `permissionLogic`.
63
+ To push user/company profile into the topbar (done automatically by `ng-auth`'s bridge service):
370
64
 
371
- **How it works:**
372
- 1. Raw menu items set via `layoutService.setMenu(items)`
373
- 2. `LayoutService.menu` is a `computed()` that filters via `filterMenuByPermissions()`
374
- 3. Permission evaluation uses `evaluateLogicNode()` from ng-shared
375
- 4. Same pattern applies to apps via `filterAppsByPermissions()`
376
-
377
- **ILogicNode Types:**
378
- ```typescript
379
- interface ILogicNode {
380
- id: string;
381
- type: 'group' | 'action' | 'role';
382
- operator?: 'AND' | 'OR'; // Required when type is 'group'
383
- actionId?: string; // Required when type is 'action'
384
- roleId?: string; // Required when type is 'role'
385
- children?: ILogicNode[]; // Required when type is 'group'
386
- }
387
- ```
388
-
389
- **Best Practice:** Use logic nodes (parent menu items without `routerLink`) with `permissionLogic` as permission gates for entire branches:
390
-
391
- ```typescript
392
- // Logic node gates all children
393
- {
394
- id: 'administration',
395
- label: 'Administration',
396
- icon: 'pi pi-cog',
397
- permissionLogic: { id: 'admin-check', type: 'action', actionId: 'admin.access' },
398
- children: [
399
- { id: 'users', label: 'Users', routerLink: ['/admin/users'] },
400
- { id: 'settings', label: 'Settings', routerLink: ['/admin/settings'] },
401
- ],
402
- }
403
- ```
404
-
405
- **Complex permission examples:**
406
- ```typescript
407
- // OR - user needs ANY permission
408
- permissionLogic: {
409
- id: 'reports-check',
410
- type: 'group',
411
- operator: 'OR',
412
- children: [
413
- { id: '1', type: 'action', actionId: 'reports.view' },
414
- { id: '2', type: 'role', roleId: 'manager-role-id' },
415
- ],
416
- }
417
-
418
- // AND - user needs ALL permissions
419
- permissionLogic: {
420
- id: 'admin-check',
421
- type: 'group',
422
- operator: 'AND',
423
- children: [
424
- { id: '1', type: 'action', actionId: 'admin.access' },
425
- { id: '2', type: 'role', roleId: 'superuser-role-id' },
426
- ],
427
- }
428
- ```
429
-
430
- ## Installation
431
-
432
- Build ng-layout after ng-core and ng-shared:
433
-
434
- ```bash
435
- npm run build:ng-core
436
- npm run build:ng-shared
437
- npm run build:ng-layout
438
- # Or build all libraries:
439
- npm run build:libs
440
- ```
441
-
442
- ## Usage Examples
443
-
444
- ### Basic Layout Setup
445
-
446
- ```typescript
447
- // app.component.ts
448
- import { Component, inject } from '@angular/core';
449
- import { RouterOutlet } from '@angular/router';
450
- import { AppLayout, LayoutService } from '@flusys/ng-layout';
451
-
452
- @Component({
453
- selector: 'app-root',
454
- standalone: true,
455
- imports: [RouterOutlet, AppLayout],
456
- template: `
457
- <app-layout>
458
- <router-outlet />
459
- </app-layout>
460
- `,
461
- host: { '[class.app-dark]': 'layoutService.isDarkTheme()' },
462
- })
463
- export class AppComponent {
464
- readonly layoutService = inject(LayoutService);
465
- }
466
- ```
467
-
468
- ### Menu Configuration
469
-
470
- ```typescript
471
- import { LayoutService, IMenuItem } from '@flusys/ng-layout';
472
-
473
- const menuItems: IMenuItem[] = [
474
- {
475
- label: 'Dashboard',
476
- icon: 'pi pi-home',
477
- routerLink: ['/dashboard'],
478
- },
479
- {
480
- label: 'Management',
481
- icon: 'pi pi-cog',
482
- children: [
483
- { label: 'Users', icon: 'pi pi-users', routerLink: ['/users'] },
484
- { label: 'Roles', icon: 'pi pi-shield', routerLink: ['/roles'] },
485
- ],
486
- },
487
- { separator: true },
488
- {
489
- label: 'Settings',
490
- icon: 'pi pi-cog',
491
- routerLink: ['/settings'],
492
- },
493
- ];
65
+ ---
494
66
 
495
- layoutService.setMenu(menuItems);
496
- ```
67
+ ## 3. Integration Tokens — Auth, Notifications, Language
497
68
 
498
- ### Auth Integration
69
+ Connect feature packages to the layout via injection tokens. Register them in `app.config.ts`.
499
70
 
500
71
  ```typescript
501
- // app.config.ts
502
- import { LAYOUT_AUTH_STATE, LAYOUT_AUTH_API } from '@flusys/ng-layout';
503
- import { AuthLayoutStateAdapter, AuthLayoutApiAdapter } from '@flusys/ng-auth';
72
+ import { provideAuthLayoutIntegration } from '@flusys/ng-auth';
73
+ import { provideNotificationProviders } from '@flusys/ng-notification';
74
+ import { provideLocalization } from '@flusys/ng-localization';
504
75
 
505
76
  export const appConfig: ApplicationConfig = {
506
77
  providers: [
507
- { provide: LAYOUT_AUTH_STATE, useExisting: AuthLayoutStateAdapter },
508
- { provide: LAYOUT_AUTH_API, useExisting: AuthLayoutApiAdapter },
78
+ ...provideAuthLayoutIntegration(), // topbar user profile + logout
79
+ ...provideNotificationProviders(), // topbar notification bell
80
+ ...provideLocalization({ ... }), // topbar language selector
509
81
  ],
510
82
  };
511
83
  ```
512
84
 
513
- ### Dynamic Menu from Backend
514
-
515
- ```typescript
516
- import { LayoutService } from '@flusys/ng-layout';
517
- import { MenuApiService } from './services/menu-api.service';
518
-
519
- @Component({...})
520
- export class AppComponent implements OnInit {
521
- private readonly layoutService = inject(LayoutService);
522
- private readonly menuApi = inject(MenuApiService);
523
-
524
- async ngOnInit() {
525
- const response = await firstValueFrom(this.menuApi.getUserMenu());
526
- if (response.success) {
527
- this.layoutService.setMenu(response.data);
528
- }
529
- }
530
- }
531
- ```
532
-
533
- ## Best Practices
534
-
535
- ### Layout Structure
536
- - Use `AppLayout` as root layout component
537
- - Set menu items via `layoutService.setMenu()` during app initialization
538
- - Use `LayoutService` for all layout state changes
539
- - Don't manipulate DOM directly for layout changes
540
-
541
- ### Menu Management
542
- - Load menus dynamically from backend (IAM module)
543
- - Group related items under parent menu items with `permissionLogic`
544
- - Use PrimeIcons for consistency
545
- - Use `children` property for nested items (not `items`)
85
+ The layout reads these tokens to render topbar slots without directly importing feature packages.
546
86
 
547
- ### Auth Integration
548
- - Use injection tokens (`LAYOUT_AUTH_STATE`, `LAYOUT_AUTH_API`)
549
- - Don't import ng-auth directly in ng-layout
550
- - Provide adapters at app level in `app.config.ts`
551
- - Layout works without auth (graceful degradation)
552
-
553
- ### Theme Management
554
- - Use CSS animations, not `@angular/animations`
555
- - Apply `app-dark` class to root element via host binding
556
- - Theme preferences persist automatically to localStorage
557
- - Test both light and dark themes for contrast
558
-
559
- **Dark Mode CSS Variables:**
560
- All components use PrimeNG CSS variables for automatic dark mode support:
561
- ```css
562
- /* Backgrounds */
563
- background-color: var(--surface-overlay); /* Dropdown panels */
564
- bg-emphasis /* Hover states */
565
-
566
- /* Text */
567
- text-color /* Primary text */
568
- text-muted-color /* Secondary text */
569
-
570
- /* Borders */
571
- border-surface /* Standard borders */
572
-
573
- /* Primary colors */
574
- bg-primary /* Primary backgrounds */
575
- text-primary-contrast /* Text on primary */
576
- ```
577
-
578
- ### Responsive Design
579
- - Use Tailwind for responsive utilities
580
- - Layout automatically handles responsive behavior (static → overlay on mobile)
581
- - Menu closes on navigation and outside clicks
582
-
583
- **Dropdown Panel Pattern:**
584
- ```css
585
- /* Full width on mobile, auto-width on desktop with min/max constraints */
586
- class="w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
587
- ```
588
-
589
- **Component Responsive Widths:**
590
- | Component | Pattern |
591
- |-----------|---------|
592
- | `AppConfigurator` | `w-[calc(100vw-2rem)] sm:w-72 max-w-72` |
593
- | `AppProfile` | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]` |
594
- | `AppCompanyBranchSelector` | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[300px] max-w-[360px]` |
595
- | `AppLauncher` | `w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]` |
596
- | `AppFloatingConfigurator` | Position: `top-4 md:top-8 right-2 md:right-8` |
597
-
598
- **Grid Responsiveness:**
599
- ```css
600
- /* 2 columns mobile, 3 columns desktop */
601
- class="grid grid-cols-2 sm:grid-cols-3 gap-2"
602
- ```
603
-
604
- **Text Truncation:**
605
- ```html
606
- <!-- Prevent overflow on long names/emails -->
607
- <div class="flex flex-col min-w-0 flex-1">
608
- <span class="truncate">{{ userName() }}</span>
609
- </div>
610
- ```
611
-
612
- ## Common Issues
613
-
614
- ### Menu Not Showing
615
-
616
- Ensure menu items are set via `LayoutService.setMenu()`:
617
- ```typescript
618
- this.layoutService.setMenu([/* items */]);
619
- ```
87
+ ---
620
88
 
621
- ### Auth Info Not in Topbar
89
+ ## 4. Clear Persisted Layout
622
90
 
623
- Provide `LAYOUT_AUTH_STATE` in `app.config.ts`:
624
91
  ```typescript
625
- { provide: LAYOUT_AUTH_STATE, useExisting: AuthLayoutStateAdapter }
626
- ```
627
-
628
- ### Theme Not Applying
92
+ import { LayoutPersistenceService } from '@flusys/ng-layout';
629
93
 
630
- Add host binding to root component:
631
- ```typescript
632
- @Component({
633
- host: { '[class.app-dark]': 'layoutService.isDarkTheme()' }
634
- })
94
+ inject(LayoutPersistenceService).clear(); // removes flusys.layout.config from localStorage
635
95
  ```
636
96
 
637
- ### Sidebar Not Responsive
638
-
639
- Layout automatically handles responsive behavior. Ensure you're using the `AppLayout` component, not a custom layout.
640
-
641
- ## API Reference
642
-
643
- ### Components
644
-
645
- | Component | Selector | Description |
646
- |-----------|----------|-------------|
647
- | `AppLayout` | `app-layout` | Main layout wrapper |
648
- | `AppTopbar` | `app-topbar` | Application header |
649
- | `AppSidebar` | `app-sidebar` | Navigation sidebar |
650
- | `AppMenu` | `app-menu` | Menu container |
651
- | `AppMenuitem` | `[app-menuitem]` | Recursive menu item |
652
- | `AppFooter` | `app-footer` | Footer |
653
- | `AppProfile` | `app-profile` | User profile dropdown |
654
- | `AppCompanyBranchSelector` | `app-company-branch-selector` | Company/branch switcher |
655
- | `AppLauncher` | `app-launcher` | App launcher grid |
656
- | `AppConfigurator` | `app-configurator` | Theme customizer |
657
- | `AppFloatingConfigurator` | `app-floating-configurator` | Floating theme button |
658
-
659
- ### Services
660
-
661
- | Service | Description |
662
- |---------|-------------|
663
- | `LayoutService` | Signal-based layout state management |
664
- | `LayoutPersistenceService` | localStorage persistence with validation |
665
-
666
- ### Injection Tokens
667
-
668
- | Token | Interface | Description |
669
- |-------|-----------|-------------|
670
- | `LAYOUT_AUTH_STATE` | `ILayoutAuthState` | Auth state signals |
671
- | `LAYOUT_AUTH_API` | `ILayoutAuthApi` | Auth actions |
672
-
673
- ### Interfaces
674
-
675
- | Interface | Description |
676
- |-----------|-------------|
677
- | `IMenuItem` | Menu item configuration |
678
- | `ILauncherApp` | App launcher item configuration |
679
- | `ILayoutAuthState` | Auth state for layout |
680
- | `ILayoutAuthApi` | Auth actions for layout |
681
- | `ICompanyInfo` | Company information |
682
- | `IBranchInfo` | Branch information |
683
- | `IUserInfo` | User information |
684
- | `LayoutConfig` | Theme/preset configuration |
685
- | `LayoutState` | UI state (menu active, etc.) |
686
- | `UserProfile` | User display data |
687
- | `CompanyProfile` | Company display data |
688
- | `MenuChangeEvent` | Menu state change event |
689
-
690
- ### Utility Functions
691
-
692
- | Function | Description |
693
- |----------|-------------|
694
- | `filterMenuByPermissions(items, permissions)` | Filter menu items by permission logic |
695
- | `filterAppsByPermissions(apps, permissions)` | Filter launcher apps by permission logic |
696
-
697
- ### Pre-defined Themes
698
-
699
- | Export | Base Preset | Primary Color |
700
- |--------|-------------|---------------|
701
- | `GreenTheme` | Material | `#01712c` |
702
- | `NavyBlueTheme` | Material | `#3535cd` |
703
-
704
- ## See Also
705
-
706
- - **[CORE-GUIDE.md](./CORE-GUIDE.md)** - Configuration, interceptors
707
- - **[SHARED-GUIDE.md](./SHARED-GUIDE.md)** - Shared components, provider interfaces
708
- - **[AUTH-GUIDE.md](./AUTH-GUIDE.md)** - Authentication integration, adapters
709
-
710
97
  ---
711
98
 
712
- **Last Updated:** 2026-02-23
713
- **Version:** 1.1.0
714
- **Angular Version:** 21
99
+ ## License
100
+
101
+ MIT © FLUSYS