@flusys/ng-layout 5.1.3 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, Injectable, DOCUMENT, signal, computed, effect, Component, afterNextRender, InjectionToken, input, DestroyRef, viewChild, ElementRef, Renderer2 } from '@angular/core';
2
+ import { inject, PLATFORM_ID, Injectable, DOCUMENT, signal, computed, effect, Component, InjectionToken, input, DestroyRef, viewChild, ElementRef, Renderer2 } from '@angular/core';
3
3
  import * as i2$3 from '@flusys/ng-shared';
4
- import { evaluateLogicNode, PermissionValidatorService, TranslatePipe, TranslateService, IconComponent, AngularModule, PrimeModule } from '@flusys/ng-shared';
4
+ import { evaluateLogicNode, PermissionValidatorService, InlineSvgComponent, TranslatePipe, TranslateService, IconComponent, AngularModule, PrimeModule } from '@flusys/ng-shared';
5
5
  import { isPlatformBrowser, NgComponentOutlet, DOCUMENT as DOCUMENT$1, NgClass } from '@angular/common';
6
6
  import { APP_CONFIG, DEFAULT_APP_NAME, DEFAULT_AUTHOR, isCompanyFeatureEnabled, isSignUpEnabled } from '@flusys/ng-core';
7
7
  import { Subject, fromEvent, filter as filter$1 } from 'rxjs';
@@ -9,7 +9,7 @@ import * as i1 from '@angular/forms';
9
9
  import { FormsModule } from '@angular/forms';
10
10
  import * as i1$2 from '@angular/router';
11
11
  import { Router, NavigationEnd, RouterModule } from '@angular/router';
12
- import { updatePreset, updateSurfacePalette, $t, definePreset } from '@primeuix/themes';
12
+ import { definePreset, updatePreset, updateSurfacePalette, $t } from '@primeuix/themes';
13
13
  import Aura from '@primeuix/themes/aura';
14
14
  import Lara from '@primeuix/themes/lara';
15
15
  import Nora from '@primeuix/themes/nora';
@@ -92,36 +92,11 @@ function filterMenuItem(item, permissionCode) {
92
92
 
93
93
  const STORAGE_KEY = 'flusys.layout.config';
94
94
  const STORAGE_VERSION = 1;
95
- /**
96
- * Service for persisting layout configuration to localStorage.
97
- * Handles loading, saving, and validation of layout preferences with SSR-safe checks.
98
- *
99
- * @example
100
- * ```typescript
101
- * const persistence = inject(LayoutPersistenceService);
102
- *
103
- * // Load saved config
104
- * const config = persistence.load();
105
- *
106
- * // Save config
107
- * persistence.save({ darkTheme: true, preset: 'Aura' });
108
- *
109
- * // Clear saved config
110
- * persistence.clear();
111
- * ```
112
- */
113
95
  class LayoutPersistenceService {
114
96
  platformId = inject(PLATFORM_ID);
115
97
  isBrowser = isPlatformBrowser(this.platformId);
116
98
  validPresets = ['Aura', 'Lara', 'Nora'];
117
99
  validMenuModes = ['static', 'overlay', 'topbar'];
118
- /**
119
- * Load configuration from localStorage.
120
- * Returns null if no saved config or invalid data.
121
- * SSR-safe (returns null on server).
122
- *
123
- * @returns Validated layout configuration or null
124
- */
125
100
  load() {
126
101
  if (!this.isBrowser)
127
102
  return null;
@@ -137,12 +112,6 @@ class LayoutPersistenceService {
137
112
  return null;
138
113
  }
139
114
  }
140
- /**
141
- * Save configuration to localStorage.
142
- * SSR-safe (no-op on server).
143
- *
144
- * @param config Layout configuration to save
145
- */
146
115
  save(config) {
147
116
  if (!this.isBrowser)
148
117
  return;
@@ -157,41 +126,25 @@ class LayoutPersistenceService {
157
126
  // Silent fail - localStorage may be full or disabled
158
127
  }
159
128
  }
160
- /**
161
- * Clear stored configuration.
162
- * SSR-safe (no-op on server).
163
- */
164
129
  clear() {
165
130
  if (!this.isBrowser)
166
131
  return;
167
132
  localStorage.removeItem(STORAGE_KEY);
168
133
  }
169
- /**
170
- * Validate and sanitize configuration.
171
- * Returns null if version mismatch.
172
- *
173
- * @param config Configuration to validate
174
- * @returns Validated configuration or null
175
- */
176
134
  validateConfig(config) {
177
- // Version mismatch - clear and return null
178
135
  if (config._version && config._version !== STORAGE_VERSION) {
179
136
  this.clear();
180
137
  return null;
181
138
  }
182
- // Validate preset - silently fix invalid values
183
139
  if (config.preset && !this.validPresets.includes(config.preset)) {
184
140
  config.preset = 'Aura';
185
141
  }
186
- // Validate menuMode - silently fix invalid values
187
142
  if (config.menuMode && !this.validMenuModes.includes(config.menuMode)) {
188
143
  config.menuMode = 'static';
189
144
  }
190
- // Validate darkTheme
191
145
  if (typeof config.darkTheme !== 'boolean') {
192
146
  config.darkTheme = false;
193
147
  }
194
- // Remove version field from returned config
195
148
  const { _version, ...cleanConfig } = config;
196
149
  return cleanConfig;
197
150
  }
@@ -200,21 +153,18 @@ class LayoutPersistenceService {
200
153
  }
201
154
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LayoutPersistenceService, decorators: [{
202
155
  type: Injectable,
203
- args: [{
204
- providedIn: 'root',
205
- }]
156
+ args: [{ providedIn: 'root' }]
206
157
  }] });
