@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 +51 -664
- package/assets/layout/_core.scss +19 -0
- package/assets/layout/_footer.scss +18 -1
- package/assets/layout/_forms.scss +12 -32
- package/assets/layout/_menu.scss +5 -5
- package/assets/layout/_responsive.scss +18 -0
- package/assets/layout/_rtl.scss +482 -0
- package/assets/layout/_topbar.scss +177 -124
- package/assets/layout/_topbar_nav.scss +345 -0
- package/assets/layout/_typography.scss +25 -30
- package/assets/layout/_utils.scss +12 -16
- package/assets/layout/layout.scss +3 -1
- package/assets/layout/variables/_common.scss +17 -17
- package/assets/layout/variables/_dark.scss +3 -3
- package/assets/layout/variables/_light.scss +3 -3
- package/assets/style.scss +3 -3
- package/fesm2022/flusys-ng-layout.mjs +1703 -1155
- package/fesm2022/flusys-ng-layout.mjs.map +1 -1
- package/package.json +9 -10
- package/types/flusys-ng-layout.d.ts +118 -132
package/README.md
CHANGED
|
@@ -1,714 +1,101 @@
|
|
|
1
|
-
# @flusys/ng-layout
|
|
1
|
+
# @flusys/ng-layout
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Application shell for the FLUSYS Angular platform — topbar, sidebar, menu, and signal-based layout state management.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/ng-layout)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
|
-
|
|
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
|
-
|
|
26
|
-
import { AppLayout } from '@flusys/ng-layout';
|
|
10
|
+
## Installation
|
|
27
11
|
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18
|
+
## 1. Set Up the Layout Route
|
|
134
19
|
|
|
135
|
-
|
|
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
|
-
|
|
23
|
+
// app.routes.ts
|
|
24
|
+
import { Routes } from '@angular/router';
|
|
25
|
+
import { AppLayout } from '@flusys/ng-layout';
|
|
146
26
|
|
|
147
|
-
const
|
|
27
|
+
export const routes: Routes = [
|
|
148
28
|
{
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
193
|
-
private readonly
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
496
|
-
```
|
|
67
|
+
## 3. Integration Tokens — Auth, Notifications, Language
|
|
497
68
|
|
|
498
|
-
|
|
69
|
+
Connect feature packages to the layout via injection tokens. Register them in `app.config.ts`.
|
|
499
70
|
|
|
500
71
|
```typescript
|
|
501
|
-
|
|
502
|
-
import {
|
|
503
|
-
import {
|
|
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
|
-
|
|
508
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
+
## 4. Clear Persisted Layout
|
|
622
90
|
|
|
623
|
-
Provide `LAYOUT_AUTH_STATE` in `app.config.ts`:
|
|
624
91
|
```typescript
|
|
625
|
-
{
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
### Theme Not Applying
|
|
92
|
+
import { LayoutPersistenceService } from '@flusys/ng-layout';
|
|
629
93
|
|
|
630
|
-
|
|
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
|
-
|
|
713
|
-
|
|
714
|
-
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT © FLUSYS
|