@flusys/ng-layout 4.1.0 → 5.0.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,76 +1,9 @@
1
1
  # @flusys/ng-layout
2
2
 
3
- > Application shell and layout system for the FLUSYS Angular platform — topbar, sidebar, menu, configurator, and layout state management.
3
+ Application shell for the FLUSYS Angular platform — topbar, sidebar, menu, and signal-based layout state management.
4
4
 
5
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
-
11
- ---
12
-
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)
39
-
40
- ---
41
-
42
- ## Overview
43
-
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.
45
-
46
- The layout integrates with auth, notification, and localization packages through injection token interfaces, maintaining clean package independence via the Provider Interface Pattern.
47
-
48
- ---
49
-
50
- ## Features
51
-
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
62
-
63
- ---
64
-
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+ |
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
74
7
 
75
8
  ---
76
9
 
@@ -82,28 +15,22 @@ npm install @flusys/ng-layout @flusys/ng-core @flusys/ng-shared
82
15
 
83
16
  ---
84
17
 
85
- ## Quick Start
18
+ ## 1. Set Up the Layout Route
86
19
 
87
- ### 1. Set Up App Layout Route
20
+ Use `AppLayout` as the parent route component. All authenticated pages become children.
88
21
 
89
22
  ```typescript
90
23
  // app.routes.ts
91
24
  import { Routes } from '@angular/router';
92
- import { AppLayoutComponent } from '@flusys/ng-layout';
25
+ import { AppLayout } from '@flusys/ng-layout';
93
26
 
94
27
  export const routes: Routes = [
95
28
  {
96
29
  path: '',
97
- component: AppLayoutComponent,
30
+ component: AppLayout,
98
31
  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
- },
32
+ { path: 'dashboard', loadComponent: () => import('./pages/dashboard.component') },
33
+ { path: 'products', loadComponent: () => import('./pages/product-list.component') },
107
34
  ],
108
35
  },
109
36
  {
@@ -113,406 +40,62 @@ export const routes: Routes = [
113
40
  ];
114
41
  ```
115
42
 
116
- ### 2. Define Menu Configuration
117
-
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
- ```
148
-
149
- ### 3. Provide Layout Configuration
150
-
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
- };
173
- ```
174
-
175
- ---
176
-
177
- ## Layout Configuration
178
-
179
- ### LayoutConfig
180
-
181
- ```typescript
182
- interface LayoutConfig {
183
- /** Menu display mode */
184
- menuMode: 'static' | 'overlay' | 'topbar';
185
-
186
- /** PrimeNG theme name */
187
- theme: string;
188
-
189
- /** Color scheme */
190
- colorScheme: 'light' | 'dark' | 'dim';
191
-
192
- /** Ripple effect */
193
- ripple?: boolean;
194
-
195
- /** Scale factor (12-16) */
196
- scale?: number;
197
-
198
- /** Menu theme */
199
- menuTheme?: 'light' | 'dark' | 'colored';
200
-
201
- /** Card border style */
202
- inputStyle?: 'outlined' | 'filled';
203
- }
204
- ```
205
-
206
- ### Menu Modes
207
-
208
- #### Static Mode (Default)
209
-
210
- Sidebar is always visible on desktop. Collapses to icons on tablet.
211
-
212
- ```typescript
213
- { provide: LAYOUT_CONFIG, useValue: { menuMode: 'static' } }
214
- ```
215
-
216
- #### Overlay Mode
217
-
218
- Sidebar slides over content. Click outside to close.
219
-
220
- ```typescript
221
- { provide: LAYOUT_CONFIG, useValue: { menuMode: 'overlay' } }
222
- ```
223
-
224
- #### Topbar Mode (v4.0.1+)
225
-
226
- Desktop renders horizontal navigation bar. Mobile falls back to vertical sidebar.
227
-
228
- ```typescript
229
- { provide: LAYOUT_CONFIG, useValue: { menuMode: 'topbar' } }
230
- ```
231
-
232
- Submenu items appear on hover in topbar mode with automatic positioning.
233
-
234
- ### Themes & Colors
235
-
236
- Built-in themes (PrimeNG):
237
-
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 |
248
-
249
- ---
250
-
251
- ## Injection Tokens
252
-
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) |
261
-
262
43
  ---
263
44
 
264
- ## LayoutService
265
-
266
- Central signal-based service for layout state management.
45
+ ## 2. LayoutService — Reading and Updating Layout State
267
46
 
268
47
  ```typescript
269
48
  import { LayoutService } from '@flusys/ng-layout';
270
49
 
271
50
  @Component({ ... })
272
51
  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
- }
52
+ private readonly layout = inject(LayoutService);
286
53
 
287
- setTheme(theme: string): void {
288
- this.layoutService.setTheme(theme);
289
- }
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
290
59
 
291
- setMenuMode(mode: 'static' | 'overlay' | 'topbar'): void {
292
- this.layoutService.setMenuMode(mode);
293
- }
294
60
  }
295
61
  ```
