@cadriciel/layout-cerema 0.0.4

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.
Files changed (34) hide show
  1. package/fesm2022/cadriciel-layout-cerema.mjs +1576 -0
  2. package/fesm2022/cadriciel-layout-cerema.mjs.map +1 -0
  3. package/package.json +35 -0
  4. package/src/assets/layout/images/avatar.png +0 -0
  5. package/src/assets/layout/images/logo-cadriciel.png +0 -0
  6. package/src/assets/layout/images/logo-cerema.png +0 -0
  7. package/src/assets/layout/images/logo-cerema.svg +4 -0
  8. package/src/assets/layout/images/logo-dark.svg +3 -0
  9. package/src/assets/layout/images/logo-light.svg +3 -0
  10. package/src/lib/cerema-styles.scss +41 -0
  11. package/src/lib/cerema-tailwind.css +10 -0
  12. package/src/lib/styles/_breadcrumb.scss +18 -0
  13. package/src/lib/styles/_config.scss +8 -0
  14. package/src/lib/styles/_content.scss +20 -0
  15. package/src/lib/styles/_fonts.scss +32 -0
  16. package/src/lib/styles/_footer.scss +26 -0
  17. package/src/lib/styles/_main.scss +29 -0
  18. package/src/lib/styles/_responsive.scss +181 -0
  19. package/src/lib/styles/_sass_variables.scss +1 -0
  20. package/src/lib/styles/_sidebar.scss +3 -0
  21. package/src/lib/styles/_topbar.scss +230 -0
  22. package/src/lib/styles/_utils.scss +19 -0
  23. package/src/lib/styles/layout.scss +15 -0
  24. package/src/lib/styles/sidebar/_sidebar_slim.scss +146 -0
  25. package/src/lib/styles/sidebar/_sidebar_slim_plus.scss +158 -0
  26. package/src/lib/styles/sidebar/_sidebar_vertical.scss +132 -0
  27. package/src/lib/styles/theme/_dark.scss +10 -0
  28. package/src/lib/styles/theme/_light.scss +10 -0
  29. package/src/lib/styles/theme/_primary.scss +10 -0
  30. package/src/lib/styles/theme/_themes.scss +3 -0
  31. package/src/lib/styles/variables/_common.scss +15 -0
  32. package/src/lib/styles/variables/_dark.scss +3 -0
  33. package/src/lib/styles/variables/_light.scss +3 -0
  34. package/types/cadriciel-layout-cerema.d.ts +112 -0