207
158
 
208
- /** Layout configuration and state management service */
209
159
  class LayoutService {
210
160
  document = inject(DOCUMENT);
211
161
  platformId = inject(PLATFORM_ID);
212
162
  isBrowser = isPlatformBrowser(this.platformId);
213
163
  persistence = inject(LayoutPersistenceService);
214
164
  appConfig = inject(APP_CONFIG, { optional: true });
215
- DEFAULT_CONFIG = {
165
+ DEFAULT_CONFIG = this.appConfig?.layoutConfig ?? {
216
166
  preset: 'Aura',
217
- primary: 'emerald',
167
+ primary: 'violet',
218
168
  surface: null,
219
169
  darkTheme: false,
220
170
  menuMode: 'static',
@@ -227,9 +177,10 @@ class LayoutService {
227
177
  menuHoverActive: false,
228
178
  topbarMenuVisible: true,
229
179
  };
180
+ memorable = this.DEFAULT_CONFIG.isMemorable !== false;
230
181
  _layoutConfig = signal({
231
182
  ...this.DEFAULT_CONFIG,
232
- ...this.persistence.load(),
183
+ ...(this.memorable ? this.persistence.load() : null),
233
184
  }, ...(ngDevMode ? [{ debugName: "_layoutConfig" }] : []));
234
185
  _layoutState = signal(this.DEFAULT_STATE, ...(ngDevMode ? [{ debugName: "_layoutState" }] : []));
235
186
  _transitionComplete = signal(false, ...(ngDevMode ? [{ debugName: "_transitionComplete" }] : []));
@@ -243,24 +194,19 @@ class LayoutService {
243
194
  permissionValidator = inject(PermissionValidatorService);
244
195
  userProfile = this._userProfile.asReadonly();
245
196
  companyProfile = this._companyProfile.asReadonly();
246
- // Static app info from config
247
197
  appName = this.appConfig?.appName ?? DEFAULT_APP_NAME;
248
198
  appLogo = this.appConfig?.appLogo ?? '';
199
+ isAppLogoSvg = this.appLogo.toLowerCase().endsWith('.svg');
249
200
  authorName = this.appConfig?.author?.name ?? DEFAULT_AUTHOR.name;
250
201
  authorUrl = this.appConfig?.author?.url ?? DEFAULT_AUTHOR.url;
251
- // Permission-filtered menu and apps
252
202
  menu = computed(() => filterMenuByPermissions(this._rawMenu(), this.permissionValidator.permissions()), ...(ngDevMode ? [{ debugName: "menu" }] : []));
253
203
  apps = computed(() => filterAppsByPermissions(this._rawApps(), this.permissionValidator.permissions()), ...(ngDevMode ? [{ debugName: "apps" }] : []));
254
204
  hasApps = computed(() => this.apps().length > 0, ...(ngDevMode ? [{ debugName: "hasApps" }] : []));
255
- // Computed signals - Layout
256
205
  isSidebarActive = computed(() => this._layoutState().overlayMenuActive ||
257
206
  this._layoutState().staticMenuMobileActive, ...(ngDevMode ? [{ debugName: "isSidebarActive" }] : []));
258
207
  isDarkTheme = computed(() => this._layoutConfig().darkTheme, ...(ngDevMode ? [{ debugName: "isDarkTheme" }] : []));
259
- getPrimary = computed(() => this._layoutConfig().primary, ...(ngDevMode ? [{ debugName: "getPrimary" }] : []));
260
- getSurface = computed(() => this._layoutConfig().surface, ...(ngDevMode ? [{ debugName: "getSurface" }] : []));
261
208
  isOverlay = computed(() => this._layoutConfig().menuMode === 'overlay', ...(ngDevMode ? [{ debugName: "isOverlay" }] : []));
262
209
  isTopbar = computed(() => this._layoutConfig().menuMode === 'topbar', ...(ngDevMode ? [{ debugName: "isTopbar" }] : []));
263
- // User profile computed signals
264
210
  userName = computed(() => this._userProfile()?.name ?? 'Guest', ...(ngDevMode ? [{ debugName: "userName" }] : []));
265
211
  userEmail = computed(() => this._userProfile()?.email ?? '', ...(ngDevMode ? [{ debugName: "userEmail" }] : []));
266
212
  userProfilePictureUrl = computed(() => this._userProfile()?.profilePictureUrl ?? null, ...(ngDevMode ? [{ debugName: "userProfilePictureUrl" }] : []));
@@ -272,23 +218,21 @@ class LayoutService {
272
218
  return this._companyProfile()?.name ?? this.appName;
273
219
  }, ...(ngDevMode ? [{ debugName: "companyName" }] : []));
274
220
  displayLogo = computed(() => this.companyLogoUrl() ?? this.appLogo, ...(ngDevMode ? [{ debugName: "displayLogo" }] : []));
275
- // RxJS Subjects for event communication
276
- configUpdate = new Subject();
221
+ isDisplayLogoSvg = computed(() => this.displayLogo().toLowerCase().endsWith('.svg'), ...(ngDevMode ? [{ debugName: "isDisplayLogoSvg" }] : []));
277
222
  overlayOpen = new Subject();
278
223
  menuSource = new Subject();
279
224
  resetSource = new Subject();
280
225
  menuSource$ = this.menuSource.asObservable();
281
226
  resetSource$ = this.resetSource.asObservable();
282
- configUpdate$ = this.configUpdate.asObservable();
283
227
  overlayOpen$ = this.overlayOpen.asObservable();
284
228
  initialized = false;
285
229
  constructor() {
286
230
  effect(() => {
287
231
  const config = this._layoutConfig();
288
- this.configUpdate.next(config);
289
232
  if (this.initialized) {
290
233
  this.handleDarkModeTransition(config);
291
- this.persistence.save(config);
234
+ if (this.memorable)
235
+ this.persistence.save(config);
292
236
  }
293
237
  this.initialized = true;
294
238
  });
@@ -365,21 +309,18 @@ class LayoutService {
365
309
  reset() {
366
310
  this.resetSource.next(true);
367
311
  }
368
- // Config & state updates
369
312
  updateLayoutConfig(config) {
370
313
  this._layoutConfig.update((prev) => ({ ...prev, ...config }));
371
314
  }
372
315
  updateLayoutState(state) {
373
316
  this._layoutState.update((prev) => ({ ...prev, ...state }));
374
317
  }
375
- // Profile setters
376
318
  setUserProfile(profile) {
377
319
  this._userProfile.set(profile);
378
320
  }
379
321
  setCompanyProfile(profile) {
380
322
  this._companyProfile.set(profile);
381
323
  }
382
- // Menu & apps
383
324
  setMenu(items) {
384
325
  this._rawMenu.set(items);
385
326
  }
@@ -404,12 +345,17 @@ class AppFooter {
404
345
  layoutService = inject(LayoutService);
405
346
  appName = this.layoutService.appName;
406
347
  displayLogo = this.layoutService.appLogo;
348
+ isLogoSvg = this.layoutService.isAppLogoSvg;
407
349
  authorName = this.layoutService.authorName;
408
350
  authorUrl = this.layoutService.authorUrl;
409
351
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
410
352
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: AppFooter, isStandalone: true, selector: "app-footer", ngImport: i0, template: `<div class="layout-footer">
411
353
  @if (displayLogo) {
412
- <img [src]="displayLogo" [alt]="appName" class="logo-image" />
354
+ @if (isLogoSvg) {
355
+ <inline-svg [src]="displayLogo" class="logo-image" />
356
+ } @else {
357
+ <img [src]="displayLogo" [alt]="appName" class="logo-image" />
358
+ }
413
359
  }
414
360
  <span class="hidden sm:inline">
415
361
  {{ appName }} {{ 'layout.footer.by' | translate }}
@@ -421,16 +367,20 @@ class AppFooter {
421
367
  >{{ authorName }}</a
422
368
  >
423
369
  </span>
424
- </div>`, isInline: true, dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }] });
370
+ </div>`, isInline: true, dependencies: [{ kind: "component", type: InlineSvgComponent, selector: "inline-svg", inputs: ["src"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
425
371
  }
426
372
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppFooter, decorators: [{
427
373
  type: Component,
428
374
  args: [{
429
375
  selector: 'app-footer',
430
- imports: [TranslatePipe],
376
+ imports: [TranslatePipe, InlineSvgComponent],
431
377
  template: `<div class="layout-footer">
432
378
  @if (displayLogo) {
433
- <img [src]="displayLogo" [alt]="appName" class="logo-image" />
379
+ @if (isLogoSvg) {
380
+ <inline-svg [src]="displayLogo" class="logo-image" />
381
+ } @else {
382
+ <img [src]="displayLogo" [alt]="appName" class="logo-image" />
383
+ }
434
384
  }
435
385
  <span class="hidden sm:inline">
436
386
  {{ appName }} {{ 'layout.footer.by' | translate }}
@@ -446,6 +396,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
446
396
  }]