296
62
 
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 |
63
+ To push user/company profile into the topbar (done automatically by `ng-auth`'s bridge service):
313
64
 
314
65
  ---
315
66
 
316
- ## LayoutState
67
+ ## 3. Integration Tokens — Auth, Notifications, Language
317
68
 
318
- Reactive state object managed by `LayoutService`:
69
+ Connect feature packages to the layout via injection tokens. Register them in `app.config.ts`.
319
70
 
320
71
  ```typescript
321
- interface LayoutState {
322
- /** Sidebar open on mobile */
323
- staticMenuMobileActive: boolean;
324
-
325
- /** Overlay sidebar open */
326
- overlayMenuActive: boolean;
327
-
328
- /** Desktop sidebar collapsed */
329
- staticMenuDesktopInactive: boolean;
330
-
331
- /** Right-click menu visible */
332
- menuHoverActive: boolean;
333
-
334
- /** Config panel open */
335
- configSidebarVisible: boolean;
336
-
337
- /** Topbar nav visible (topbar mode) */
338
- topbarMenuVisible: boolean;
339
- }
340
- ```
341
-
342
- ---
343
-
344
- ## Components
345
-
346
- ### AppLayoutComponent
347
-
348
- Root layout component. Use this as the parent route component.
349
-
350
- ```typescript
351
- import { AppLayoutComponent } from '@flusys/ng-layout';
72
+ import { provideAuthLayoutIntegration } from '@flusys/ng-auth';
73
+ import { provideNotificationProviders } from '@flusys/ng-notification';
74
+ import { provideLocalization } from '@flusys/ng-localization';
352
75
 
353
- // In routes:
354
- { path: '', component: AppLayoutComponent, children: [...] }
76
+ export const appConfig: ApplicationConfig = {
77
+ providers: [
78
+ ...provideAuthLayoutIntegration(), // topbar user profile + logout
79
+ ...provideNotificationProviders(), // topbar notification bell
80
+ ...provideLocalization({ ... }), // topbar language selector
81
+ ],
82
+ };
355
83
  ```
356
84
 
357
- Renders:
358
- - `AppTopbarComponent` (header)
359
- - `AppSidebarComponent` (left navigation)
360
- - `<router-outlet>` (page content)
361
- - `AppConfiguratorComponent` (floating settings panel)
362
-
363
- ### AppTopbarComponent
364
-
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)
372
-
373
- ### AppSidebarComponent
374
-
375
- Left navigation panel containing:
376
- - `AppMenuComponent` (navigation items)
377
- - `AppCompanyBranchSelectorComponent` (if company selection enabled)
378
-
379
- ### AppMenuComponent
380
-
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
386
-
387
- ### AppMenuitemComponent
388
-
389
- Individual menu item component. Handles:
390
- - Route navigation
391
- - Submenu expand/collapse
392
- - Topbar hover positioning
393
- - `routerLinkActiveOptions`
394
-
395
- ### AppCompanyBranchSelectorComponent
396
-
397
- Dropdown selectors for company and branch. Shown in sidebar when `services.auth.features.companySelection` is enabled.
398
-
399
- ### AppConfiguratorComponent
400
-
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
85
+ The layout reads these tokens to render topbar slots without directly importing feature packages.
406
86
 
407
87
  ---
408
88
 
409
- ## Configuration Persistence
410
-
411
- `LayoutPersistenceService` automatically saves and restores layout preferences to `localStorage`:
412
-
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:
89
+ ## 4. Clear Persisted Layout
423
90
 
424
91
  ```typescript
425
92
  import { LayoutPersistenceService } from '@flusys/ng-layout';
426
93
 
427
- this.persistenceService.clear();
94
+ inject(LayoutPersistenceService).clear(); // removes flusys.layout.config from localStorage
428
95
  ```
429
96
 
430
97
  ---
431
98
 