@@ -0,0 +1,1576 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, inject, effect, ViewChild, Component, PLATFORM_ID, computed, Input, HostBinding, ElementRef } from '@angular/core';
3
+ import * as i2$1 from '@angular/common';
4
+ import { CommonModule, isPlatformBrowser } from '@angular/common';
5
+ import * as i2 from '@angular/router';
6
+ import { RouterModule, Router, NavigationEnd } from '@angular/router';
7
+ import { Subject, BehaviorSubject, filter as filter$1 } from 'rxjs';
8
+ import * as i3 from 'primeng/styleclass';
9
+ import { StyleClassModule } from 'primeng/styleclass';
10
+ import { LayoutService as LayoutService$1, UserService, MenuService } from '@cadriciel/core';
11
+ import * as i4$2 from 'primeng/ripple';
12
+ import { Ripple, RippleModule } from 'primeng/ripple';
13
+ import { InputText } from 'primeng/inputtext';
14
+ import * as i4 from 'primeng/button';
15
+ import { ButtonModule } from 'primeng/button';
16
+ import { IconField } from 'primeng/iconfield';
17
+ import { InputIcon } from 'primeng/inputicon';
18
+ import * as i2$2 from '@angular/forms';
19
+ import { FormsModule } from '@angular/forms';
20
+ import { updatePreset, updateSurfacePalette, $t } from '@primeng/themes';
21
+ import Aura from '@primeng/themes/aura';
22
+ import Lara from '@primeng/themes/lara';
23
+ import { PrimeNG } from 'primeng/config';
24
+ import * as i3$1 from 'primeng/selectbutton';
25
+ import { SelectButtonModule } from 'primeng/selectbutton';
26
+ import * as i4$1 from 'primeng/drawer';
27
+ import { DrawerModule } from 'primeng/drawer';
28
+ import { ToggleSwitchModule } from 'primeng/toggleswitch';
29
+ import * as i5 from 'primeng/radiobutton';
30
+ import { RadioButtonModule } from 'primeng/radiobutton';
31
+ import { filter } from 'rxjs/operators';
32
+ import { trigger, state, style, transition, animate } from '@angular/animations';
33
+ import * as i5$1 from 'primeng/tooltip';
34
+ import { TooltipModule } from 'primeng/tooltip';
35
+
36
+ class LayoutService extends LayoutService$1 {
37
+ menuSource = new Subject();
38
+ tabOpen = new Subject();
39
+ tabClose = new Subject();
40
+ tabs = [];
41
+ menuSource$ = this.menuSource.asObservable();
42
+ tabOpen$ = this.tabOpen.asObservable();
43
+ tabClose$ = this.tabClose.asObservable();
44
+ constructor() {
45
+ super();
46
+ }
47
+ onMenuStateChange(event) {
48
+ this.menuSource.next(event);
49
+ }
50
+ showProfileSidebar() {
51
+ this.layoutState.update((prev) => ({ ...prev, profileSidebarVisible: true }));
52
+ }
53
+ onTabOpen(value) {
54
+ this.tabOpen.next(value);
55
+ }
56
+ openTab(value) {
57
+ this.tabs = [...this.tabs, value];
58
+ }
59
+ onTabClose(value, index) {
60
+ this.tabClose.next({ tab: value, index: index });
61
+ }
62
+ closeTab(index) {
63
+ this.tabs.splice(index, 1);
64
+ this.tabs = [...this.tabs];
65
+ }
66
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
67
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LayoutService, providedIn: 'root' });
68
+ }
69
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LayoutService, decorators: [{
70
+ type: Injectable,
71
+ args: [{
72
+ providedIn: 'root'
73
+ }]
74
+ }], ctorParameters: () => [] });
75
+
76
+ class AppTopbar {
77
+ menu = [];
78
+ searchInput;
79
+ menuButton;
80
+ searchActive = false;
81
+ layoutService = inject(LayoutService);
82
+ userService = inject(UserService);
83
+ constructor() {
84
+ effect(() => {
85
+ console.log('AppTopbar: layoutConfig appName:', this.layoutService.layoutConfig().appName);
86
+ console.log('AppTopbar: layoutConfig logoUrl:', this.layoutService.layoutConfig().logoUrl);
87
+ console.log('AppTopbar: layoutConfig full:', this.layoutService.layoutConfig());
88
+ });
89
+ }
90
+ // Expose the signal directly for the template
91
+ currentUserSignal = this.userService.currentUser;
92
+ formatRoles(roles) {
93
+ if (!roles || roles.length === 0)
94
+ return 'Utilisateur';
95
+ return roles.join(', ');
96
+ }
97
+ get userInitials() {
98
+ const user = this.currentUserSignal();
99
+ if (!user)
100
+ return 'CK';
101
+ const fp = user.prenom ? user.prenom.charAt(0).toUpperCase() : '';
102
+ const fn = user.nom ? user.nom.charAt(0).toUpperCase() : '';
103
+ return `${fp}${fn}`;
104
+ }
105
+ onMenuButtonClick() {
106
+ this.layoutService.onMenuToggle();
107
+ }
108
+ activateSearch() {
109
+ this.searchActive = true;
110
+ setTimeout(() => {
111
+ this.searchInput.nativeElement.focus();
112
+ }, 100);
113
+ }
114
+ deactivateSearch() {
115
+ this.searchActive = false;
116
+ }
117
+ removeTab(event, item, index) {
118
+ this.layoutService.onTabClose(item, index);
119
+ event.preventDefault();
120
+ }
121
+ get layoutTheme() {
122
+ return this.layoutService.layoutConfig().layoutTheme;
123
+ }
124
+ set layoutTheme(value) {
125
+ this.layoutService.layoutConfig.update((state) => ({ ...state, layoutTheme: value }));
126
+ }
127
+ get logo() {
128
+ const path = 'assets/layout/images/logo-';
129
+ const logo = this.layoutService.isDarkTheme() || this.layoutService.layoutConfig().layoutTheme === 'primaryColor' ? 'light.svg' : 'dark.svg';
130
+ return path + logo;
131
+ }
132
+ get tabs() {
133
+ return this.layoutService.tabs;
134
+ }
135
+ onConfigButtonClick() {
136
+ this.layoutService.showConfigSidebar();
137
+ }
138
+ toggleConfigSidebar() {
139
+ let layoutState = this.layoutService.layoutState();
140
+ if (this.layoutService.isSidebarActive()) {
141
+ layoutState.overlayMenuActive = false;
142
+ layoutState.overlaySubmenuActive = false;
143
+ layoutState.staticMenuMobileActive = false;
144
+ layoutState.menuHoverActive = false;
145
+ layoutState.configSidebarVisible = false;
146
+ }
147
+ layoutState.configSidebarVisible = !layoutState.configSidebarVisible;
148
+ this.layoutService.layoutState.set(layoutState);
149
+ }
150
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppTopbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
151
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: AppTopbar, isStandalone: true, selector: "[app-topbar]", host: { classAttribute: "layout-topbar" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchinput"], descendants: true }, { propertyName: "menuButton", first: true, predicate: ["menubutton"], descendants: true }], ngImport: i0, template: `
152
+ <div class="layout-topbar">
153
+ <a class="app-logo" routerLink="/">
154
+ <div class="logo-container">
155
+ <img src="assets/layout/images/logo-cadriciel.png" alt="Cadriciel Logo" class="cerema-logo" />
156
+ <div class="separator" *ngIf="layoutService.layoutConfig().logoUrl"></div>
157
+ <img *ngIf="layoutService.layoutConfig().logoUrl" [src]="layoutService.layoutConfig().logoUrl" [alt]="layoutService.layoutConfig().logoAlt" class="app-logo-img" />
158
+ </div>
159
+ <div class="flex flex-col justify-center ml-2">
160
+ <span class="app-name leading-none">{{ layoutService.layoutConfig().appName || 'Cadriciel' }}</span>
161
+ <span class="text-sm text-surface-500 dark:text-surface-400 leading-none mt-1" *ngIf="layoutService.layoutConfig().appDescription">{{ layoutService.layoutConfig().appDescription }}</span>
162
+ </div>
163
+ </a>
164
+
165
+ <button #menubutton class="topbar-menubutton p-link" type="button" (click)="onMenuButtonClick()">
166
+ <span></span>
167
+ </button>
168
+
169
+ <ul class="topbar-menu">
170
+ <li *ngFor="let item of tabs; let i = index">
171
+ <a
172
+ [routerLink]="item.routerLink"
173
+ routerLinkActive="active-route"
174
+ [routerLinkActiveOptions]="item.routerLinkActiveOptions || { paths: 'exact', queryParams: 'ignored', fragment: 'ignored', matrixParams: 'ignored' }"
175
+ [fragment]="item.fragment"
176
+ [queryParamsHandling]="item.queryParamsHandling"
177
+ [preserveFragment]="item.preserveFragment!"
178
+ [skipLocationChange]="item.skipLocationChange!"
179
+ [replaceUrl]="item.replaceUrl!"
180
+ [state]="item.state"
181
+ [queryParams]="item.queryParams"
182
+ >
183
+ <span>{{ item.label }}</span>
184
+ </a>
185
+ <i class="pi pi-times" (click)="removeTab($event, item, i)"></i>
186
+ </li>
187
+ <li *ngIf="!tabs || tabs.length === 0" class="topbar-menu-empty">Use (cmd + click) on a menu item to open a tab</li>
188
+ </ul>
189
+
190
+ <div class="topbar-actions">
191
+ <p-button icon="pi pi-palette" rounded (onClick)="layoutService.showConfigSidebar()"></p-button>
192
+ <div class="topbar-search" [ngClass]="{ 'topbar-search-active': searchActive }">
193
+ <button pButton [rounded]="true" severity="secondary" type="button" icon="pi pi-search" (click)="activateSearch()"></button>
194
+
195
+ <div class="search-input-wrapper">
196
+ <p-icon-field>
197
+ <input #searchinput type="text" pInputText placeholder="Search" (blur)="deactivateSearch()" (keydown.escape)="deactivateSearch()" />
198
+ <p-inputicon class="pi pi-search" />
199
+ </p-icon-field>
200
+ </div>
201
+ </div>
202
+
203
+ <div class="topbar-profile">
204
+ <button class="topbar-profile-button" type="button" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveToClass="hidden" leaveActiveClass="animate-fadeout" [hideOnOutsideClick]="true">
205
+ @if (layoutService.layoutState().userAvatarUrl) {
206
+ <img alt="avatar" [src]="layoutService.layoutState().userAvatarUrl" />
207
+ } @else {
208
+ <div class="flex items-center justify-center w-8 h-8 rounded-full bg-primary text-primary-contrast font-bold text-sm shrink-0 mr-2">
209
+ {{ userInitials }}
210
+ </div>
211
+ }
212
+
213
+ <span class="profile-details">
214
+ <span class="profile-name">{{ currentUserSignal()?.prenom }} {{ currentUserSignal()?.nom }}</span>
215
+ <span class="profile-job">{{ formatRoles(currentUserSignal()?.roleActifList) }}</span>
216
+ </span>
217
+ <i class="pi pi-angle-down"></i>
218
+ </button>
219
+ <ul class="list-none p-4 m-0 rounded-border shadow hidden absolute bg-surface-0 dark:bg-surface-900 origin-top w-full sm:w-48 mt-2 right-0 top-auto">
220
+ <li>
221
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
222
+ <i class="pi pi-user !mr-4"></i>
223
+ <span class="hidden sm:inline">Profile</span>
224
+ </a>
225
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
226
+ <i class="pi pi-inbox !mr-4"></i>
227
+ <span class="hidden sm:inline">Inbox</span>
228
+ </a>
229
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
230
+ <i class="pi pi-cog !mr-4"></i>
231
+ <span class="hidden sm:inline">Settings</span>
232
+ </a>
233
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
234
+ <i class="pi pi-power-off !mr-4"></i>
235
+ <span class="hidden sm:inline">Sign Out</span>
236
+ </a>
237
+ </li>
238
+ </ul>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i3.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: Ripple, selector: "[pRipple]" }, { kind: "directive", type: InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i4.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: i4.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }] });
243
+ }
244
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppTopbar, decorators: [{
245
+ type: Component,
246
+ args: [{
247
+ selector: '[app-topbar]',
248
+ standalone: true,
249
+ imports: [RouterModule, CommonModule, StyleClassModule, FormsModule, Ripple, InputText, ButtonModule, IconField, InputIcon],
250
+ template: `
251
+ <div class="layout-topbar">
252
+ <a class="app-logo" routerLink="/">
253
+ <div class="logo-container">
254
+ <img src="assets/layout/images/logo-cadriciel.png" alt="Cadriciel Logo" class="cerema-logo" />
255
+ <div class="separator" *ngIf="layoutService.layoutConfig().logoUrl"></div>
256
+ <img *ngIf="layoutService.layoutConfig().logoUrl" [src]="layoutService.layoutConfig().logoUrl" [alt]="layoutService.layoutConfig().logoAlt" class="app-logo-img" />
257
+ </div>
258
+ <div class="flex flex-col justify-center ml-2">
259
+ <span class="app-name leading-none">{{ layoutService.layoutConfig().appName || 'Cadriciel' }}</span>
260
+ <span class="text-sm text-surface-500 dark:text-surface-400 leading-none mt-1" *ngIf="layoutService.layoutConfig().appDescription">{{ layoutService.layoutConfig().appDescription }}</span>
261
+ </div>
262
+ </a>
263
+
264
+ <button #menubutton class="topbar-menubutton p-link" type="button" (click)="onMenuButtonClick()">
265
+ <span></span>
266
+ </button>
267
+
268
+ <ul class="topbar-menu">
269
+ <li *ngFor="let item of tabs; let i = index">
270
+ <a
271
+ [routerLink]="item.routerLink"
272
+ routerLinkActive="active-route"
273
+ [routerLinkActiveOptions]="item.routerLinkActiveOptions || { paths: 'exact', queryParams: 'ignored', fragment: 'ignored', matrixParams: 'ignored' }"
274
+ [fragment]="item.fragment"
275
+ [queryParamsHandling]="item.queryParamsHandling"
276
+ [preserveFragment]="item.preserveFragment!"
277
+ [skipLocationChange]="item.skipLocationChange!"
278
+ [replaceUrl]="item.replaceUrl!"
279
+ [state]="item.state"
280
+ [queryParams]="item.queryParams"
281
+ >
282
+ <span>{{ item.label }}</span>
283
+ </a>
284
+ <i class="pi pi-times" (click)="removeTab($event, item, i)"></i>
285
+ </li>
286
+ <li *ngIf="!tabs || tabs.length === 0" class="topbar-menu-empty">Use (cmd + click) on a menu item to open a tab</li>
287
+ </ul>
288
+
289
+ <div class="topbar-actions">
290
+ <p-button icon="pi pi-palette" rounded (onClick)="layoutService.showConfigSidebar()"></p-button>
291
+ <div class="topbar-search" [ngClass]="{ 'topbar-search-active': searchActive }">
292
+ <button pButton [rounded]="true" severity="secondary" type="button" icon="pi pi-search" (click)="activateSearch()"></button>
293
+
294
+ <div class="search-input-wrapper">
295
+ <p-icon-field>
296
+ <input #searchinput type="text" pInputText placeholder="Search" (blur)="deactivateSearch()" (keydown.escape)="deactivateSearch()" />
297
+ <p-inputicon class="pi pi-search" />
298
+ </p-icon-field>
299
+ </div>
300
+ </div>
301
+
302
+ <div class="topbar-profile">
303
+ <button class="topbar-profile-button" type="button" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveToClass="hidden" leaveActiveClass="animate-fadeout" [hideOnOutsideClick]="true">
304
+ @if (layoutService.layoutState().userAvatarUrl) {
305
+ <img alt="avatar" [src]="layoutService.layoutState().userAvatarUrl" />
306
+ } @else {
307
+ <div class="flex items-center justify-center w-8 h-8 rounded-full bg-primary text-primary-contrast font-bold text-sm shrink-0 mr-2">
308
+ {{ userInitials }}
309
+ </div>
310
+ }
311
+
312
+ <span class="profile-details">
313
+ <span class="profile-name">{{ currentUserSignal()?.prenom }} {{ currentUserSignal()?.nom }}</span>
314
+ <span class="profile-job">{{ formatRoles(currentUserSignal()?.roleActifList) }}</span>
315
+ </span>
316
+ <i class="pi pi-angle-down"></i>
317
+ </button>
318
+ <ul class="list-none p-4 m-0 rounded-border shadow hidden absolute bg-surface-0 dark:bg-surface-900 origin-top w-full sm:w-48 mt-2 right-0 top-auto">
319
+ <li>
320
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
321
+ <i class="pi pi-user !mr-4"></i>
322
+ <span class="hidden sm:inline">Profile</span>
323
+ </a>
324
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
325
+ <i class="pi pi-inbox !mr-4"></i>
326
+ <span class="hidden sm:inline">Inbox</span>
327
+ </a>
328
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
329
+ <i class="pi pi-cog !mr-4"></i>
330
+ <span class="hidden sm:inline">Settings</span>
331
+ </a>
332
+ <a pRipple class="flex p-2 rounded-border items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer">
333
+ <i class="pi pi-power-off !mr-4"></i>
334
+ <span class="hidden sm:inline">Sign Out</span>
335
+ </a>
336
+ </li>
337
+ </ul>
338
+ </div>
339
+ </div>
340
+ </div>
341
+ `,
342
+ host: {
343
+ class: 'layout-topbar'
344
+ }
345
+ }]
346
+ }], ctorParameters: () => [], propDecorators: { searchInput: [{
347
+ type: ViewChild,
348
+ args: ['searchinput']
349
+ }], menuButton: [{
350
+ type: ViewChild,
351
+ args: ['menubutton']
352
+ }] } });
353
+
354
+ class AppFooter {
355
+ layoutService = inject(LayoutService);
356
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: AppFooter, isStandalone: true, selector: "[app-footer]", host: { classAttribute: "layout-footer" }, ngImport: i0, template: ` <div class="footer-start">
358
+ <img src="assets/layout/images/logo-dark.svg" alt="logo" />
359
+ <span class="app-name">Cerema</span>
360
+ </div>
361
+ <div class="footer-right">
362
+ <span>v{{ layoutService.layoutConfig().appVersion || '0.0.0' }}</span>
363
+ </div>`, isInline: true });
364
+ }
365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppFooter, decorators: [{
366
+ type: Component,
367
+ args: [{
368
+ standalone: true,
369
+ selector: '[app-footer]',
370
+ template: ` <div class="footer-start">
371
+ <img src="assets/layout/images/logo-dark.svg" alt="logo" />
372
+ <span class="app-name">Cerema</span>
373
+ </div>
374
+ <div class="footer-right">
375
+ <span>v{{ layoutService.layoutConfig().appVersion || '0.0.0' }}</span>
376
+ </div>`,
377
+ host: {
378
+ class: 'layout-footer'
379
+ }
380
+ }]
381
+ }] });
382
+
383
+ const presets = {
384
+ Aura,
385
+ Lara
386
+ };
387
+ class AppConfigurator {
388
+ simple = false;
389
+ router = inject(Router);
390
+ config = inject(PrimeNG);
391
+ layoutService = inject(LayoutService);
392
+ platformId = inject(PLATFORM_ID);
393
+ primeng = inject(PrimeNG);
394
+ presets = Object.keys(presets);
395
+ themeOptions = [
396
+ { name: 'Light', value: false },
397
+ { name: 'Dark', value: true }
398
+ ];
399
+ ngOnInit() {
400
+ if (isPlatformBrowser(this.platformId)) {
401
+ this.onPresetChange(this.layoutService.layoutConfig().preset);
402
+ }
403
+ }
404
+ surfaces = [
405
+ {
406
+ name: 'slate',
407
+ palette: {
408
+ 0: '#ffffff',
409
+ 50: '#f8fafc',
410
+ 100: '#f1f5f9',
411
+ 200: '#e2e8f0',
412
+ 300: '#cbd5e1',
413
+ 400: '#94a3b8',
414
+ 500: '#64748b',
415
+ 600: '#475569',
416
+ 700: '#334155',
417
+ 800: '#1e293b',
418
+ 900: '#0f172a',
419
+ 950: '#020617'
420
+ }
421
+ },
422
+ {
423
+ name: 'gray',
424
+ palette: {
425
+ 0: '#ffffff',
426
+ 50: '#f9fafb',
427
+ 100: '#f3f4f6',
428
+ 200: '#e5e7eb',
429
+ 300: '#d1d5db',
430
+ 400: '#9ca3af',
431
+ 500: '#6b7280',
432
+ 600: '#4b5563',
433
+ 700: '#374151',
434
+ 800: '#1f2937',
435
+ 900: '#111827',
436
+ 950: '#030712'
437
+ }
438
+ },
439
+ {
440
+ name: 'zinc',
441
+ palette: {
442
+ 0: '#ffffff',
443
+ 50: '#fafafa',
444
+ 100: '#f4f4f5',
445
+ 200: '#e4e4e7',
446
+ 300: '#d4d4d8',
447
+ 400: '#a1a1aa',
448
+ 500: '#71717a',
449
+ 600: '#52525b',
450
+ 700: '#3f3f46',
451
+ 800: '#27272a',
452
+ 900: '#18181b',
453
+ 950: '#09090b'
454
+ }
455
+ },
456
+ {
457
+ name: 'neutral',
458
+ palette: {
459
+ 0: '#ffffff',
460
+ 50: '#fafafa',
461
+ 100: '#f5f5f5',
462
+ 200: '#e5e5e5',
463
+ 300: '#d4d4d4',
464
+ 400: '#a3a3a3',
465
+ 500: '#737373',
466
+ 600: '#525252',
467
+ 700: '#404040',
468
+ 800: '#262626',
469
+ 900: '#171717',
470
+ 950: '#0a0a0a'
471
+ }
472
+ },
473
+ {
474
+ name: 'stone',
475
+ palette: {
476
+ 0: '#ffffff',
477
+ 50: '#fafaf9',
478
+ 100: '#f5f5f4',
479
+ 200: '#e7e5e4',
480
+ 300: '#d6d3d1',
481
+ 400: '#a8a29e',
482
+ 500: '#78716c',
483
+ 600: '#57534e',
484
+ 700: '#44403c',
485
+ 800: '#292524',
486
+ 900: '#1c1917',
487
+ 950: '#0c0a09'
488
+ }
489
+ },
490
+ {
491
+ name: 'soho',
492
+ palette: {
493
+ 0: '#ffffff',
494
+ 50: '#ececec',
495
+ 100: '#dedfdf',
496
+ 200: '#c4c4c6',
497
+ 300: '#adaeb0',
498
+ 400: '#97979b',
499
+ 500: '#7f8084',
500
+ 600: '#6a6b70',
501
+ 700: '#55565b',
502
+ 800: '#3f4046',
503
+ 900: '#2c2c34',
504
+ 950: '#16161d'
505
+ }
506
+ },
507
+ {
508
+ name: 'viva',
509
+ palette: {
510
+ 0: '#ffffff',
511
+ 50: '#f3f3f3',
512
+ 100: '#e7e7e8',
513
+ 200: '#cfd0d0',
514
+ 300: '#b7b8b9',
515
+ 400: '#9fa1a1',
516
+ 500: '#87898a',
517
+ 600: '#6e7173',
518
+ 700: '#565a5b',
519
+ 800: '#3e4244',
520
+ 900: '#262b2c',
521
+ 950: '#0e1315'
522
+ }
523
+ },
524
+ {
525
+ name: 'ocean',
526
+ palette: {
527
+ 0: '#ffffff',
528
+ 50: '#fbfcfc',
529
+ 100: '#F7F9F8',
530
+ 200: '#EFF3F2',
531
+ 300: '#DADEDD',
532
+ 400: '#B1B7B6',
533
+ 500: '#828787',
534
+ 600: '#5F7274',
535
+ 700: '#415B61',
536
+ 800: '#29444E',
537
+ 900: '#183240',
538
+ 950: '#0c1920'
539
+ }
540
+ }
541
+ ];
542
+ selectedPrimaryColor = computed(() => {
543
+ return this.layoutService.layoutConfig().primary;
544
+ }, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
545
+ selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "selectedSurfaceColor" }] : []));
546
+ selectedPreset = computed(() => this.layoutService.layoutConfig().preset, ...(ngDevMode ? [{ debugName: "selectedPreset" }] : []));
547
+ get menuMode() {
548
+ return this.layoutService.layoutConfig().menuMode;
549
+ }
550
+ set menuMode(val) {
551
+ this.layoutService.layoutConfig.update((state) => ({
552
+ ...state,
553
+ menuMode: val
554
+ }));
555
+ }
556
+ get visible() {
557
+ return this.layoutService.layoutState().configSidebarVisible;
558
+ }
559
+ set visible(val) {
560
+ this.layoutService.layoutState.update((state) => ({
561
+ ...state,
562
+ configSidebarVisible: val
563
+ }));
564
+ }
565
+ darkTheme = computed(() => this.layoutService.layoutConfig().darkTheme, ...(ngDevMode ? [{ debugName: "darkTheme" }] : []));
566
+ selectedSurface = computed(() => this.layoutService.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "selectedSurface" }] : []));
567
+ primaryColors = computed(() => {
568
+ const presetPalette = presets[this.layoutService.layoutConfig().preset].primitive;
569
+ const colors = ['emerald', 'green', 'lime', 'orange', 'amber', 'yellow', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose'];
570
+ const palettes = [{ name: 'noir', palette: {} }];
571
+ colors.forEach((color) => {
572
+ palettes.push({
573
+ name: color,
574
+ palette: presetPalette?.[color]
575
+ });
576
+ });
577
+ return palettes;
578
+ }, ...(ngDevMode ? [{ debugName: "primaryColors" }] : []));
579
+ layoutTheme = computed(() => {
580
+ return this.layoutService.layoutConfig().layoutTheme;
581
+ }, ...(ngDevMode ? [{ debugName: "layoutTheme" }] : []));
582
+ onLayoutThemeChange(theme) {
583
+ this.layoutService.layoutConfig.update((state) => ({
584
+ ...state,
585
+ layoutTheme: theme
586
+ }));
587
+ }
588
+ getPresetExt() {
589
+ const color = this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {};
590
+ if (color.name === 'noir') {
591
+ return {
592
+ semantic: {
593
+ primary: {
594
+ 50: '{surface.50}',
595
+ 100: '{surface.100}',
596
+ 200: '{surface.200}',
597
+ 300: '{surface.300}',
598
+ 400: '{surface.400}',
599
+ 500: '{surface.500}',
600
+ 600: '{surface.600}',
601
+ 700: '{surface.700}',
602
+ 800: '{surface.800}',
603
+ 900: '{surface.900}',
604
+ 950: '{surface.950}'
605
+ },
606
+ colorScheme: {
607
+ light: {
608
+ primary: {
609
+ color: '{primary.950}',
610
+ contrastColor: '#ffffff',
611
+ hoverColor: '{primary.800}',
612
+ activeColor: '{primary.700}'
613
+ },
614
+ highlight: {
615
+ background: '{primary.950}',
616
+ focusBackground: '{primary.700}',
617
+ color: '#ffffff',
618
+ focusColor: '#ffffff'
619
+ }
620
+ },
621
+ dark: {
622
+ primary: {
623
+ color: '{primary.50}',
624
+ contrastColor: '{primary.950}',
625
+ hoverColor: '{primary.200}',
626
+ activeColor: '{primary.300}'
627
+ },
628
+ highlight: {
629
+ background: '{primary.50}',
630
+ focusBackground: '{primary.300}',
631
+ color: '{primary.950}',
632
+ focusColor: '{primary.950}'
633
+ }
634
+ }
635
+ }
636
+ }
637
+ };
638
+ }
639
+ else {
640
+ return {
641
+ semantic: {
642
+ primary: color.palette,
643
+ colorScheme: {
644
+ light: {
645
+ primary: {
646
+ color: '{primary.500}',
647
+ contrastColor: '#ffffff',
648
+ hoverColor: '{primary.600}',
649
+ activeColor: '{primary.700}'
650
+ },
651
+ highlight: {
652
+ background: '{primary.50}',
653
+ focusBackground: '{primary.100}',
654
+ color: '{primary.700}',
655
+ focusColor: '{primary.800}'
656
+ }
657
+ },
658
+ dark: {
659
+ primary: {
660
+ color: '{primary.400}',
661
+ contrastColor: '{surface.900}',
662
+ hoverColor: '{primary.300}',
663
+ activeColor: '{primary.200}'
664
+ },
665
+ highlight: {
666
+ background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
667
+ focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
668
+ color: 'rgba(255,255,255,.87)',
669
+ focusColor: 'rgba(255,255,255,.87)'
670
+ }
671
+ }
672
+ }
673
+ }
674
+ };
675
+ }
676
+ }
677
+ updateColors(event, type, color) {
678
+ if (type === 'primary') {
679
+ this.layoutService.layoutConfig.update((state) => ({
680
+ ...state,
681
+ primary: color.name
682
+ }));
683
+ }
684
+ else if (type === 'surface') {
685
+ this.layoutService.layoutConfig.update((state) => ({
686
+ ...state,
687
+ surface: color.name
688
+ }));
689
+ }
690
+ this.applyTheme(type, color);
691
+ event.stopPropagation();
692
+ }
693
+ applyTheme(type, color) {
694
+ if (type === 'primary') {
695
+ updatePreset(this.getPresetExt());
696
+ }
697
+ else if (type === 'surface') {
698
+ updateSurfacePalette(color.palette);
699
+ }
700
+ }
701
+ onPresetChange(event) {
702
+ this.layoutService.layoutConfig.update((state) => ({
703
+ ...state,
704
+ preset: event
705
+ }));
706
+ const preset = presets[event];
707
+ const surfacePalette = this.surfaces.find((s) => s.name === this.selectedSurfaceColor())?.palette;
708
+ $t().preset(preset).preset(this.getPresetExt()).surfacePalette(surfacePalette).use({ useDefaultOptions: true });
709
+ }
710
+ toggleDarkMode() {
711
+ this.executeDarkModeToggle();
712
+ }
713
+ executeDarkModeToggle() {
714
+ this.layoutService.layoutConfig.update((state) => ({
715
+ ...state,
716
+ darkTheme: !state.darkTheme,
717
+ layoutTheme: state.layoutTheme === 'primaryColor' && !state.darkTheme ? 'colorScheme' : state.layoutTheme
718
+ }));
719
+ }
720
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppConfigurator, deps: [], target: i0.ɵɵFactoryTarget.Component });
721
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: AppConfigurator, isStandalone: true, selector: "app-configurator", inputs: { simple: "simple" }, ngImport: i0, template: `
722
+ <p-drawer [(visible)]="visible" (onHide)="layoutService.hideConfigSidebar()" position="right" [transitionOptions]="'.3s cubic-bezier(0, 0, 0.2, 1)'" styleClass="layout-config-sidebar w-80" header="Settings">
723
+ <div class="flex flex-col gap-4">
724
+ <div>
725
+ <span class="text-lg font-semibold">Primary</span>
726
+ <div class="pt-2 flex gap-2 flex-wrap">
727
+ @for (primaryColor of primaryColors(); track primaryColor.name) {
728
+ <button
729
+ type="button"
730
+ [title]="primaryColor.name"
731
+ (click)="updateColors($event, 'primary', primaryColor)"
732
+ [ngClass]="{
733
+ 'outline-primary': primaryColor.name === selectedPrimaryColor()
734
+ }"
735
+ class="cursor-pointer w-6 h-6 rounded-full flex flex-shrink-0 items-center justify-center p-0 outline-none outline-offset-1"
736
+ [style]="{
737
+ 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
738
+ }"
739
+ ></button>
740
+ }
741
+ </div>
742
+ </div>
743
+
744
+ <div>
745
+ <span class="text-lg font-semibold">Surface</span>
746
+ <div class="pt-2 flex gap-2 flex-wrap">
747
+ @for (surface of surfaces; track surface.name) {
748
+ <button
749
+ type="button"
750
+ [title]="surface.name"
751
+ (click)="updateColors($event, 'surface', surface)"
752
+ class="cursor-pointer w-6 h-6 rounded-full flex flex-shrink-0 items-center justify-center p-0 outline-none outline-offset-1"
753
+ [ngClass]="{
754
+ 'outline-primary': selectedSurface() ? selectedSurface() === surface.name : darkTheme() ? surface.name === 'zinc' : surface.name === 'slate'
755
+ }"
756
+ [style]="{
757
+ 'background-color': surface?.palette?.['500']
758
+ }"
759
+ ></button>
760
+ }
761
+ </div>
762
+ </div>
763
+
764
+ <div class="flex flex-col gap-2">
765
+ <span class="text-lg font-semibold">Presets</span>
766
+ <p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small"></p-selectbutton>
767
+ </div>
768
+
769
+ <!-- 'Color Scheme' Section -->
770
+ <div class="flex flex-col gap-2">
771
+ <span class="text-lg font-semibold">Color Scheme</span>
772
+ <p-selectbutton [ngModel]="darkTheme()" (ngModelChange)="toggleDarkMode()" [options]="themeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false"></p-selectbutton>
773
+ </div>
774
+
775
+ <div class="flex flex-col gap-2">
776
+ <span class="text-lg font-semibold">Menu Type</span>
777
+ <div class="flex flex-wrap flex-col gap-3">
778
+ <div class="flex">
779
+ <div class="flex items-center gap-2 w-1/2">
780
+ <p-radio-button name="menuMode" value="static" [(ngModel)]="menuMode"></p-radio-button>
781
+ <label for="static">Static</label>
782
+ </div>
783
+
784
+ <div class="flex items-center gap-2 w-1/2">
785
+ <p-radio-button name="menuMode" value="overlay" [(ngModel)]="menuMode"></p-radio-button>
786
+ <label for="overlay">Overlay</label>
787
+ </div>
788
+ </div>
789
+ <div class="flex">
790
+ <div class="flex items-center gap-2 w-1/2">
791
+ <p-radio-button name="menuMode" value="slim" [(ngModel)]="menuMode"></p-radio-button>
792
+ <label for="slim">Slim</label>
793
+ </div>
794
+ <div class="flex items-center gap-2 w-1/2">
795
+ <p-radio-button name="menuMode" value="slim-plus" [(ngModel)]="menuMode" inputId="slim-plus"></p-radio-button>
796
+ <label for="slim-plus">Slim+</label>
797
+ </div>
798
+ </div>
799
+ </div>
800
+ </div>
801
+
802
+ <div class="flex flex-col gap-2">
803
+ <span class="text-lg font-semibold">Layout Theme</span>
804
+ <div class="flex flex-col gap-4">
805
+ <div class="flex items-center gap-2">
806
+ <p-radio-button name="layoutTheme" value="colorScheme" [ngModel]="layoutTheme()" inputId="colorScheme" (ngModelChange)="onLayoutThemeChange('colorScheme')"></p-radio-button>
807
+ <label for="colorScheme">Color Scheme</label>
808
+ </div>
809
+ <div class="flex items-center gap-2">
810
+ <p-radio-button name="layoutTheme" value="primaryColor" [ngModel]="layoutTheme()" (ngModelChange)="onLayoutThemeChange('primaryColor')" inputId="primaryColor" [disabled]="layoutService.isDarkTheme()"></p-radio-button>
811
+ <label for="primaryColor">Primary Color (Light Only)</label>
812
+ </div>
813
+ </div>
814
+ </div>
815
+ </div>
816
+ </p-drawer>
817
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i3$1.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4$1.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i5.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "styleClass", "autofocus", "binary", "variant", "size"], outputs: ["onClick", "onFocus", "onBlur"] }] });
818
+ }
819
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppConfigurator, decorators: [{
820
+ type: Component,
821
+ args: [{
822
+ selector: 'app-configurator',
823
+ standalone: true,
824
+ imports: [CommonModule, FormsModule, SelectButtonModule, DrawerModule, ToggleSwitchModule, RadioButtonModule],
825
+ template: `
826
+ <p-drawer [(visible)]="visible" (onHide)="layoutService.hideConfigSidebar()" position="right" [transitionOptions]="'.3s cubic-bezier(0, 0, 0.2, 1)'" styleClass="layout-config-sidebar w-80" header="Settings">
827
+ <div class="flex flex-col gap-4">
828
+ <div>
829
+ <span class="text-lg font-semibold">Primary</span>
830
+ <div class="pt-2 flex gap-2 flex-wrap">
831
+ @for (primaryColor of primaryColors(); track primaryColor.name) {
832
+ <button
833
+ type="button"
834
+ [title]="primaryColor.name"
835
+ (click)="updateColors($event, 'primary', primaryColor)"
836
+ [ngClass]="{
837
+ 'outline-primary': primaryColor.name === selectedPrimaryColor()
838
+ }"
839
+ class="cursor-pointer w-6 h-6 rounded-full flex flex-shrink-0 items-center justify-center p-0 outline-none outline-offset-1"
840
+ [style]="{
841
+ 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
842
+ }"
843
+ ></button>
844
+ }
845
+ </div>
846
+ </div>
847
+
848
+ <div>
849
+ <span class="text-lg font-semibold">Surface</span>
850
+ <div class="pt-2 flex gap-2 flex-wrap">
851
+ @for (surface of surfaces; track surface.name) {
852
+ <button
853
+ type="button"
854
+ [title]="surface.name"
855
+ (click)="updateColors($event, 'surface', surface)"
856
+ class="cursor-pointer w-6 h-6 rounded-full flex flex-shrink-0 items-center justify-center p-0 outline-none outline-offset-1"
857
+ [ngClass]="{
858
+ 'outline-primary': selectedSurface() ? selectedSurface() === surface.name : darkTheme() ? surface.name === 'zinc' : surface.name === 'slate'
859
+ }"
860
+ [style]="{
861
+ 'background-color': surface?.palette?.['500']
862
+ }"
863
+ ></button>
864
+ }
865
+ </div>
866
+ </div>
867
+
868
+ <div class="flex flex-col gap-2">
869
+ <span class="text-lg font-semibold">Presets</span>
870
+ <p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small"></p-selectbutton>
871
+ </div>
872
+
873
+ <!-- 'Color Scheme' Section -->
874
+ <div class="flex flex-col gap-2">
875
+ <span class="text-lg font-semibold">Color Scheme</span>
876
+ <p-selectbutton [ngModel]="darkTheme()" (ngModelChange)="toggleDarkMode()" [options]="themeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false"></p-selectbutton>
877
+ </div>
878
+
879
+ <div class="flex flex-col gap-2">
880
+ <span class="text-lg font-semibold">Menu Type</span>
881
+ <div class="flex flex-wrap flex-col gap-3">
882
+ <div class="flex">
883
+ <div class="flex items-center gap-2 w-1/2">
884
+ <p-radio-button name="menuMode" value="static" [(ngModel)]="menuMode"></p-radio-button>
885
+ <label for="static">Static</label>
886
+ </div>
887
+
888
+ <div class="flex items-center gap-2 w-1/2">
889
+ <p-radio-button name="menuMode" value="overlay" [(ngModel)]="menuMode"></p-radio-button>
890
+ <label for="overlay">Overlay</label>
891
+ </div>
892
+ </div>
893
+ <div class="flex">
894
+ <div class="flex items-center gap-2 w-1/2">
895
+ <p-radio-button name="menuMode" value="slim" [(ngModel)]="menuMode"></p-radio-button>
896
+ <label for="slim">Slim</label>
897
+ </div>
898
+ <div class="flex items-center gap-2 w-1/2">
899
+ <p-radio-button name="menuMode" value="slim-plus" [(ngModel)]="menuMode" inputId="slim-plus"></p-radio-button>
900
+ <label for="slim-plus">Slim+</label>
901
+ </div>
902
+ </div>
903
+ </div>
904
+ </div>
905
+
906
+ <div class="flex flex-col gap-2">
907
+ <span class="text-lg font-semibold">Layout Theme</span>
908
+ <div class="flex flex-col gap-4">
909
+ <div class="flex items-center gap-2">
910
+ <p-radio-button name="layoutTheme" value="colorScheme" [ngModel]="layoutTheme()" inputId="colorScheme" (ngModelChange)="onLayoutThemeChange('colorScheme')"></p-radio-button>
911
+ <label for="colorScheme">Color Scheme</label>
912
+ </div>
913
+ <div class="flex items-center gap-2">
914
+ <p-radio-button name="layoutTheme" value="primaryColor" [ngModel]="layoutTheme()" (ngModelChange)="onLayoutThemeChange('primaryColor')" inputId="primaryColor" [disabled]="layoutService.isDarkTheme()"></p-radio-button>
915
+ <label for="primaryColor">Primary Color (Light Only)</label>
916
+ </div>
917
+ </div>
918
+ </div>
919
+ </div>
920
+ </p-drawer>
921
+ `
922
+ }]
923
+ }], propDecorators: { simple: [{
924
+ type: Input
925
+ }] } });
926
+
927
+ class AppBreadcrumb {
928
+ router;
929
+ _breadcrumbs$ = new BehaviorSubject([]);
930
+ breadcrumbs$ = this._breadcrumbs$.asObservable();
931
+ constructor(router) {
932
+ this.router = router;
933
+ this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event) => {
934
+ const root = this.router.routerState.snapshot.root;
935
+ const breadcrumbs = [];
936
+ this.addBreadcrumb(root, [], breadcrumbs);
937
+ this._breadcrumbs$.next(breadcrumbs);
938
+ });
939
+ }
940
+ addBreadcrumb(route, parentUrl, breadcrumbs) {
941
+ const routeUrl = parentUrl.concat(route.url.map((url) => url.path));
942
+ const breadcrumb = route.data['breadcrumb'];
943
+ const parentBreadcrumb = route.parent && route.parent.data ? route.parent.data['breadcrumb'] : null;
944
+ if (breadcrumb && breadcrumb !== parentBreadcrumb) {
945
+ breadcrumbs.push({
946
+ label: route.data['breadcrumb'],
947
+ url: '/' + routeUrl.join('/')
948
+ });
949
+ }
950
+ if (route.firstChild) {
951
+ this.addBreadcrumb(route.firstChild, routeUrl, breadcrumbs);
952
+ }
953
+ }
954
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppBreadcrumb, deps: [{ token: i2.Router }], target: i0.ɵɵFactoryTarget.Component });
955
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: AppBreadcrumb, isStandalone: true, selector: "[app-breadcrumb]", host: { classAttribute: "layout-breadcrumb" }, ngImport: i0, template: ` <ol>
956
+ <li>
957
+ <a [routerLink]="['/']">
958
+ <i class="pi pi-home"></i>
959
+ </a>
960
+ </li>
961
+ <li class="layout-breadcrumb-chevron">/</li>
962
+ <ng-template ngFor let-item let-last="last" [ngForOf]="breadcrumbs$ | async">
963
+ <li style="cursor: pointer;">{{ item.label }}</li>
964
+ <li *ngIf="!last" class="layout-breadcrumb-chevron">/</li>
965
+ </ng-template>
966
+ </ol>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i2$1.AsyncPipe, name: "async" }] });
967
+ }
968
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppBreadcrumb, decorators: [{
969
+ type: Component,
970
+ args: [{
971
+ selector: '[app-breadcrumb]',
972
+ standalone: true,
973
+ imports: [CommonModule, RouterModule],
974
+ template: ` <ol>
975
+ <li>
976
+ <a [routerLink]="['/']">
977
+ <i class="pi pi-home"></i>
978
+ </a>
979
+ </li>
980
+ <li class="layout-breadcrumb-chevron">/</li>
981
+ <ng-template ngFor let-item let-last="last" [ngForOf]="breadcrumbs$ | async">
982
+ <li style="cursor: pointer;">{{ item.label }}</li>
983
+ <li *ngIf="!last" class="layout-breadcrumb-chevron">/</li>
984
+ </ng-template>
985
+ </ol>`,
986
+ host: {
987
+ class: 'layout-breadcrumb'
988
+ }
989
+ }]
990
+ }], ctorParameters: () => [{ type: i2.Router }] });
991
+
992
+ class AppMenuitem {
993
+ layoutService;
994
+ router;
995
+ item;
996
+ index;
997
+ root;
998
+ parentKey;
999
+ submenu;
1000
+ get activeClass() {
1001
+ return this.active;
1002
+ }
1003
+ active = false;
1004
+ menuSourceSubscription;
1005
+ menuResetSubscription;
1006
+ key = '';
1007
+ get submenuAnimation() {
1008
+ if (this.layoutService.isDesktop() && (this.layoutService.isHorizontal() || this.layoutService.isSlim() || this.layoutService.isSlimPlus())) {
1009
+ return this.active ? 'visible' : 'hidden';
1010
+ }
1011
+ else
1012
+ return this.root ? 'expanded' : this.active ? 'expanded' : 'collapsed';
1013
+ }
1014
+ isSlim = computed(() => this.layoutService.isSlim(), ...(ngDevMode ? [{ debugName: "isSlim" }] : []));
1015
+ isSlimPlus = computed(() => this.layoutService.isSlimPlus(), ...(ngDevMode ? [{ debugName: "isSlimPlus" }] : []));
1016
+ isHorizontal = computed(() => this.layoutService.isHorizontal(), ...(ngDevMode ? [{ debugName: "isHorizontal" }] : []));
1017
+ get isDesktop() {
1018
+ return this.layoutService.isDesktop();
1019
+ }
1020
+ get isMobile() {
1021
+ return this.layoutService.isMobile();
1022
+ }
1023
+ constructor(layoutService, router) {
1024
+ this.layoutService = layoutService;
1025
+ this.router = router;
1026
+ this.menuSourceSubscription = this.layoutService.menuSource$.subscribe((value) => {
1027
+ Promise.resolve(null).then(() => {
1028
+ if (value.routeEvent) {
1029
+ this.active = value.key === this.key || value.key.startsWith(this.key + '-') ? true : false;
1030
+ }
1031
+ else {
1032
+ if (value.key !== this.key && !value.key.startsWith(this.key + '-')) {
1033
+ this.active = false;
1034
+ }
1035
+ }
1036
+ });
1037
+ });
1038
+ this.menuResetSubscription = this.layoutService.resetSource$.subscribe(() => {
1039
+ this.active = false;
1040
+ });
1041
+ this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((params) => {
1042
+ if (this.isSlimPlus() || this.isSlim() || this.isHorizontal()) {
1043
+ this.active = false;
1044
+ }
1045
+ else {
1046
+ if (this.item.routerLink) {
1047
+ this.updateActiveStateFromRoute();
1048
+ }
1049
+ }
1050
+ });
1051
+ }
1052
+ ngOnInit() {
1053
+ this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index);
1054
+ if (!(this.isSlimPlus() || this.isSlim() || this.isHorizontal()) && this.item.routerLink) {
1055
+ this.updateActiveStateFromRoute();
1056
+ }
1057
+ }
1058
+ ngAfterViewChecked() {
1059
+ if (this.root && this.active && this.isDesktop && (this.isHorizontal() || this.isSlim() || this.isSlimPlus())) {
1060
+ this.calculatePosition(this.submenu?.nativeElement, this.submenu?.nativeElement.parentElement);
1061
+ }
1062
+ }
1063
+ updateActiveStateFromRoute() {
1064
+ let activeRoute = this.router.isActive(this.item.routerLink[0], {
1065
+ paths: 'exact',
1066
+ queryParams: 'ignored',
1067
+ matrixParams: 'ignored',
1068
+ fragment: 'ignored'
1069
+ });
1070
+ if (activeRoute) {
1071
+ this.layoutService.onMenuStateChange({
1072
+ key: this.key,
1073
+ routeEvent: true
1074
+ });
1075
+ }
1076
+ }
1077
+ onSubmenuAnimated(event) {
1078
+ if (event.toState === 'visible' && this.isDesktop && (this.isHorizontal() || this.isSlim() || this.isSlimPlus())) {
1079
+ const el = event.element;
1080
+ const elParent = el.parentElement;
1081
+ this.calculatePosition(el, elParent);
1082
+ }
1083
+ }
1084
+ calculatePosition(overlay, target) {
1085
+ if (overlay) {
1086
+ const { left, top } = target.getBoundingClientRect();
1087
+ const vHeight = window.innerHeight;
1088
+ const oHeight = overlay.offsetHeight;
1089
+ const topbarEl = document.querySelector('.layout-topbar');
1090
+ const topbarHeight = topbarEl?.offsetHeight || 0;
1091
+ // reset
1092
+ overlay.style.top = '';
1093
+ overlay.style.left = '';
1094
+ if (this.layoutService.isSlim() || this.layoutService.isSlimPlus()) {
1095
+ const topOffset = top - topbarHeight;
1096
+ const height = topOffset + oHeight + topbarHeight;
1097
+ overlay.style.top = vHeight < height ? `${topOffset - (height - vHeight)}px` : `${topOffset}px`;
1098
+ }
1099
+ }
1100
+ }
1101
+ itemClick(event) {
1102
+ // avoid processing disabled items
1103
+ if (this.item.disabled) {
1104
+ event.preventDefault();
1105
+ return;
1106
+ }
1107
+ // navigate with hover
1108
+ if ((this.root && this.isSlim()) || this.isHorizontal() || this.isSlimPlus()) {
1109
+ this.layoutService.layoutState.update((val) => ({
1110
+ ...val,
1111
+ menuHoverActive: !val.menuHoverActive
1112
+ }));
1113
+ }
1114
+ // execute command
1115
+ if (this.item.command) {
1116
+ this.item.command({ originalEvent: event, item: this.item });
1117
+ }
1118
+ // add tab
1119
+ if ((event.metaKey || event.ctrlKey) && this.item.routerLink && (!this.item.data || !this.item.data.fullPage)) {
1120
+ this.layoutService.onTabOpen(this.item);
1121
+ event.preventDefault();
1122
+ }
1123
+ // toggle active state
1124
+ if (this.item.items) {
1125
+ this.active = !this.active;
1126
+ if (this.root && this.active && (this.isSlim() || this.isHorizontal() || this.isSlimPlus())) {
1127
+ this.layoutService.onOverlaySubmenuOpen();
1128
+ }
1129
+ }
1130
+ else {
1131
+ if (this.layoutService.isMobile()) {
1132
+ this.layoutService.layoutState.update((val) => ({
1133
+ ...val,
1134
+ staticMenuMobileActive: false
1135
+ }));
1136
+ }
1137
+ if (this.isSlim() || this.isHorizontal() || this.isSlimPlus()) {
1138
+ this.layoutService.reset();
1139
+ this.layoutService.layoutState.update((val) => ({
1140
+ ...val,
1141
+ menuHoverActive: false
1142
+ }));
1143
+ }
1144
+ }
1145
+ this.layoutService.onMenuStateChange({ key: this.key });
1146
+ }
1147
+ onMouseEnter() {
1148
+ // activate item on hover
1149
+ if (this.root && (this.isSlim() || this.isHorizontal() || this.isSlimPlus()) && this.layoutService.isDesktop()) {
1150
+ if (this.layoutService.layoutState().menuHoverActive) {
1151
+ this.active = true;
1152
+ this.layoutService.onMenuStateChange({ key: this.key });
1153
+ }
1154
+ }
1155
+ }
1156
+ ngOnDestroy() {
1157
+ if (this.menuSourceSubscription) {
1158
+ this.menuSourceSubscription.unsubscribe();
1159
+ }
1160
+ if (this.menuResetSubscription) {
1161
+ this.menuResetSubscription.unsubscribe();
1162
+ }
1163
+ }
1164
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppMenuitem, deps: [{ token: LayoutService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component });
1165
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: AppMenuitem, isStandalone: true, selector: "[app-menuitem]", inputs: { item: "item", index: "index", root: "root", parentKey: "parentKey" }, host: { properties: { "class.layout-root-menuitem": "this.root", "class.active-menuitem": "this.activeClass" } }, viewQueries: [{ propertyName: "submenu", first: true, predicate: ["submenu"], descendants: true }], ngImport: i0, template: `
1166
+ <ng-container>
1167
+ <div *ngIf="root && item.visible !== false" class="layout-menuitem-root-text">
1168
+ {{ item.label }}
1169
+ </div>
1170
+ <a
1171
+ *ngIf="(!item.routerLink || item.items) && item.visible !== false"
1172
+ [attr.href]="item.url"
1173
+ (click)="itemClick($event)"
1174
+ (mouseenter)="onMouseEnter()"
1175
+ [ngClass]="item.class"
1176
+ [attr.target]="item.target"
1177
+ tabindex="0"
1178
+ pRipple
1179
+ [pTooltip]="item.label"
1180
+ [tooltipDisabled]="!(isSlim() && root && !active)"
1181
+ >
1182
+ <i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
1183
+ <span class="layout-menuitem-text">{{ item.label }}</span>
1184
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
1185
+ </a>
1186
+ <a
1187
+ *ngIf="item.routerLink && !item.items && item.visible !== false"
1188
+ (click)="itemClick($event)"
1189
+ (mouseenter)="onMouseEnter()"
1190
+ [ngClass]="item.class"
1191
+ [routerLink]="item.routerLink"
1192
+ routerLinkActive="active-route"
1193
+ [routerLinkActiveOptions]="
1194
+ item.routerLinkActiveOptions || {
1195
+ paths: 'exact',
1196
+ queryParams: 'ignored',
1197
+ matrixParams: 'ignored',
1198
+ fragment: 'ignored'
1199
+ }
1200
+ "
1201
+ [fragment]="item.fragment"
1202
+ [queryParamsHandling]="item.queryParamsHandling"
1203
+ [preserveFragment]="item.preserveFragment"
1204
+ [skipLocationChange]="item.skipLocationChange"
1205
+ [replaceUrl]="item.replaceUrl"
1206
+ [state]="item.state"
1207
+ [queryParams]="item.queryParams"
1208
+ [attr.target]="item.target"
1209
+ tabindex="0"
1210
+ pRipple
1211
+ [pTooltip]="item.label"
1212
+ [tooltipDisabled]="!(isSlim() && root)"
1213
+ >
1214
+ <i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
1215
+ <span class="layout-menuitem-text">{{ item.label }}</span>
1216
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
1217
+ </a>
1218
+
1219
+ <ul #submenu *ngIf="item.items && item.visible !== false" [@children]="submenuAnimation" (@children.done)="onSubmenuAnimated($event)">
1220
+ <ng-template ngFor let-child let-i="index" [ngForOf]="item.items">
1221
+ <li app-menuitem [item]="child" [index]="i" [parentKey]="key" [class]="child['badgeClass']"></li>
1222
+ </ng-template>
1223
+ </ul>
1224
+ </ng-container>
1225
+ `, isInline: true, dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "root", "parentKey"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i4$2.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], animations: [
1226
+ trigger('children', [
1227
+ state('collapsed', style({
1228
+ height: '0'
1229
+ })),
1230
+ state('expanded', style({
1231
+ height: '*'
1232
+ })),
1233
+ state('hidden', style({
1234
+ display: 'none'
1235
+ })),
1236
+ state('visible', style({
1237
+ display: 'block'
1238
+ })),
1239
+ transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
1240
+ ])
1241
+ ] });
1242
+ }
1243
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppMenuitem, decorators: [{
1244
+ type: Component,
1245
+ args: [{
1246
+ // eslint-disable-next-line @angular-eslint/component-selector
1247
+ selector: '[app-menuitem]',
1248
+ imports: [CommonModule, RouterModule, RippleModule, TooltipModule],
1249
+ template: `
1250
+ <ng-container>
1251
+ <div *ngIf="root && item.visible !== false" class="layout-menuitem-root-text">
1252
+ {{ item.label }}
1253
+ </div>
1254
+ <a
1255
+ *ngIf="(!item.routerLink || item.items) && item.visible !== false"
1256
+ [attr.href]="item.url"
1257
+ (click)="itemClick($event)"
1258
+ (mouseenter)="onMouseEnter()"
1259
+ [ngClass]="item.class"
1260
+ [attr.target]="item.target"
1261
+ tabindex="0"
1262
+ pRipple
1263
+ [pTooltip]="item.label"
1264
+ [tooltipDisabled]="!(isSlim() && root && !active)"
1265
+ >
1266
+ <i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
1267
+ <span class="layout-menuitem-text">{{ item.label }}</span>
1268
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
1269
+ </a>
1270
+ <a
1271
+ *ngIf="item.routerLink && !item.items && item.visible !== false"
1272
+ (click)="itemClick($event)"
1273
+ (mouseenter)="onMouseEnter()"
1274
+ [ngClass]="item.class"
1275
+ [routerLink]="item.routerLink"
1276
+ routerLinkActive="active-route"
1277
+ [routerLinkActiveOptions]="
1278
+ item.routerLinkActiveOptions || {
1279
+ paths: 'exact',
1280
+ queryParams: 'ignored',
1281
+ matrixParams: 'ignored',
1282
+ fragment: 'ignored'
1283
+ }
1284
+ "
1285
+ [fragment]="item.fragment"
1286
+ [queryParamsHandling]="item.queryParamsHandling"
1287
+ [preserveFragment]="item.preserveFragment"
1288
+ [skipLocationChange]="item.skipLocationChange"
1289
+ [replaceUrl]="item.replaceUrl"
1290
+ [state]="item.state"
1291
+ [queryParams]="item.queryParams"
1292
+ [attr.target]="item.target"
1293
+ tabindex="0"
1294
+ pRipple
1295
+ [pTooltip]="item.label"
1296
+ [tooltipDisabled]="!(isSlim() && root)"
1297
+ >
1298
+ <i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
1299
+ <span class="layout-menuitem-text">{{ item.label }}</span>
1300
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
1301
+ </a>
1302
+
1303
+ <ul #submenu *ngIf="item.items && item.visible !== false" [@children]="submenuAnimation" (@children.done)="onSubmenuAnimated($event)">
1304
+ <ng-template ngFor let-child let-i="index" [ngForOf]="item.items">
1305
+ <li app-menuitem [item]="child" [index]="i" [parentKey]="key" [class]="child['badgeClass']"></li>
1306
+ </ng-template>
1307
+ </ul>
1308
+ </ng-container>
1309
+ `,
1310
+ animations: [
1311
+ trigger('children', [
1312
+ state('collapsed', style({
1313
+ height: '0'
1314
+ })),
1315
+ state('expanded', style({
1316
+ height: '*'
1317
+ })),
1318
+ state('hidden', style({
1319
+ display: 'none'
1320
+ })),
1321
+ state('visible', style({
1322
+ display: 'block'
1323
+ })),
1324
+ transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
1325
+ ])
1326
+ ],
1327
+ }]
1328
+ }], ctorParameters: () => [{ type: LayoutService }, { type: i2.Router }], propDecorators: { item: [{
1329
+ type: Input
1330
+ }], index: [{
1331
+ type: Input
1332
+ }], root: [{
1333
+ type: Input
1334
+ }, {
1335
+ type: HostBinding,
1336
+ args: ['class.layout-root-menuitem']
1337
+ }], parentKey: [{
1338
+ type: Input
1339
+ }], submenu: [{
1340
+ type: ViewChild,
1341
+ args: ['submenu']
1342
+ }], activeClass: [{
1343
+ type: HostBinding,
1344
+ args: ['class.active-menuitem']
1345
+ }] } });
1346
+
1347
+ class AppMenu {
1348
+ el = inject(ElementRef);
1349
+ menuService = inject(MenuService);
1350
+ menuContainer;
1351
+ model = [];
1352
+ ngOnInit() {
1353
+ this.model = this.menuService.getMenu();
1354
+ }
1355
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
1356
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: AppMenu, isStandalone: true, selector: "[app-menu]", viewQueries: [{ propertyName: "menuContainer", first: true, predicate: ["menuContainer"], descendants: true }], ngImport: i0, template: `<div class="layout-menu-container" #menuContainer>
1357
+ <ul class="layout-menu">
1358
+ <ng-container *ngFor="let item of model; let i = index">
1359
+ <li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
1360
+ <li *ngIf="item.separator" class="menu-separator"></li>
1361
+ </ng-container>
1362
+ </ul>
1363
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "root", "parentKey"] }, { kind: "ngmodule", type: RouterModule }] });
1364
+ }
1365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppMenu, decorators: [{
1366
+ type: Component,
1367
+ args: [{
1368
+ selector: '[app-menu]',
1369
+ standalone: true,
1370
+ imports: [CommonModule, AppMenuitem, RouterModule],
1371
+ template: `<div class="layout-menu-container" #menuContainer>
1372
+ <ul class="layout-menu">
1373
+ <ng-container *ngFor="let item of model; let i = index">
1374
+ <li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
1375
+ <li *ngIf="item.separator" class="menu-separator"></li>
1376
+ </ng-container>
1377
+ </ul>
1378
+ </div>`
1379
+ }]
1380
+ }], propDecorators: { menuContainer: [{
1381
+ type: ViewChild,
1382
+ args: ['menuContainer']
1383
+ }] } });
1384
+
1385
+ class AppSidebar {
1386
+ el = inject(ElementRef);
1387
+ appMenu;
1388
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
1389
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: AppSidebar, isStandalone: true, selector: "[app-sidebar]", viewQueries: [{ propertyName: "appMenu", first: true, predicate: AppMenu, descendants: true }], ngImport: i0, template: `<div app-menu class="layout-sidebar"></div>`, isInline: true, dependencies: [{ kind: "component", type: AppMenu, selector: "[app-menu]" }] });
1390
+ }
1391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppSidebar, decorators: [{
1392
+ type: Component,
1393
+ args: [{
1394
+ selector: '[app-sidebar]',
1395
+ standalone: true,
1396
+ imports: [AppMenu],
1397
+ template: `<div app-menu class="layout-sidebar"></div>`,
1398
+ }]
1399
+ }], propDecorators: { appMenu: [{
1400
+ type: ViewChild,
1401
+ args: [AppMenu]
1402
+ }] } });
1403
+
1404
+ class AppLayout {
1405
+ layoutService;
1406
+ renderer;
1407
+ router;
1408
+ overlayMenuOpenSubscription;
1409
+ tabOpenSubscription;
1410
+ tabCloseSubscription;
1411
+ menuOutsideClickListener;
1412
+ menuScrollListener;
1413
+ appSidebar;
1414
+ appTopbar;
1415
+ constructor(layoutService, renderer, router) {
1416
+ this.layoutService = layoutService;
1417
+ this.renderer = renderer;
1418
+ this.router = router;
1419
+ this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => {
1420
+ if (!this.menuOutsideClickListener) {
1421
+ this.menuOutsideClickListener = this.renderer.listen('document', 'click', (event) => {
1422
+ const isOutsideClicked = !(this.appSidebar.appMenu.el.nativeElement.isSameNode(event.target) ||
1423
+ this.appSidebar.appMenu.el.nativeElement.contains(event.target) ||
1424
+ this.appTopbar.menuButton.nativeElement.isSameNode(event.target) ||
1425
+ this.appTopbar.menuButton.nativeElement.contains(event.target));
1426
+ if (isOutsideClicked) {
1427
+ this.hideMenu();
1428
+ }
1429
+ });
1430
+ }
1431
+ if ((this.layoutService.isSlim() || this.layoutService.isSlimPlus()) && !this.menuScrollListener) {
1432
+ this.menuScrollListener = this.renderer.listen(this.appSidebar.appMenu.menuContainer.nativeElement, 'scroll', (event) => {
1433
+ if (this.layoutService.isDesktop()) {
1434
+ this.hideMenu();
1435
+ }
1436
+ });
1437
+ }
1438
+ if (this.layoutService.layoutState().staticMenuMobileActive) {
1439
+ this.blockBodyScroll();
1440
+ }
1441
+ });
1442
+ this.router.events.pipe(filter$1((event) => event instanceof NavigationEnd)).subscribe(() => {
1443
+ this.hideMenu();
1444
+ });
1445
+ this.tabOpenSubscription = this.layoutService.tabOpen$.subscribe((tab) => {
1446
+ this.router.navigate(tab.routerLink);
1447
+ this.layoutService.openTab(tab);
1448
+ });
1449
+ this.tabCloseSubscription = this.layoutService.tabClose$.subscribe((event) => {
1450
+ if (this.router.isActive(event.tab.routerLink[0], { paths: 'subset', queryParams: 'subset', fragment: 'ignored', matrixParams: 'ignored' })) {
1451
+ const tabs = this.layoutService.tabs;
1452
+ if (tabs.length > 1) {
1453
+ if (event.index === tabs.length - 1)
1454
+ this.router.navigate(tabs[tabs.length - 2].routerLink);
1455
+ else
1456
+ this.router.navigate(tabs[event.index + 1].routerLink);
1457
+ }
1458
+ else {
1459
+ this.router.navigate(['/']);
1460
+ }
1461
+ }
1462
+ this.layoutService.closeTab(event.index);
1463
+ });
1464
+ }
1465
+ blockBodyScroll() {
1466
+ if (document.body.classList) {
1467
+ document.body.classList.add('blocked-scroll');
1468
+ }
1469
+ else {
1470
+ document.body.className += ' blocked-scroll';
1471
+ }
1472
+ }
1473
+ unblockBodyScroll() {
1474
+ if (document.body.classList) {
1475
+ document.body.classList.remove('blocked-scroll');
1476
+ }
1477
+ else {
1478
+ document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
1479
+ }
1480
+ }
1481
+ hideMenu() {
1482
+ this.layoutService.layoutState.update((prev) => ({ ...prev, overlayMenuActive: false, staticMenuMobileActive: false, menuHoverActive: false }));
1483
+ this.layoutService.reset();
1484
+ if (this.menuOutsideClickListener) {
1485
+ this.menuOutsideClickListener();
1486
+ this.menuOutsideClickListener = null;
1487
+ }
1488
+ if (this.menuScrollListener) {
1489
+ this.menuScrollListener();
1490
+ this.menuScrollListener = null;
1491
+ }
1492
+ this.unblockBodyScroll();
1493
+ }
1494
+ get containerClass() {
1495
+ return {
1496
+ 'layout-slim': this.layoutService.layoutConfig().menuMode === 'slim',
1497
+ 'layout-slim-plus': this.layoutService.layoutConfig().menuMode === 'slim-plus',
1498
+ 'layout-static': this.layoutService.layoutConfig().menuMode === 'static',
1499
+ 'layout-overlay': this.layoutService.layoutConfig().menuMode === 'overlay',
1500
+ 'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive,
1501
+ 'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive,
1502
+ 'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static',
1503
+ 'layout-light': this.layoutService.layoutConfig().layoutTheme === 'colorScheme' && !this.layoutService.isDarkTheme(),
1504
+ 'layout-dark': this.layoutService.layoutConfig().layoutTheme === 'colorScheme' && this.layoutService.isDarkTheme(),
1505
+ 'layout-primary': !this.layoutService.isDarkTheme() && this.layoutService.layoutConfig().layoutTheme === 'primaryColor'
1506
+ };
1507
+ }
1508
+ ngOnDestroy() {
1509
+ if (this.overlayMenuOpenSubscription) {
1510
+ this.overlayMenuOpenSubscription.unsubscribe();
1511
+ }
1512
+ if (this.menuOutsideClickListener) {
1513
+ this.menuOutsideClickListener();
1514
+ }
1515
+ if (this.tabOpenSubscription) {
1516
+ this.tabOpenSubscription.unsubscribe();
1517
+ }
1518
+ if (this.tabCloseSubscription) {
1519
+ this.tabCloseSubscription.unsubscribe();
1520
+ }
1521
+ }
1522
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppLayout, deps: [{ token: LayoutService }, { token: i0.Renderer2 }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component });
1523
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: AppLayout, isStandalone: true, selector: "app-layout", viewQueries: [{ propertyName: "appSidebar", first: true, predicate: AppSidebar, descendants: true }, { propertyName: "appTopbar", first: true, predicate: AppTopbar, descendants: true }], ngImport: i0, template: `
1524
+ <div class="layout-container" [ngClass]="containerClass">
1525
+ <div app-topbar></div>
1526
+ <div app-sidebar></div>
1527
+ <div class="layout-content-wrapper">
1528
+ <div class="layout-content">
1529
+ <div class="layout-content-inner">
1530
+ <nav app-breadcrumb></nav>
1531
+ <router-outlet></router-outlet>
1532
+ <div app-footer></div>
1533
+ </div>
1534
+ </div>
1535
+ </div>
1536
+ </div>
1537
+ <app-configurator />
1538
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppTopbar, selector: "[app-topbar]" }, { kind: "component", type: AppSidebar, selector: "[app-sidebar]" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppFooter, selector: "[app-footer]" }, { kind: "component", type: AppConfigurator, selector: "app-configurator", inputs: ["simple"] }, { kind: "component", type: AppBreadcrumb, selector: "[app-breadcrumb]" }] });
1539
+ }
1540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AppLayout, decorators: [{
1541
+ type: Component,
1542
+ args: [{
1543
+ selector: 'app-layout',
1544
+ standalone: true,
1545
+ imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppFooter, AppConfigurator, AppBreadcrumb],
1546
+ template: `
1547
+ <div class="layout-container" [ngClass]="containerClass">
1548
+ <div app-topbar></div>
1549
+ <div app-sidebar></div>
1550
+ <div class="layout-content-wrapper">
1551
+ <div class="layout-content">
1552
+ <div class="layout-content-inner">
1553
+ <nav app-breadcrumb></nav>
1554
+ <router-outlet></router-outlet>
1555
+ <div app-footer></div>
1556
+ </div>
1557
+ </div>
1558
+ </div>
1559
+ </div>
1560
+ <app-configurator />
1561
+ `,
1562
+ }]
1563
+ }], ctorParameters: () => [{ type: LayoutService }, { type: i0.Renderer2 }, { type: i2.Router }], propDecorators: { appSidebar: [{
1564
+ type: ViewChild,
1565
+ args: [AppSidebar]
1566
+ }], appTopbar: [{
1567
+ type: ViewChild,
1568
+ args: [AppTopbar]
1569
+ }] } });
1570
+
1571
+ /**
1572
+ * Generated bundle index. Do not edit.
1573
+ */
1574
+
1575
+ export { AppLayout, LayoutService };
1576
+ //# sourceMappingURL=cadriciel-layout-cerema.mjs.map