@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 +377 -481
- package/assets/layout/_footer.scss +7 -0
- package/assets/layout/_responsive.scss +18 -0
- package/assets/layout/_topbar.scss +159 -128
- package/assets/layout/_topbar_nav.scss +350 -0
- package/assets/layout/layout.scss +2 -1
- package/fesm2022/flusys-ng-layout.mjs +1031 -468
- package/fesm2022/flusys-ng-layout.mjs.map +1 -1
- package/package.json +3 -3
- package/types/flusys-ng-layout.d.ts +63 -7
package/README.md
CHANGED
|
@@ -1,622 +1,518 @@
|
|
|
1
|
-
# @flusys/ng-layout
|
|
1
|
+
# @flusys/ng-layout
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Application shell and layout system for the FLUSYS Angular platform — topbar, sidebar, menu, configurator, and layout state management.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/ng-layout)
|
|
6
|
+
[](https://angular.io)
|
|
7
|
+
[](https://www.typescriptlang.org)
|
|
8
|
+
[](https://primeng.org)
|
|
9
|
+
[](LICENSE)
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
---
|
|
6
12
|
|
|
7
|
-
|
|
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
|
-
|
|
40
|
+
---
|
|
10
41
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
50
|
+
## Features
|
|
52
51
|
|
|
53
|
-
|
|
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
|
-
|
|
63
|
+
---
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
77
|
+
## Installation
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
```bash
|
|
80
|
+
npm install @flusys/ng-layout @flusys/ng-core @flusys/ng-shared
|
|
81
|
+
```
|
|
76
82
|
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
87
|
+
### 1. Set Up App Layout Route
|
|
92
88
|
|
|
93
|
-
|
|
89
|
+
```typescript
|
|
90
|
+
// app.routes.ts
|
|
91
|
+
import { Routes } from '@angular/router';
|
|
92
|
+
import { AppLayoutComponent } from '@flusys/ng-layout';
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
149
|
+
### 3. Provide Layout Configuration
|
|
115
150
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
:
|
|
133
|
-
:
|
|
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
|
-
|
|
175
|
+
---
|
|
137
176
|
|
|
138
|
-
|
|
177
|
+
## Layout Configuration
|
|
139
178
|
|
|
140
|
-
###
|
|
179
|
+
### LayoutConfig
|
|
141
180
|
|
|
142
|
-
|
|
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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
151
|
-
|
|
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
|
-
|
|
195
|
+
/** Scale factor (12-16) */
|
|
196
|
+
scale?: number;
|
|
158
197
|
|
|
159
|
-
|
|
198
|
+
/** Menu theme */
|
|
199
|
+
menuTheme?: 'light' | 'dark' | 'colored';
|
|
160
200
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
+
#### Static Mode (Default)
|
|
177
209
|
|
|
178
|
-
|
|
210
|
+
Sidebar is always visible on desktop. Collapses to icons on tablet.
|
|
179
211
|
|
|
180
212
|
```typescript
|
|
181
|
-
|
|
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
|
-
|
|
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
|
-
|
|
218
|
+
Sidebar slides over content. Click outside to close.
|
|
201
219
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
224
|
+
#### Topbar Mode (v4.0.1+)
|
|
215
225
|
|
|
216
|
-
|
|
226
|
+
Desktop renders horizontal navigation bar. Mobile falls back to vertical sidebar.
|
|
217
227
|
|
|
218
228
|
```typescript
|
|
219
|
-
|
|
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
|
-
|
|
232
|
+
Submenu items appear on hover in topbar mode with automatic positioning.
|
|
227
233
|
|
|
228
|
-
|
|
234
|
+
### Themes & Colors
|
|
229
235
|
|
|
230
|
-
|
|
236
|
+
Built-in themes (PrimeNG):
|
|
231
237
|
|
|
232
|
-
|
|
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
|
-
|
|
249
|
+
---
|
|
235
250
|
|
|
236
|
-
|
|
251
|
+
## Injection Tokens
|
|
237
252
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
248
|
-
staticMenuDesktopInactive?: boolean;
|
|
249
|
-
overlayMenuActive?: boolean;
|
|
250
|
-
staticMenuMobileActive?: boolean;
|
|
251
|
-
menuHoverActive?: boolean;
|
|
252
|
-
}
|
|
262
|
+
---
|
|
253
263
|
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
316
|
+
## LayoutState
|
|
337
317
|
|
|
338
|
-
|
|
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
|
-
|
|
325
|
+
/** Overlay sidebar open */
|
|
326
|
+
overlayMenuActive: boolean;
|
|
347
327
|
|
|
348
|
-
|
|
328
|
+
/** Desktop sidebar collapsed */
|
|
329
|
+
staticMenuDesktopInactive: boolean;
|
|
349
330
|
|
|
350
|
-
|
|
331
|
+
/** Right-click menu visible */
|
|
332
|
+
menuHoverActive: boolean;
|
|
351
333
|
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
361
|
-
|
|
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
|
-
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Components
|
|
345
|
+
|
|
346
|
+
### AppLayoutComponent
|
|
371
347
|
|
|
372
|
-
|
|
348
|
+
Root layout component. Use this as the parent route component.
|
|
373
349
|
|
|
374
350
|
```typescript
|
|
375
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
{ provide: LAYOUT_AUTH_API, useExisting: AuthLayoutApiAdapter },
|
|
382
|
-
]
|
|
353
|
+
// In routes:
|
|
354
|
+
{ path: '', component: AppLayoutComponent, children: [...] }
|
|
383
355
|
```
|
|
384
356
|
|
|
385
|
-
|
|
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
|
-
|
|
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
|
-
|
|
373
|
+
### AppSidebarComponent
|
|
392
374
|
|
|
393
|
-
|
|
375
|
+
Left navigation panel containing:
|
|
376
|
+
- `AppMenuComponent` (navigation items)
|
|
377
|
+
- `AppCompanyBranchSelectorComponent` (if company selection enabled)
|
|
394
378
|
|
|
395
|
-
|
|
396
|
-
2. `LayoutService.menu` / `apps` are computed signals that filter via utilities
|
|
397
|
-
3. Uses `evaluateLogicNode()` from ng-shared
|
|
379
|
+
### AppMenuComponent
|
|
398
380
|
|
|
399
|
-
|
|
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
|
-
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
###
|
|
395
|
+
### AppCompanyBranchSelectorComponent
|
|
419
396
|
|
|
420
|
-
|
|
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
|
-
###
|
|
399
|
+
### AppConfiguratorComponent
|
|
444
400
|
|
|
445
|
-
-
|
|
446
|
-
-
|
|
447
|
-
-
|
|
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
|
-
##
|
|
409
|
+
## Configuration Persistence
|
|
452
410
|
|
|
453
|
-
|
|
411
|
+
`LayoutPersistenceService` automatically saves and restores layout preferences to `localStorage`:
|
|
454
412
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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 {
|
|
425
|
+
import { LayoutPersistenceService } from '@flusys/ng-layout';
|
|
426
|
+
|
|
427
|
+
this.persistenceService.clear();
|
|
462
428
|
```
|
|
463
429
|
|
|
464
430
|
---
|
|
465
431
|
|
|
466
|
-
##
|
|
432
|
+
## Integration Tokens
|
|
467
433
|
|
|
468
|
-
###
|
|
434
|
+
### Auth Integration (from ng-auth)
|
|
469
435
|
|
|
470
436
|
```typescript
|
|
471
|
-
@
|
|
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
|
-
|
|
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
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
501
|
-
|
|
456
|
+
**`LAYOUT_AUTH_API`** interface:
|
|
502
457
|
```typescript
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
532
|
-
readonly isActive = this._isActive.asReadonly();
|
|
467
|
+
import { provideNotificationProviders } from '@flusys/ng-notification';
|
|
533
468
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
this._isActive.update(v => !v);
|
|
469
|
+
...provideNotificationProviders()
|
|
470
|
+
// Provides: LAYOUT_NOTIFICATION_BELL
|
|
537
471
|
```
|
|
538
472
|
|
|
539
|
-
###
|
|
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
|
-
|
|
475
|
+
```typescript
|
|
476
|
+
import { provideLocalization } from '@flusys/ng-localization';
|
|
549
477
|
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
##
|
|
484
|
+
## Troubleshooting
|
|
561
485
|
|
|
562
|
-
|
|
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
|
-
|
|
490
|
+
**Menu items don't highlight active route**
|
|
574
491
|
|
|
575
|
-
|
|
492
|
+
Add `routerLinkActiveOptions: { exact: true }` to leaf menu items and `{ exact: false }` to parent items:
|
|
576
493
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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
|
-
|
|
502
|
+
**Topbar submenus appear behind other elements**
|
|
592
503
|
|
|
593
|
-
|
|
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
|
-
|
|
506
|
+
**Theme not changing**
|
|
599
507
|
|
|
600
|
-
|
|
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
|
-
|
|
510
|
+
**`No provider for LAYOUT_AUTH_STATE`**
|
|
606
511
|
|
|
607
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
518
|
+
MIT © FLUSYS
|