432
- ## Integration Tokens
433
-
434
- ### Auth Integration (from ng-auth)
435
-
436
- ```typescript
437
- import { provideAuthLayoutIntegration } from '@flusys/ng-auth';
438
-
439
- // app.config.ts providers:
440
- ...provideAuthLayoutIntegration()
441
- // Provides: LAYOUT_AUTH_STATE, LAYOUT_AUTH_API
442
- ```
443
-
444
- **`LAYOUT_AUTH_STATE`** interface:
445
- ```typescript
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
- }
454
- ```
455
-
456
- **`LAYOUT_AUTH_API`** interface:
457
- ```typescript
458
- interface ILayoutAuthApi {
459
- logout(): Observable<void>;
460
- selectCompany(companyId: string, branchId: string): Observable<void>;
461
- }
462
- ```
463
-
464
- ### Notification Bell Integration (from ng-notification)
465
-
466
- ```typescript
467
- import { provideNotificationProviders } from '@flusys/ng-notification';
468
-
469
- ...provideNotificationProviders()
470
- // Provides: LAYOUT_NOTIFICATION_BELL
471
- ```
472
-
473
- ### Language Selector Integration (from ng-localization)
474
-
475
- ```typescript
476
- import { provideLocalization } from '@flusys/ng-localization';
477
-
478
- ...provideLocalization()
479
- // Provides: LAYOUT_LANGUAGE_SELECTOR
480
- ```
481
-
482
- ---
483
-
484
- ## Troubleshooting
485
-
486
- **Sidebar doesn't open on mobile**
487
-
488
- Ensure `AppLayoutComponent` is the parent route component, not just imported as a standalone component in a non-route context.
489
-
490
- **Menu items don't highlight active route**
491
-
492
- Add `routerLinkActiveOptions: { exact: true }` to leaf menu items and `{ exact: false }` to parent items:
493
-
494
- ```typescript
495
- {
496
- labelKey: 'menu.dashboard',
497
- routerLink: ['/dashboard'],
498
- routerLinkActiveOptions: { exact: true },
499
- }
500
- ```
501
-
502
- **Topbar submenus appear behind other elements**
503
-
504
- Set a higher `z-index` on `.topbar-submenu` in your global styles, or ensure no parent has `overflow: hidden`.
505
-
506
- **Theme not changing**
507
-
508
- Check that `LayoutPersistenceService` is provided (it's automatic when `LayoutService` is used). If the theme is cached, call `persistenceService.clear()` once.
509
-
510
- **`No provider for LAYOUT_AUTH_STATE`**
511
-
512
- You must call `provideAuthLayoutIntegration()` in `app.config.ts` after enabling `ng-auth`.
513
-
514
- ---
515
-
516
99
  ## License
517
100
 
518
101
  MIT © FLUSYS
@@ -2,7 +2,7 @@
2
2
  display: flex;
3
3
  align-items: center;
4
4
  justify-content: center;
5
- padding: 1rem 0 1rem 0;
5
+ padding: 1rem 0;
6
6
  gap: 0.5rem;
7
7
  border-top: 1px solid var(--surface-border);
8
8
 
@@ -13,3 +13,13 @@
13
13
  object-fit: contain;
14
14
  }
15
15
  }
16
+
17
+ @media (max-width: 639px) {
18
+ .layout-footer {
19
+ padding: 0.75rem 0;
20
+
21
+ .logo-image {
22
+ height: 1.5rem;
23
+ }
24
+ }
25
+ }
@@ -190,3 +190,27 @@
190
190
  }
191
191
  }
192
192
  }
193
+
194
+ @media (max-width: 639px) {
195
+ .layout-topbar {
196
+ padding: 0 0.75rem;
197
+ gap: 0.5rem;
198
+
199
+ .layout-topbar-actions {
200
+ gap: 0.25rem;
201
+ }
202
+
203
+ .layout-config-menu {
204
+ gap: 0.25rem;
205
+ }
206
+
207
+ .layout-topbar-action {
208
+ width: 2.25rem;
209
+ height: 2.25rem;
210
+ }
211
+
212
+ .layout-topbar-menu {
213
+ right: 0.75rem;
214
+ }
215
+ }
216
+ }
@@ -66,10 +66,6 @@ $submenu-icon-margin: 0.5rem;
66
66
  flex: 1;
67
67
  }
68
68
 
69
- &:hover {
70
- background-color: var(--surface-hover);
71
- }
72
-
73
69
  &.active-route {
74
70
  background-color: var(--p-primary-color);
75
71
  color: var(--p-primary-contrast-color);
@@ -218,7 +214,7 @@ $submenu-icon-margin: 0.5rem;
218
214
  flex: 1 1 auto;
219
215
  padding: 0 1rem;
220
216
  overflow-x: auto;
221
- overflow-y: visible;
217
+ overflow-y: hidden;
222
218
  -webkit-overflow-scrolling: touch;
223
219
  scrollbar-width: none;
224
220
 
@@ -332,9 +328,8 @@ $submenu-icon-margin: 0.5rem;
332
328
  }
333
329
  }
334
330
 
335
- // Show Level 1 submenu on hover or active state
331
+ // Show Level 1 submenu on hover only (not active state - prevents submenu staying open)
336
332
  &:hover > ul,
337
- &.active-menuitem > ul,
338
333
  > ul:hover {
339
334
  @include show-submenu;
340
335
  }