447
397
  }] });
448
398
 
399
+ const _presets = { Aura, Lara, Nora };
400
+ const _noirPrimary = {
401
+ 50: '{surface.50}', 100: '{surface.100}', 200: '{surface.200}',
402
+ 300: '{surface.300}', 400: '{surface.400}', 500: '{surface.500}',
403
+ 600: '{surface.600}', 700: '{surface.700}', 800: '{surface.800}',
404
+ 900: '{surface.900}', 950: '{surface.950}',
405
+ };
406
+ const _noraScheme = {
407
+ light: {
408
+ primary: { color: '{primary.600}', contrastColor: '#ffffff', hoverColor: '{primary.700}', activeColor: '{primary.800}' },
409
+ highlight: { background: '{primary.600}', focusBackground: '{primary.700}', color: '#ffffff', focusColor: '#ffffff' },
410
+ },
411
+ dark: {
412
+ primary: { color: '{primary.500}', contrastColor: '{surface.900}', hoverColor: '{primary.400}', activeColor: '{primary.300}' },
413
+ highlight: { background: '{primary.500}', focusBackground: '{primary.400}', color: '{surface.900}', focusColor: '{surface.900}' },
414
+ },
415
+ };
416
+ const _defaultScheme = {
417
+ light: {
418
+ primary: { color: '{primary.500}', contrastColor: '#ffffff', hoverColor: '{primary.600}', activeColor: '{primary.700}' },
419
+ highlight: { background: '{primary.50}', focusBackground: '{primary.100}', color: '{primary.700}', focusColor: '{primary.800}' },
420
+ },
421
+ dark: {
422
+ primary: { color: '{primary.400}', contrastColor: '{surface.900}', hoverColor: '{primary.300}', activeColor: '{primary.200}' },
423
+ highlight: {
424
+ background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
425
+ focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
426
+ color: 'rgba(255,255,255,.87)',
427
+ focusColor: 'rgba(255,255,255,.87)',
428
+ },
429
+ },
430
+ };
431
+ function buildSemanticExt(palette, presetName) {
432
+ if (palette === null) {
433
+ return {
434
+ semantic: {
435
+ primary: _noirPrimary,
436
+ colorScheme: {
437
+ light: {
438
+ primary: { color: '{primary.950}', contrastColor: '#ffffff', hoverColor: '{primary.800}', activeColor: '{primary.700}' },
439
+ highlight: { background: '{primary.950}', focusBackground: '{primary.700}', color: '#ffffff', focusColor: '#ffffff' },
440
+ },
441
+ dark: {
442
+ primary: { color: '{primary.50}', contrastColor: '{primary.950}', hoverColor: '{primary.200}', activeColor: '{primary.300}' },
443
+ highlight: { background: '{primary.50}', focusBackground: '{primary.300}', color: '{primary.950}', focusColor: '{primary.950}' },
444
+ },
445
+ },
446
+ },
447
+ };
448
+ }
449
+ return { semantic: { primary: palette, colorScheme: presetName === 'Nora' ? _noraScheme : _defaultScheme } };
450
+ }
451
+ function buildPreset(presetName, primary) {
452
+ const base = _presets[presetName in _presets ? presetName : 'Aura'];
453
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
454
+ const palette = primary === 'noir' ? null : base.primitive?.[primary];
455
+ if (primary !== 'noir' && !palette)
456
+ return base;
457
+ return definePreset(base, buildSemanticExt(palette, presetName));
458
+ }
459
+ function buildBootstrapPreset(envConfig) {
460
+ const preset = envConfig?.preset ?? 'Aura';
461
+ const primary = envConfig?.primary ?? 'violet';
462
+ if (envConfig?.isMemorable !== false) {
463
+ try {
464
+ const stored = localStorage.getItem('flusys.layout.config');
465
+ if (stored) {
466
+ const parsed = JSON.parse(stored);
467
+ return buildPreset(parsed.preset ?? preset, parsed.primary ?? primary);
468
+ }
469
+ }
470
+ catch { /* SSR or storage disabled */ }
471
+ }
472
+ return buildPreset(preset, primary);
473
+ }
474
+
449
475
  const presets = {
450
476
  Aura,
451
477
  Lara,
@@ -453,9 +479,8 @@ const presets = {
453
479
  };
454
480
  class AppConfigurator {
455
481
  router = inject(Router);
456
- translateAdapter = inject(TranslateService, {
457
- optional: true,
458
- });
482
+ translateAdapter = inject(TranslateService, { optional: true });
483
+ platformId = inject(PLATFORM_ID);
459
484
  layoutService = inject(LayoutService);
460
485
  presets = Object.keys(presets);
461
486
  showMenuModeButton = signal(!this.router.url.includes('auth'), ...(ngDevMode ? [{ debugName: "showMenuModeButton" }] : []));
@@ -470,7 +495,6 @@ class AppConfigurator {
470
495
  value: 'overlay',
471
496
  },
472
497
  ];
473
- // Only show topbar mode on desktop
474
498
  if (this.layoutService.isDesktop()) {
475
499
  options.push({
476
500
  label: this.translate('layout.configurator.menu.mode.topbar'),
@@ -480,11 +504,10 @@ class AppConfigurator {
480
504
  return options;
481
505
  }, ...(ngDevMode ? [{ debugName: "menuModeOptions" }] : []));
482
506
  constructor() {
483
- // Use afterNextRender for browser-only initialization (replaces isServer check)
484
- afterNextRender(() => {
507
+ if (isPlatformBrowser(this.platformId)) {
485
508
  const preset = this.layoutService.layoutConfig().preset ?? 'Aura';
486
509
  this.onPresetChange(preset);
487
- });
510
+ }
488
511
  }
489
512
  surfaces = [
490
513
  {
@@ -624,9 +647,7 @@ class AppConfigurator {
624
647
  },
625
648
  },
626
649
  ];
627
- selectedPrimaryColor = computed(() => {
628
- return this.layoutService.layoutConfig().primary;
629
- }, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
650
+ selectedPrimaryColor = computed(() => this.layoutService.layoutConfig().primary, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
630
651
  selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "selectedSurfaceColor" }] : []));
631
652
  isSurfaceSelected(surfaceName) {
632
653
  const selectedSurface = this.selectedSurfaceColor();
@@ -670,133 +691,10 @@ class AppConfigurator {
670
691
  return palettes;
671
692
  }, ...(ngDevMode ? [{ debugName: "primaryColors" }] : []));
672
693
  getPresetExt() {
673
- const color = this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {};
674
- const preset = this.layoutService.layoutConfig().preset;
675
- if (color.name === 'noir') {
676
- return {
677
- semantic: {
678
- primary: {
679
- 50: '{surface.50}',
680
- 100: '{surface.100}',
681
- 200: '{surface.200}',
682
- 300: '{surface.300}',
683
- 400: '{surface.400}',
684
- 500: '{surface.500}',
685
- 600: '{surface.600}',
686
- 700: '{surface.700}',
687
- 800: '{surface.800}',
688
- 900: '{surface.900}',
689
- 950: '{surface.950}',
690
- },
691
- colorScheme: {
692
- light: {
693
- primary: {
694
- color: '{primary.950}',
695
- contrastColor: '#ffffff',
696
- hoverColor: '{primary.800}',
697
- activeColor: '{primary.700}',
698
- },
699
- highlight: {
700
- background: '{primary.950}',
701
- focusBackground: '{primary.700}',
702
- color: '#ffffff',
703
- focusColor: '#ffffff',
704
- },
705
- },
706
- dark: {
707
- primary: {
708
- color: '{primary.50}',
709
- contrastColor: '{primary.950}',
710
- hoverColor: '{primary.200}',
711
- activeColor: '{primary.300}',
712
- },
713
- highlight: {
714
- background: '{primary.50}',
715
- focusBackground: '{primary.300}',
716
- color: '{primary.950}',
717
- focusColor: '{primary.950}',
718
- },
719
- },
720
- },
721
- },
722
- };
723
- }
724
- else {
725
- if (preset === 'Nora') {
726
- return {
727
- semantic: {
728
- primary: color.palette,
729
- colorScheme: {
730
- light: {
731
- primary: {
732
- color: '{primary.600}',
733
- contrastColor: '#ffffff',
734
- hoverColor: '{primary.700}',
735
- activeColor: '{primary.800}',
736
- },
737
- highlight: {
738
- background: '{primary.600}',
739
- focusBackground: '{primary.700}',
740
- color: '#ffffff',
741
- focusColor: '#ffffff',
742
- },
743
- },
744
- dark: {
745
- primary: {
746
- color: '{primary.500}',
747
- contrastColor: '{surface.900}',
748
- hoverColor: '{primary.400}',
749
- activeColor: '{primary.300}',
750
- },
751
- highlight: {
752
- background: '{primary.500}',
753
- focusBackground: '{primary.400}',
754
- color: '{surface.900}',
755
- focusColor: '{surface.900}',
756
- },
757
- },
758
- },
759
- },
760
- };
761
- }
762
- else {
763
- return {
764
- semantic: {
765
- primary: color.palette,
766
- colorScheme: {
767
- light: {
768
- primary: {
769
- color: '{primary.500}',
770
- contrastColor: '#ffffff',
771
- hoverColor: '{primary.600}',
772
- activeColor: '{primary.700}',
773
- },
774
- highlight: {
775
- background: '{primary.50}',
776
- focusBackground: '{primary.100}',
777
- color: '{primary.700}',
778
- focusColor: '{primary.800}',
779
- },
780
- },
781
- dark: {
782
- primary: {
783
- color: '{primary.400}',
784
- contrastColor: '{surface.900}',
785
- hoverColor: '{primary.300}',
786
- activeColor: '{primary.200}',
787
- },
788
- highlight: {
789
- background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
790
- focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
791
- color: 'rgba(255,255,255,.87)',
792
- focusColor: 'rgba(255,255,255,.87)',
793
- },
794
- },
795
- },
796
- },
797
- };
798
- }
799
- }
694
+ const color = this.primaryColors().find(c => c.name === this.selectedPrimaryColor()) ?? {};
695
+ const preset = this.layoutService.layoutConfig().preset ?? 'Aura';
696
+ const palette = color.name === 'noir' ? null : (color.palette ?? {});
697
+ return buildSemanticExt(palette, preset);
800
698
  }
801
699
  updateColors(event, type, color) {
802
700
  if (type === 'primary') {
@@ -2555,7 +2453,15 @@ class AppTopbar {
2555
2453
  }
2556
2454
  <a class="layout-topbar-logo" routerLink="/">
2557
2455
  @if (displayLogo()) {
2558
- <img [src]="displayLogo()" [alt]="companyName()" class="logo-image" />
2456
+ @if (layoutService.isDisplayLogoSvg()) {
2457
+ <inline-svg [src]="displayLogo()" class="logo-image" />
2458
+ } @else {
2459
+ <img
2460
+ [src]="displayLogo()"
2461
+ [alt]="companyName()"
2462
+ class="logo-image"
2463
+ />
2464
+ }
2559
2465
  }
2560
2466
  <span class="hidden sm:inline">{{ companyName() }}</span>
2561
2467
  </a>
@@ -2649,7 +2555,7 @@ class AppTopbar {
2649
2555
  </div>
2650
2556
  </div>
2651
2557
  </div>
2652
- </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }, { kind: "component", type: AppMenu, selector: "app-menu" }, { kind: "component", type: AppProfile, selector: "app-profile" }, { kind: "component", type: AppCompanyBranchSelector, selector: "app-company-branch-selector" }, { kind: "component", type: AppLauncher, selector: "app-launcher" }, { kind: "component", type: AppSearchbar, selector: "app-searchbar" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2558
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: InlineSvgComponent, selector: "inline-svg", inputs: ["src"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }, { kind: "component", type: AppMenu, selector: "app-menu" }, { kind: "component", type: AppProfile, selector: "app-profile" }, { kind: "component", type: AppCompanyBranchSelector, selector: "app-company-branch-selector" }, { kind: "component", type: AppLauncher, selector: "app-launcher" }, { kind: "component", type: AppSearchbar, selector: "app-searchbar" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2653
2559
  }
2654
2560
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppTopbar, decorators: [{
2655
2561
  type: Component,
@@ -2660,6 +2566,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2660
2566
  StyleClassModule,
2661
2567
  NgComponentOutlet,
2662
2568
  TranslatePipe,
2569
+ InlineSvgComponent,
2663
2570
  AppConfigurator,
2664
2571
  AppMenu,
2665
2572
  AppProfile,
@@ -2683,7 +2590,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2683
2590
  }
2684
2591
  <a class="layout-topbar-logo" routerLink="/">
2685
2592
  @if (displayLogo()) {
2686
- <img [src]="displayLogo()" [alt]="companyName()" class="logo-image" />
2593
+ @if (layoutService.isDisplayLogoSvg()) {
2594
+ <inline-svg [src]="displayLogo()" class="logo-image" />
2595
+ } @else {
2596
+ <img
2597
+ [src]="displayLogo()"
2598
+ [alt]="companyName()"
2599
+ class="logo-image"
2600
+ />
2601
+ }
2687
2602
  }
2688
2603
  <span class="hidden sm:inline">{{ companyName() }}</span>
2689
2604
  </a>
@@ -2997,8 +2912,13 @@ const LAYOUT_MESSAGES = {
2997
2912
  'menu.forms': 'Forms',
2998
2913
  'menu.email': 'Email',
2999
2914
  'menu.event.manager': 'Event Manager',
2915
+ 'menu.task.manager': 'Task Manager',
3000
2916
  'menu.notifications': 'Notifications',
3001
2917
  'menu.localization': 'Localization',
2918
+ 'menu.projects': 'Projects',
2919
+ 'menu.projects.active': 'Active Projects',
2920
+ 'menu.projects.archived': 'Archived',
2921
+ 'menu.projects.reports': 'Reports',
3002
2922
  // Those are layout part from notification module, but we put them here to avoid circular dependency between layout and notification module
3003
2923
  // components/notification-bell/notification-bell.component.ts
3004
2924
  'notification.title': 'Notifications',
@@ -3019,5 +2939,5 @@ const LAYOUT_MESSAGES = {
3019
2939
  * Generated bundle index. Do not edit.
3020
2940
  */
3021
2941
 
3022
- export { AppCompanyBranchSelector, AppConfigurator, AppFloatingConfigurator, AppFooter, AppLauncher, AppLayout, AppMenu, AppMenuitem, AppProfile, AppSearchbar, AppSidebar, AppTopbar, GreenTheme, LAYOUT_AUTH_API, LAYOUT_AUTH_STATE, LAYOUT_IS_RTL, LAYOUT_LANGUAGE_SELECTOR, LAYOUT_LANGUAGE_SELECTOR_PANEL, LAYOUT_MESSAGES, LAYOUT_NOTIFICATION_BELL, LAYOUT_SEARCHBAR, LAYOUT_SEARCH_ADAPTER, LayoutPersistenceService, LayoutService, NavyBlueTheme, filterAppsByPermissions, filterMenuByPermissions };
2942
+ export { AppCompanyBranchSelector, AppConfigurator, AppFloatingConfigurator, AppFooter, AppLauncher, AppLayout, AppMenu, AppMenuitem, AppProfile, AppSearchbar, AppSidebar, AppTopbar, GreenTheme, LAYOUT_AUTH_API, LAYOUT_AUTH_STATE, LAYOUT_IS_RTL, LAYOUT_LANGUAGE_SELECTOR, LAYOUT_LANGUAGE_SELECTOR_PANEL, LAYOUT_MESSAGES, LAYOUT_NOTIFICATION_BELL, LAYOUT_SEARCHBAR, LAYOUT_SEARCH_ADAPTER, LayoutPersistenceService, LayoutService, NavyBlueTheme, buildBootstrapPreset, buildPreset, buildSemanticExt, filterAppsByPermissions, filterMenuByPermissions };
3023
2943
  //# sourceMappingURL=flusys-ng-layout.mjs.map