@flusys/ng-layout 9.1.0 → 9.1.2

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,465 +1,372 @@
1
- import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, Service, DOCUMENT, signal, DestroyRef, computed, effect, Renderer2, Component, input, viewChild, InjectionToken, ElementRef } from '@angular/core';
3
- import { isPlatformBrowser, DOCUMENT as DOCUMENT$1, NgComponentOutlet } from '@angular/common';
4
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
- import { APP_CONFIG, DEFAULT_APP_NAME, DEFAULT_AUTHOR, isCompanyFeatureEnabled, isSignUpEnabled } from '@flusys/ng-core';
6
- import { evaluateLogicNode, PermissionValidatorService, InlineSvgComponent, TranslatePipe, IconComponent, TranslateService, IsEmptyImageDirective } from '@flusys/ng-shared';
7
- import { Subject, fromEvent, filter } from 'rxjs';
8
- import { PRESET_NAMES, UiIconComponent, applyTheme, SURFACE_PALETTES, PRIMARY_PALETTES, NOIR_PRIMARY, applyPrimaryPalette, applySurfacePalette, PRESETS, SelectButtonComponent, MessageService, FButtonDirective, SelectComponent, StyleClassDirective, IconFieldComponent, InputIconComponent, InputTextDirective } from '@flusys/ng-ui';
9
- export { applyTheme, buildBootstrapPreset } from '@flusys/ng-ui';
10
- import { Router, NavigationEnd, isActive, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
11
- import { filter as filter$1 } from 'rxjs/operators';
12
- import * as i1 from '@angular/forms';
13
- import { FormsModule } from '@angular/forms';
14
-
15
- /** Filter launcher apps based on user permission codes */
1
+ import * as i0 from "@angular/core";
2
+ import { Component, DOCUMENT, DestroyRef, ElementRef, InjectionToken, PLATFORM_ID, Renderer2, Service, computed, effect, inject, input, signal, viewChild } from "@angular/core";
3
+ import { DOCUMENT as DOCUMENT$1, NgComponentOutlet, isPlatformBrowser } from "@angular/common";
4
+ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
5
+ import { APP_CONFIG, DEFAULT_APP_NAME, DEFAULT_AUTHOR, isCompanyFeatureEnabled, isSignUpEnabled } from "@flusys/ng-core";
6
+ import { IconComponent, InlineSvgComponent, IsEmptyImageDirective, PermissionValidatorService, TranslatePipe, TranslateService, buildBranchTreeNodes, evaluateLogicNode } from "@flusys/ng-shared";
7
+ import { Subject, filter, fromEvent } from "rxjs";
8
+ import { FButtonDirective, IconFieldComponent, InputIconComponent, InputTextDirective, MessageService, NOIR_PRIMARY, PRESETS, PRESET_NAMES, PRIMARY_PALETTES, SURFACE_PALETTES, SelectButtonComponent, SelectComponent, StyleClassDirective, TreeSelectComponent, UiIconComponent, applyPrimaryPalette, applySurfacePalette, applyTheme, applyTheme as applyTheme$1, buildBootstrapPreset } from "@flusys/ng-ui";
9
+ import { NavigationEnd, Router, RouterLink, RouterLinkActive, RouterOutlet, isActive } from "@angular/router";
10
+ import { filter as filter$1 } from "rxjs/operators";
11
+ import * as i1 from "@angular/forms";
12
+ import { FormsModule } from "@angular/forms";
16
13
  function filterAppsByPermissions(apps, permissionCodes) {
17
- return apps.filter((app) => !app.permissionLogic ||
18
- evaluateLogicNode(app.permissionLogic, permissionCodes));
14
+ return apps.filter((app) => !app.permissionLogic || evaluateLogicNode(app.permissionLogic, permissionCodes));
19
15
  }
20
-
21
- /**
22
- * Filter menu items based on permission and role checker
23
- *
24
- * @param items - Array of menu items to filter
25
- * @param hasPermission - Function to check if user has permission
26
- * @param hasRole - Function to check if user has role (optional, defaults to always false)
27
- * @returns Filtered menu items array
28
- *
29
- * @example
30
- * ```typescript
31
- * const filteredMenu = filterMenuByPermissions(
32
- * menuItems,
33
- * (actionCode) => permissionState.hasAction(actionCode)
34
- * );
35
- * ```
36
- */
37
16
  function filterMenuByPermissions(items, permissionCode) {
38
- return items
39
- .map((item) => filterMenuItem(item, permissionCode))
40
- .filter((item) => item !== null);
17
+ return items.map((item) => filterMenuItem(item, permissionCode)).filter((item) => item !== null);
41
18
  }
42
- /**
43
- * Filter a single menu item and its children recursively
44
- *
45
- * @param item - Menu item to filter
46
- * @param hasPermission - Function to check if user has permission
47
- * @param hasRole - Function to check if user has role
48
- * @returns Filtered menu item or null if user doesn't have permission
49
- */
50
19
  function filterMenuItem(item, permissionCode) {
51
- // Skip separators - they have no permission checks
52
- if (item.separator) {
53
- return item;
54
- }
55
- // Check permission logic tree
56
- if (item.permissionLogic) {
57
- const hasAccess = evaluateLogicNode(item.permissionLogic, permissionCode);
58
- if (!hasAccess) {
59
- return null; // User doesn't pass permission logic
60
- }
61
- }
62
- // Filter child items recursively
63
- if (item.children && item.children.length > 0) {
64
- const filteredChildren = filterMenuByPermissions(item.children, permissionCode);
65
- // If parent has no visible children, hide parent too
66
- if (filteredChildren.length === 0) {
67
- return null;
68
- }
69
- return { ...item, children: filteredChildren };
70
- }
71
- return item;
20
+ if (item.separator) return item;
21
+ if (item.permissionLogic) {
22
+ if (!evaluateLogicNode(item.permissionLogic, permissionCode)) return null;
23
+ }
24
+ if (item.children && item.children.length > 0) {
25
+ const filteredChildren = filterMenuByPermissions(item.children, permissionCode);
26
+ if (filteredChildren.length === 0) return null;
27
+ return {
28
+ ...item,
29
+ children: filteredChildren
30
+ };
31
+ }
32
+ return item;
72
33
  }
73
-
74
- const STORAGE_KEY = 'flusys.layout.config';
34
+ const STORAGE_KEY = "flusys.layout.config";
75
35
  const STORAGE_VERSION = 1;
76
- // Sourced from the theme package so a new preset never silently fails validation.
77
36
  const VALID_PRESETS = PRESET_NAMES;
78
- const VALID_MENU_MODES = ['static', 'overlay', 'topbar'];
79
- const VALID_LAYOUT_THEMES = ['classic', 'modern'];
80
- class LayoutPersistenceService {
81
- platformId = inject(PLATFORM_ID);
82
- isBrowser = isPlatformBrowser(this.platformId);
83
- load() {
84
- if (!this.isBrowser)
85
- return null;
86
- try {
87
- const stored = localStorage.getItem(STORAGE_KEY);
88
- if (!stored)
89
- return null;
90
- return this.validateConfig(JSON.parse(stored));
91
- }
92
- catch {
93
- this.clear();
94
- return null;
95
- }
96
- }
97
- save(config) {
98
- if (!this.isBrowser)
99
- return;
100
- try {
101
- const toStore = {
102
- ...config,
103
- _version: STORAGE_VERSION,
104
- };
105
- localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore));
106
- }
107
- catch {
108
- // localStorage may be full or disabled — the config simply stays unsaved.
109
- }
110
- }
111
- clear() {
112
- if (!this.isBrowser)
113
- return;
114
- localStorage.removeItem(STORAGE_KEY);
115
- }
116
- /**
117
- * Coerces a stored value only when it is present and unrecognised. A field the
118
- * user never changed stays absent so the app's own default still wins when this
119
- * result is merged over it.
120
- */
121
- validateConfig(stored) {
122
- if (stored._version && stored._version !== STORAGE_VERSION) {
123
- this.clear();
124
- return null;
125
- }
126
- const { _version, ...config } = stored;
127
- if (config.preset !== undefined && !VALID_PRESETS.includes(config.preset)) {
128
- config.preset = 'Aura';
129
- }
130
- if (config.menuMode !== undefined &&
131
- !VALID_MENU_MODES.includes(config.menuMode)) {
132
- config.menuMode = 'static';
133
- }
134
- if (config.layoutTheme !== undefined &&
135
- !VALID_LAYOUT_THEMES.includes(config.layoutTheme)) {
136
- config.layoutTheme = 'classic';
137
- }
138
- if (config.darkTheme !== undefined && typeof config.darkTheme !== 'boolean') {
139
- config.darkTheme = false;
140
- }
141
- return config;
142
- }
143
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LayoutPersistenceService, deps: [], target: i0.ɵɵFactoryTarget.Service });
144
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: LayoutPersistenceService });
145
- }
146
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LayoutPersistenceService, decorators: [{
147
- type: Service
148
- }] });
149
-
150
- /** Matches the 991px shell breakpoint both skins use in SCSS. */
37
+ const VALID_MENU_MODES = [
38
+ "static",
39
+ "overlay",
40
+ "topbar"
41
+ ];
42
+ const VALID_LAYOUT_THEMES = ["classic", "modern"];
43
+ var LayoutPersistenceService = class LayoutPersistenceService {
44
+ platformId = inject(PLATFORM_ID);
45
+ isBrowser = isPlatformBrowser(this.platformId);
46
+ load() {
47
+ if (!this.isBrowser) return null;
48
+ try {
49
+ const stored = localStorage.getItem(STORAGE_KEY);
50
+ if (!stored) return null;
51
+ return this.validateConfig(JSON.parse(stored));
52
+ } catch {
53
+ this.clear();
54
+ return null;
55
+ }
56
+ }
57
+ save(config) {
58
+ if (!this.isBrowser) return;
59
+ try {
60
+ const toStore = {
61
+ ...config,
62
+ _version: STORAGE_VERSION
63
+ };
64
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore));
65
+ } catch {}
66
+ }
67
+ clear() {
68
+ if (!this.isBrowser) return;
69
+ localStorage.removeItem(STORAGE_KEY);
70
+ }
71
+ validateConfig(stored) {
72
+ if (stored._version && stored._version !== STORAGE_VERSION) {
73
+ this.clear();
74
+ return null;
75
+ }
76
+ const { _version, ...config } = stored;
77
+ if (config.preset !== void 0 && !VALID_PRESETS.includes(config.preset)) config.preset = "Aura";
78
+ if (config.menuMode !== void 0 && !VALID_MENU_MODES.includes(config.menuMode)) config.menuMode = "static";
79
+ if (config.layoutTheme !== void 0 && !VALID_LAYOUT_THEMES.includes(config.layoutTheme)) config.layoutTheme = "classic";
80
+ if (config.darkTheme !== void 0 && typeof config.darkTheme !== "boolean") config.darkTheme = false;
81
+ return config;
82
+ }
83
+ static ɵfac = i0.ɵɵngDeclareFactory({
84
+ minVersion: "12.0.0",
85
+ version: "22.2.1",
86
+ ngImport: i0,
87
+ type: LayoutPersistenceService,
88
+ deps: [],
89
+ target: i0.ɵɵFactoryTarget.Service
90
+ });
91
+ static ɵprov = i0.ɵɵngDeclareService({
92
+ minVersion: "22.0.0",
93
+ version: "22.2.1",
94
+ ngImport: i0,
95
+ type: LayoutPersistenceService
96
+ });
97
+ };
98
+ i0.ɵɵngDeclareClassMetadata({
99
+ minVersion: "12.0.0",
100
+ version: "22.2.1",
101
+ ngImport: i0,
102
+ type: LayoutPersistenceService,
103
+ decorators: [{ type: Service }]
104
+ });
151
105
  const DESKTOP_BREAKPOINT = 991;
152
- class LayoutService {
153
- document = inject(DOCUMENT);
154
- platformId = inject(PLATFORM_ID);
155
- isBrowser = isPlatformBrowser(this.platformId);
156
- persistence = inject(LayoutPersistenceService);
157
- appConfig = inject(APP_CONFIG, { optional: true });
158
- DEFAULT_CONFIG = this.appConfig?.layoutConfig ?? {
159
- preset: 'Aura',
160
- primary: 'violet',
161
- surface: null,
162
- darkTheme: false,
163
- menuMode: 'static',
164
- layoutTheme: 'classic',
165
- };
166
- DEFAULT_STATE = {
167
- staticMenuDesktopInactive: false,
168
- overlayMenuActive: false,
169
- configSidebarVisible: false,
170
- staticMenuMobileActive: false,
171
- menuHoverActive: false,
172
- };
173
- memorable = this.DEFAULT_CONFIG.isMemorable !== false;
174
- _layoutConfig = signal({
175
- ...this.DEFAULT_CONFIG,
176
- ...(this.memorable ? this.persistence.load() : null),
177
- }, /* @ts-ignore */
178
- ...(ngDevMode ? [{ debugName: "_layoutConfig" }] : /* istanbul ignore next */ []));
179
- _layoutState = signal(this.DEFAULT_STATE, /* @ts-ignore */
180
- ...(ngDevMode ? [{ debugName: "_layoutState" }] : /* istanbul ignore next */ []));
181
- _transitionComplete = signal(false, /* @ts-ignore */
182
- ...(ngDevMode ? [{ debugName: "_transitionComplete" }] : /* istanbul ignore next */ []));
183
- layoutConfig = this._layoutConfig.asReadonly();
184
- layoutState = this._layoutState.asReadonly();
185
- transitionComplete = this._transitionComplete.asReadonly();
186
- _userProfile = signal(null, /* @ts-ignore */
187
- ...(ngDevMode ? [{ debugName: "_userProfile" }] : /* istanbul ignore next */ []));
188
- _companyProfile = signal(null, /* @ts-ignore */
189
- ...(ngDevMode ? [{ debugName: "_companyProfile" }] : /* istanbul ignore next */ []));
190
- _rawMenu = signal([], /* @ts-ignore */
191
- ...(ngDevMode ? [{ debugName: "_rawMenu" }] : /* istanbul ignore next */ []));
192
- _rawApps = signal([], /* @ts-ignore */
193
- ...(ngDevMode ? [{ debugName: "_rawApps" }] : /* istanbul ignore next */ []));
194
- permissionValidator = inject(PermissionValidatorService);
195
- destroyRef = inject(DestroyRef);
196
- _windowWidth = signal(this.isBrowser ? window.innerWidth : DESKTOP_BREAKPOINT + 1, /* @ts-ignore */
197
- ...(ngDevMode ? [{ debugName: "_windowWidth" }] : /* istanbul ignore next */ []));
198
- userProfile = this._userProfile.asReadonly();
199
- companyProfile = this._companyProfile.asReadonly();
200
- appName = this.appConfig?.appName ?? DEFAULT_APP_NAME;
201
- appLogo = this.appConfig?.appLogo ?? '';
202
- isAppLogoSvg = this.appLogo.toLowerCase().endsWith('.svg');
203
- authorName = this.appConfig?.author?.name ?? DEFAULT_AUTHOR.name;
204
- authorUrl = this.appConfig?.author?.url ?? DEFAULT_AUTHOR.url;
205
- menu = computed(() => filterMenuByPermissions(this._rawMenu(), this.permissionValidator.permissions()), /* @ts-ignore */
206
- ...(ngDevMode ? [{ debugName: "menu" }] : /* istanbul ignore next */ []));
207
- apps = computed(() => filterAppsByPermissions(this._rawApps(), this.permissionValidator.permissions()), /* @ts-ignore */
208
- ...(ngDevMode ? [{ debugName: "apps" }] : /* istanbul ignore next */ []));
209
- hasApps = computed(() => this.apps().length > 0, /* @ts-ignore */
210
- ...(ngDevMode ? [{ debugName: "hasApps" }] : /* istanbul ignore next */ []));
211
- isSidebarActive = computed(() => this._layoutState().overlayMenuActive ||
212
- this._layoutState().staticMenuMobileActive, /* @ts-ignore */
213
- ...(ngDevMode ? [{ debugName: "isSidebarActive" }] : /* istanbul ignore next */ []));
214
- isDesktop = computed(() => this._windowWidth() > DESKTOP_BREAKPOINT, /* @ts-ignore */
215
- ...(ngDevMode ? [{ debugName: "isDesktop" }] : /* istanbul ignore next */ []));
216
- isMobile = computed(() => !this.isDesktop(), /* @ts-ignore */
217
- ...(ngDevMode ? [{ debugName: "isMobile" }] : /* istanbul ignore next */ []));
218
- isDarkTheme = computed(() => this._layoutConfig().darkTheme, /* @ts-ignore */
219
- ...(ngDevMode ? [{ debugName: "isDarkTheme" }] : /* istanbul ignore next */ []));
220
- isOverlay = computed(() => this._layoutConfig().menuMode === 'overlay', /* @ts-ignore */
221
- ...(ngDevMode ? [{ debugName: "isOverlay" }] : /* istanbul ignore next */ []));
222
- layoutTheme = computed(() => this._layoutConfig().layoutTheme ?? 'classic', /* @ts-ignore */
223
- ...(ngDevMode ? [{ debugName: "layoutTheme" }] : /* istanbul ignore next */ []));
224
- isModernLayout = computed(() => this.layoutTheme() === 'modern', /* @ts-ignore */
225
- ...(ngDevMode ? [{ debugName: "isModernLayout" }] : /* istanbul ignore next */ []));
226
- isClassicLayout = computed(() => this.layoutTheme() === 'classic', /* @ts-ignore */
227
- ...(ngDevMode ? [{ debugName: "isClassicLayout" }] : /* istanbul ignore next */ []));
228
- /** The modern skin has no horizontal nav, so topbar mode only applies to the classic skin. */
229
- isTopbar = computed(() => this._layoutConfig().menuMode === 'topbar' && !this.isModernLayout(), /* @ts-ignore */
230
- ...(ngDevMode ? [{ debugName: "isTopbar" }] : /* istanbul ignore next */ []));
231
- userName = computed(() => this._userProfile()?.name ?? 'Guest', /* @ts-ignore */
232
- ...(ngDevMode ? [{ debugName: "userName" }] : /* istanbul ignore next */ []));
233
- userEmail = computed(() => this._userProfile()?.email ?? '', /* @ts-ignore */
234
- ...(ngDevMode ? [{ debugName: "userEmail" }] : /* istanbul ignore next */ []));
235
- // `||`, not `??`, throughout the block below: the backend returns "no picture" and
236
- // "no logo" as an empty string as often as it does null, and an empty string would
237
- // slip past `??` and render as a broken image.
238
- userProfilePictureUrl = computed(() => this._userProfile()?.profilePictureUrl || null, /* @ts-ignore */
239
- ...(ngDevMode ? [{ debugName: "userProfilePictureUrl" }] : /* istanbul ignore next */ []));
240
- companyLogoUrl = computed(() => this._companyProfile()?.logoUrl || null, /* @ts-ignore */
241
- ...(ngDevMode ? [{ debugName: "companyLogoUrl" }] : /* istanbul ignore next */ []));
242
- isAuthenticated = computed(() => !!this._userProfile(), /* @ts-ignore */
243
- ...(ngDevMode ? [{ debugName: "isAuthenticated" }] : /* istanbul ignore next */ []));
244
- companyName = computed(() => {
245
- if (!this.appConfig || !isCompanyFeatureEnabled(this.appConfig))
246
- return this.appName;
247
- return this._companyProfile()?.name || this.appName;
248
- }, /* @ts-ignore */
249
- ...(ngDevMode ? [{ debugName: "companyName" }] : /* istanbul ignore next */ []));
250
- displayLogo = computed(() => this.companyLogoUrl() ?? this.appLogo, /* @ts-ignore */
251
- ...(ngDevMode ? [{ debugName: "displayLogo" }] : /* istanbul ignore next */ []));
252
- isDisplayLogoSvg = computed(() => this.displayLogo().toLowerCase().endsWith('.svg'), /* @ts-ignore */
253
- ...(ngDevMode ? [{ debugName: "isDisplayLogoSvg" }] : /* istanbul ignore next */ []));
254
- overlayOpen = new Subject();
255
- menuSource = new Subject();
256
- resetSource = new Subject();
257
- menuSource$ = this.menuSource.asObservable();
258
- resetSource$ = this.resetSource.asObservable();
259
- overlayOpen$ = this.overlayOpen.asObservable();
260
- initialized = false;
261
- appliedDarkTheme = this._layoutConfig().darkTheme;
262
- constructor() {
263
- if (this.isBrowser) {
264
- this.toggleDarkMode();
265
- fromEvent(window, 'resize')
266
- .pipe(takeUntilDestroyed(this.destroyRef))
267
- .subscribe(() => {
268
- this._windowWidth.set(window.innerWidth);
269
- });
270
- }
271
- effect(() => {
272
- const config = this._layoutConfig();
273
- if (this.initialized) {
274
- // Only the dark-mode flip is worth a view transition. Every other field —
275
- // preset, primary, menu mode, layout skin — used to start one too, which
276
- // cross-faded the whole page for a change that never touched it.
277
- if (config.darkTheme !== this.appliedDarkTheme) {
278
- this.appliedDarkTheme = config.darkTheme;
279
- this.handleDarkModeTransition(config);
280
- }
281
- if (this.memorable)
282
- this.persistence.save(config);
283
- }
284
- this.initialized = true;
285
- });
286
- }
287
- toggleDarkMode(config) {
288
- const isDark = (config ?? this._layoutConfig()).darkTheme;
289
- this.document.documentElement.classList.toggle('app-dark', isDark);
290
- }
291
- handleDarkModeTransition(config) {
292
- const doc = this.document;
293
- if ('startViewTransition' in doc &&
294
- typeof doc.startViewTransition === 'function') {
295
- doc
296
- .startViewTransition(() => this.toggleDarkMode(config))
297
- .ready.then(() => this.onTransitionEnd())
298
- .catch(() => { });
299
- }
300
- else {
301
- this.toggleDarkMode(config);
302
- this.onTransitionEnd();
303
- }
304
- }
305
- onTransitionEnd() {
306
- this._transitionComplete.set(true);
307
- setTimeout(() => this._transitionComplete.set(false));
308
- }
309
- onMenuToggle() {
310
- const state = this._layoutState();
311
- // Mobile always uses staticMenuMobileActive regardless of menu mode
312
- if (!this.isDesktop()) {
313
- const newMobileActive = !state.staticMenuMobileActive;
314
- this._layoutState.update((prev) => ({
315
- ...prev,
316
- staticMenuMobileActive: newMobileActive,
317
- }));
318
- if (newMobileActive)
319
- this.overlayOpen.next();
320
- return;
321
- }
322
- // Desktop: use overlay or static mode
323
- if (this.isOverlay()) {
324
- const newOverlayActive = !state.overlayMenuActive;
325
- this._layoutState.update((prev) => ({
326
- ...prev,
327
- overlayMenuActive: newOverlayActive,
328
- }));
329
- if (newOverlayActive)
330
- this.overlayOpen.next();
331
- }
332
- else {
333
- this._layoutState.update((prev) => ({
334
- ...prev,
335
- staticMenuDesktopInactive: !state.staticMenuDesktopInactive,
336
- }));
337
- }
338
- }
339
- onMenuStateChange(event) {
340
- this.menuSource.next(event);
341
- }
342
- reset() {
343
- this.resetSource.next(true);
344
- }
345
- updateLayoutConfig(config) {
346
- this._layoutConfig.update((prev) => ({ ...prev, ...config }));
347
- }
348
- updateLayoutState(state) {
349
- this._layoutState.update((prev) => ({ ...prev, ...state }));
350
- }
351
- setUserProfile(profile) {
352
- this._userProfile.set(profile);
353
- }
354
- setCompanyProfile(profile) {
355
- this._companyProfile.set(profile);
356
- }
357
- setMenu(items) {
358
- this._rawMenu.set(items);
359
- }
360
- clearMenu() {
361
- this._rawMenu.set([]);
362
- }
363
- setApps(apps) {
364
- this._rawApps.set(apps);
365
- }
366
- clearApps() {
367
- this._rawApps.set([]);
368
- }
369
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Service });
370
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: LayoutService });
371
- }
372
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LayoutService, decorators: [{
373
- type: Service
374
- }], ctorParameters: () => [] });
375
-
376
- /**
377
- * Shared shell behaviour for every layout skin.
378
- * Holds the menu open/close lifecycle so a skin only owns markup and styling.
379
- */
380
- class LayoutShellBase {
381
- destroyRef = inject(DestroyRef);
382
- document = inject(DOCUMENT);
383
- renderer = inject(Renderer2);
384
- router = inject(Router);
385
- layoutService = inject(LayoutService);
386
- menuOutsideClickListener = null;
387
- /** Extra work a skin needs on every completed navigation. */
388
- onNavigated() { }
389
- constructor() {
390
- this.layoutService.overlayOpen$
391
- .pipe(takeUntilDestroyed(this.destroyRef))
392
- .subscribe(() => {
393
- if (!this.menuOutsideClickListener) {
394
- this.menuOutsideClickListener = this.renderer.listen(this.document, 'click', (event) => {
395
- if (this.isOutsideClicked(event)) {
396
- this.hideMenu();
397
- }
398
- });
399
- }
400
- if (this.layoutService.layoutState().staticMenuMobileActive) {
401
- this.blockBodyScroll();
402
- }
403
- });
404
- this.router.events
405
- .pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef))
406
- .subscribe(() => {
407
- this.hideMenu();
408
- this.onNavigated();
409
- });
410
- }
411
- isOutsideClicked(event) {
412
- const eventTarget = event.target;
413
- // querySelectorAll, not querySelector: a skin may render the same control more
414
- // than once (the modern shell has a menu button in both the sidebar and the
415
- // topbar) and matching only the first one reads a real click as an outside one.
416
- return !this.outsideClickSelectors().some((selector) => Array.from(this.document.querySelectorAll(selector)).some((element) => element.isSameNode(eventTarget) || element.contains(eventTarget)));
417
- }
418
- hideMenu() {
419
- this.layoutService.updateLayoutState({
420
- overlayMenuActive: false,
421
- staticMenuMobileActive: false,
422
- menuHoverActive: false,
423
- });
424
- if (this.menuOutsideClickListener) {
425
- this.menuOutsideClickListener();
426
- this.menuOutsideClickListener = null;
427
- }
428
- this.unblockBodyScroll();
429
- }
430
- blockBodyScroll() {
431
- this.document.body.classList.add('blocked-scroll');
432
- }
433
- unblockBodyScroll() {
434
- this.document.body.classList.remove('blocked-scroll');
435
- }
436
- isOverlay = this.layoutService.isOverlay;
437
- /** Delegated so a skin that has no horizontal nav is never treated as being in it. */
438
- isTopbarMode = this.layoutService.isTopbar;
439
- isOverlayActive = computed(() => this.layoutService.layoutState().overlayMenuActive, /* @ts-ignore */
440
- ...(ngDevMode ? [{ debugName: "isOverlayActive" }] : /* istanbul ignore next */ []));
441
- isMobileActive = computed(() => this.layoutService.layoutState().staticMenuMobileActive, /* @ts-ignore */
442
- ...(ngDevMode ? [{ debugName: "isMobileActive" }] : /* istanbul ignore next */ []));
443
- /**
444
- * The desktop sidebar is collapsed in place. Only meaningful for the modes that
445
- * keep the sidebar in the layout flow — an overlay drawer and the horizontal nav
446
- * have nothing to collapse.
447
- */
448
- isDesktopCollapsed = computed(() => !!this.layoutService.layoutState().staticMenuDesktopInactive &&
449
- !this.isOverlay() &&
450
- !this.isTopbarMode(), /* @ts-ignore */
451
- ...(ngDevMode ? [{ debugName: "isDesktopCollapsed" }] : /* istanbul ignore next */ []));
452
- }
453
-
454
- class AppFooter {
455
- layoutService = inject(LayoutService);
456
- appName = this.layoutService.appName;
457
- displayLogo = this.layoutService.appLogo;
458
- isLogoSvg = this.layoutService.isAppLogoSvg;
459
- authorName = this.layoutService.authorName;
460
- authorUrl = this.layoutService.authorUrl;
461
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
462
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppFooter, isStandalone: true, selector: "app-footer", ngImport: i0, template: `<div class="layout-footer">
106
+ var LayoutService = class LayoutService {
107
+ document = inject(DOCUMENT);
108
+ platformId = inject(PLATFORM_ID);
109
+ isBrowser = isPlatformBrowser(this.platformId);
110
+ persistence = inject(LayoutPersistenceService);
111
+ appConfig = inject(APP_CONFIG, { optional: true });
112
+ DEFAULT_CONFIG = this.appConfig?.layoutConfig ?? {
113
+ preset: "Aura",
114
+ primary: "violet",
115
+ surface: null,
116
+ darkTheme: false,
117
+ menuMode: "static",
118
+ layoutTheme: "classic"
119
+ };
120
+ DEFAULT_STATE = {
121
+ staticMenuDesktopInactive: false,
122
+ overlayMenuActive: false,
123
+ configSidebarVisible: false,
124
+ staticMenuMobileActive: false,
125
+ menuHoverActive: false
126
+ };
127
+ memorable = this.DEFAULT_CONFIG.isMemorable !== false;
128
+ _layoutConfig = signal({
129
+ ...this.DEFAULT_CONFIG,
130
+ ...this.memorable ? this.persistence.load() : null
131
+ }, ...ngDevMode ? [{ debugName: "_layoutConfig" }] : /* istanbul ignore next */ []);
132
+ _layoutState = signal(this.DEFAULT_STATE, ...ngDevMode ? [{ debugName: "_layoutState" }] : /* istanbul ignore next */ []);
133
+ _transitionComplete = signal(false, ...ngDevMode ? [{ debugName: "_transitionComplete" }] : /* istanbul ignore next */ []);
134
+ layoutConfig = this._layoutConfig.asReadonly();
135
+ layoutState = this._layoutState.asReadonly();
136
+ transitionComplete = this._transitionComplete.asReadonly();
137
+ _userProfile = signal(null, ...ngDevMode ? [{ debugName: "_userProfile" }] : /* istanbul ignore next */ []);
138
+ _companyProfile = signal(null, ...ngDevMode ? [{ debugName: "_companyProfile" }] : /* istanbul ignore next */ []);
139
+ _rawMenu = signal([], ...ngDevMode ? [{ debugName: "_rawMenu" }] : /* istanbul ignore next */ []);
140
+ _rawApps = signal([], ...ngDevMode ? [{ debugName: "_rawApps" }] : /* istanbul ignore next */ []);
141
+ permissionValidator = inject(PermissionValidatorService);
142
+ destroyRef = inject(DestroyRef);
143
+ _windowWidth = signal(this.isBrowser ? window.innerWidth : 992, ...ngDevMode ? [{ debugName: "_windowWidth" }] : /* istanbul ignore next */ []);
144
+ userProfile = this._userProfile.asReadonly();
145
+ companyProfile = this._companyProfile.asReadonly();
146
+ appName = this.appConfig?.appName ?? DEFAULT_APP_NAME;
147
+ appLogo = this.appConfig?.appLogo ?? "";
148
+ isAppLogoSvg = this.appLogo.toLowerCase().endsWith(".svg");
149
+ authorName = this.appConfig?.author?.name ?? DEFAULT_AUTHOR.name;
150
+ authorUrl = this.appConfig?.author?.url ?? DEFAULT_AUTHOR.url;
151
+ menu = computed(() => filterMenuByPermissions(this._rawMenu(), this.permissionValidator.permissions()), ...ngDevMode ? [{ debugName: "menu" }] : /* istanbul ignore next */ []);
152
+ apps = computed(() => filterAppsByPermissions(this._rawApps(), this.permissionValidator.permissions()), ...ngDevMode ? [{ debugName: "apps" }] : /* istanbul ignore next */ []);
153
+ hasApps = computed(() => this.apps().length > 0, ...ngDevMode ? [{ debugName: "hasApps" }] : /* istanbul ignore next */ []);
154
+ isSidebarActive = computed(() => this._layoutState().overlayMenuActive || this._layoutState().staticMenuMobileActive, ...ngDevMode ? [{ debugName: "isSidebarActive" }] : /* istanbul ignore next */ []);
155
+ isDesktop = computed(() => this._windowWidth() > DESKTOP_BREAKPOINT, ...ngDevMode ? [{ debugName: "isDesktop" }] : /* istanbul ignore next */ []);
156
+ isMobile = computed(() => !this.isDesktop(), ...ngDevMode ? [{ debugName: "isMobile" }] : /* istanbul ignore next */ []);
157
+ isDarkTheme = computed(() => this._layoutConfig().darkTheme, ...ngDevMode ? [{ debugName: "isDarkTheme" }] : /* istanbul ignore next */ []);
158
+ isOverlay = computed(() => this._layoutConfig().menuMode === "overlay", ...ngDevMode ? [{ debugName: "isOverlay" }] : /* istanbul ignore next */ []);
159
+ layoutTheme = computed(() => this._layoutConfig().layoutTheme ?? "classic", ...ngDevMode ? [{ debugName: "layoutTheme" }] : /* istanbul ignore next */ []);
160
+ isModernLayout = computed(() => this.layoutTheme() === "modern", ...ngDevMode ? [{ debugName: "isModernLayout" }] : /* istanbul ignore next */ []);
161
+ isClassicLayout = computed(() => this.layoutTheme() === "classic", ...ngDevMode ? [{ debugName: "isClassicLayout" }] : /* istanbul ignore next */ []);
162
+ isTopbar = computed(() => this._layoutConfig().menuMode === "topbar" && !this.isModernLayout(), ...ngDevMode ? [{ debugName: "isTopbar" }] : /* istanbul ignore next */ []);
163
+ userName = computed(() => this._userProfile()?.name ?? "Guest", ...ngDevMode ? [{ debugName: "userName" }] : /* istanbul ignore next */ []);
164
+ userEmail = computed(() => this._userProfile()?.email ?? "", ...ngDevMode ? [{ debugName: "userEmail" }] : /* istanbul ignore next */ []);
165
+ userProfilePictureUrl = computed(() => this._userProfile()?.profilePictureUrl || null, ...ngDevMode ? [{ debugName: "userProfilePictureUrl" }] : /* istanbul ignore next */ []);
166
+ companyLogoUrl = computed(() => this._companyProfile()?.logoUrl || null, ...ngDevMode ? [{ debugName: "companyLogoUrl" }] : /* istanbul ignore next */ []);
167
+ isAuthenticated = computed(() => !!this._userProfile(), ...ngDevMode ? [{ debugName: "isAuthenticated" }] : /* istanbul ignore next */ []);
168
+ companyName = computed(() => {
169
+ if (!this.appConfig || !isCompanyFeatureEnabled(this.appConfig)) return this.appName;
170
+ return this._companyProfile()?.name || this.appName;
171
+ }, ...ngDevMode ? [{ debugName: "companyName" }] : /* istanbul ignore next */ []);
172
+ displayLogo = computed(() => this.companyLogoUrl() ?? this.appLogo, ...ngDevMode ? [{ debugName: "displayLogo" }] : /* istanbul ignore next */ []);
173
+ isDisplayLogoSvg = computed(() => this.displayLogo().toLowerCase().endsWith(".svg"), ...ngDevMode ? [{ debugName: "isDisplayLogoSvg" }] : /* istanbul ignore next */ []);
174
+ overlayOpen = new Subject();
175
+ menuSource = new Subject();
176
+ resetSource = new Subject();
177
+ menuSource$ = this.menuSource.asObservable();
178
+ resetSource$ = this.resetSource.asObservable();
179
+ overlayOpen$ = this.overlayOpen.asObservable();
180
+ initialized = false;
181
+ appliedDarkTheme = this._layoutConfig().darkTheme;
182
+ constructor() {
183
+ if (this.isBrowser) {
184
+ this.toggleDarkMode();
185
+ fromEvent(window, "resize").pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
186
+ this._windowWidth.set(window.innerWidth);
187
+ });
188
+ }
189
+ effect(() => {
190
+ const config = this._layoutConfig();
191
+ if (this.initialized) {
192
+ if (config.darkTheme !== this.appliedDarkTheme) {
193
+ this.appliedDarkTheme = config.darkTheme;
194
+ this.handleDarkModeTransition(config);
195
+ }
196
+ if (this.memorable) this.persistence.save(config);
197
+ }
198
+ this.initialized = true;
199
+ });
200
+ }
201
+ toggleDarkMode(config) {
202
+ const isDark = (config ?? this._layoutConfig()).darkTheme;
203
+ this.document.documentElement.classList.toggle("app-dark", isDark);
204
+ }
205
+ handleDarkModeTransition(config) {
206
+ const doc = this.document;
207
+ if ("startViewTransition" in doc && typeof doc.startViewTransition === "function") doc.startViewTransition(() => this.toggleDarkMode(config)).ready.then(() => this.onTransitionEnd()).catch(() => {});
208
+ else {
209
+ this.toggleDarkMode(config);
210
+ this.onTransitionEnd();
211
+ }
212
+ }
213
+ onTransitionEnd() {
214
+ this._transitionComplete.set(true);
215
+ setTimeout(() => this._transitionComplete.set(false));
216
+ }
217
+ onMenuToggle() {
218
+ const state = this._layoutState();
219
+ if (!this.isDesktop()) {
220
+ const newMobileActive = !state.staticMenuMobileActive;
221
+ this._layoutState.update((prev) => ({
222
+ ...prev,
223
+ staticMenuMobileActive: newMobileActive
224
+ }));
225
+ if (newMobileActive) this.overlayOpen.next();
226
+ return;
227
+ }
228
+ if (this.isOverlay()) {
229
+ const newOverlayActive = !state.overlayMenuActive;
230
+ this._layoutState.update((prev) => ({
231
+ ...prev,
232
+ overlayMenuActive: newOverlayActive
233
+ }));
234
+ if (newOverlayActive) this.overlayOpen.next();
235
+ } else this._layoutState.update((prev) => ({
236
+ ...prev,
237
+ staticMenuDesktopInactive: !state.staticMenuDesktopInactive
238
+ }));
239
+ }
240
+ onMenuStateChange(event) {
241
+ this.menuSource.next(event);
242
+ }
243
+ reset() {
244
+ this.resetSource.next(true);
245
+ }
246
+ updateLayoutConfig(config) {
247
+ this._layoutConfig.update((prev) => ({
248
+ ...prev,
249
+ ...config
250
+ }));
251
+ }
252
+ updateLayoutState(state) {
253
+ this._layoutState.update((prev) => ({
254
+ ...prev,
255
+ ...state
256
+ }));
257
+ }
258
+ setUserProfile(profile) {
259
+ this._userProfile.set(profile);
260
+ }
261
+ setCompanyProfile(profile) {
262
+ this._companyProfile.set(profile);
263
+ }
264
+ setMenu(items) {
265
+ this._rawMenu.set(items);
266
+ }
267
+ clearMenu() {
268
+ this._rawMenu.set([]);
269
+ }
270
+ setApps(apps) {
271
+ this._rawApps.set(apps);
272
+ }
273
+ clearApps() {
274
+ this._rawApps.set([]);
275
+ }
276
+ static ɵfac = i0.ɵɵngDeclareFactory({
277
+ minVersion: "12.0.0",
278
+ version: "22.2.1",
279
+ ngImport: i0,
280
+ type: LayoutService,
281
+ deps: [],
282
+ target: i0.ɵɵFactoryTarget.Service
283
+ });
284
+ static ɵprov = i0.ɵɵngDeclareService({
285
+ minVersion: "22.0.0",
286
+ version: "22.2.1",
287
+ ngImport: i0,
288
+ type: LayoutService
289
+ });
290
+ };
291
+ i0.ɵɵngDeclareClassMetadata({
292
+ minVersion: "12.0.0",
293
+ version: "22.2.1",
294
+ ngImport: i0,
295
+ type: LayoutService,
296
+ decorators: [{ type: Service }],
297
+ ctorParameters: () => []
298
+ });
299
+ var LayoutShellBase = class {
300
+ destroyRef = inject(DestroyRef);
301
+ document = inject(DOCUMENT);
302
+ renderer = inject(Renderer2);
303
+ router = inject(Router);
304
+ layoutService = inject(LayoutService);
305
+ menuOutsideClickListener = null;
306
+ onNavigated() {}
307
+ constructor() {
308
+ this.layoutService.overlayOpen$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
309
+ if (!this.menuOutsideClickListener) this.menuOutsideClickListener = this.renderer.listen(this.document, "click", (event) => {
310
+ if (this.isOutsideClicked(event)) this.hideMenu();
311
+ });
312
+ if (this.layoutService.layoutState().staticMenuMobileActive) this.blockBodyScroll();
313
+ });
314
+ this.router.events.pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef)).subscribe(() => {
315
+ this.hideMenu();
316
+ this.onNavigated();
317
+ });
318
+ }
319
+ isOutsideClicked(event) {
320
+ const eventTarget = event.target;
321
+ return !this.outsideClickSelectors().some((selector) => Array.from(this.document.querySelectorAll(selector)).some((element) => element.isSameNode(eventTarget) || element.contains(eventTarget)));
322
+ }
323
+ hideMenu() {
324
+ this.layoutService.updateLayoutState({
325
+ overlayMenuActive: false,
326
+ staticMenuMobileActive: false,
327
+ menuHoverActive: false
328
+ });
329
+ if (this.menuOutsideClickListener) {
330
+ this.menuOutsideClickListener();
331
+ this.menuOutsideClickListener = null;
332
+ }
333
+ this.unblockBodyScroll();
334
+ }
335
+ blockBodyScroll() {
336
+ this.document.body.classList.add("blocked-scroll");
337
+ }
338
+ unblockBodyScroll() {
339
+ this.document.body.classList.remove("blocked-scroll");
340
+ }
341
+ isOverlay = this.layoutService.isOverlay;
342
+ isTopbarMode = this.layoutService.isTopbar;
343
+ isOverlayActive = computed(() => this.layoutService.layoutState().overlayMenuActive, ...ngDevMode ? [{ debugName: "isOverlayActive" }] : /* istanbul ignore next */ []);
344
+ isMobileActive = computed(() => this.layoutService.layoutState().staticMenuMobileActive, ...ngDevMode ? [{ debugName: "isMobileActive" }] : /* istanbul ignore next */ []);
345
+ isDesktopCollapsed = computed(() => !!this.layoutService.layoutState().staticMenuDesktopInactive && !this.isOverlay() && !this.isTopbarMode(), ...ngDevMode ? [{ debugName: "isDesktopCollapsed" }] : /* istanbul ignore next */ []);
346
+ };
347
+ var AppFooter = class AppFooter {
348
+ layoutService = inject(LayoutService);
349
+ appName = this.layoutService.appName;
350
+ displayLogo = this.layoutService.appLogo;
351
+ isLogoSvg = this.layoutService.isAppLogoSvg;
352
+ authorName = this.layoutService.authorName;
353
+ authorUrl = this.layoutService.authorUrl;
354
+ static ɵfac = i0.ɵɵngDeclareFactory({
355
+ minVersion: "12.0.0",
356
+ version: "22.2.1",
357
+ ngImport: i0,
358
+ type: AppFooter,
359
+ deps: [],
360
+ target: i0.ɵɵFactoryTarget.Component
361
+ });
362
+ static ɵcmp = i0.ɵɵngDeclareComponent({
363
+ minVersion: "17.0.0",
364
+ version: "22.2.1",
365
+ type: AppFooter,
366
+ isStandalone: true,
367
+ selector: "app-footer",
368
+ ngImport: i0,
369
+ template: `<div class="layout-footer">
463
370
  @if (displayLogo) {
464
371
  @if (isLogoSvg) {
465
372
  <inline-svg [src]="displayLogo" class="logo-image" />
@@ -477,14 +384,31 @@ class AppFooter {
477
384
  >{{ authorName }}</a
478
385
  >
479
386
  </span>
480
- </div>`, isInline: true, dependencies: [{ kind: "component", type: InlineSvgComponent, selector: "inline-svg", inputs: ["src"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
481
- }
482
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppFooter, decorators: [{
483
- type: Component,
484
- args: [{
485
- selector: 'app-footer',
486
- imports: [TranslatePipe, InlineSvgComponent],
487
- template: `<div class="layout-footer">
387
+ </div>`,
388
+ isInline: true,
389
+ dependencies: [{
390
+ kind: "component",
391
+ type: InlineSvgComponent,
392
+ selector: "inline-svg",
393
+ inputs: ["src"]
394
+ }, {
395
+ kind: "pipe",
396
+ type: TranslatePipe,
397
+ name: "translate"
398
+ }]
399
+ });
400
+ };
401
+ i0.ɵɵngDeclareClassMetadata({
402
+ minVersion: "12.0.0",
403
+ version: "22.2.1",
404
+ ngImport: i0,
405
+ type: AppFooter,
406
+ decorators: [{
407
+ type: Component,
408
+ args: [{
409
+ selector: "app-footer",
410
+ imports: [TranslatePipe, InlineSvgComponent],
411
+ template: `<div class="layout-footer">
488
412
  @if (displayLogo) {
489
413
  @if (isLogoSvg) {
490
414
  <inline-svg [src]="displayLogo" class="logo-image" />
@@ -502,132 +426,133 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
502
426
  >{{ authorName }}</a
503
427
  >
504
428
  </span>
505
- </div>`,
506
- }]
507
- }] });
508
-
509
- class AppMenuitem {
510
- item = input.required(/* @ts-ignore */
511
- ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
512
- index = input.required(/* @ts-ignore */
513
- ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
514
- parentKey = input('', /* @ts-ignore */
515
- ...(ngDevMode ? [{ debugName: "parentKey" }] : /* istanbul ignore next */ []));
516
- router = inject(Router);
517
- layoutService = inject(LayoutService);
518
- destroyRef = inject(DestroyRef);
519
- parentLink = viewChild('parentLink', /* @ts-ignore */
520
- ...(ngDevMode ? [{ debugName: "parentLink" }] : /* istanbul ignore next */ []));
521
- _active = signal(false, /* @ts-ignore */
522
- ...(ngDevMode ? [{ debugName: "_active" }] : /* istanbul ignore next */ []));
523
- active = this._active.asReadonly();
524
- key = computed(() => {
525
- const parent = this.parentKey();
526
- return parent ? `${parent}-${this.index()}` : String(this.index());
527
- }, /* @ts-ignore */
528
- ...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
529
- routerLink = computed(() => {
530
- const menuItem = this.item();
531
- return menuItem.routerLink ?? [];
532
- }, /* @ts-ignore */
533
- ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
534
- // Computed options for routerLinkActive - use 'exact' for root, 'subset' for others
535
- routerLinkActiveOptions = computed(() => {
536
- const link = this.routerLink();
537
- const pathsMatch = link[0] === '/' ? 'exact' : 'subset';
538
- return {
539
- paths: pathsMatch,
540
- queryParams: 'ignored',
541
- matrixParams: 'ignored',
542
- fragment: 'ignored',
543
- };
544
- }, /* @ts-ignore */
545
- ...(ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : /* istanbul ignore next */ []));
546
- constructor() {
547
- this.layoutService.menuSource$
548
- .pipe(takeUntilDestroyed(this.destroyRef))
549
- .subscribe((value) => {
550
- Promise.resolve(null).then(() => {
551
- const currentKey = this.key();
552
- if (value.routeEvent) {
553
- this._active.set(value.key === currentKey ||
554
- value.key?.startsWith(currentKey + '-'));
555
- }
556
- else if (value.key !== currentKey &&
557
- !value.key?.startsWith(currentKey + '-')) {
558
- this._active.set(false);
559
- }
560
- });
561
- });
562
- this.layoutService.resetSource$
563
- .pipe(takeUntilDestroyed(this.destroyRef))
564
- .subscribe(() => this._active.set(false));
565
- this.router.events
566
- .pipe(filter$1((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef))
567
- .subscribe(() => {
568
- if (this.item().routerLink) {
569
- this.updateActiveStateFromRoute();
570
- }
571
- });
572
- effect(() => {
573
- const menuItem = this.item();
574
- if (menuItem.routerLink) {
575
- this.updateActiveStateFromRoute();
576
- }
577
- });
578
- effect(() => {
579
- if (this.active() && this.parentLink()) {
580
- Promise.resolve().then(() => this.updateSubmenuPosition());
581
- }
582
- });
583
- }
584
- onMouseEnter() {
585
- if (this.layoutService.isTopbar() && this.item().children) {
586
- this.updateSubmenuPosition();
587
- }
588
- }
589
- updateSubmenuPosition() {
590
- const linkEl = this.parentLink()?.nativeElement;
591
- if (!linkEl || typeof linkEl.getBoundingClientRect !== 'function')
592
- return;
593
- const rect = linkEl.getBoundingClientRect();
594
- const hostEl = linkEl.closest('[app-menuitem]');
595
- if (!hostEl)
596
- return;
597
- // Position submenu to the left of the parent link and above it
598
- const left = rect.left;
599
- const top = rect.top + 30; // 8px gap above the menu item
600
- hostEl.style.setProperty('--submenu-left', `${left}px`);
601
- hostEl.style.setProperty('--submenu-top', `${top}px`);
602
- }
603
- updateActiveStateFromRoute() {
604
- const link = this.routerLink();
605
- if (!link.length)
606
- return;
607
- // Use 'exact' for root path to avoid matching all routes
608
- // Use 'subset' for all other paths to match child routes
609
- const pathsMatch = link[0] === '/' ? 'exact' : 'subset';
610
- const active = isActive(link[0], this.router, {
611
- paths: pathsMatch,
612
- queryParams: 'ignored',
613
- matrixParams: 'ignored',
614
- fragment: 'ignored',
615
- })();
616
- if (active) {
617
- this.layoutService.onMenuStateChange({
618
- key: this.key(),
619
- routeEvent: true,
620
- });
621
- }
622
- }
623
- itemClick() {
624
- if (this.item().children && !this.layoutService.isTopbar()) {
625
- this._active.update((prev) => !prev);
626
- }
627
- this.layoutService.onMenuStateChange({ key: this.key() });
628
- }
629
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppMenuitem, deps: [], target: i0.ɵɵFactoryTarget.Component });
630
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppMenuitem, isStandalone: true, selector: "[app-menuitem]", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, parentKey: { classPropertyName: "parentKey", publicName: "parentKey", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.active-menuitem": "active()" } }, viewQueries: [{ propertyName: "parentLink", first: true, predicate: ["parentLink"], descendants: true, isSignal: true }], ngImport: i0, template: `
429
+ </div>`
430
+ }]
431
+ }]
432
+ });
433
+ var AppMenuitem = class AppMenuitem {
434
+ item = input.required(...ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []);
435
+ index = input.required(...ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []);
436
+ parentKey = input("", ...ngDevMode ? [{ debugName: "parentKey" }] : /* istanbul ignore next */ []);
437
+ router = inject(Router);
438
+ layoutService = inject(LayoutService);
439
+ destroyRef = inject(DestroyRef);
440
+ parentLink = viewChild("parentLink", ...ngDevMode ? [{ debugName: "parentLink" }] : /* istanbul ignore next */ []);
441
+ _active = signal(false, ...ngDevMode ? [{ debugName: "_active" }] : /* istanbul ignore next */ []);
442
+ active = this._active.asReadonly();
443
+ key = computed(() => {
444
+ const parent = this.parentKey();
445
+ return parent ? `${parent}-${this.index()}` : String(this.index());
446
+ }, ...ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []);
447
+ routerLink = computed(() => {
448
+ return this.item().routerLink ?? [];
449
+ }, ...ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []);
450
+ routerLinkActiveOptions = computed(() => {
451
+ return {
452
+ paths: this.routerLink()[0] === "/" ? "exact" : "subset",
453
+ queryParams: "ignored",
454
+ matrixParams: "ignored",
455
+ fragment: "ignored"
456
+ };
457
+ }, ...ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : /* istanbul ignore next */ []);
458
+ constructor() {
459
+ this.layoutService.menuSource$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
460
+ Promise.resolve(null).then(() => {
461
+ const currentKey = this.key();
462
+ if (value.routeEvent) this._active.set(value.key === currentKey || value.key?.startsWith(currentKey + "-"));
463
+ else if (value.key !== currentKey && !value.key?.startsWith(currentKey + "-")) this._active.set(false);
464
+ });
465
+ });
466
+ this.layoutService.resetSource$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this._active.set(false));
467
+ this.router.events.pipe(filter$1((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef)).subscribe(() => {
468
+ if (this.item().routerLink) this.updateActiveStateFromRoute();
469
+ });
470
+ effect(() => {
471
+ if (this.item().routerLink) this.updateActiveStateFromRoute();
472
+ });
473
+ effect(() => {
474
+ if (this.active() && this.parentLink()) Promise.resolve().then(() => this.updateSubmenuPosition());
475
+ });
476
+ }
477
+ onMouseEnter() {
478
+ if (this.layoutService.isTopbar() && this.item().children) this.updateSubmenuPosition();
479
+ }
480
+ updateSubmenuPosition() {
481
+ const linkEl = this.parentLink()?.nativeElement;
482
+ if (!linkEl || typeof linkEl.getBoundingClientRect !== "function") return;
483
+ const rect = linkEl.getBoundingClientRect();
484
+ const hostEl = linkEl.closest("[app-menuitem]");
485
+ if (!hostEl) return;
486
+ const left = rect.left;
487
+ const top = rect.top + 30;
488
+ hostEl.style.setProperty("--submenu-left", `${left}px`);
489
+ hostEl.style.setProperty("--submenu-top", `${top}px`);
490
+ }
491
+ updateActiveStateFromRoute() {
492
+ const link = this.routerLink();
493
+ if (!link.length) return;
494
+ const pathsMatch = link[0] === "/" ? "exact" : "subset";
495
+ if (isActive(link[0], this.router, {
496
+ paths: pathsMatch,
497
+ queryParams: "ignored",
498
+ matrixParams: "ignored",
499
+ fragment: "ignored"
500
+ })()) this.layoutService.onMenuStateChange({
501
+ key: this.key(),
502
+ routeEvent: true
503
+ });
504
+ }
505
+ itemClick() {
506
+ if (this.item().children && !this.layoutService.isTopbar()) this._active.update((prev) => !prev);
507
+ this.layoutService.onMenuStateChange({ key: this.key() });
508
+ }
509
+ static ɵfac = i0.ɵɵngDeclareFactory({
510
+ minVersion: "12.0.0",
511
+ version: "22.2.1",
512
+ ngImport: i0,
513
+ type: AppMenuitem,
514
+ deps: [],
515
+ target: i0.ɵɵFactoryTarget.Component
516
+ });
517
+ static ɵcmp = i0.ɵɵngDeclareComponent({
518
+ minVersion: "17.0.0",
519
+ version: "22.2.1",
520
+ type: AppMenuitem,
521
+ isStandalone: true,
522
+ selector: "[app-menuitem]",
523
+ inputs: {
524
+ item: {
525
+ classPropertyName: "item",
526
+ publicName: "item",
527
+ isSignal: true,
528
+ isRequired: true,
529
+ transformFunction: null
530
+ },
531
+ index: {
532
+ classPropertyName: "index",
533
+ publicName: "index",
534
+ isSignal: true,
535
+ isRequired: true,
536
+ transformFunction: null
537
+ },
538
+ parentKey: {
539
+ classPropertyName: "parentKey",
540
+ publicName: "parentKey",
541
+ isSignal: true,
542
+ isRequired: false,
543
+ transformFunction: null
544
+ }
545
+ },
546
+ host: { properties: { "class.active-menuitem": "active()" } },
547
+ viewQueries: [{
548
+ propertyName: "parentLink",
549
+ first: true,
550
+ predicate: ["parentLink"],
551
+ descendants: true,
552
+ isSignal: true
553
+ }],
554
+ ngImport: i0,
555
+ template: `
631
556
  <ng-container>
632
557
  @if (item().children?.length) {
633
558
  <a
@@ -696,11 +621,94 @@ class AppMenuitem {
696
621
  </ul>
697
622
  }
698
623
  </ng-container>
699
- `, isInline: true, styles: [":host ul{overflow:hidden;transition:max-height .4s cubic-bezier(.86,0,.07,1)}:host ul.submenu-collapsed{max-height:0}:host ul.submenu-expanded{max-height:1000px}\n"], dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "parentKey"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["icon", "iconType"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
700
- }
701
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppMenuitem, decorators: [{
702
- type: Component,
703
- args: [{ selector: '[app-menuitem]', imports: [UiIconComponent, IconComponent, RouterLink, RouterLinkActive, TranslatePipe], template: `
624
+ `,
625
+ isInline: true,
626
+ styles: [":host ul{overflow:hidden;transition:max-height .4s cubic-bezier(.86,0,.07,1)}:host ul.submenu-collapsed{max-height:0}:host ul.submenu-expanded{max-height:1000px}\n"],
627
+ dependencies: [
628
+ {
629
+ kind: "component",
630
+ type: AppMenuitem,
631
+ selector: "[app-menuitem]",
632
+ inputs: [
633
+ "item",
634
+ "index",
635
+ "parentKey"
636
+ ]
637
+ },
638
+ {
639
+ kind: "component",
640
+ type: UiIconComponent,
641
+ selector: "ui-icon",
642
+ inputs: [
643
+ "name",
644
+ "size",
645
+ "spin",
646
+ "fill",
647
+ "severity"
648
+ ]
649
+ },
650
+ {
651
+ kind: "component",
652
+ type: IconComponent,
653
+ selector: "lib-icon",
654
+ inputs: ["icon", "iconType"]
655
+ },
656
+ {
657
+ kind: "directive",
658
+ type: RouterLink,
659
+ selector: "[routerLink]",
660
+ inputs: [
661
+ "target",
662
+ "queryParams",
663
+ "fragment",
664
+ "queryParamsHandling",
665
+ "state",
666
+ "info",
667
+ "relativeTo",
668
+ "preserveFragment",
669
+ "skipLocationChange",
670
+ "replaceUrl",
671
+ "browserUrl",
672
+ "routerLink"
673
+ ]
674
+ },
675
+ {
676
+ kind: "directive",
677
+ type: RouterLinkActive,
678
+ selector: "[routerLinkActive]",
679
+ inputs: [
680
+ "routerLinkActiveOptions",
681
+ "ariaCurrentWhenActive",
682
+ "routerLinkActive"
683
+ ],
684
+ outputs: ["isActiveChange"],
685
+ exportAs: ["routerLinkActive"]
686
+ },
687
+ {
688
+ kind: "pipe",
689
+ type: TranslatePipe,
690
+ name: "translate"
691
+ }
692
+ ]
693
+ });
694
+ };
695
+ i0.ɵɵngDeclareClassMetadata({
696
+ minVersion: "12.0.0",
697
+ version: "22.2.1",
698
+ ngImport: i0,
699
+ type: AppMenuitem,
700
+ decorators: [{
701
+ type: Component,
702
+ args: [{
703
+ selector: "[app-menuitem]",
704
+ imports: [
705
+ UiIconComponent,
706
+ IconComponent,
707
+ RouterLink,
708
+ RouterLinkActive,
709
+ TranslatePipe
710
+ ],
711
+ template: `
704
712
  <ng-container>
705
713
  @if (item().children?.length) {
706
714
  <a
@@ -769,27 +777,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
769
777
  </ul>
770
778
  }
771
779
  </ng-container>
772
- `, host: {
773
- '[class.active-menuitem]': 'active()',
774
- }, styles: [":host ul{overflow:hidden;transition:max-height .4s cubic-bezier(.86,0,.07,1)}:host ul.submenu-collapsed{max-height:0}:host ul.submenu-expanded{max-height:1000px}\n"] }]
775
- }], ctorParameters: () => [], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], parentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentKey", required: false }] }], parentLink: [{ type: i0.ViewChild, args: ['parentLink', { isSignal: true }] }] } });
776
-
777
- /**
778
- * Main menu component that displays filtered menu items.
779
- * Menu is automatically filtered based on user permissions using the permission checker (logicNode pattern).
780
- * The filtering happens internally in LayoutService via computed signal.
781
- */
782
- class AppMenu {
783
- layoutService = inject(LayoutService);
784
- /**
785
- * Filtered menu items from layout service.
786
- * This signal is computed in LayoutService and automatically updates when:
787
- * - Raw menu changes (setMenu called)
788
- * - Permission state changes (user permissions updated)
789
- */
790
- menuItems = this.layoutService.menu;
791
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
792
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppMenu, isStandalone: true, selector: "app-menu", ngImport: i0, template: `<div class="layout-menu">
780
+ `,
781
+ host: { "[class.active-menuitem]": "active()" },
782
+ styles: [":host ul{overflow:hidden;transition:max-height .4s cubic-bezier(.86,0,.07,1)}:host ul.submenu-collapsed{max-height:0}:host ul.submenu-expanded{max-height:1000px}\n"]
783
+ }]
784
+ }],
785
+ ctorParameters: () => [],
786
+ propDecorators: {
787
+ item: [{
788
+ type: i0.Input,
789
+ args: [{
790
+ isSignal: true,
791
+ alias: "item",
792
+ required: true
793
+ }]
794
+ }],
795
+ index: [{
796
+ type: i0.Input,
797
+ args: [{
798
+ isSignal: true,
799
+ alias: "index",
800
+ required: true
801
+ }]
802
+ }],
803
+ parentKey: [{
804
+ type: i0.Input,
805
+ args: [{
806
+ isSignal: true,
807
+ alias: "parentKey",
808
+ required: false
809
+ }]
810
+ }],
811
+ parentLink: [{
812
+ type: i0.ViewChild,
813
+ args: ["parentLink", { isSignal: true }]
814
+ }]
815
+ }
816
+ });
817
+ var AppMenu = class AppMenu {
818
+ layoutService = inject(LayoutService);
819
+ menuItems = this.layoutService.menu;
820
+ static ɵfac = i0.ɵɵngDeclareFactory({
821
+ minVersion: "12.0.0",
822
+ version: "22.2.1",
823
+ ngImport: i0,
824
+ type: AppMenu,
825
+ deps: [],
826
+ target: i0.ɵɵFactoryTarget.Component
827
+ });
828
+ static ɵcmp = i0.ɵɵngDeclareComponent({
829
+ minVersion: "17.0.0",
830
+ version: "22.2.1",
831
+ type: AppMenu,
832
+ isStandalone: true,
833
+ selector: "app-menu",
834
+ ngImport: i0,
835
+ template: `<div class="layout-menu">
793
836
  <ul>
794
837
  @for (
795
838
  item of menuItems();
@@ -799,14 +842,31 @@ class AppMenu {
799
842
  <li app-menuitem [item]="item" [index]="i"></li>
800
843
  }
801
844
  </ul>
802
- </div>`, isInline: true, dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "parentKey"] }] });
803
- }
804
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppMenu, decorators: [{
805
- type: Component,
806
- args: [{
807
- selector: 'app-menu',
808
- imports: [AppMenuitem],
809
- template: `<div class="layout-menu">
845
+ </div>`,
846
+ isInline: true,
847
+ dependencies: [{
848
+ kind: "component",
849
+ type: AppMenuitem,
850
+ selector: "[app-menuitem]",
851
+ inputs: [
852
+ "item",
853
+ "index",
854
+ "parentKey"
855
+ ]
856
+ }]
857
+ });
858
+ };
859
+ i0.ɵɵngDeclareClassMetadata({
860
+ minVersion: "12.0.0",
861
+ version: "22.2.1",
862
+ ngImport: i0,
863
+ type: AppMenu,
864
+ decorators: [{
865
+ type: Component,
866
+ args: [{
867
+ selector: "app-menu",
868
+ imports: [AppMenuitem],
869
+ template: `<div class="layout-menu">
810
870
  <ul>
811
871
  @for (
812
872
  item of menuItems();
@@ -816,41 +876,78 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
816
876
  <li app-menuitem [item]="item" [index]="i"></li>
817
877
  }
818
878
  </ul>
819
- </div>`,
820
- }]
821
- }] });
822
-
823
- class AppSidebar {
824
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
825
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: AppSidebar, isStandalone: true, selector: "app-sidebar", ngImport: i0, template: `
879
+ </div>`
880
+ }]
881
+ }]
882
+ });
883
+ var AppSidebar = class AppSidebar {
884
+ static ɵfac = i0.ɵɵngDeclareFactory({
885
+ minVersion: "12.0.0",
886
+ version: "22.2.1",
887
+ ngImport: i0,
888
+ type: AppSidebar,
889
+ deps: [],
890
+ target: i0.ɵɵFactoryTarget.Component
891
+ });
892
+ static ɵcmp = i0.ɵɵngDeclareComponent({
893
+ minVersion: "14.0.0",
894
+ version: "22.2.1",
895
+ type: AppSidebar,
896
+ isStandalone: true,
897
+ selector: "app-sidebar",
898
+ ngImport: i0,
899
+ template: `
826
900
  <div class="layout-sidebar">
827
901
  <app-menu />
828
902
  </div>
829
- `, isInline: true, dependencies: [{ kind: "component", type: AppMenu, selector: "app-menu" }] });
830
- }
831
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppSidebar, decorators: [{
832
- type: Component,
833
- args: [{
834
- selector: 'app-sidebar',
835
- imports: [AppMenu],
836
- template: `
903
+ `,
904
+ isInline: true,
905
+ dependencies: [{
906
+ kind: "component",
907
+ type: AppMenu,
908
+ selector: "app-menu"
909
+ }]
910
+ });
911
+ };
912
+ i0.ɵɵngDeclareClassMetadata({
913
+ minVersion: "12.0.0",
914
+ version: "22.2.1",
915
+ ngImport: i0,
916
+ type: AppSidebar,
917
+ decorators: [{
918
+ type: Component,
919
+ args: [{
920
+ selector: "app-sidebar",
921
+ imports: [AppMenu],
922
+ template: `
837
923
  <div class="layout-sidebar">
838
924
  <app-menu />
839
925
  </div>
840
- `,
841
- }]
842
- }] });
843
-
844
- /**
845
- * Home link showing the active company logo and name.
846
- * Every skin renders this same markup; sizing and colour come from the bar it sits in.
847
- */
848
- class AppBrandLogo {
849
- layoutService = inject(LayoutService);
850
- companyName = this.layoutService.companyName;
851
- displayLogo = this.layoutService.displayLogo;
852
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppBrandLogo, deps: [], target: i0.ɵɵFactoryTarget.Component });
853
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppBrandLogo, isStandalone: true, selector: "app-brand-logo", host: { styleAttribute: "display: contents" }, ngImport: i0, template: `
926
+ `
927
+ }]
928
+ }]
929
+ });
930
+ var AppBrandLogo = class AppBrandLogo {
931
+ layoutService = inject(LayoutService);
932
+ companyName = this.layoutService.companyName;
933
+ displayLogo = this.layoutService.displayLogo;
934
+ static ɵfac = i0.ɵɵngDeclareFactory({
935
+ minVersion: "12.0.0",
936
+ version: "22.2.1",
937
+ ngImport: i0,
938
+ type: AppBrandLogo,
939
+ deps: [],
940
+ target: i0.ɵɵFactoryTarget.Component
941
+ });
942
+ static ɵcmp = i0.ɵɵngDeclareComponent({
943
+ minVersion: "17.0.0",
944
+ version: "22.2.1",
945
+ type: AppBrandLogo,
946
+ isStandalone: true,
947
+ selector: "app-brand-logo",
948
+ host: { styleAttribute: "display: contents" },
949
+ ngImport: i0,
950
+ template: `
854
951
  <a class="layout-brand-logo" routerLink="/">
855
952
  @if (displayLogo()) {
856
953
  @if (layoutService.isDisplayLogoSvg()) {
@@ -861,15 +958,46 @@ class AppBrandLogo {
861
958
  }
862
959
  <span>{{ companyName() }}</span>
863
960
  </a>
864
- `, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: InlineSvgComponent, selector: "inline-svg", inputs: ["src"] }] });
865
- }
866
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppBrandLogo, decorators: [{
867
- type: Component,
868
- args: [{
869
- selector: 'app-brand-logo',
870
- imports: [RouterLink, InlineSvgComponent],
871
- host: { style: 'display: contents' },
872
- template: `
961
+ `,
962
+ isInline: true,
963
+ dependencies: [{
964
+ kind: "directive",
965
+ type: RouterLink,
966
+ selector: "[routerLink]",
967
+ inputs: [
968
+ "target",
969
+ "queryParams",
970
+ "fragment",
971
+ "queryParamsHandling",
972
+ "state",
973
+ "info",
974
+ "relativeTo",
975
+ "preserveFragment",
976
+ "skipLocationChange",
977
+ "replaceUrl",
978
+ "browserUrl",
979
+ "routerLink"
980
+ ]
981
+ }, {
982
+ kind: "component",
983
+ type: InlineSvgComponent,
984
+ selector: "inline-svg",
985
+ inputs: ["src"]
986
+ }]
987
+ });
988
+ };
989
+ i0.ɵɵngDeclareClassMetadata({
990
+ minVersion: "12.0.0",
991
+ version: "22.2.1",
992
+ ngImport: i0,
993
+ type: AppBrandLogo,
994
+ decorators: [{
995
+ type: Component,
996
+ args: [{
997
+ selector: "app-brand-logo",
998
+ imports: [RouterLink, InlineSvgComponent],
999
+ host: { style: "display: contents" },
1000
+ template: `
873
1001
  <a class="layout-brand-logo" routerLink="/">
874
1002
  @if (displayLogo()) {
875
1003
  @if (layoutService.isDisplayLogoSvg()) {
@@ -880,134 +1008,153 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
880
1008
  }
881
1009
  <span>{{ companyName() }}</span>
882
1010
  </a>
883
- `,
884
- }]
885
- }] });
886
-
887
- const LAYOUT_AUTH_STATE = new InjectionToken('LAYOUT_AUTH_STATE');
888
- const LAYOUT_AUTH_API = new InjectionToken('LAYOUT_AUTH_API');
889
- const LAYOUT_NOTIFICATION_BELL = new InjectionToken('LAYOUT_NOTIFICATION_BELL');
890
- const LAYOUT_LANGUAGE_SELECTOR = new InjectionToken('LAYOUT_LANGUAGE_SELECTOR');
891
- const LAYOUT_LANGUAGE_SELECTOR_PANEL = new InjectionToken('LAYOUT_LANGUAGE_SELECTOR_PANEL');
892
- const LAYOUT_SEARCHBAR = new InjectionToken('LAYOUT_SEARCHBAR');
893
- const LAYOUT_IS_RTL = new InjectionToken('LAYOUT_IS_RTL');
894
- const LAYOUT_SEARCH_ADAPTER = new InjectionToken('LAYOUT_SEARCH_ADAPTER');
895
-
896
- class AppConfigurator {
897
- translateAdapter = inject(TranslateService, { optional: true });
898
- platformId = inject(PLATFORM_ID);
899
- layoutService = inject(LayoutService);
900
- showMenuModeButton = input(true, /* @ts-ignore */
901
- ...(ngDevMode ? [{ debugName: "showMenuModeButton" }] : /* istanbul ignore next */ []));
902
- presets = PRESET_NAMES;
903
- menuModeOptions = computed(() => {
904
- const options = [
905
- { label: this.translate('layout.configurator.menu.mode.static'), value: 'static' },
906
- { label: this.translate('layout.configurator.menu.mode.overlay'), value: 'overlay' },
907
- ];
908
- // Topbar mode renders a horizontal nav, which only the classic skin provides.
909
- if (this.layoutService.isDesktop() && !this.layoutService.isModernLayout()) {
910
- options.push({ label: this.translate('layout.configurator.menu.mode.topbar'), value: 'topbar' });
911
- }
912
- return options;
913
- }, /* @ts-ignore */
914
- ...(ngDevMode ? [{ debugName: "menuModeOptions" }] : /* istanbul ignore next */ []));
915
- layoutThemeOptions = computed(() => [
916
- { label: this.translate('layout.configurator.layout.theme.classic'), value: 'classic' },
917
- { label: this.translate('layout.configurator.layout.theme.modern'), value: 'modern' },
918
- ], /* @ts-ignore */
919
- ...(ngDevMode ? [{ debugName: "layoutThemeOptions" }] : /* istanbul ignore next */ []));
920
- constructor() {
921
- if (isPlatformBrowser(this.platformId)) {
922
- effect(() => {
923
- const config = this.layoutService.layoutConfig();
924
- applyTheme({
925
- preset: (config.preset ?? 'Aura'),
926
- primary: config.primary ?? 'violet',
927
- surface: config.surface ?? null,
928
- darkTheme: config.darkTheme ?? false,
929
- });
930
- });
931
- }
932
- }
933
- surfaces = Object.entries(SURFACE_PALETTES).map(([name, pal]) => ({
934
- name,
935
- palette: pal,
936
- }));
937
- selectedPrimaryColor = computed(() => this.layoutService.layoutConfig().primary, /* @ts-ignore */
938
- ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : /* istanbul ignore next */ []));
939
- selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface, /* @ts-ignore */
940
- ...(ngDevMode ? [{ debugName: "selectedSurfaceColor" }] : /* istanbul ignore next */ []));
941
- isSurfaceSelected(surfaceName) {
942
- const sel = this.selectedSurfaceColor();
943
- if (sel)
944
- return sel === surfaceName;
945
- const isDark = this.layoutService.layoutConfig().darkTheme;
946
- return isDark ? surfaceName === 'zinc' : surfaceName === 'slate';
947
- }
948
- selectedPreset = computed(() => this.layoutService.layoutConfig().preset, /* @ts-ignore */
949
- ...(ngDevMode ? [{ debugName: "selectedPreset" }] : /* istanbul ignore next */ []));
950
- menuMode = computed(() => this.layoutService.layoutConfig().menuMode, /* @ts-ignore */
951
- ...(ngDevMode ? [{ debugName: "menuMode" }] : /* istanbul ignore next */ []));
952
- layoutTheme = this.layoutService.layoutTheme;
953
- primaryColors = computed(() => {
954
- const palettes = [{ name: 'noir', palette: {} }];
955
- for (const [name, pal] of Object.entries(PRIMARY_PALETTES)) {
956
- palettes.push({ name, palette: pal });
957
- }
958
- return palettes;
959
- }, /* @ts-ignore */
960
- ...(ngDevMode ? [{ debugName: "primaryColors" }] : /* istanbul ignore next */ []));
961
- updateColors(event, type, color) {
962
- if (type === 'primary') {
963
- this.layoutService.updateLayoutConfig({ primary: color.name });
964
- }
965
- else if (type === 'surface') {
966
- this.layoutService.updateLayoutConfig({ surface: color.name });
967
- }
968
- this._applyColorChange(type, color);
969
- event.stopPropagation();
970
- }
971
- _applyColorChange(type, color) {
972
- const isDark = this.layoutService.layoutConfig().darkTheme ?? false;
973
- if (type === 'primary') {
974
- const primary = color.name ?? 'violet';
975
- // NOIR_PRIMARY, not a copy of a surface palette: its shades are references to
976
- // the active surface ramp, which is what lets applyPrimaryPalette recognise the
977
- // neutral and derive the same tokens a preset switch to Nora would.
978
- const pal = primary === 'noir' ? NOIR_PRIMARY : PRIMARY_PALETTES[primary];
979
- if (pal)
980
- applyPrimaryPalette(pal, isDark);
981
- }
982
- else if (type === 'surface') {
983
- const surfacePal = SURFACE_PALETTES[color.name ?? 'slate'];
984
- if (surfacePal)
985
- applySurfacePalette(surfacePal);
986
- }
987
- }
988
- onPresetChange(event) {
989
- const primary = PRESETS[event]?.signaturePrimary ?? 'violet';
990
- this.layoutService.updateLayoutConfig({ preset: event, primary });
991
- const isDark = this.layoutService.layoutConfig().darkTheme ?? false;
992
- const surface = this.layoutService.layoutConfig().surface ?? null;
993
- applyTheme({ preset: event, primary, surface, darkTheme: isDark });
994
- }
995
- onMenuModeChange(event) {
996
- this.layoutService.updateLayoutConfig({ menuMode: event });
997
- }
998
- onLayoutThemeChange(event) {
999
- const menuMode = this.layoutService.layoutConfig().menuMode;
1000
- this.layoutService.updateLayoutConfig({
1001
- layoutTheme: event,
1002
- // Modern skin has no horizontal nav — fall back to the static sidebar.
1003
- menuMode: event === 'modern' && menuMode === 'topbar' ? 'static' : menuMode,
1004
- });
1005
- }
1006
- translate(key) {
1007
- return this.translateAdapter?.translate(key) ?? key;
1008
- }
1009
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppConfigurator, deps: [], target: i0.ɵɵFactoryTarget.Component });
1010
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppConfigurator, isStandalone: true, selector: "app-configurator", inputs: { showMenuModeButton: { classPropertyName: "showMenuModeButton", publicName: "showMenuModeButton", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "background-color: var(--surface-overlay); inset-inline-end: 0;", classAttribute: "hidden absolute top-[3.25rem] w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-xl" }, ngImport: i0, template: `
1011
+ `
1012
+ }]
1013
+ }]
1014
+ });
1015
+ const LAYOUT_AUTH_STATE = new InjectionToken("LAYOUT_AUTH_STATE");
1016
+ const LAYOUT_AUTH_API = new InjectionToken("LAYOUT_AUTH_API");
1017
+ const LAYOUT_NOTIFICATION_BELL = new InjectionToken("LAYOUT_NOTIFICATION_BELL");
1018
+ const LAYOUT_LANGUAGE_SELECTOR = new InjectionToken("LAYOUT_LANGUAGE_SELECTOR");
1019
+ const LAYOUT_LANGUAGE_SELECTOR_PANEL = new InjectionToken("LAYOUT_LANGUAGE_SELECTOR_PANEL");
1020
+ const LAYOUT_SEARCHBAR = new InjectionToken("LAYOUT_SEARCHBAR");
1021
+ const LAYOUT_IS_RTL = new InjectionToken("LAYOUT_IS_RTL");
1022
+ const LAYOUT_SEARCH_ADAPTER = new InjectionToken("LAYOUT_SEARCH_ADAPTER");
1023
+ var AppConfigurator = class AppConfigurator {
1024
+ translateAdapter = inject(TranslateService, { optional: true });
1025
+ platformId = inject(PLATFORM_ID);
1026
+ layoutService = inject(LayoutService);
1027
+ showMenuModeButton = input(true, ...ngDevMode ? [{ debugName: "showMenuModeButton" }] : /* istanbul ignore next */ []);
1028
+ presets = PRESET_NAMES;
1029
+ menuModeOptions = computed(() => {
1030
+ const options = [{
1031
+ label: this.translate("layout.configurator.menu.mode.static"),
1032
+ value: "static"
1033
+ }, {
1034
+ label: this.translate("layout.configurator.menu.mode.overlay"),
1035
+ value: "overlay"
1036
+ }];
1037
+ if (this.layoutService.isDesktop() && !this.layoutService.isModernLayout()) options.push({
1038
+ label: this.translate("layout.configurator.menu.mode.topbar"),
1039
+ value: "topbar"
1040
+ });
1041
+ return options;
1042
+ }, ...ngDevMode ? [{ debugName: "menuModeOptions" }] : /* istanbul ignore next */ []);
1043
+ layoutThemeOptions = computed(() => [{
1044
+ label: this.translate("layout.configurator.layout.theme.classic"),
1045
+ value: "classic"
1046
+ }, {
1047
+ label: this.translate("layout.configurator.layout.theme.modern"),
1048
+ value: "modern"
1049
+ }], ...ngDevMode ? [{ debugName: "layoutThemeOptions" }] : /* istanbul ignore next */ []);
1050
+ constructor() {
1051
+ if (isPlatformBrowser(this.platformId)) effect(() => {
1052
+ const config = this.layoutService.layoutConfig();
1053
+ applyTheme$1({
1054
+ preset: config.preset ?? "Aura",
1055
+ primary: config.primary ?? "violet",
1056
+ surface: config.surface ?? null,
1057
+ darkTheme: config.darkTheme ?? false
1058
+ });
1059
+ });
1060
+ }
1061
+ surfaces = Object.entries(SURFACE_PALETTES).map(([name, pal]) => ({
1062
+ name,
1063
+ palette: pal
1064
+ }));
1065
+ selectedPrimaryColor = computed(() => this.layoutService.layoutConfig().primary, ...ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : /* istanbul ignore next */ []);
1066
+ selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface, ...ngDevMode ? [{ debugName: "selectedSurfaceColor" }] : /* istanbul ignore next */ []);
1067
+ isSurfaceSelected(surfaceName) {
1068
+ const sel = this.selectedSurfaceColor();
1069
+ if (sel) return sel === surfaceName;
1070
+ return this.layoutService.layoutConfig().darkTheme ? surfaceName === "zinc" : surfaceName === "slate";
1071
+ }
1072
+ selectedPreset = computed(() => this.layoutService.layoutConfig().preset, ...ngDevMode ? [{ debugName: "selectedPreset" }] : /* istanbul ignore next */ []);
1073
+ menuMode = computed(() => this.layoutService.layoutConfig().menuMode, ...ngDevMode ? [{ debugName: "menuMode" }] : /* istanbul ignore next */ []);
1074
+ layoutTheme = this.layoutService.layoutTheme;
1075
+ primaryColors = computed(() => {
1076
+ const palettes = [{
1077
+ name: "noir",
1078
+ palette: {}
1079
+ }];
1080
+ for (const [name, pal] of Object.entries(PRIMARY_PALETTES)) palettes.push({
1081
+ name,
1082
+ palette: pal
1083
+ });
1084
+ return palettes;
1085
+ }, ...ngDevMode ? [{ debugName: "primaryColors" }] : /* istanbul ignore next */ []);
1086
+ updateColors(event, type, color) {
1087
+ if (type === "primary") this.layoutService.updateLayoutConfig({ primary: color.name });
1088
+ else if (type === "surface") this.layoutService.updateLayoutConfig({ surface: color.name });
1089
+ this._applyColorChange(type, color);
1090
+ event.stopPropagation();
1091
+ }
1092
+ _applyColorChange(type, color) {
1093
+ const isDark = this.layoutService.layoutConfig().darkTheme ?? false;
1094
+ if (type === "primary") {
1095
+ const primary = color.name ?? "violet";
1096
+ const pal = primary === "noir" ? NOIR_PRIMARY : PRIMARY_PALETTES[primary];
1097
+ if (pal) applyPrimaryPalette(pal, isDark);
1098
+ } else if (type === "surface") {
1099
+ const surfacePal = SURFACE_PALETTES[color.name ?? "slate"];
1100
+ if (surfacePal) applySurfacePalette(surfacePal);
1101
+ }
1102
+ }
1103
+ onPresetChange(event) {
1104
+ const primary = PRESETS[event]?.signaturePrimary ?? "violet";
1105
+ this.layoutService.updateLayoutConfig({
1106
+ preset: event,
1107
+ primary
1108
+ });
1109
+ const isDark = this.layoutService.layoutConfig().darkTheme ?? false;
1110
+ const surface = this.layoutService.layoutConfig().surface ?? null;
1111
+ applyTheme$1({
1112
+ preset: event,
1113
+ primary,
1114
+ surface,
1115
+ darkTheme: isDark
1116
+ });
1117
+ }
1118
+ onMenuModeChange(event) {
1119
+ this.layoutService.updateLayoutConfig({ menuMode: event });
1120
+ }
1121
+ onLayoutThemeChange(event) {
1122
+ const menuMode = this.layoutService.layoutConfig().menuMode;
1123
+ this.layoutService.updateLayoutConfig({
1124
+ layoutTheme: event,
1125
+ menuMode: event === "modern" && menuMode === "topbar" ? "static" : menuMode
1126
+ });
1127
+ }
1128
+ translate(key) {
1129
+ return this.translateAdapter?.translate(key) ?? key;
1130
+ }
1131
+ static ɵfac = i0.ɵɵngDeclareFactory({
1132
+ minVersion: "12.0.0",
1133
+ version: "22.2.1",
1134
+ ngImport: i0,
1135
+ type: AppConfigurator,
1136
+ deps: [],
1137
+ target: i0.ɵɵFactoryTarget.Component
1138
+ });
1139
+ static ɵcmp = i0.ɵɵngDeclareComponent({
1140
+ minVersion: "17.0.0",
1141
+ version: "22.2.1",
1142
+ type: AppConfigurator,
1143
+ isStandalone: true,
1144
+ selector: "app-configurator",
1145
+ inputs: { showMenuModeButton: {
1146
+ classPropertyName: "showMenuModeButton",
1147
+ publicName: "showMenuModeButton",
1148
+ isSignal: true,
1149
+ isRequired: false,
1150
+ transformFunction: null
1151
+ } },
1152
+ host: {
1153
+ styleAttribute: "background-color: var(--surface-overlay); inset-inline-end: 0;",
1154
+ classAttribute: "hidden absolute top-[3.25rem] w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-xl"
1155
+ },
1156
+ ngImport: i0,
1157
+ template: `
1011
1158
  <div class="flex flex-col gap-4">
1012
1159
  <div>
1013
1160
  <span class="text-sm text-muted-color font-semibold">{{
@@ -1090,14 +1237,73 @@ class AppConfigurator {
1090
1237
  </div>
1091
1238
  }
1092
1239
  </div>
1093
- `, isInline: true, dependencies: [{ kind: "component", type: SelectButtonComponent, selector: "f-selectbutton", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "multiple", "disabled", "invalid", "allowEmpty", "size", "styleClass", "style"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1094
- }
1095
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppConfigurator, decorators: [{
1096
- type: Component,
1097
- args: [{
1098
- selector: 'app-configurator',
1099
- imports: [SelectButtonComponent, FormsModule, TranslatePipe],
1100
- template: `
1240
+ `,
1241
+ isInline: true,
1242
+ dependencies: [
1243
+ {
1244
+ kind: "component",
1245
+ type: SelectButtonComponent,
1246
+ selector: "f-selectbutton",
1247
+ inputs: [
1248
+ "options",
1249
+ "optionLabel",
1250
+ "optionValue",
1251
+ "optionDisabled",
1252
+ "multiple",
1253
+ "disabled",
1254
+ "invalid",
1255
+ "allowEmpty",
1256
+ "size",
1257
+ "styleClass",
1258
+ "style"
1259
+ ],
1260
+ outputs: ["onChange"]
1261
+ },
1262
+ {
1263
+ kind: "ngmodule",
1264
+ type: FormsModule
1265
+ },
1266
+ {
1267
+ kind: "directive",
1268
+ type: i1.NgControlStatus,
1269
+ selector: "[formControlName],[ngModel],[formControl]"
1270
+ },
1271
+ {
1272
+ kind: "directive",
1273
+ type: i1.NgModel,
1274
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
1275
+ inputs: [
1276
+ "name",
1277
+ "disabled",
1278
+ "ngModel",
1279
+ "ngModelOptions"
1280
+ ],
1281
+ outputs: ["ngModelChange"],
1282
+ exportAs: ["ngModel"]
1283
+ },
1284
+ {
1285
+ kind: "pipe",
1286
+ type: TranslatePipe,
1287
+ name: "translate"
1288
+ }
1289
+ ]
1290
+ });
1291
+ };
1292
+ i0.ɵɵngDeclareClassMetadata({
1293
+ minVersion: "12.0.0",
1294
+ version: "22.2.1",
1295
+ ngImport: i0,
1296
+ type: AppConfigurator,
1297
+ decorators: [{
1298
+ type: Component,
1299
+ args: [{
1300
+ selector: "app-configurator",
1301
+ imports: [
1302
+ SelectButtonComponent,
1303
+ FormsModule,
1304
+ TranslatePipe
1305
+ ],
1306
+ template: `
1101
1307
  <div class="flex flex-col gap-4">
1102
1308
  <div>
1103
1309
  <span class="text-sm text-muted-color font-semibold">{{
@@ -1181,167 +1387,147 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
1181
1387
  }
1182
1388
  </div>
1183
1389
  `,
1184
- host: {
1185
- class: 'hidden absolute top-[3.25rem] w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-xl',
1186
- style: 'background-color: var(--surface-overlay); inset-inline-end: 0;',
1187
- },
1188
- }]
1189
- }], ctorParameters: () => [], propDecorators: { showMenuModeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMenuModeButton", required: false }] }] } });
1190
-
1191
- /**
1192
- * View Transitions API type definitions
1193
- * @see https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
1194
- */
1195
-
1196
- /** Company/branch switcher displayed in top bar */
1197
- class AppCompanyBranchSelector {
1198
- destroyRef = inject(DestroyRef);
1199
- authState = inject(LAYOUT_AUTH_STATE, { optional: true });
1200
- authApi = inject(LAYOUT_AUTH_API, { optional: true });
1201
- messageService = inject(MessageService);
1202
- translateAdapter = inject(TranslateService, {
1203
- optional: true,
1204
- });
1205
- document = inject(DOCUMENT$1);
1206
- elementRef = inject(ElementRef);
1207
- _isActive = signal(false, /* @ts-ignore */
1208
- ...(ngDevMode ? [{ debugName: "_isActive" }] : /* istanbul ignore next */ []));
1209
- isActive = this._isActive.asReadonly();
1210
- constructor() {
1211
- fromEvent(this.document, 'click')
1212
- .pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this.isActive()))
1213
- .subscribe((event) => {
1214
- const target = event.target;
1215
- if (!this.elementRef.nativeElement.contains(target)) {
1216
- this._isActive.set(false);
1217
- }
1218
- });
1219
- }
1220
- currentCompanyName = computed(() => this.authState?.currentCompanyInfo()?.name ??
1221
- this.translate('layout.company.branch.selector.no.company'), /* @ts-ignore */
1222
- ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : /* istanbul ignore next */ []));
1223
- currentBranchName = computed(() => this.authState?.currentBranchInfo()?.name ?? null, /* @ts-ignore */
1224
- ...(ngDevMode ? [{ debugName: "currentBranchName" }] : /* istanbul ignore next */ []));
1225
- _companies = signal([], /* @ts-ignore */
1226
- ...(ngDevMode ? [{ debugName: "_companies" }] : /* istanbul ignore next */ []));
1227
- companies = this._companies.asReadonly();
1228
- _branches = signal([], /* @ts-ignore */
1229
- ...(ngDevMode ? [{ debugName: "_branches" }] : /* istanbul ignore next */ []));
1230
- branches = this._branches.asReadonly();
1231
- _selectedCompanyId = signal(null, /* @ts-ignore */
1232
- ...(ngDevMode ? [{ debugName: "_selectedCompanyId" }] : /* istanbul ignore next */ []));
1233
- selectedCompanyId = this._selectedCompanyId.asReadonly();
1234
- _selectedBranchId = signal(null, /* @ts-ignore */
1235
- ...(ngDevMode ? [{ debugName: "_selectedBranchId" }] : /* istanbul ignore next */ []));
1236
- selectedBranchId = this._selectedBranchId.asReadonly();
1237
- _isLoadingCompanies = signal(false, /* @ts-ignore */
1238
- ...(ngDevMode ? [{ debugName: "_isLoadingCompanies" }] : /* istanbul ignore next */ []));
1239
- isLoadingCompanies = this._isLoadingCompanies.asReadonly();
1240
- _isLoadingBranches = signal(false, /* @ts-ignore */
1241
- ...(ngDevMode ? [{ debugName: "_isLoadingBranches" }] : /* istanbul ignore next */ []));
1242
- isLoadingBranches = this._isLoadingBranches.asReadonly();
1243
- _isSwitching = signal(false, /* @ts-ignore */
1244
- ...(ngDevMode ? [{ debugName: "_isSwitching" }] : /* istanbul ignore next */ []));
1245
- isSwitching = this._isSwitching.asReadonly();
1246
- canSwitch = computed(() => {
1247
- const selectedCompany = this.selectedCompanyId();
1248
- if (!selectedCompany)
1249
- return false;
1250
- const currentCompanyId = this.authState?.currentCompanyInfo()?.id;
1251
- const currentBranchId = this.authState?.currentBranchInfo()?.id;
1252
- const selectedBranch = this.selectedBranchId();
1253
- return (selectedCompany !== currentCompanyId || selectedBranch !== currentBranchId);
1254
- }, /* @ts-ignore */
1255
- ...(ngDevMode ? [{ debugName: "canSwitch" }] : /* istanbul ignore next */ []));
1256
- onPanelToggle() {
1257
- this._isActive.update((v) => !v);
1258
- if (this.isActive() && this.companies().length === 0) {
1259
- this.loadCompanies();
1260
- }
1261
- }
1262
- loadCompanies() {
1263
- if (!this.authApi)
1264
- return;
1265
- this._isLoadingCompanies.set(true);
1266
- this.authApi
1267
- .getMyCompanies()
1268
- .pipe(takeUntilDestroyed(this.destroyRef))
1269
- .subscribe({
1270
- next: (companies) => {
1271
- this._companies.set(companies);
1272
- this._isLoadingCompanies.set(false);
1273
- },
1274
- error: () => {
1275
- // Error toast handled by global interceptor
1276
- this._isLoadingCompanies.set(false);
1277
- },
1278
- });
1279
- }
1280
- onCompanyChange(companyId) {
1281
- this._selectedCompanyId.set(companyId);
1282
- if (!companyId) {
1283
- this._branches.set([]);
1284
- this._selectedBranchId.set(null);
1285
- return;
1286
- }
1287
- this.loadBranches(companyId);
1288
- }
1289
- onBranchChange(branchId) {
1290
- this._selectedBranchId.set(branchId);
1291
- }
1292
- loadBranches(companyId) {
1293
- if (!this.authApi)
1294
- return;
1295
- this._isLoadingBranches.set(true);
1296
- this._selectedBranchId.set(null);
1297
- this.authApi
1298
- .getMyCompanyBranches(companyId)
1299
- .pipe(takeUntilDestroyed(this.destroyRef))
1300
- .subscribe({
1301
- next: (branches) => {
1302
- this._branches.set(branches);
1303
- this._isLoadingBranches.set(false);
1304
- // Auto-select if only one branch available
1305
- if (branches.length === 1) {
1306
- this._selectedBranchId.set(branches[0].id);
1307
- }
1308
- },
1309
- error: () => {
1310
- // Error toast handled by global interceptor
1311
- this._isLoadingBranches.set(false);
1312
- },
1313
- });
1314
- }
1315
- onSwitch() {
1316
- const selectedCompany = this.selectedCompanyId();
1317
- const selectedBranch = this.selectedBranchId();
1318
- if (!this.authApi || !selectedCompany)
1319
- return;
1320
- if (this.branches().length > 0 && !selectedBranch) {
1321
- this.messageService.add({
1322
- severity: 'warn',
1323
- summary: this.translate('layout.company.branch.selector.branch.required'),
1324
- detail: this.translate('layout.company.branch.selector.please.select.branch'),
1325
- });
1326
- return;
1327
- }
1328
- this._isSwitching.set(true);
1329
- this.authApi
1330
- .switchCompany(selectedCompany, selectedBranch || '')
1331
- .pipe(takeUntilDestroyed(this.destroyRef))
1332
- .subscribe({
1333
- next: () => { },
1334
- error: () => {
1335
- // Error toast handled by global interceptor
1336
- this._isSwitching.set(false);
1337
- },
1338
- });
1339
- }
1340
- translate(key) {
1341
- return this.translateAdapter?.translate(key) ?? key;
1342
- }
1343
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppCompanyBranchSelector, deps: [], target: i0.ɵɵFactoryTarget.Component });
1344
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppCompanyBranchSelector, isStandalone: true, selector: "app-company-branch-selector", host: { classAttribute: "relative" }, ngImport: i0, template: `
1390
+ host: {
1391
+ class: "hidden absolute top-[3.25rem] w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-xl",
1392
+ style: "background-color: var(--surface-overlay); inset-inline-end: 0;"
1393
+ }
1394
+ }]
1395
+ }],
1396
+ ctorParameters: () => [],
1397
+ propDecorators: { showMenuModeButton: [{
1398
+ type: i0.Input,
1399
+ args: [{
1400
+ isSignal: true,
1401
+ alias: "showMenuModeButton",
1402
+ required: false
1403
+ }]
1404
+ }] }
1405
+ });
1406
+ var AppCompanyBranchSelector = class AppCompanyBranchSelector {
1407
+ destroyRef = inject(DestroyRef);
1408
+ authState = inject(LAYOUT_AUTH_STATE, { optional: true });
1409
+ authApi = inject(LAYOUT_AUTH_API, { optional: true });
1410
+ messageService = inject(MessageService);
1411
+ translateAdapter = inject(TranslateService, { optional: true });
1412
+ document = inject(DOCUMENT$1);
1413
+ elementRef = inject(ElementRef);
1414
+ _isActive = signal(false, ...ngDevMode ? [{ debugName: "_isActive" }] : /* istanbul ignore next */ []);
1415
+ isActive = this._isActive.asReadonly();
1416
+ constructor() {
1417
+ fromEvent(this.document, "click").pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this.isActive())).subscribe((event) => {
1418
+ const target = event.target;
1419
+ if (!this.elementRef.nativeElement.contains(target)) this._isActive.set(false);
1420
+ });
1421
+ }
1422
+ currentCompanyName = computed(() => this.authState?.currentCompanyInfo()?.name ?? this.translate("layout.company.branch.selector.no.company"), ...ngDevMode ? [{ debugName: "currentCompanyName" }] : /* istanbul ignore next */ []);
1423
+ currentBranchName = computed(() => this.authState?.currentBranchInfo()?.name ?? null, ...ngDevMode ? [{ debugName: "currentBranchName" }] : /* istanbul ignore next */ []);
1424
+ _companies = signal([], ...ngDevMode ? [{ debugName: "_companies" }] : /* istanbul ignore next */ []);
1425
+ companies = this._companies.asReadonly();
1426
+ _branches = signal([], ...ngDevMode ? [{ debugName: "_branches" }] : /* istanbul ignore next */ []);
1427
+ branches = this._branches.asReadonly();
1428
+ branchTreeNodes = computed(() => buildBranchTreeNodes(this.branches()), ...ngDevMode ? [{ debugName: "branchTreeNodes" }] : /* istanbul ignore next */ []);
1429
+ _selectedCompanyId = signal(null, ...ngDevMode ? [{ debugName: "_selectedCompanyId" }] : /* istanbul ignore next */ []);
1430
+ selectedCompanyId = this._selectedCompanyId.asReadonly();
1431
+ _selectedBranchId = signal(null, ...ngDevMode ? [{ debugName: "_selectedBranchId" }] : /* istanbul ignore next */ []);
1432
+ selectedBranchId = this._selectedBranchId.asReadonly();
1433
+ _isLoadingCompanies = signal(false, ...ngDevMode ? [{ debugName: "_isLoadingCompanies" }] : /* istanbul ignore next */ []);
1434
+ isLoadingCompanies = this._isLoadingCompanies.asReadonly();
1435
+ _isLoadingBranches = signal(false, ...ngDevMode ? [{ debugName: "_isLoadingBranches" }] : /* istanbul ignore next */ []);
1436
+ isLoadingBranches = this._isLoadingBranches.asReadonly();
1437
+ _isSwitching = signal(false, ...ngDevMode ? [{ debugName: "_isSwitching" }] : /* istanbul ignore next */ []);
1438
+ isSwitching = this._isSwitching.asReadonly();
1439
+ canSwitch = computed(() => {
1440
+ const selectedCompany = this.selectedCompanyId();
1441
+ if (!selectedCompany) return false;
1442
+ const currentCompanyId = this.authState?.currentCompanyInfo()?.id;
1443
+ const currentBranchId = this.authState?.currentBranchInfo()?.id;
1444
+ const selectedBranch = this.selectedBranchId();
1445
+ return selectedCompany !== currentCompanyId || selectedBranch !== currentBranchId;
1446
+ }, ...ngDevMode ? [{ debugName: "canSwitch" }] : /* istanbul ignore next */ []);
1447
+ onPanelToggle() {
1448
+ this._isActive.update((v) => !v);
1449
+ if (this.isActive() && this.companies().length === 0) this.loadCompanies();
1450
+ }
1451
+ loadCompanies() {
1452
+ if (!this.authApi) return;
1453
+ this._isLoadingCompanies.set(true);
1454
+ this.authApi.getMyCompanies().pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
1455
+ next: (companies) => {
1456
+ this._companies.set(companies);
1457
+ this._isLoadingCompanies.set(false);
1458
+ },
1459
+ error: () => {
1460
+ this._isLoadingCompanies.set(false);
1461
+ }
1462
+ });
1463
+ }
1464
+ onCompanyChange(companyId) {
1465
+ this._selectedCompanyId.set(companyId);
1466
+ if (!companyId) {
1467
+ this._branches.set([]);
1468
+ this._selectedBranchId.set(null);
1469
+ return;
1470
+ }
1471
+ this.loadBranches(companyId);
1472
+ }
1473
+ onBranchChange(branchId) {
1474
+ this._selectedBranchId.set(branchId);
1475
+ }
1476
+ loadBranches(companyId) {
1477
+ if (!this.authApi) return;
1478
+ this._isLoadingBranches.set(true);
1479
+ this._selectedBranchId.set(null);
1480
+ this.authApi.getMyCompanyBranches(companyId).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
1481
+ next: (branches) => {
1482
+ this._branches.set(branches);
1483
+ this._isLoadingBranches.set(false);
1484
+ if (branches.length === 1) this._selectedBranchId.set(branches[0].id);
1485
+ },
1486
+ error: () => {
1487
+ this._isLoadingBranches.set(false);
1488
+ }
1489
+ });
1490
+ }
1491
+ onSwitch() {
1492
+ const selectedCompany = this.selectedCompanyId();
1493
+ const selectedBranch = this.selectedBranchId();
1494
+ if (!this.authApi || !selectedCompany) return;
1495
+ if (this.branches().length > 0 && !selectedBranch) {
1496
+ this.messageService.add({
1497
+ severity: "warn",
1498
+ summary: this.translate("layout.company.branch.selector.branch.required"),
1499
+ detail: this.translate("layout.company.branch.selector.please.select.branch")
1500
+ });
1501
+ return;
1502
+ }
1503
+ this._isSwitching.set(true);
1504
+ this.authApi.switchCompany(selectedCompany, selectedBranch || "").pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
1505
+ next: () => {},
1506
+ error: () => {
1507
+ this._isSwitching.set(false);
1508
+ }
1509
+ });
1510
+ }
1511
+ translate(key) {
1512
+ return this.translateAdapter?.translate(key) ?? key;
1513
+ }
1514
+ static ɵfac = i0.ɵɵngDeclareFactory({
1515
+ minVersion: "12.0.0",
1516
+ version: "22.2.1",
1517
+ ngImport: i0,
1518
+ type: AppCompanyBranchSelector,
1519
+ deps: [],
1520
+ target: i0.ɵɵFactoryTarget.Component
1521
+ });
1522
+ static ɵcmp = i0.ɵɵngDeclareComponent({
1523
+ minVersion: "17.0.0",
1524
+ version: "22.2.1",
1525
+ type: AppCompanyBranchSelector,
1526
+ isStandalone: true,
1527
+ selector: "app-company-branch-selector",
1528
+ host: { classAttribute: "relative" },
1529
+ ngImport: i0,
1530
+ template: `
1345
1531
  <button
1346
1532
  type="button"
1347
1533
  class="layout-topbar-action"
@@ -1394,17 +1580,19 @@ class AppCompanyBranchSelector {
1394
1580
  <label class="text-sm text-muted-color font-semibold">{{
1395
1581
  'layout.company.branch.selector.branch' | translate
1396
1582
  }}</label>
1397
- <f-select
1398
- [options]="branches()"
1583
+ <f-treeselect
1584
+ [options]="branchTreeNodes()"
1399
1585
  [ngModel]="selectedBranchId()"
1400
1586
  (ngModelChange)="onBranchChange($event)"
1401
- optionLabel="name"
1402
- optionValue="id"
1403
1587
  [placeholder]="
1404
1588
  'layout.company.branch.selector.select.branch' | translate
1405
1589
  "
1590
+ [filter]="true"
1591
+ [filterPlaceholder]="'shared.search' | translate"
1592
+ [emptyMessage]="'shared.no.results' | translate"
1593
+ [disabled]="isLoadingBranches()"
1594
+ appendTo="self"
1406
1595
  class="w-full"
1407
- [loading]="isLoadingBranches()"
1408
1596
  />
1409
1597
  </div>
1410
1598
  }
@@ -1427,22 +1615,191 @@ class AppCompanyBranchSelector {
1427
1615
  </div>
1428
1616
  </div>
1429
1617
  </div>
1430
- `, isInline: true, dependencies: [{ kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy", "selectedObject"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "directive", type: StyleClassDirective, selector: "[fStyleClass]", inputs: ["fStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1431
- }
1432
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppCompanyBranchSelector, decorators: [{
1433
- type: Component,
1434
- args: [{
1435
- selector: 'app-company-branch-selector',
1436
- imports: [
1437
- FButtonDirective,
1438
- SelectComponent,
1439
- StyleClassDirective,
1440
- UiIconComponent,
1441
- FormsModule,
1442
- TranslatePipe,
1443
- ],
1444
- host: { class: 'relative' },
1445
- template: `
1618
+ `,
1619
+ isInline: true,
1620
+ dependencies: [
1621
+ {
1622
+ kind: "directive",
1623
+ type: FButtonDirective,
1624
+ selector: "[fButton]",
1625
+ inputs: [
1626
+ "severity",
1627
+ "text",
1628
+ "outlined",
1629
+ "link",
1630
+ "variant",
1631
+ "rounded",
1632
+ "raised",
1633
+ "fluid",
1634
+ "disabled",
1635
+ "loading",
1636
+ "loadingIcon",
1637
+ "size",
1638
+ "label",
1639
+ "icon",
1640
+ "iconPos",
1641
+ "ariaLabel",
1642
+ "plain"
1643
+ ]
1644
+ },
1645
+ {
1646
+ kind: "component",
1647
+ type: SelectComponent,
1648
+ selector: "f-select",
1649
+ inputs: [
1650
+ "options",
1651
+ "optionLabel",
1652
+ "optionValue",
1653
+ "placeholder",
1654
+ "disabled",
1655
+ "filter",
1656
+ "filterBy",
1657
+ "filterMatchMode",
1658
+ "filterLocale",
1659
+ "filterPlaceholder",
1660
+ "resetFilterOnHide",
1661
+ "autofocusFilter",
1662
+ "emptyMessage",
1663
+ "emptyFilterMessage",
1664
+ "showClear",
1665
+ "loading",
1666
+ "loadingIcon",
1667
+ "dropdownIcon",
1668
+ "clearIcon",
1669
+ "inputId",
1670
+ "invalid",
1671
+ "touched",
1672
+ "styleClass",
1673
+ "style",
1674
+ "appendTo",
1675
+ "editable",
1676
+ "readonly",
1677
+ "autofocus",
1678
+ "checkmark",
1679
+ "group",
1680
+ "optionGroupLabel",
1681
+ "optionGroupChildren",
1682
+ "virtualScroll",
1683
+ "virtualScrollItemSize",
1684
+ "lazy",
1685
+ "selectedObject"
1686
+ ],
1687
+ outputs: [
1688
+ "onFilter",
1689
+ "onChange",
1690
+ "onShow",
1691
+ "onHide",
1692
+ "onBlur",
1693
+ "onFocus",
1694
+ "onClear",
1695
+ "onLazyLoad"
1696
+ ]
1697
+ },
1698
+ {
1699
+ kind: "directive",
1700
+ type: StyleClassDirective,
1701
+ selector: "[fStyleClass]",
1702
+ inputs: [
1703
+ "fStyleClass",
1704
+ "enterFromClass",
1705
+ "enterActiveClass",
1706
+ "enterToClass",
1707
+ "leaveFromClass",
1708
+ "leaveActiveClass",
1709
+ "leaveToClass",
1710
+ "hideOnOutsideClick",
1711
+ "toggleClass"
1712
+ ]
1713
+ },
1714
+ {
1715
+ kind: "component",
1716
+ type: TreeSelectComponent,
1717
+ selector: "f-treeselect",
1718
+ inputs: [
1719
+ "options",
1720
+ "placeholder",
1721
+ "inputId",
1722
+ "disabled",
1723
+ "filter",
1724
+ "filterBy",
1725
+ "filterPlaceholder",
1726
+ "selectionMode",
1727
+ "display",
1728
+ "propagateSelectionDown",
1729
+ "propagateSelectionUp",
1730
+ "showClear",
1731
+ "virtualScroll",
1732
+ "virtualScrollItemSize",
1733
+ "emptyMessage",
1734
+ "invalid",
1735
+ "touched",
1736
+ "styleClass",
1737
+ "style",
1738
+ "appendTo"
1739
+ ]
1740
+ },
1741
+ {
1742
+ kind: "component",
1743
+ type: UiIconComponent,
1744
+ selector: "ui-icon",
1745
+ inputs: [
1746
+ "name",
1747
+ "size",
1748
+ "spin",
1749
+ "fill",
1750
+ "severity"
1751
+ ]
1752
+ },
1753
+ {
1754
+ kind: "ngmodule",
1755
+ type: FormsModule
1756
+ },
1757
+ {
1758
+ kind: "directive",
1759
+ type: i1.NgControlStatus,
1760
+ selector: "[formControlName],[ngModel],[formControl]"
1761
+ },
1762
+ {
1763
+ kind: "directive",
1764
+ type: i1.NgModel,
1765
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
1766
+ inputs: [
1767
+ "name",
1768
+ "disabled",
1769
+ "ngModel",
1770
+ "ngModelOptions"
1771
+ ],
1772
+ outputs: ["ngModelChange"],
1773
+ exportAs: ["ngModel"]
1774
+ },
1775
+ {
1776
+ kind: "pipe",
1777
+ type: TranslatePipe,
1778
+ name: "translate"
1779
+ }
1780
+ ]
1781
+ });
1782
+ };
1783
+ i0.ɵɵngDeclareClassMetadata({
1784
+ minVersion: "12.0.0",
1785
+ version: "22.2.1",
1786
+ ngImport: i0,
1787
+ type: AppCompanyBranchSelector,
1788
+ decorators: [{
1789
+ type: Component,
1790
+ args: [{
1791
+ selector: "app-company-branch-selector",
1792
+ imports: [
1793
+ FButtonDirective,
1794
+ SelectComponent,
1795
+ StyleClassDirective,
1796
+ TreeSelectComponent,
1797
+ UiIconComponent,
1798
+ FormsModule,
1799
+ TranslatePipe
1800
+ ],
1801
+ host: { class: "relative" },
1802
+ template: `
1446
1803
  <button
1447
1804
  type="button"
1448
1805
  class="layout-topbar-action"
@@ -1495,17 +1852,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
1495
1852
  <label class="text-sm text-muted-color font-semibold">{{
1496
1853
  'layout.company.branch.selector.branch' | translate
1497
1854
  }}</label>
1498
- <f-select
1499
- [options]="branches()"
1855
+ <f-treeselect
1856
+ [options]="branchTreeNodes()"
1500
1857
  [ngModel]="selectedBranchId()"
1501
1858
  (ngModelChange)="onBranchChange($event)"
1502
- optionLabel="name"
1503
- optionValue="id"
1504
1859
  [placeholder]="
1505
1860
  'layout.company.branch.selector.select.branch' | translate
1506
1861
  "
1862
+ [filter]="true"
1863
+ [filterPlaceholder]="'shared.search' | translate"
1864
+ [emptyMessage]="'shared.no.results' | translate"
1865
+ [disabled]="isLoadingBranches()"
1866
+ appendTo="self"
1507
1867
  class="w-full"
1508
- [loading]="isLoadingBranches()"
1509
1868
  />
1510
1869
  </div>
1511
1870
  }
@@ -1528,33 +1887,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
1528
1887
  </div>
1529
1888
  </div>
1530
1889
  </div>
1531
- `,
1532
- }]
1533
- }], ctorParameters: () => [] });
1534
-
1535
- class AppLauncher {
1536
- layoutService = inject(LayoutService);
1537
- destroyRef = inject(DestroyRef);
1538
- document = inject(DOCUMENT$1);
1539
- elementRef = inject(ElementRef);
1540
- _isActive = signal(false, /* @ts-ignore */
1541
- ...(ngDevMode ? [{ debugName: "_isActive" }] : /* istanbul ignore next */ []));
1542
- isActive = this._isActive.asReadonly();
1543
- constructor() {
1544
- fromEvent(this.document, 'click')
1545
- .pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this.isActive()))
1546
- .subscribe((event) => {
1547
- const target = event.target;
1548
- if (!this.elementRef.nativeElement.contains(target)) {
1549
- this._isActive.set(false);
1550
- }
1551
- });
1552
- }
1553
- toggleActive() {
1554
- this._isActive.update((v) => !v);
1555
- }
1556
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppLauncher, deps: [], target: i0.ɵɵFactoryTarget.Component });
1557
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppLauncher, isStandalone: true, selector: "app-launcher", host: { classAttribute: "relative" }, ngImport: i0, template: `
1890
+ `
1891
+ }]
1892
+ }],
1893
+ ctorParameters: () => []
1894
+ });
1895
+ var AppLauncher = class AppLauncher {
1896
+ layoutService = inject(LayoutService);
1897
+ destroyRef = inject(DestroyRef);
1898
+ document = inject(DOCUMENT$1);
1899
+ elementRef = inject(ElementRef);
1900
+ _isActive = signal(false, ...ngDevMode ? [{ debugName: "_isActive" }] : /* istanbul ignore next */ []);
1901
+ isActive = this._isActive.asReadonly();
1902
+ constructor() {
1903
+ fromEvent(this.document, "click").pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this.isActive())).subscribe((event) => {
1904
+ const target = event.target;
1905
+ if (!this.elementRef.nativeElement.contains(target)) this._isActive.set(false);
1906
+ });
1907
+ }
1908
+ toggleActive() {
1909
+ this._isActive.update((v) => !v);
1910
+ }
1911
+ static ɵfac = i0.ɵɵngDeclareFactory({
1912
+ minVersion: "12.0.0",
1913
+ version: "22.2.1",
1914
+ ngImport: i0,
1915
+ type: AppLauncher,
1916
+ deps: [],
1917
+ target: i0.ɵɵFactoryTarget.Component
1918
+ });
1919
+ static ɵcmp = i0.ɵɵngDeclareComponent({
1920
+ minVersion: "17.0.0",
1921
+ version: "22.2.1",
1922
+ type: AppLauncher,
1923
+ isStandalone: true,
1924
+ selector: "app-launcher",
1925
+ host: { classAttribute: "relative" },
1926
+ ngImport: i0,
1927
+ template: `
1558
1928
  <button
1559
1929
  type="button"
1560
1930
  class="layout-topbar-action"
@@ -1610,15 +1980,61 @@ class AppLauncher {
1610
1980
  </div>
1611
1981
  }
1612
1982
  </div>
1613
- `, isInline: true, dependencies: [{ kind: "directive", type: StyleClassDirective, selector: "[fStyleClass]", inputs: ["fStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1614
- }
1615
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppLauncher, decorators: [{
1616
- type: Component,
1617
- args: [{
1618
- selector: 'app-launcher',
1619
- imports: [StyleClassDirective, UiIconComponent, TranslatePipe],
1620
- host: { class: 'relative' },
1621
- template: `
1983
+ `,
1984
+ isInline: true,
1985
+ dependencies: [
1986
+ {
1987
+ kind: "directive",
1988
+ type: StyleClassDirective,
1989
+ selector: "[fStyleClass]",
1990
+ inputs: [
1991
+ "fStyleClass",
1992
+ "enterFromClass",
1993
+ "enterActiveClass",
1994
+ "enterToClass",
1995
+ "leaveFromClass",
1996
+ "leaveActiveClass",
1997
+ "leaveToClass",
1998
+ "hideOnOutsideClick",
1999
+ "toggleClass"
2000
+ ]
2001
+ },
2002
+ {
2003
+ kind: "component",
2004
+ type: UiIconComponent,
2005
+ selector: "ui-icon",
2006
+ inputs: [
2007
+ "name",
2008
+ "size",
2009
+ "spin",
2010
+ "fill",
2011
+ "severity"
2012
+ ]
2013
+ },
2014
+ {
2015
+ kind: "pipe",
2016
+ type: TranslatePipe,
2017
+ name: "translate"
2018
+ }
2019
+ ]
2020
+ });
2021
+ };
2022
+ i0.ɵɵngDeclareClassMetadata({
2023
+ minVersion: "12.0.0",
2024
+ version: "22.2.1",
2025
+ ngImport: i0,
2026
+ type: AppLauncher,
2027
+ decorators: [{
2028
+ type: Component,
2029
+ args: [{
2030
+ selector: "app-launcher",
2031
+ imports: [
2032
+ StyleClassDirective,
2033
+ UiIconComponent,
2034
+ TranslatePipe
2035
+ ],
2036
+ host: { class: "relative" },
2037
+ template: `
1622
2038
  <button
1623
2039
  type="button"
1624
2040
  class="layout-topbar-action"
@@ -1674,95 +2090,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
1674
2090
  </div>
1675
2091
  }
1676
2092
  </div>
1677
- `,
1678
- }]
1679
- }], ctorParameters: () => [] });
1680
-
1681
- class AppProfile {
1682
- destroyRef = inject(DestroyRef);
1683
- authState = inject(LAYOUT_AUTH_STATE, { optional: true });
1684
- authApi = inject(LAYOUT_AUTH_API, { optional: true });
1685
- layoutService = inject(LayoutService);
1686
- messageService = inject(MessageService);
1687
- translateAdapter = inject(TranslateService, {
1688
- optional: true,
1689
- });
1690
- appConfig = inject(APP_CONFIG, { optional: true });
1691
- document = inject(DOCUMENT$1);
1692
- elementRef = inject(ElementRef);
1693
- _isActive = signal(false, /* @ts-ignore */
1694
- ...(ngDevMode ? [{ debugName: "_isActive" }] : /* istanbul ignore next */ []));
1695
- isActive = this._isActive.asReadonly();
1696
- constructor() {
1697
- fromEvent(this.document, 'click')
1698
- .pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this.isActive()))
1699
- .subscribe((event) => {
1700
- const target = event.target;
1701
- if (!this.elementRef.nativeElement.contains(target)) {
1702
- this._isActive.set(false);
1703
- }
1704
- });
1705
- }
1706
- togglePanel() {
1707
- this._isActive.update((v) => !v);
1708
- }
1709
- userName = computed(() => this.authState?.loginUserData()?.name ??
1710
- this.translate('layout.profile.guest'), /* @ts-ignore */
1711
- ...(ngDevMode ? [{ debugName: "userName" }] : /* istanbul ignore next */ []));
1712
- userEmail = computed(() => this.authState?.loginUserData()?.email ?? '', /* @ts-ignore */
1713
- ...(ngDevMode ? [{ debugName: "userEmail" }] : /* istanbul ignore next */ []));
1714
- profilePicture = computed(() => this.layoutService.userProfilePictureUrl() ?? '', /* @ts-ignore */
1715
- ...(ngDevMode ? [{ debugName: "profilePicture" }] : /* istanbul ignore next */ []));
1716
- signUpEnabled = computed(() => this.appConfig ? isSignUpEnabled(this.appConfig) : false, /* @ts-ignore */
1717
- ...(ngDevMode ? [{ debugName: "signUpEnabled" }] : /* istanbul ignore next */ []));
1718
- logout() {
1719
- if (!this.authApi)
1720
- return;
1721
- this.authApi
1722
- .logOut()
1723
- .pipe(takeUntilDestroyed(this.destroyRef))
1724
- .subscribe({
1725
- next: (result) => {
1726
- const r = result;
1727
- this.messageService.add({
1728
- severity: 'success',
1729
- summary: this.translate('shared.success'),
1730
- detail: r?.messageKey
1731
- ? this.translate(r.messageKey, r.messageVariables)
1732
- : (r?.message ?? ''),
1733
- });
1734
- this.authApi?.navigateLogin(false);
1735
- },
1736
- error: () => {
1737
- // Error toast handled by global interceptor
1738
- },
1739
- });
1740
- }
1741
- copySignUpLink() {
1742
- const slug = this.authState?.currentCompanyInfo()?.slug ?? '';
1743
- const signUpLink = `${window.location.origin}/auth/register?companySlug=${slug}`;
1744
- navigator.clipboard
1745
- .writeText(signUpLink)
1746
- .then(() => {
1747
- this.messageService.add({
1748
- severity: 'success',
1749
- summary: this.translate('layout.profile.link.copied'),
1750
- detail: this.translate('layout.profile.sign.up.link.copied'),
1751
- });
1752
- })
1753
- .catch(() => {
1754
- this.messageService.add({
1755
- severity: 'error',
1756
- summary: this.translate('shared.error'),
1757
- detail: this.translate('layout.profile.failed.copy.sign.up.link'),
1758
- });
1759
- });
1760
- }
1761
- translate(key, vars) {
1762
- return this.translateAdapter?.translate(key, vars) ?? key;
1763
- }
1764
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppProfile, deps: [], target: i0.ɵɵFactoryTarget.Component });
1765
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppProfile, isStandalone: true, selector: "app-profile", host: { classAttribute: "relative" }, ngImport: i0, template: `
2093
+ `
2094
+ }]
2095
+ }],
2096
+ ctorParameters: () => []
2097
+ });
2098
+ var AppProfile = class AppProfile {
2099
+ destroyRef = inject(DestroyRef);
2100
+ authState = inject(LAYOUT_AUTH_STATE, { optional: true });
2101
+ authApi = inject(LAYOUT_AUTH_API, { optional: true });
2102
+ layoutService = inject(LayoutService);
2103
+ messageService = inject(MessageService);
2104
+ translateAdapter = inject(TranslateService, { optional: true });
2105
+ appConfig = inject(APP_CONFIG, { optional: true });
2106
+ document = inject(DOCUMENT$1);
2107
+ elementRef = inject(ElementRef);
2108
+ _isActive = signal(false, ...ngDevMode ? [{ debugName: "_isActive" }] : /* istanbul ignore next */ []);
2109
+ isActive = this._isActive.asReadonly();
2110
+ constructor() {
2111
+ fromEvent(this.document, "click").pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this.isActive())).subscribe((event) => {
2112
+ const target = event.target;
2113
+ if (!this.elementRef.nativeElement.contains(target)) this._isActive.set(false);
2114
+ });
2115
+ }
2116
+ togglePanel() {
2117
+ this._isActive.update((v) => !v);
2118
+ }
2119
+ userName = computed(() => this.authState?.loginUserData()?.name ?? this.translate("layout.profile.guest"), ...ngDevMode ? [{ debugName: "userName" }] : /* istanbul ignore next */ []);
2120
+ userEmail = computed(() => this.authState?.loginUserData()?.email ?? "", ...ngDevMode ? [{ debugName: "userEmail" }] : /* istanbul ignore next */ []);
2121
+ profilePicture = computed(() => this.layoutService.userProfilePictureUrl() ?? "", ...ngDevMode ? [{ debugName: "profilePicture" }] : /* istanbul ignore next */ []);
2122
+ signUpEnabled = computed(() => this.appConfig ? isSignUpEnabled(this.appConfig) : false, ...ngDevMode ? [{ debugName: "signUpEnabled" }] : /* istanbul ignore next */ []);
2123
+ logout() {
2124
+ if (!this.authApi) return;
2125
+ this.authApi.logOut().pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
2126
+ next: (result) => {
2127
+ const r = result;
2128
+ this.messageService.add({
2129
+ severity: "success",
2130
+ summary: this.translate("shared.success"),
2131
+ detail: r?.messageKey ? this.translate(r.messageKey, r.messageVariables) : r?.message ?? ""
2132
+ });
2133
+ this.authApi?.navigateLogin(false);
2134
+ },
2135
+ error: () => {}
2136
+ });
2137
+ }
2138
+ copySignUpLink() {
2139
+ const slug = this.authState?.currentCompanyInfo()?.slug ?? "";
2140
+ const signUpLink = `${window.location.origin}/auth/register?companySlug=${slug}`;
2141
+ navigator.clipboard.writeText(signUpLink).then(() => {
2142
+ this.messageService.add({
2143
+ severity: "success",
2144
+ summary: this.translate("layout.profile.link.copied"),
2145
+ detail: this.translate("layout.profile.sign.up.link.copied")
2146
+ });
2147
+ }).catch(() => {
2148
+ this.messageService.add({
2149
+ severity: "error",
2150
+ summary: this.translate("shared.error"),
2151
+ detail: this.translate("layout.profile.failed.copy.sign.up.link")
2152
+ });
2153
+ });
2154
+ }
2155
+ translate(key, vars) {
2156
+ return this.translateAdapter?.translate(key, vars) ?? key;
2157
+ }
2158
+ static ɵfac = i0.ɵɵngDeclareFactory({
2159
+ minVersion: "12.0.0",
2160
+ version: "22.2.1",
2161
+ ngImport: i0,
2162
+ type: AppProfile,
2163
+ deps: [],
2164
+ target: i0.ɵɵFactoryTarget.Component
2165
+ });
2166
+ static ɵcmp = i0.ɵɵngDeclareComponent({
2167
+ minVersion: "17.0.0",
2168
+ version: "22.2.1",
2169
+ type: AppProfile,
2170
+ isStandalone: true,
2171
+ selector: "app-profile",
2172
+ host: { classAttribute: "relative" },
2173
+ ngImport: i0,
2174
+ template: `
1766
2175
  <button
1767
2176
  type="button"
1768
2177
  class="layout-topbar-action"
@@ -1835,15 +2244,88 @@ class AppProfile {
1835
2244
  </div>
1836
2245
  </div>
1837
2246
  </div>
1838
- `, isInline: true, dependencies: [{ kind: "directive", type: StyleClassDirective, selector: "[fStyleClass]", inputs: ["fStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: IsEmptyImageDirective, selector: "img", inputs: ["src"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1839
- }
1840
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppProfile, decorators: [{
1841
- type: Component,
1842
- args: [{
1843
- selector: 'app-profile',
1844
- imports: [StyleClassDirective, UiIconComponent, RouterLink, IsEmptyImageDirective, TranslatePipe],
1845
- host: { class: 'relative' },
1846
- template: `
2247
+ `,
2248
+ isInline: true,
2249
+ dependencies: [
2250
+ {
2251
+ kind: "directive",
2252
+ type: StyleClassDirective,
2253
+ selector: "[fStyleClass]",
2254
+ inputs: [
2255
+ "fStyleClass",
2256
+ "enterFromClass",
2257
+ "enterActiveClass",
2258
+ "enterToClass",
2259
+ "leaveFromClass",
2260
+ "leaveActiveClass",
2261
+ "leaveToClass",
2262
+ "hideOnOutsideClick",
2263
+ "toggleClass"
2264
+ ]
2265
+ },
2266
+ {
2267
+ kind: "component",
2268
+ type: UiIconComponent,
2269
+ selector: "ui-icon",
2270
+ inputs: [
2271
+ "name",
2272
+ "size",
2273
+ "spin",
2274
+ "fill",
2275
+ "severity"
2276
+ ]
2277
+ },
2278
+ {
2279
+ kind: "directive",
2280
+ type: RouterLink,
2281
+ selector: "[routerLink]",
2282
+ inputs: [
2283
+ "target",
2284
+ "queryParams",
2285
+ "fragment",
2286
+ "queryParamsHandling",
2287
+ "state",
2288
+ "info",
2289
+ "relativeTo",
2290
+ "preserveFragment",
2291
+ "skipLocationChange",
2292
+ "replaceUrl",
2293
+ "browserUrl",
2294
+ "routerLink"
2295
+ ]
2296
+ },
2297
+ {
2298
+ kind: "directive",
2299
+ type: IsEmptyImageDirective,
2300
+ selector: "img",
2301
+ inputs: ["src"]
2302
+ },
2303
+ {
2304
+ kind: "pipe",
2305
+ type: TranslatePipe,
2306
+ name: "translate"
2307
+ }
2308
+ ]
2309
+ });
2310
+ };
2311
+ i0.ɵɵngDeclareClassMetadata({
2312
+ minVersion: "12.0.0",
2313
+ version: "22.2.1",
2314
+ ngImport: i0,
2315
+ type: AppProfile,
2316
+ decorators: [{
2317
+ type: Component,
2318
+ args: [{
2319
+ selector: "app-profile",
2320
+ imports: [
2321
+ StyleClassDirective,
2322
+ UiIconComponent,
2323
+ RouterLink,
2324
+ IsEmptyImageDirective,
2325
+ TranslatePipe
2326
+ ],
2327
+ host: { class: "relative" },
2328
+ template: `
1847
2329
  <button
1848
2330
  type="button"
1849
2331
  class="layout-topbar-action"
@@ -1916,151 +2398,136 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
1916
2398
  </div>
1917
2399
  </div>
1918
2400
  </div>
1919
- `,
1920
- }]
1921
- }], ctorParameters: () => [] });
1922
-
1923
- class AppSearchbar {
1924
- layoutService = inject(LayoutService);
1925
- router = inject(Router);
1926
- searchAdapter = inject(LAYOUT_SEARCH_ADAPTER, {
1927
- optional: true,
1928
- });
1929
- destroyRef = inject(DestroyRef);
1930
- document = inject(DOCUMENT$1);
1931
- searchTerm = signal('', /* @ts-ignore */
1932
- ...(ngDevMode ? [{ debugName: "searchTerm" }] : /* istanbul ignore next */ []));
1933
- suggestionsOpen = signal(false, /* @ts-ignore */
1934
- ...(ngDevMode ? [{ debugName: "suggestionsOpen" }] : /* istanbul ignore next */ []));
1935
- suggestions = signal([], /* @ts-ignore */
1936
- ...(ngDevMode ? [{ debugName: "suggestions" }] : /* istanbul ignore next */ []));
1937
- focusedIndex = signal(-1, /* @ts-ignore */
1938
- ...(ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []));
1939
- isSearchExpanded = signal(false, /* @ts-ignore */
1940
- ...(ngDevMode ? [{ debugName: "isSearchExpanded" }] : /* istanbul ignore next */ []));
1941
- isResponsive = signal(typeof window !== 'undefined' ? window.innerWidth < 768 : false, /* @ts-ignore */
1942
- ...(ngDevMode ? [{ debugName: "isResponsive" }] : /* istanbul ignore next */ []));
1943
- searchWrapper = viewChild('searchWrapper', /* @ts-ignore */
1944
- ...(ngDevMode ? [{ debugName: "searchWrapper" }] : /* istanbul ignore next */ []));
1945
- searchInput = viewChild('searchInput', /* @ts-ignore */
1946
- ...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
1947
- searchTimeout;
1948
- constructor() {
1949
- // Track responsive breakpoint (mobile < 768px)
1950
- if (typeof window !== 'undefined') {
1951
- fromEvent(window, 'resize')
1952
- .pipe(takeUntilDestroyed(this.destroyRef))
1953
- .subscribe(() => {
1954
- this.isResponsive.set(window.innerWidth < 768);
1955
- });
1956
- }
1957
- // Close dropdown/search on outside click
1958
- fromEvent(this.document, 'click')
1959
- .pipe(takeUntilDestroyed(this.destroyRef))
1960
- .subscribe((event) => {
1961
- const wrapper = this.searchWrapper()?.nativeElement;
1962
- const target = event.target;
1963
- if (wrapper && !wrapper.contains(target)) {
1964
- if (this.suggestionsOpen()) {
1965
- this.closeSuggestions();
1966
- }
1967
- if (this.isSearchExpanded() &&
1968
- (this.layoutService.isTopbar() || this.isResponsive())) {
1969
- this.closeSearch();
1970
- }
1971
- }
1972
- });
1973
- // Debounced search for suggestions
1974
- effect(() => {
1975
- const query = this.searchTerm();
1976
- clearTimeout(this.searchTimeout);
1977
- if (!this.searchAdapter || !query.trim()) {
1978
- this.suggestions.set([]);
1979
- this.focusedIndex.set(-1);
1980
- return;
1981
- }
1982
- this.searchTimeout = setTimeout(() => {
1983
- this.searchAdapter.getSuggestions(query)
1984
- .pipe(takeUntilDestroyed(this.destroyRef))
1985
- .subscribe((results) => {
1986
- this.suggestions.set(results);
1987
- this.focusedIndex.set(-1);
1988
- this.suggestionsOpen.set(results.length > 0);
1989
- });
1990
- }, 300);
1991
- });
1992
- }
1993
- onFocus() {
1994
- if (this.suggestions().length > 0) {
1995
- this.suggestionsOpen.set(true);
1996
- }
1997
- }
1998
- handleKeydown(event) {
1999
- const items = this.suggestions();
2000
- const current = this.focusedIndex();
2001
- switch (event.key) {
2002
- case 'ArrowDown':
2003
- event.preventDefault();
2004
- this.focusedIndex.set(current < items.length - 1 ? current + 1 : 0);
2005
- this.suggestionsOpen.set(items.length > 0);
2006
- break;
2007
- case 'ArrowUp':
2008
- event.preventDefault();
2009
- this.focusedIndex.set(current > 0 ? current - 1 : items.length - 1);
2010
- break;
2011
- case 'Escape':
2012
- event.preventDefault();
2013
- this.closeSuggestions();
2014
- break;
2015
- }
2016
- }
2017
- handleEnter() {
2018
- const current = this.focusedIndex();
2019
- const items = this.suggestions();
2020
- if (current >= 0 && current < items.length) {
2021
- this.selectSuggestion(items[current]);
2022
- }
2023
- else if (items.length > 0) {
2024
- this.selectSuggestion(items[0]);
2025
- }
2026
- else {
2027
- this.onSearch();
2028
- }
2029
- }
2030
- selectSuggestion(item) {
2031
- if (item.routerLink) {
2032
- this.router.navigate(item.routerLink);
2033
- }
2034
- this.searchTerm.set('');
2035
- this.closeSuggestions();
2036
- if (this.layoutService.isTopbar() || this.isResponsive()) {
2037
- this.closeSearch();
2038
- }
2039
- }
2040
- closeSuggestions() {
2041
- this.suggestionsOpen.set(false);
2042
- this.focusedIndex.set(-1);
2043
- }
2044
- toggleSearch() {
2045
- this.isSearchExpanded.set(!this.isSearchExpanded());
2046
- if (this.isSearchExpanded()) {
2047
- setTimeout(() => {
2048
- this.searchInput()?.nativeElement?.focus();
2049
- }, 100);
2050
- }
2051
- }
2052
- closeSearch() {
2053
- this.isSearchExpanded.set(false);
2054
- this.closeSuggestions();
2055
- }
2056
- onSearch() {
2057
- const query = this.searchTerm();
2058
- if (this.searchAdapter?.onSearch && query.trim()) {
2059
- this.searchAdapter.onSearch(query);
2060
- }
2061
- }
2062
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppSearchbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2063
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppSearchbar, isStandalone: true, selector: "app-searchbar", viewQueries: [{ propertyName: "searchWrapper", first: true, predicate: ["searchWrapper"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
2401
+ `
2402
+ }]
2403
+ }],
2404
+ ctorParameters: () => []
2405
+ });
2406
+ var AppSearchbar = class AppSearchbar {
2407
+ layoutService = inject(LayoutService);
2408
+ router = inject(Router);
2409
+ searchAdapter = inject(LAYOUT_SEARCH_ADAPTER, { optional: true });
2410
+ destroyRef = inject(DestroyRef);
2411
+ document = inject(DOCUMENT$1);
2412
+ searchTerm = signal("", ...ngDevMode ? [{ debugName: "searchTerm" }] : /* istanbul ignore next */ []);
2413
+ suggestionsOpen = signal(false, ...ngDevMode ? [{ debugName: "suggestionsOpen" }] : /* istanbul ignore next */ []);
2414
+ suggestions = signal([], ...ngDevMode ? [{ debugName: "suggestions" }] : /* istanbul ignore next */ []);
2415
+ focusedIndex = signal(-1, ...ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []);
2416
+ isSearchExpanded = signal(false, ...ngDevMode ? [{ debugName: "isSearchExpanded" }] : /* istanbul ignore next */ []);
2417
+ isResponsive = signal(typeof window !== "undefined" ? window.innerWidth < 768 : false, ...ngDevMode ? [{ debugName: "isResponsive" }] : /* istanbul ignore next */ []);
2418
+ searchWrapper = viewChild("searchWrapper", ...ngDevMode ? [{ debugName: "searchWrapper" }] : /* istanbul ignore next */ []);
2419
+ searchInput = viewChild("searchInput", ...ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []);
2420
+ searchTimeout;
2421
+ constructor() {
2422
+ if (typeof window !== "undefined") fromEvent(window, "resize").pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
2423
+ this.isResponsive.set(window.innerWidth < 768);
2424
+ });
2425
+ fromEvent(this.document, "click").pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
2426
+ const wrapper = this.searchWrapper()?.nativeElement;
2427
+ const target = event.target;
2428
+ if (wrapper && !wrapper.contains(target)) {
2429
+ if (this.suggestionsOpen()) this.closeSuggestions();
2430
+ if (this.isSearchExpanded() && (this.layoutService.isTopbar() || this.isResponsive())) this.closeSearch();
2431
+ }
2432
+ });
2433
+ effect(() => {
2434
+ const query = this.searchTerm();
2435
+ clearTimeout(this.searchTimeout);
2436
+ if (!this.searchAdapter || !query.trim()) {
2437
+ this.suggestions.set([]);
2438
+ this.focusedIndex.set(-1);
2439
+ return;
2440
+ }
2441
+ this.searchTimeout = setTimeout(() => {
2442
+ this.searchAdapter.getSuggestions(query).pipe(takeUntilDestroyed(this.destroyRef)).subscribe((results) => {
2443
+ this.suggestions.set(results);
2444
+ this.focusedIndex.set(-1);
2445
+ this.suggestionsOpen.set(results.length > 0);
2446
+ });
2447
+ }, 300);
2448
+ });
2449
+ }
2450
+ onFocus() {
2451
+ if (this.suggestions().length > 0) this.suggestionsOpen.set(true);
2452
+ }
2453
+ handleKeydown(event) {
2454
+ const items = this.suggestions();
2455
+ const current = this.focusedIndex();
2456
+ switch (event.key) {
2457
+ case "ArrowDown":
2458
+ event.preventDefault();
2459
+ this.focusedIndex.set(current < items.length - 1 ? current + 1 : 0);
2460
+ this.suggestionsOpen.set(items.length > 0);
2461
+ break;
2462
+ case "ArrowUp":
2463
+ event.preventDefault();
2464
+ this.focusedIndex.set(current > 0 ? current - 1 : items.length - 1);
2465
+ break;
2466
+ case "Escape":
2467
+ event.preventDefault();
2468
+ this.closeSuggestions();
2469
+ }
2470
+ }
2471
+ handleEnter() {
2472
+ const current = this.focusedIndex();
2473
+ const items = this.suggestions();
2474
+ if (current >= 0 && current < items.length) this.selectSuggestion(items[current]);
2475
+ else if (items.length > 0) this.selectSuggestion(items[0]);
2476
+ else this.onSearch();
2477
+ }
2478
+ selectSuggestion(item) {
2479
+ if (item.routerLink) this.router.navigate(item.routerLink);
2480
+ this.searchTerm.set("");
2481
+ this.closeSuggestions();
2482
+ if (this.layoutService.isTopbar() || this.isResponsive()) this.closeSearch();
2483
+ }
2484
+ closeSuggestions() {
2485
+ this.suggestionsOpen.set(false);
2486
+ this.focusedIndex.set(-1);
2487
+ }
2488
+ toggleSearch() {
2489
+ this.isSearchExpanded.set(!this.isSearchExpanded());
2490
+ if (this.isSearchExpanded()) setTimeout(() => {
2491
+ this.searchInput()?.nativeElement?.focus();
2492
+ }, 100);
2493
+ }
2494
+ closeSearch() {
2495
+ this.isSearchExpanded.set(false);
2496
+ this.closeSuggestions();
2497
+ }
2498
+ onSearch() {
2499
+ const query = this.searchTerm();
2500
+ if (this.searchAdapter?.onSearch && query.trim()) this.searchAdapter.onSearch(query);
2501
+ }
2502
+ static ɵfac = i0.ɵɵngDeclareFactory({
2503
+ minVersion: "12.0.0",
2504
+ version: "22.2.1",
2505
+ ngImport: i0,
2506
+ type: AppSearchbar,
2507
+ deps: [],
2508
+ target: i0.ɵɵFactoryTarget.Component
2509
+ });
2510
+ static ɵcmp = i0.ɵɵngDeclareComponent({
2511
+ minVersion: "17.0.0",
2512
+ version: "22.2.1",
2513
+ type: AppSearchbar,
2514
+ isStandalone: true,
2515
+ selector: "app-searchbar",
2516
+ viewQueries: [{
2517
+ propertyName: "searchWrapper",
2518
+ first: true,
2519
+ predicate: ["searchWrapper"],
2520
+ descendants: true,
2521
+ isSignal: true
2522
+ }, {
2523
+ propertyName: "searchInput",
2524
+ first: true,
2525
+ predicate: ["searchInput"],
2526
+ descendants: true,
2527
+ isSignal: true
2528
+ }],
2529
+ ngImport: i0,
2530
+ template: `
2064
2531
  <div
2065
2532
  class="search-container h-full"
2066
2533
  #searchWrapper
@@ -2164,18 +2631,104 @@ class AppSearchbar {
2164
2631
  }
2165
2632
  }
2166
2633
  </div>
2167
- `, isInline: true, styles: [".search-container{display:flex;align-items:center;position:relative;width:100%;flex:0 1 auto}.search-container.topbar-mode{width:auto}.search-dropdown-panel{position:absolute;top:100%;right:0;margin-top:.5rem;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1100;padding:1rem .75rem .75rem;min-width:300px;max-width:450px;animation:slideDown .2s ease-out}.search-suggestions{margin-top:.5rem;border-top:1px solid var(--surface-border);padding-top:.5rem;background-color:inherit}.suggestions-list{list-style:none;margin:0;padding:0;max-height:360px;overflow-y:auto;background-color:inherit}.suggestion-item{padding:.75rem 1rem;cursor:pointer;display:flex;align-items:center;gap:.75rem;color:var(--text-color);border-bottom:1px solid var(--surface-border);transition:background-color .15s ease,color .15s ease}.suggestion-item:last-child{border-bottom:none}.suggestion-item:hover,.suggestion-item.focused{background-color:var(--surface-50);color:var(--primary-color)}.suggestion-icon{color:var(--text-color-secondary);font-size:1rem;flex-shrink:0;transition:color .15s ease}.suggestion-item:hover .suggestion-icon,.suggestion-item.focused .suggestion-icon{color:var(--primary-color)}.suggestion-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-dropdown{position:absolute;top:calc(100% + .5rem);left:0;right:0;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1000;overflow:hidden}.search-dropdown .suggestions-list{background-color:inherit}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@media(max-width:768px){.search-dropdown-panel{min-width:280px;max-width:400px}}@media(max-width:576px){.search-dropdown-panel{position:fixed;top:var(--layout-shell-topbar-bottom, 4rem);left:0;right:0;bottom:auto;width:100vw;min-width:100vw;max-width:100vw;margin-top:0;padding:1rem;border-radius:0;border-bottom:1px solid var(--surface-border);z-index:1200}.search-dropdown-panel .search-iconfield{margin-bottom:1rem}.search-dropdown{max-width:100%;left:0!important;right:0!important;transform:none;position:static;border-radius:var(--f-content-border-radius);margin-top:0}.suggestion-item{padding:.75rem}}\n"], dependencies: [{ kind: "component", type: IconFieldComponent, selector: "f-iconfield", inputs: ["iconPosition", "styleClass"] }, { kind: "component", type: InputIconComponent, selector: "f-inputicon", inputs: ["icon", "position", "styleClass", "style"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2168
- }
2169
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppSearchbar, decorators: [{
2170
- type: Component,
2171
- args: [{ selector: 'app-searchbar', imports: [
2172
- IconFieldComponent,
2173
- InputIconComponent,
2174
- InputTextDirective,
2175
- UiIconComponent,
2176
- FormsModule,
2177
- TranslatePipe,
2178
- ], template: `
2634
+ `,
2635
+ isInline: true,
2636
+ styles: [".search-container{display:flex;align-items:center;position:relative;width:100%;flex:0 1 auto}.search-container.topbar-mode{width:auto}.search-dropdown-panel{position:absolute;top:100%;right:0;margin-top:.5rem;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1100;padding:1rem .75rem .75rem;min-width:300px;max-width:450px;animation:slideDown .2s ease-out}.search-suggestions{margin-top:.5rem;border-top:1px solid var(--surface-border);padding-top:.5rem;background-color:inherit}.suggestions-list{list-style:none;margin:0;padding:0;max-height:360px;overflow-y:auto;background-color:inherit}.suggestion-item{padding:.75rem 1rem;cursor:pointer;display:flex;align-items:center;gap:.75rem;color:var(--text-color);border-bottom:1px solid var(--surface-border);transition:background-color .15s ease,color .15s ease}.suggestion-item:last-child{border-bottom:none}.suggestion-item:hover,.suggestion-item.focused{background-color:var(--surface-50);color:var(--primary-color)}.suggestion-icon{color:var(--text-color-secondary);font-size:1rem;flex-shrink:0;transition:color .15s ease}.suggestion-item:hover .suggestion-icon,.suggestion-item.focused .suggestion-icon{color:var(--primary-color)}.suggestion-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-dropdown{position:absolute;top:calc(100% + .5rem);left:0;right:0;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1000;overflow:hidden}.search-dropdown .suggestions-list{background-color:inherit}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@media(max-width:768px){.search-dropdown-panel{min-width:280px;max-width:400px}}@media(max-width:576px){.search-dropdown-panel{position:fixed;top:var(--layout-shell-topbar-bottom, 4rem);left:0;right:0;bottom:auto;width:100vw;min-width:100vw;max-width:100vw;margin-top:0;padding:1rem;border-radius:0;border-bottom:1px solid var(--surface-border);z-index:1200}.search-dropdown-panel .search-iconfield{margin-bottom:1rem}.search-dropdown{max-width:100%;left:0!important;right:0!important;transform:none;position:static;border-radius:var(--f-content-border-radius);margin-top:0}.suggestion-item{padding:.75rem}}\n"],
2637
+ dependencies: [
2638
+ {
2639
+ kind: "component",
2640
+ type: IconFieldComponent,
2641
+ selector: "f-iconfield",
2642
+ inputs: ["iconPosition", "styleClass"]
2643
+ },
2644
+ {
2645
+ kind: "component",
2646
+ type: InputIconComponent,
2647
+ selector: "f-inputicon",
2648
+ inputs: [
2649
+ "icon",
2650
+ "position",
2651
+ "styleClass",
2652
+ "style"
2653
+ ]
2654
+ },
2655
+ {
2656
+ kind: "directive",
2657
+ type: InputTextDirective,
2658
+ selector: "[fInputText]",
2659
+ inputs: [
2660
+ "invalid",
2661
+ "touched",
2662
+ "variant",
2663
+ "size",
2664
+ "fluid"
2665
+ ]
2666
+ },
2667
+ {
2668
+ kind: "component",
2669
+ type: UiIconComponent,
2670
+ selector: "ui-icon",
2671
+ inputs: [
2672
+ "name",
2673
+ "size",
2674
+ "spin",
2675
+ "fill",
2676
+ "severity"
2677
+ ]
2678
+ },
2679
+ {
2680
+ kind: "ngmodule",
2681
+ type: FormsModule
2682
+ },
2683
+ {
2684
+ kind: "directive",
2685
+ type: i1.DefaultValueAccessor,
2686
+ selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"
2687
+ },
2688
+ {
2689
+ kind: "directive",
2690
+ type: i1.NgControlStatus,
2691
+ selector: "[formControlName],[ngModel],[formControl]"
2692
+ },
2693
+ {
2694
+ kind: "directive",
2695
+ type: i1.NgModel,
2696
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
2697
+ inputs: [
2698
+ "name",
2699
+ "disabled",
2700
+ "ngModel",
2701
+ "ngModelOptions"
2702
+ ],
2703
+ outputs: ["ngModelChange"],
2704
+ exportAs: ["ngModel"]
2705
+ },
2706
+ {
2707
+ kind: "pipe",
2708
+ type: TranslatePipe,
2709
+ name: "translate"
2710
+ }
2711
+ ]
2712
+ });
2713
+ };
2714
+ i0.ɵɵngDeclareClassMetadata({
2715
+ minVersion: "12.0.0",
2716
+ version: "22.2.1",
2717
+ ngImport: i0,
2718
+ type: AppSearchbar,
2719
+ decorators: [{
2720
+ type: Component,
2721
+ args: [{
2722
+ selector: "app-searchbar",
2723
+ imports: [
2724
+ IconFieldComponent,
2725
+ InputIconComponent,
2726
+ InputTextDirective,
2727
+ UiIconComponent,
2728
+ FormsModule,
2729
+ TranslatePipe
2730
+ ],
2731
+ template: `
2179
2732
  <div
2180
2733
  class="search-container h-full"
2181
2734
  #searchWrapper
@@ -2279,56 +2832,71 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
2279
2832
  }
2280
2833
  }
2281
2834
  </div>
2282
- `, styles: [".search-container{display:flex;align-items:center;position:relative;width:100%;flex:0 1 auto}.search-container.topbar-mode{width:auto}.search-dropdown-panel{position:absolute;top:100%;right:0;margin-top:.5rem;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1100;padding:1rem .75rem .75rem;min-width:300px;max-width:450px;animation:slideDown .2s ease-out}.search-suggestions{margin-top:.5rem;border-top:1px solid var(--surface-border);padding-top:.5rem;background-color:inherit}.suggestions-list{list-style:none;margin:0;padding:0;max-height:360px;overflow-y:auto;background-color:inherit}.suggestion-item{padding:.75rem 1rem;cursor:pointer;display:flex;align-items:center;gap:.75rem;color:var(--text-color);border-bottom:1px solid var(--surface-border);transition:background-color .15s ease,color .15s ease}.suggestion-item:last-child{border-bottom:none}.suggestion-item:hover,.suggestion-item.focused{background-color:var(--surface-50);color:var(--primary-color)}.suggestion-icon{color:var(--text-color-secondary);font-size:1rem;flex-shrink:0;transition:color .15s ease}.suggestion-item:hover .suggestion-icon,.suggestion-item.focused .suggestion-icon{color:var(--primary-color)}.suggestion-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-dropdown{position:absolute;top:calc(100% + .5rem);left:0;right:0;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1000;overflow:hidden}.search-dropdown .suggestions-list{background-color:inherit}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@media(max-width:768px){.search-dropdown-panel{min-width:280px;max-width:400px}}@media(max-width:576px){.search-dropdown-panel{position:fixed;top:var(--layout-shell-topbar-bottom, 4rem);left:0;right:0;bottom:auto;width:100vw;min-width:100vw;max-width:100vw;margin-top:0;padding:1rem;border-radius:0;border-bottom:1px solid var(--surface-border);z-index:1200}.search-dropdown-panel .search-iconfield{margin-bottom:1rem}.search-dropdown{max-width:100%;left:0!important;right:0!important;transform:none;position:static;border-radius:var(--f-content-border-radius);margin-top:0}.suggestion-item{padding:.75rem}}\n"] }]
2283
- }], ctorParameters: () => [], propDecorators: { searchWrapper: [{ type: i0.ViewChild, args: ['searchWrapper', { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
2284
-
2285
- /**
2286
- * Trailing action rail of the topbar — search, theme controls and the widget menu.
2287
- * Skins own the bar around it; the rail itself is identical in every skin, and
2288
- * `display: contents` on the host keeps it a direct flex child of the bar.
2289
- */
2290
- class AppTopbarActions {
2291
- appConfig = inject(APP_CONFIG);
2292
- document = inject(DOCUMENT$1);
2293
- destroyRef = inject(DestroyRef);
2294
- layoutService = inject(LayoutService);
2295
- notificationBellComponent = inject(LAYOUT_NOTIFICATION_BELL, {
2296
- optional: true,
2297
- });
2298
- languageSelectorComponent = inject(LAYOUT_LANGUAGE_SELECTOR, {
2299
- optional: true,
2300
- });
2301
- searchAdapter = inject(LAYOUT_SEARCH_ADAPTER, {
2302
- optional: true,
2303
- });
2304
- configContainer = viewChild('configContainer', /* @ts-ignore */
2305
- ...(ngDevMode ? [{ debugName: "configContainer" }] : /* istanbul ignore next */ []));
2306
- _configPanelOpen = signal(false, /* @ts-ignore */
2307
- ...(ngDevMode ? [{ debugName: "_configPanelOpen" }] : /* istanbul ignore next */ []));
2308
- configPanelOpen = this._configPanelOpen.asReadonly();
2309
- hasSearchAdapter = computed(() => !!this.searchAdapter, /* @ts-ignore */
2310
- ...(ngDevMode ? [{ debugName: "hasSearchAdapter" }] : /* istanbul ignore next */ []));
2311
- enableCompanyFeature = isCompanyFeatureEnabled(this.appConfig);
2312
- constructor() {
2313
- fromEvent(this.document, 'click')
2314
- .pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this._configPanelOpen()))
2315
- .subscribe((event) => {
2316
- const configEl = this.configContainer()?.nativeElement;
2317
- if (configEl && !configEl.contains(event.target)) {
2318
- this._configPanelOpen.set(false);
2319
- }
2320
- });
2321
- }
2322
- toggleDarkMode() {
2323
- this.layoutService.updateLayoutConfig({
2324
- darkTheme: !this.layoutService.layoutConfig().darkTheme,
2325
- });
2326
- }
2327
- toggleConfigPanel() {
2328
- this._configPanelOpen.update((open) => !open);
2329
- }
2330
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppTopbarActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
2331
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppTopbarActions, isStandalone: true, selector: "app-topbar-actions", host: { styleAttribute: "display: contents" }, viewQueries: [{ propertyName: "configContainer", first: true, predicate: ["configContainer"], descendants: true, isSignal: true }], ngImport: i0, template: `
2835
+ `,
2836
+ styles: [".search-container{display:flex;align-items:center;position:relative;width:100%;flex:0 1 auto}.search-container.topbar-mode{width:auto}.search-dropdown-panel{position:absolute;top:100%;right:0;margin-top:.5rem;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1100;padding:1rem .75rem .75rem;min-width:300px;max-width:450px;animation:slideDown .2s ease-out}.search-suggestions{margin-top:.5rem;border-top:1px solid var(--surface-border);padding-top:.5rem;background-color:inherit}.suggestions-list{list-style:none;margin:0;padding:0;max-height:360px;overflow-y:auto;background-color:inherit}.suggestion-item{padding:.75rem 1rem;cursor:pointer;display:flex;align-items:center;gap:.75rem;color:var(--text-color);border-bottom:1px solid var(--surface-border);transition:background-color .15s ease,color .15s ease}.suggestion-item:last-child{border-bottom:none}.suggestion-item:hover,.suggestion-item.focused{background-color:var(--surface-50);color:var(--primary-color)}.suggestion-icon{color:var(--text-color-secondary);font-size:1rem;flex-shrink:0;transition:color .15s ease}.suggestion-item:hover .suggestion-icon,.suggestion-item.focused .suggestion-icon{color:var(--primary-color)}.suggestion-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-dropdown{position:absolute;top:calc(100% + .5rem);left:0;right:0;background-color:var(--surface-card);border:1px solid var(--surface-border);border-radius:var(--f-content-border-radius);box-shadow:var(--f-shadow-xl);z-index:1000;overflow:hidden}.search-dropdown .suggestions-list{background-color:inherit}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@media(max-width:768px){.search-dropdown-panel{min-width:280px;max-width:400px}}@media(max-width:576px){.search-dropdown-panel{position:fixed;top:var(--layout-shell-topbar-bottom, 4rem);left:0;right:0;bottom:auto;width:100vw;min-width:100vw;max-width:100vw;margin-top:0;padding:1rem;border-radius:0;border-bottom:1px solid var(--surface-border);z-index:1200}.search-dropdown-panel .search-iconfield{margin-bottom:1rem}.search-dropdown{max-width:100%;left:0!important;right:0!important;transform:none;position:static;border-radius:var(--f-content-border-radius);margin-top:0}.suggestion-item{padding:.75rem}}\n"]
2837
+ }]
2838
+ }],
2839
+ ctorParameters: () => [],
2840
+ propDecorators: {
2841
+ searchWrapper: [{
2842
+ type: i0.ViewChild,
2843
+ args: ["searchWrapper", { isSignal: true }]
2844
+ }],
2845
+ searchInput: [{
2846
+ type: i0.ViewChild,
2847
+ args: ["searchInput", { isSignal: true }]
2848
+ }]
2849
+ }
2850
+ });
2851
+ var AppTopbarActions = class AppTopbarActions {
2852
+ appConfig = inject(APP_CONFIG);
2853
+ document = inject(DOCUMENT$1);
2854
+ destroyRef = inject(DestroyRef);
2855
+ layoutService = inject(LayoutService);
2856
+ notificationBellComponent = inject(LAYOUT_NOTIFICATION_BELL, { optional: true });
2857
+ languageSelectorComponent = inject(LAYOUT_LANGUAGE_SELECTOR, { optional: true });
2858
+ searchAdapter = inject(LAYOUT_SEARCH_ADAPTER, { optional: true });
2859
+ configContainer = viewChild("configContainer", ...ngDevMode ? [{ debugName: "configContainer" }] : /* istanbul ignore next */ []);
2860
+ _configPanelOpen = signal(false, ...ngDevMode ? [{ debugName: "_configPanelOpen" }] : /* istanbul ignore next */ []);
2861
+ configPanelOpen = this._configPanelOpen.asReadonly();
2862
+ hasSearchAdapter = computed(() => !!this.searchAdapter, ...ngDevMode ? [{ debugName: "hasSearchAdapter" }] : /* istanbul ignore next */ []);
2863
+ enableCompanyFeature = isCompanyFeatureEnabled(this.appConfig);
2864
+ constructor() {
2865
+ fromEvent(this.document, "click").pipe(takeUntilDestroyed(this.destroyRef), filter$1(() => this._configPanelOpen())).subscribe((event) => {
2866
+ const configEl = this.configContainer()?.nativeElement;
2867
+ if (configEl && !configEl.contains(event.target)) this._configPanelOpen.set(false);
2868
+ });
2869
+ }
2870
+ toggleDarkMode() {
2871
+ this.layoutService.updateLayoutConfig({ darkTheme: !this.layoutService.layoutConfig().darkTheme });
2872
+ }
2873
+ toggleConfigPanel() {
2874
+ this._configPanelOpen.update((open) => !open);
2875
+ }
2876
+ static ɵfac = i0.ɵɵngDeclareFactory({
2877
+ minVersion: "12.0.0",
2878
+ version: "22.2.1",
2879
+ ngImport: i0,
2880
+ type: AppTopbarActions,
2881
+ deps: [],
2882
+ target: i0.ɵɵFactoryTarget.Component
2883
+ });
2884
+ static ɵcmp = i0.ɵɵngDeclareComponent({
2885
+ minVersion: "17.0.0",
2886
+ version: "22.2.1",
2887
+ type: AppTopbarActions,
2888
+ isStandalone: true,
2889
+ selector: "app-topbar-actions",
2890
+ host: { styleAttribute: "display: contents" },
2891
+ viewQueries: [{
2892
+ propertyName: "configContainer",
2893
+ first: true,
2894
+ predicate: ["configContainer"],
2895
+ descendants: true,
2896
+ isSignal: true
2897
+ }],
2898
+ ngImport: i0,
2899
+ template: `
2332
2900
  <div class="layout-topbar-actions">
2333
2901
  @if (hasSearchAdapter()) {
2334
2902
  <app-searchbar />
@@ -2396,25 +2964,107 @@ class AppTopbarActions {
2396
2964
  </div>
2397
2965
  </div>
2398
2966
  </div>
2399
- `, isInline: true, dependencies: [{ kind: "directive", type: StyleClassDirective, selector: "[fStyleClass]", inputs: ["fStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator", inputs: ["showMenuModeButton"] }, { 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" }] });
2400
- }
2401
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppTopbarActions, decorators: [{
2402
- type: Component,
2403
- args: [{
2404
- selector: 'app-topbar-actions',
2405
- imports: [
2406
- StyleClassDirective,
2407
- UiIconComponent,
2408
- NgComponentOutlet,
2409
- TranslatePipe,
2410
- AppConfigurator,
2411
- AppProfile,
2412
- AppCompanyBranchSelector,
2413
- AppLauncher,
2414
- AppSearchbar,
2415
- ],
2416
- host: { style: 'display: contents' },
2417
- template: `
2967
+ `,
2968
+ isInline: true,
2969
+ dependencies: [
2970
+ {
2971
+ kind: "directive",
2972
+ type: StyleClassDirective,
2973
+ selector: "[fStyleClass]",
2974
+ inputs: [
2975
+ "fStyleClass",
2976
+ "enterFromClass",
2977
+ "enterActiveClass",
2978
+ "enterToClass",
2979
+ "leaveFromClass",
2980
+ "leaveActiveClass",
2981
+ "leaveToClass",
2982
+ "hideOnOutsideClick",
2983
+ "toggleClass"
2984
+ ]
2985
+ },
2986
+ {
2987
+ kind: "component",
2988
+ type: UiIconComponent,
2989
+ selector: "ui-icon",
2990
+ inputs: [
2991
+ "name",
2992
+ "size",
2993
+ "spin",
2994
+ "fill",
2995
+ "severity"
2996
+ ]
2997
+ },
2998
+ {
2999
+ kind: "directive",
3000
+ type: NgComponentOutlet,
3001
+ selector: "[ngComponentOutlet]",
3002
+ inputs: [
3003
+ "ngComponentOutlet",
3004
+ "ngComponentOutletInputs",
3005
+ "ngComponentOutletInjector",
3006
+ "ngComponentOutletEnvironmentInjector",
3007
+ "ngComponentOutletContent",
3008
+ "ngComponentOutletNgModule"
3009
+ ],
3010
+ exportAs: ["ngComponentOutlet"]
3011
+ },
3012
+ {
3013
+ kind: "component",
3014
+ type: AppConfigurator,
3015
+ selector: "app-configurator",
3016
+ inputs: ["showMenuModeButton"]
3017
+ },
3018
+ {
3019
+ kind: "component",
3020
+ type: AppProfile,
3021
+ selector: "app-profile"
3022
+ },
3023
+ {
3024
+ kind: "component",
3025
+ type: AppCompanyBranchSelector,
3026
+ selector: "app-company-branch-selector"
3027
+ },
3028
+ {
3029
+ kind: "component",
3030
+ type: AppLauncher,
3031
+ selector: "app-launcher"
3032
+ },
3033
+ {
3034
+ kind: "component",
3035
+ type: AppSearchbar,
3036
+ selector: "app-searchbar"
3037
+ },
3038
+ {
3039
+ kind: "pipe",
3040
+ type: TranslatePipe,
3041
+ name: "translate"
3042
+ }
3043
+ ]
3044
+ });
3045
+ };
3046
+ i0.ɵɵngDeclareClassMetadata({
3047
+ minVersion: "12.0.0",
3048
+ version: "22.2.1",
3049
+ ngImport: i0,
3050
+ type: AppTopbarActions,
3051
+ decorators: [{
3052
+ type: Component,
3053
+ args: [{
3054
+ selector: "app-topbar-actions",
3055
+ imports: [
3056
+ StyleClassDirective,
3057
+ UiIconComponent,
3058
+ NgComponentOutlet,
3059
+ TranslatePipe,
3060
+ AppConfigurator,
3061
+ AppProfile,
3062
+ AppCompanyBranchSelector,
3063
+ AppLauncher,
3064
+ AppSearchbar
3065
+ ],
3066
+ host: { style: "display: contents" },
3067
+ template: `
2418
3068
  <div class="layout-topbar-actions">
2419
3069
  @if (hasSearchAdapter()) {
2420
3070
  <app-searchbar />
@@ -2482,69 +3132,94 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
2482
3132
  </div>
2483
3133
  </div>
2484
3134
  </div>
2485
- `,
2486
- }]
2487
- }], ctorParameters: () => [], propDecorators: { configContainer: [{ type: i0.ViewChild, args: ['configContainer', { isSignal: true }] }] } });
2488
-
2489
- class AppTopbar {
2490
- layoutService = inject(LayoutService);
2491
- navContainer = viewChild('navContainer', /* @ts-ignore */
2492
- ...(ngDevMode ? [{ debugName: "navContainer" }] : /* istanbul ignore next */ []));
2493
- _scrollThumb = signal({ width: 0, offset: 0 }, /* @ts-ignore */
2494
- ...(ngDevMode ? [{ debugName: "_scrollThumb" }] : /* istanbul ignore next */ []));
2495
- scrollThumb = this._scrollThumb.asReadonly();
2496
- constructor() {
2497
- effect((onCleanup) => {
2498
- const nav = this.navContainer();
2499
- if (!nav) {
2500
- this._scrollThumb.set({ width: 0, offset: 0 });
2501
- return;
2502
- }
2503
- const subscription = fromEvent(nav.nativeElement, 'scroll').subscribe(() => {
2504
- this.recalculateHoveredSubmenu(nav.nativeElement);
2505
- this.syncScrollThumb();
2506
- });
2507
- const resizeObserver = typeof ResizeObserver === 'undefined'
2508
- ? null
2509
- : new ResizeObserver(() => this.syncScrollThumb());
2510
- resizeObserver?.observe(nav.nativeElement);
2511
- onCleanup(() => {
2512
- subscription.unsubscribe();
2513
- resizeObserver?.disconnect();
2514
- });
2515
- });
2516
- }
2517
- syncScrollThumb() {
2518
- const nav = this.navContainer()?.nativeElement;
2519
- if (!nav)
2520
- return;
2521
- const { clientWidth, scrollWidth, scrollLeft } = nav;
2522
- if (scrollWidth <= clientWidth + 1) {
2523
- this._scrollThumb.set({ width: 0, offset: 0 });
2524
- return;
2525
- }
2526
- const ratio = clientWidth / scrollWidth;
2527
- this._scrollThumb.set({
2528
- width: Math.round(clientWidth * ratio),
2529
- offset: Math.round(Math.abs(scrollLeft) * ratio),
2530
- });
2531
- }
2532
- /**
2533
- * Horizontal-nav submenus are `position: fixed`, so scrolling the nav strip leaves
2534
- * an open submenu behind — re-anchor it to the item it belongs to.
2535
- */
2536
- recalculateHoveredSubmenu(navEl) {
2537
- navEl.querySelectorAll('li:hover').forEach((li) => {
2538
- const link = li.querySelector(':scope > a');
2539
- if (!link)
2540
- return;
2541
- const rect = link.getBoundingClientRect();
2542
- li.style.setProperty('--submenu-left', `${rect.left - 12}px`);
2543
- li.style.setProperty('--submenu-top', `${rect.bottom + 12}px`);
2544
- });
2545
- }
2546
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppTopbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2547
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppTopbar, isStandalone: true, selector: "app-topbar", viewQueries: [{ propertyName: "navContainer", first: true, predicate: ["navContainer"], descendants: true, isSignal: true }], ngImport: i0, template: `<div class="layout-topbar">
3135
+ `
3136
+ }]
3137
+ }],
3138
+ ctorParameters: () => [],
3139
+ propDecorators: { configContainer: [{
3140
+ type: i0.ViewChild,
3141
+ args: ["configContainer", { isSignal: true }]
3142
+ }] }
3143
+ });
3144
+ var AppTopbar = class AppTopbar {
3145
+ layoutService = inject(LayoutService);
3146
+ navContainer = viewChild("navContainer", ...ngDevMode ? [{ debugName: "navContainer" }] : /* istanbul ignore next */ []);
3147
+ _scrollThumb = signal({
3148
+ width: 0,
3149
+ offset: 0
3150
+ }, ...ngDevMode ? [{ debugName: "_scrollThumb" }] : /* istanbul ignore next */ []);
3151
+ scrollThumb = this._scrollThumb.asReadonly();
3152
+ constructor() {
3153
+ effect((onCleanup) => {
3154
+ const nav = this.navContainer();
3155
+ if (!nav) {
3156
+ this._scrollThumb.set({
3157
+ width: 0,
3158
+ offset: 0
3159
+ });
3160
+ return;
3161
+ }
3162
+ const subscription = fromEvent(nav.nativeElement, "scroll").subscribe(() => {
3163
+ this.recalculateHoveredSubmenu(nav.nativeElement);
3164
+ this.syncScrollThumb();
3165
+ });
3166
+ const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => this.syncScrollThumb());
3167
+ resizeObserver?.observe(nav.nativeElement);
3168
+ onCleanup(() => {
3169
+ subscription.unsubscribe();
3170
+ resizeObserver?.disconnect();
3171
+ });
3172
+ });
3173
+ }
3174
+ syncScrollThumb() {
3175
+ const nav = this.navContainer()?.nativeElement;
3176
+ if (!nav) return;
3177
+ const { clientWidth, scrollWidth, scrollLeft } = nav;
3178
+ if (scrollWidth <= clientWidth + 1) {
3179
+ this._scrollThumb.set({
3180
+ width: 0,
3181
+ offset: 0
3182
+ });
3183
+ return;
3184
+ }
3185
+ const ratio = clientWidth / scrollWidth;
3186
+ this._scrollThumb.set({
3187
+ width: Math.round(clientWidth * ratio),
3188
+ offset: Math.round(Math.abs(scrollLeft) * ratio)
3189
+ });
3190
+ }
3191
+ recalculateHoveredSubmenu(navEl) {
3192
+ navEl.querySelectorAll("li:hover").forEach((li) => {
3193
+ const link = li.querySelector(":scope > a");
3194
+ if (!link) return;
3195
+ const rect = link.getBoundingClientRect();
3196
+ li.style.setProperty("--submenu-left", `${rect.left - 12}px`);
3197
+ li.style.setProperty("--submenu-top", `${rect.bottom + 12}px`);
3198
+ });
3199
+ }
3200
+ static ɵfac = i0.ɵɵngDeclareFactory({
3201
+ minVersion: "12.0.0",
3202
+ version: "22.2.1",
3203
+ ngImport: i0,
3204
+ type: AppTopbar,
3205
+ deps: [],
3206
+ target: i0.ɵɵFactoryTarget.Component
3207
+ });
3208
+ static ɵcmp = i0.ɵɵngDeclareComponent({
3209
+ minVersion: "17.0.0",
3210
+ version: "22.2.1",
3211
+ type: AppTopbar,
3212
+ isStandalone: true,
3213
+ selector: "app-topbar",
3214
+ viewQueries: [{
3215
+ propertyName: "navContainer",
3216
+ first: true,
3217
+ predicate: ["navContainer"],
3218
+ descendants: true,
3219
+ isSignal: true
3220
+ }],
3221
+ ngImport: i0,
3222
+ template: `<div class="layout-topbar">
2548
3223
  <div
2549
3224
  class="layout-topbar-logo-container"
2550
3225
  [class.layout-topbar-logo-container-desktop]="layoutService.isDesktop()"
@@ -2589,14 +3264,61 @@ class AppTopbar {
2589
3264
  }
2590
3265
 
2591
3266
  <app-topbar-actions />
2592
- </div>`, isInline: true, dependencies: [{ kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: AppBrandLogo, selector: "app-brand-logo" }, { kind: "component", type: AppMenu, selector: "app-menu" }, { kind: "component", type: AppTopbarActions, selector: "app-topbar-actions" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2593
- }
2594
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppTopbar, decorators: [{
2595
- type: Component,
2596
- args: [{
2597
- selector: 'app-topbar',
2598
- imports: [UiIconComponent, TranslatePipe, AppBrandLogo, AppMenu, AppTopbarActions],
2599
- template: `<div class="layout-topbar">
3267
+ </div>`,
3268
+ isInline: true,
3269
+ dependencies: [
3270
+ {
3271
+ kind: "component",
3272
+ type: UiIconComponent,
3273
+ selector: "ui-icon",
3274
+ inputs: [
3275
+ "name",
3276
+ "size",
3277
+ "spin",
3278
+ "fill",
3279
+ "severity"
3280
+ ]
3281
+ },
3282
+ {
3283
+ kind: "component",
3284
+ type: AppBrandLogo,
3285
+ selector: "app-brand-logo"
3286
+ },
3287
+ {
3288
+ kind: "component",
3289
+ type: AppMenu,
3290
+ selector: "app-menu"
3291
+ },
3292
+ {
3293
+ kind: "component",
3294
+ type: AppTopbarActions,
3295
+ selector: "app-topbar-actions"
3296
+ },
3297
+ {
3298
+ kind: "pipe",
3299
+ type: TranslatePipe,
3300
+ name: "translate"
3301
+ }
3302
+ ]
3303
+ });
3304
+ };
3305
+ i0.ɵɵngDeclareClassMetadata({
3306
+ minVersion: "12.0.0",
3307
+ version: "22.2.1",
3308
+ ngImport: i0,
3309
+ type: AppTopbar,
3310
+ decorators: [{
3311
+ type: Component,
3312
+ args: [{
3313
+ selector: "app-topbar",
3314
+ imports: [
3315
+ UiIconComponent,
3316
+ TranslatePipe,
3317
+ AppBrandLogo,
3318
+ AppMenu,
3319
+ AppTopbarActions
3320
+ ],
3321
+ template: `<div class="layout-topbar">
2600
3322
  <div
2601
3323
  class="layout-topbar-logo-container"
2602
3324
  [class.layout-topbar-logo-container-desktop]="layoutService.isDesktop()"
@@ -2641,18 +3363,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
2641
3363
  }
2642
3364
 
2643
3365
  <app-topbar-actions />
2644
- </div>`,
2645
- }]
2646
- }], ctorParameters: () => [], propDecorators: { navContainer: [{ type: i0.ViewChild, args: ['navContainer', { isSignal: true }] }] } });
2647
-
2648
- class AppClassicLayout extends LayoutShellBase {
2649
- isStatic = computed(() => this.layoutService.layoutConfig().menuMode === 'static', /* @ts-ignore */
2650
- ...(ngDevMode ? [{ debugName: "isStatic" }] : /* istanbul ignore next */ []));
2651
- outsideClickSelectors() {
2652
- return ['.layout-sidebar', '.layout-menu-button', '.layout-topbar-nav'];
2653
- }
2654
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppClassicLayout, deps: null, target: i0.ɵɵFactoryTarget.Component });
2655
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: AppClassicLayout, isStandalone: true, selector: "app-classic-layout", usesInheritance: true, ngImport: i0, template: `
3366
+ </div>`
3367
+ }]
3368
+ }],
3369
+ ctorParameters: () => [],
3370
+ propDecorators: { navContainer: [{
3371
+ type: i0.ViewChild,
3372
+ args: ["navContainer", { isSignal: true }]
3373
+ }] }
3374
+ });
3375
+ var AppClassicLayout = class AppClassicLayout extends LayoutShellBase {
3376
+ isStatic = computed(() => this.layoutService.layoutConfig().menuMode === "static", ...ngDevMode ? [{ debugName: "isStatic" }] : /* istanbul ignore next */ []);
3377
+ outsideClickSelectors() {
3378
+ return [
3379
+ ".layout-sidebar",
3380
+ ".layout-menu-button",
3381
+ ".layout-topbar-nav"
3382
+ ];
3383
+ }
3384
+ static ɵfac = i0.ɵɵngDeclareFactory({
3385
+ minVersion: "12.0.0",
3386
+ version: "22.2.1",
3387
+ ngImport: i0,
3388
+ type: AppClassicLayout,
3389
+ deps: null,
3390
+ target: i0.ɵɵFactoryTarget.Component
3391
+ });
3392
+ static ɵcmp = i0.ɵɵngDeclareComponent({
3393
+ minVersion: "14.0.0",
3394
+ version: "22.2.1",
3395
+ type: AppClassicLayout,
3396
+ isStandalone: true,
3397
+ selector: "app-classic-layout",
3398
+ usesInheritance: true,
3399
+ ngImport: i0,
3400
+ template: `
2656
3401
  <div
2657
3402
  class="layout-wrapper"
2658
3403
  [class.layout-overlay]="isOverlay()"
@@ -2672,14 +3417,56 @@ class AppClassicLayout extends LayoutShellBase {
2672
3417
  </div>
2673
3418
  <div class="layout-mask animate-fadein"></div>
2674
3419
  </div>
2675
- `, isInline: true, dependencies: [{ kind: "component", type: AppTopbar, selector: "app-topbar" }, { kind: "component", type: AppSidebar, selector: "app-sidebar" }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppFooter, selector: "app-footer" }] });
2676
- }
2677
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppClassicLayout, decorators: [{
2678
- type: Component,
2679
- args: [{
2680
- selector: 'app-classic-layout',
2681
- imports: [AppTopbar, AppSidebar, RouterOutlet, AppFooter],
2682
- template: `
3420
+ `,
3421
+ isInline: true,
3422
+ dependencies: [
3423
+ {
3424
+ kind: "component",
3425
+ type: AppTopbar,
3426
+ selector: "app-topbar"
3427
+ },
3428
+ {
3429
+ kind: "component",
3430
+ type: AppSidebar,
3431
+ selector: "app-sidebar"
3432
+ },
3433
+ {
3434
+ kind: "directive",
3435
+ type: RouterOutlet,
3436
+ selector: "router-outlet",
3437
+ inputs: ["name", "routerOutletData"],
3438
+ outputs: [
3439
+ "activate",
3440
+ "deactivate",
3441
+ "attach",
3442
+ "detach"
3443
+ ],
3444
+ exportAs: ["outlet"]
3445
+ },
3446
+ {
3447
+ kind: "component",
3448
+ type: AppFooter,
3449
+ selector: "app-footer"
3450
+ }
3451
+ ]
3452
+ });
3453
+ };
3454
+ i0.ɵɵngDeclareClassMetadata({
3455
+ minVersion: "12.0.0",
3456
+ version: "22.2.1",
3457
+ ngImport: i0,
3458
+ type: AppClassicLayout,
3459
+ decorators: [{
3460
+ type: Component,
3461
+ args: [{
3462
+ selector: "app-classic-layout",
3463
+ imports: [
3464
+ AppTopbar,
3465
+ AppSidebar,
3466
+ RouterOutlet,
3467
+ AppFooter
3468
+ ],
3469
+ template: `
2683
3470
  <div
2684
3471
  class="layout-wrapper"
2685
3472
  [class.layout-overlay]="isOverlay()"
@@ -2699,21 +3486,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
2699
3486
  </div>
2700
3487
  <div class="layout-mask animate-fadein"></div>
2701
3488
  </div>
2702
- `,
2703
- }]
2704
- }] });
2705
-
2706
- class AppModernSidebar {
2707
- layoutService = inject(LayoutService);
2708
- /**
2709
- * Only the in-flow column can be collapsed from here. As an overlay or mobile
2710
- * drawer the sidebar is opened by the topbar button and dismissed by the mask,
2711
- * so a second toggle inside it would be dead weight next to the brand.
2712
- */
2713
- showMenuButton = computed(() => !this.layoutService.isMobile() && !this.layoutService.isOverlay(), /* @ts-ignore */
2714
- ...(ngDevMode ? [{ debugName: "showMenuButton" }] : /* istanbul ignore next */ []));
2715
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppModernSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2716
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppModernSidebar, isStandalone: true, selector: "app-modern-sidebar", ngImport: i0, template: `
3489
+ `
3490
+ }]
3491
+ }]
3492
+ });
3493
+ var AppModernSidebar = class AppModernSidebar {
3494
+ layoutService = inject(LayoutService);
3495
+ showMenuButton = computed(() => !this.layoutService.isMobile() && !this.layoutService.isOverlay(), ...ngDevMode ? [{ debugName: "showMenuButton" }] : /* istanbul ignore next */ []);
3496
+ static ɵfac = i0.ɵɵngDeclareFactory({
3497
+ minVersion: "12.0.0",
3498
+ version: "22.2.1",
3499
+ ngImport: i0,
3500
+ type: AppModernSidebar,
3501
+ deps: [],
3502
+ target: i0.ɵɵFactoryTarget.Component
3503
+ });
3504
+ static ɵcmp = i0.ɵɵngDeclareComponent({
3505
+ minVersion: "17.0.0",
3506
+ version: "22.2.1",
3507
+ type: AppModernSidebar,
3508
+ isStandalone: true,
3509
+ selector: "app-modern-sidebar",
3510
+ ngImport: i0,
3511
+ template: `
2717
3512
  <aside class="layout-modern-sidebar">
2718
3513
  <div class="layout-modern-brand">
2719
3514
  <app-brand-logo />
@@ -2735,14 +3530,61 @@ class AppModernSidebar {
2735
3530
  <app-footer />
2736
3531
  </div>
2737
3532
  </aside>
2738
- `, isInline: true, dependencies: [{ kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: AppBrandLogo, selector: "app-brand-logo" }, { kind: "component", type: AppMenu, selector: "app-menu" }, { kind: "component", type: AppFooter, selector: "app-footer" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2739
- }
2740
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppModernSidebar, decorators: [{
2741
- type: Component,
2742
- args: [{
2743
- selector: 'app-modern-sidebar',
2744
- imports: [UiIconComponent, TranslatePipe, AppBrandLogo, AppMenu, AppFooter],
2745
- template: `
3533
+ `,
3534
+ isInline: true,
3535
+ dependencies: [
3536
+ {
3537
+ kind: "component",
3538
+ type: UiIconComponent,
3539
+ selector: "ui-icon",
3540
+ inputs: [
3541
+ "name",
3542
+ "size",
3543
+ "spin",
3544
+ "fill",
3545
+ "severity"
3546
+ ]
3547
+ },
3548
+ {
3549
+ kind: "component",
3550
+ type: AppBrandLogo,
3551
+ selector: "app-brand-logo"
3552
+ },
3553
+ {
3554
+ kind: "component",
3555
+ type: AppMenu,
3556
+ selector: "app-menu"
3557
+ },
3558
+ {
3559
+ kind: "component",
3560
+ type: AppFooter,
3561
+ selector: "app-footer"
3562
+ },
3563
+ {
3564
+ kind: "pipe",
3565
+ type: TranslatePipe,
3566
+ name: "translate"
3567
+ }
3568
+ ]
3569
+ });
3570
+ };
3571
+ i0.ɵɵngDeclareClassMetadata({
3572
+ minVersion: "12.0.0",
3573
+ version: "22.2.1",
3574
+ ngImport: i0,
3575
+ type: AppModernSidebar,
3576
+ decorators: [{
3577
+ type: Component,
3578
+ args: [{
3579
+ selector: "app-modern-sidebar",
3580
+ imports: [
3581
+ UiIconComponent,
3582
+ TranslatePipe,
3583
+ AppBrandLogo,
3584
+ AppMenu,
3585
+ AppFooter
3586
+ ],
3587
+ template: `
2746
3588
  <aside class="layout-modern-sidebar">
2747
3589
  <div class="layout-modern-brand">
2748
3590
  <app-brand-logo />
@@ -2764,37 +3606,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
2764
3606
  <app-footer />
2765
3607
  </div>
2766
3608
  </aside>
2767
- `,
2768
- }]
2769
- }] });
2770
-
2771
- class AppModernTopbar {
2772
- destroyRef = inject(DestroyRef);
2773
- router = inject(Router);
2774
- layoutService = inject(LayoutService);
2775
- _pageTitle = signal('', /* @ts-ignore */
2776
- ...(ngDevMode ? [{ debugName: "_pageTitle" }] : /* istanbul ignore next */ []));
2777
- pageTitle = this._pageTitle.asReadonly();
2778
- /** The sidebar is a drawer (not a column) in these modes, so the topbar owns the brand. */
2779
- showBrand = computed(() => this.layoutService.isMobile() ||
2780
- this.layoutService.isOverlay() ||
2781
- !!this.layoutService.layoutState().staticMenuDesktopInactive, /* @ts-ignore */
2782
- ...(ngDevMode ? [{ debugName: "showBrand" }] : /* istanbul ignore next */ []));
2783
- constructor() {
2784
- this.resolvePageTitle();
2785
- this.router.events
2786
- .pipe(filter$1((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef))
2787
- .subscribe(() => this.resolvePageTitle());
2788
- }
2789
- resolvePageTitle() {
2790
- let route = this.router.routerState.snapshot.root;
2791
- while (route.firstChild) {
2792
- route = route.firstChild;
2793
- }
2794
- this._pageTitle.set(route.title ?? '');
2795
- }
2796
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppModernTopbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2797
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppModernTopbar, isStandalone: true, selector: "app-modern-topbar", ngImport: i0, template: `
3609
+ `
3610
+ }]
3611
+ }]
3612
+ });
3613
+ var AppModernTopbar = class AppModernTopbar {
3614
+ destroyRef = inject(DestroyRef);
3615
+ router = inject(Router);
3616
+ layoutService = inject(LayoutService);
3617
+ _pageTitle = signal("", ...ngDevMode ? [{ debugName: "_pageTitle" }] : /* istanbul ignore next */ []);
3618
+ pageTitle = this._pageTitle.asReadonly();
3619
+ showBrand = computed(() => this.layoutService.isMobile() || this.layoutService.isOverlay() || !!this.layoutService.layoutState().staticMenuDesktopInactive, ...ngDevMode ? [{ debugName: "showBrand" }] : /* istanbul ignore next */ []);
3620
+ constructor() {
3621
+ this.resolvePageTitle();
3622
+ this.router.events.pipe(filter$1((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef)).subscribe(() => this.resolvePageTitle());
3623
+ }
3624
+ resolvePageTitle() {
3625
+ let route = this.router.routerState.snapshot.root;
3626
+ while (route.firstChild) route = route.firstChild;
3627
+ this._pageTitle.set(route.title ?? "");
3628
+ }
3629
+ static ɵfac = i0.ɵɵngDeclareFactory({
3630
+ minVersion: "12.0.0",
3631
+ version: "22.2.1",
3632
+ ngImport: i0,
3633
+ type: AppModernTopbar,
3634
+ deps: [],
3635
+ target: i0.ɵɵFactoryTarget.Component
3636
+ });
3637
+ static ɵcmp = i0.ɵɵngDeclareComponent({
3638
+ minVersion: "17.0.0",
3639
+ version: "22.2.1",
3640
+ type: AppModernTopbar,
3641
+ isStandalone: true,
3642
+ selector: "app-modern-topbar",
3643
+ ngImport: i0,
3644
+ template: `
2798
3645
  <header class="layout-modern-topbar">
2799
3646
  @if (showBrand()) {
2800
3647
  <button
@@ -2813,14 +3660,55 @@ class AppModernTopbar {
2813
3660
 
2814
3661
  <app-topbar-actions />
2815
3662
  </header>
2816
- `, isInline: true, dependencies: [{ kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: AppBrandLogo, selector: "app-brand-logo" }, { kind: "component", type: AppTopbarActions, selector: "app-topbar-actions" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2817
- }
2818
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppModernTopbar, decorators: [{
2819
- type: Component,
2820
- args: [{
2821
- selector: 'app-modern-topbar',
2822
- imports: [UiIconComponent, TranslatePipe, AppBrandLogo, AppTopbarActions],
2823
- template: `
3663
+ `,
3664
+ isInline: true,
3665
+ dependencies: [
3666
+ {
3667
+ kind: "component",
3668
+ type: UiIconComponent,
3669
+ selector: "ui-icon",
3670
+ inputs: [
3671
+ "name",
3672
+ "size",
3673
+ "spin",
3674
+ "fill",
3675
+ "severity"
3676
+ ]
3677
+ },
3678
+ {
3679
+ kind: "component",
3680
+ type: AppBrandLogo,
3681
+ selector: "app-brand-logo"
3682
+ },
3683
+ {
3684
+ kind: "component",
3685
+ type: AppTopbarActions,
3686
+ selector: "app-topbar-actions"
3687
+ },
3688
+ {
3689
+ kind: "pipe",
3690
+ type: TranslatePipe,
3691
+ name: "translate"
3692
+ }
3693
+ ]
3694
+ });
3695
+ };
3696
+ i0.ɵɵngDeclareClassMetadata({
3697
+ minVersion: "12.0.0",
3698
+ version: "22.2.1",
3699
+ ngImport: i0,
3700
+ type: AppModernTopbar,
3701
+ decorators: [{
3702
+ type: Component,
3703
+ args: [{
3704
+ selector: "app-modern-topbar",
3705
+ imports: [
3706
+ UiIconComponent,
3707
+ TranslatePipe,
3708
+ AppBrandLogo,
3709
+ AppTopbarActions
3710
+ ],
3711
+ template: `
2824
3712
  <header class="layout-modern-topbar">
2825
3713
  @if (showBrand()) {
2826
3714
  <button
@@ -2839,25 +3727,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
2839
3727
 
2840
3728
  <app-topbar-actions />
2841
3729
  </header>
2842
- `,
2843
- }]
2844
- }], ctorParameters: () => [] });
2845
-
2846
- class AppModernLayout extends LayoutShellBase {
2847
- content = viewChild('content', /* @ts-ignore */
2848
- ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
2849
- outsideClickSelectors() {
2850
- return ['.layout-modern-sidebar', '.layout-modern-menu-button'];
2851
- }
2852
- /**
2853
- * The page scrolls inside the content card rather than the window, so the router's
2854
- * scroll restoration never reaches it — reset it here instead.
2855
- */
2856
- onNavigated() {
2857
- this.content()?.nativeElement.scrollTo({ top: 0 });
2858
- }
2859
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppModernLayout, deps: null, target: i0.ɵɵFactoryTarget.Component });
2860
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.5", type: AppModernLayout, isStandalone: true, selector: "app-modern-layout", viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
3730
+ `
3731
+ }]
3732
+ }],
3733
+ ctorParameters: () => []
3734
+ });
3735
+ var AppModernLayout = class AppModernLayout extends LayoutShellBase {
3736
+ content = viewChild("content", ...ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []);
3737
+ outsideClickSelectors() {
3738
+ return [".layout-modern-sidebar", ".layout-modern-menu-button"];
3739
+ }
3740
+ onNavigated() {
3741
+ this.content()?.nativeElement.scrollTo({ top: 0 });
3742
+ }
3743
+ static ɵfac = i0.ɵɵngDeclareFactory({
3744
+ minVersion: "12.0.0",
3745
+ version: "22.2.1",
3746
+ ngImport: i0,
3747
+ type: AppModernLayout,
3748
+ deps: null,
3749
+ target: i0.ɵɵFactoryTarget.Component
3750
+ });
3751
+ static ɵcmp = i0.ɵɵngDeclareComponent({
3752
+ minVersion: "17.2.0",
3753
+ version: "22.2.1",
3754
+ type: AppModernLayout,
3755
+ isStandalone: true,
3756
+ selector: "app-modern-layout",
3757
+ viewQueries: [{
3758
+ propertyName: "content",
3759
+ first: true,
3760
+ predicate: ["content"],
3761
+ descendants: true,
3762
+ isSignal: true
3763
+ }],
3764
+ usesInheritance: true,
3765
+ ngImport: i0,
3766
+ template: `
2861
3767
  <div
2862
3768
  class="layout-modern"
2863
3769
  [class.layout-modern-overlay]="isOverlay()"
@@ -2874,14 +3780,50 @@ class AppModernLayout extends LayoutShellBase {
2874
3780
  </div>
2875
3781
  <div class="layout-modern-mask animate-fadein"></div>
2876
3782
  </div>
2877
- `, isInline: true, dependencies: [{ kind: "component", type: AppModernTopbar, selector: "app-modern-topbar" }, { kind: "component", type: AppModernSidebar, selector: "app-modern-sidebar" }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
2878
- }
2879
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppModernLayout, decorators: [{
2880
- type: Component,
2881
- args: [{
2882
- selector: 'app-modern-layout',
2883
- imports: [AppModernTopbar, AppModernSidebar, RouterOutlet],
2884
- template: `
3783
+ `,
3784
+ isInline: true,
3785
+ dependencies: [
3786
+ {
3787
+ kind: "component",
3788
+ type: AppModernTopbar,
3789
+ selector: "app-modern-topbar"
3790
+ },
3791
+ {
3792
+ kind: "component",
3793
+ type: AppModernSidebar,
3794
+ selector: "app-modern-sidebar"
3795
+ },
3796
+ {
3797
+ kind: "directive",
3798
+ type: RouterOutlet,
3799
+ selector: "router-outlet",
3800
+ inputs: ["name", "routerOutletData"],
3801
+ outputs: [
3802
+ "activate",
3803
+ "deactivate",
3804
+ "attach",
3805
+ "detach"
3806
+ ],
3807
+ exportAs: ["outlet"]
3808
+ }
3809
+ ]
3810
+ });
3811
+ };
3812
+ i0.ɵɵngDeclareClassMetadata({
3813
+ minVersion: "12.0.0",
3814
+ version: "22.2.1",
3815
+ ngImport: i0,
3816
+ type: AppModernLayout,
3817
+ decorators: [{
3818
+ type: Component,
3819
+ args: [{
3820
+ selector: "app-modern-layout",
3821
+ imports: [
3822
+ AppModernTopbar,
3823
+ AppModernSidebar,
3824
+ RouterOutlet
3825
+ ],
3826
+ template: `
2885
3827
  <div
2886
3828
  class="layout-modern"
2887
3829
  [class.layout-modern-overlay]="isOverlay()"
@@ -2898,54 +3840,94 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
2898
3840
  </div>
2899
3841
  <div class="layout-modern-mask animate-fadein"></div>
2900
3842
  </div>
2901
- `,
2902
- }]
2903
- }], propDecorators: { content: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }] } });
2904
-
2905
- /**
2906
- * Layout shell entry point.
2907
- * Renders the skin selected by `LayoutConfig.layoutTheme` — every skin shares
2908
- * the same services, menu and topbar components, only the markup and styling differ.
2909
- */
2910
- class AppLayout {
2911
- layoutService = inject(LayoutService);
2912
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
2913
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppLayout, isStandalone: true, selector: "app-layout", ngImport: i0, template: `
3843
+ `
3844
+ }]
3845
+ }],
3846
+ propDecorators: { content: [{
3847
+ type: i0.ViewChild,
3848
+ args: ["content", { isSignal: true }]
3849
+ }] }
3850
+ });
3851
+ var AppLayout = class AppLayout {
3852
+ layoutService = inject(LayoutService);
3853
+ static ɵfac = i0.ɵɵngDeclareFactory({
3854
+ minVersion: "12.0.0",
3855
+ version: "22.2.1",
3856
+ ngImport: i0,
3857
+ type: AppLayout,
3858
+ deps: [],
3859
+ target: i0.ɵɵFactoryTarget.Component
3860
+ });
3861
+ static ɵcmp = i0.ɵɵngDeclareComponent({
3862
+ minVersion: "17.0.0",
3863
+ version: "22.2.1",
3864
+ type: AppLayout,
3865
+ isStandalone: true,
3866
+ selector: "app-layout",
3867
+ ngImport: i0,
3868
+ template: `
2914
3869
  @if (layoutService.isModernLayout()) {
2915
3870
  <app-modern-layout />
2916
3871
  } @else {
2917
3872
  <app-classic-layout />
2918
3873
  }
2919
- `, isInline: true, dependencies: [{ kind: "component", type: AppClassicLayout, selector: "app-classic-layout" }, { kind: "component", type: AppModernLayout, selector: "app-modern-layout" }] });
2920
- }
2921
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppLayout, decorators: [{
2922
- type: Component,
2923
- args: [{
2924
- selector: 'app-layout',
2925
- imports: [AppClassicLayout, AppModernLayout],
2926
- template: `
3874
+ `,
3875
+ isInline: true,
3876
+ dependencies: [{
3877
+ kind: "component",
3878
+ type: AppClassicLayout,
3879
+ selector: "app-classic-layout"
3880
+ }, {
3881
+ kind: "component",
3882
+ type: AppModernLayout,
3883
+ selector: "app-modern-layout"
3884
+ }]
3885
+ });
3886
+ };
3887
+ i0.ɵɵngDeclareClassMetadata({
3888
+ minVersion: "12.0.0",
3889
+ version: "22.2.1",
3890
+ ngImport: i0,
3891
+ type: AppLayout,
3892
+ decorators: [{
3893
+ type: Component,
3894
+ args: [{
3895
+ selector: "app-layout",
3896
+ imports: [AppClassicLayout, AppModernLayout],
3897
+ template: `
2927
3898
  @if (layoutService.isModernLayout()) {
2928
3899
  <app-modern-layout />
2929
3900
  } @else {
2930
3901
  <app-classic-layout />
2931
3902
  }
2932
- `,
2933
- }]
2934
- }] });
2935
-
2936
- class AppFloatingConfigurator {
2937
- layoutService = inject(LayoutService);
2938
- languageSelectorPanel = inject(LAYOUT_LANGUAGE_SELECTOR_PANEL, {
2939
- optional: true,
2940
- });
2941
- isDarkTheme = computed(() => this.layoutService.layoutConfig().darkTheme, /* @ts-ignore */
2942
- ...(ngDevMode ? [{ debugName: "isDarkTheme" }] : /* istanbul ignore next */ []));
2943
- toggleDarkMode() {
2944
- const currentDarkTheme = this.layoutService.layoutConfig().darkTheme;
2945
- this.layoutService.updateLayoutConfig({ darkTheme: !currentDarkTheme });
2946
- }
2947
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppFloatingConfigurator, deps: [], target: i0.ɵɵFactoryTarget.Component });
2948
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AppFloatingConfigurator, isStandalone: true, selector: "app-floating-configurator", ngImport: i0, template: `
3903
+ `
3904
+ }]
3905
+ }]
3906
+ });
3907
+ var AppFloatingConfigurator = class AppFloatingConfigurator {
3908
+ layoutService = inject(LayoutService);
3909
+ languageSelectorPanel = inject(LAYOUT_LANGUAGE_SELECTOR_PANEL, { optional: true });
3910
+ isDarkTheme = computed(() => this.layoutService.layoutConfig().darkTheme, ...ngDevMode ? [{ debugName: "isDarkTheme" }] : /* istanbul ignore next */ []);
3911
+ toggleDarkMode() {
3912
+ const currentDarkTheme = this.layoutService.layoutConfig().darkTheme;
3913
+ this.layoutService.updateLayoutConfig({ darkTheme: !currentDarkTheme });
3914
+ }
3915
+ static ɵfac = i0.ɵɵngDeclareFactory({
3916
+ minVersion: "12.0.0",
3917
+ version: "22.2.1",
3918
+ ngImport: i0,
3919
+ type: AppFloatingConfigurator,
3920
+ deps: [],
3921
+ target: i0.ɵɵFactoryTarget.Component
3922
+ });
3923
+ static ɵcmp = i0.ɵɵngDeclareComponent({
3924
+ minVersion: "17.0.0",
3925
+ version: "22.2.1",
3926
+ type: AppFloatingConfigurator,
3927
+ isStandalone: true,
3928
+ selector: "app-floating-configurator",
3929
+ ngImport: i0,
3930
+ template: `
2949
3931
  <div
2950
3932
  class="floating-configurator fixed flex flex-col md:flex-row gap-2 md:gap-4 top-4 md:top-8 z-50"
2951
3933
  >
@@ -2993,11 +3975,102 @@ class AppFloatingConfigurator {
2993
3975
  <app-configurator [showMenuModeButton]="false" />
2994
3976
  </div>
2995
3977
  </div>
2996
- `, isInline: true, styles: [".floating-configurator{inset-inline-end:.5rem}@media(min-width:768px){.floating-configurator{inset-inline-end:2rem}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "directive", type: StyleClassDirective, selector: "[fStyleClass]", inputs: ["fStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator", inputs: ["showMenuModeButton"] }] });
2997
- }
2998
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AppFloatingConfigurator, decorators: [{
2999
- type: Component,
3000
- args: [{ selector: 'app-floating-configurator', imports: [NgComponentOutlet, FButtonDirective, StyleClassDirective, UiIconComponent, AppConfigurator], template: `
3978
+ `,
3979
+ isInline: true,
3980
+ styles: [".floating-configurator{inset-inline-end:.5rem}@media(min-width:768px){.floating-configurator{inset-inline-end:2rem}}\n"],
3981
+ dependencies: [
3982
+ {
3983
+ kind: "directive",
3984
+ type: NgComponentOutlet,
3985
+ selector: "[ngComponentOutlet]",
3986
+ inputs: [
3987
+ "ngComponentOutlet",
3988
+ "ngComponentOutletInputs",
3989
+ "ngComponentOutletInjector",
3990
+ "ngComponentOutletEnvironmentInjector",
3991
+ "ngComponentOutletContent",
3992
+ "ngComponentOutletNgModule"
3993
+ ],
3994
+ exportAs: ["ngComponentOutlet"]
3995
+ },
3996
+ {
3997
+ kind: "directive",
3998
+ type: FButtonDirective,
3999
+ selector: "[fButton]",
4000
+ inputs: [
4001
+ "severity",
4002
+ "text",
4003
+ "outlined",
4004
+ "link",
4005
+ "variant",
4006
+ "rounded",
4007
+ "raised",
4008
+ "fluid",
4009
+ "disabled",
4010
+ "loading",
4011
+ "loadingIcon",
4012
+ "size",
4013
+ "label",
4014
+ "icon",
4015
+ "iconPos",
4016
+ "ariaLabel",
4017
+ "plain"
4018
+ ]
4019
+ },
4020
+ {
4021
+ kind: "directive",
4022
+ type: StyleClassDirective,
4023
+ selector: "[fStyleClass]",
4024
+ inputs: [
4025
+ "fStyleClass",
4026
+ "enterFromClass",
4027
+ "enterActiveClass",
4028
+ "enterToClass",
4029
+ "leaveFromClass",
4030
+ "leaveActiveClass",
4031
+ "leaveToClass",
4032
+ "hideOnOutsideClick",
4033
+ "toggleClass"
4034
+ ]
4035
+ },
4036
+ {
4037
+ kind: "component",
4038
+ type: UiIconComponent,
4039
+ selector: "ui-icon",
4040
+ inputs: [
4041
+ "name",
4042
+ "size",
4043
+ "spin",
4044
+ "fill",
4045
+ "severity"
4046
+ ]
4047
+ },
4048
+ {
4049
+ kind: "component",
4050
+ type: AppConfigurator,
4051
+ selector: "app-configurator",
4052
+ inputs: ["showMenuModeButton"]
4053
+ }
4054
+ ]
4055
+ });
4056
+ };
4057
+ i0.ɵɵngDeclareClassMetadata({
4058
+ minVersion: "12.0.0",
4059
+ version: "22.2.1",
4060
+ ngImport: i0,
4061
+ type: AppFloatingConfigurator,
4062
+ decorators: [{
4063
+ type: Component,
4064
+ args: [{
4065
+ selector: "app-floating-configurator",
4066
+ imports: [
4067
+ NgComponentOutlet,
4068
+ FButtonDirective,
4069
+ StyleClassDirective,
4070
+ UiIconComponent,
4071
+ AppConfigurator
4072
+ ],
4073
+ template: `
3001
4074
  <div
3002
4075
  class="floating-configurator fixed flex flex-col md:flex-row gap-2 md:gap-4 top-4 md:top-8 z-50"
3003
4076
  >
@@ -3045,119 +4118,98 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
3045
4118
  <app-configurator [showMenuModeButton]="false" />
3046
4119
  </div>
3047
4120
  </div>
3048
- `, styles: [".floating-configurator{inset-inline-end:.5rem}@media(min-width:768px){.floating-configurator{inset-inline-end:2rem}}\n"] }]
3049
- }] });
3050
-
3051
- // Shell entry point (renders the configured layout skin)
3052
-
4121
+ `,
4122
+ styles: [".floating-configurator{inset-inline-end:.5rem}@media(min-width:768px){.floating-configurator{inset-inline-end:2rem}}\n"]
4123
+ }]
4124
+ }]
4125
+ });
3053
4126
  function buildPreset(presetName, primary) {
3054
- return { preset: presetName, primary };
4127
+ return {
4128
+ preset: presetName,
4129
+ primary
4130
+ };
3055
4131
  }
3056
-
3057
4132
  const NavyBlueTheme = {
3058
- preset: 'Aura',
3059
- primary: 'indigo',
4133
+ preset: "Aura",
4134
+ primary: "indigo"
3060
4135
  };
3061
-
3062
4136
  const GreenTheme = {
3063
- preset: 'Aura',
3064
- primary: 'green',
4137
+ preset: "Aura",
4138
+ primary: "green"
3065
4139
  };
3066
-
3067
4140
  const LAYOUT_MESSAGES = {
3068
- // components/shared/top-bar/app.profile.ts
3069
- 'layout.profile.title': 'Profile',
3070
- 'layout.profile.profile.picture.alt': 'Profile picture',
3071
- 'layout.profile.copy.sign.up.link': 'Copy SignUp Link',
3072
- 'layout.profile.logout': 'Logout',
3073
- 'layout.profile.guest': 'Guest',
3074
- 'layout.profile.link.copied': 'Link Copied',
3075
- 'layout.profile.sign.up.link.copied': 'Sign Up Link copied.',
3076
- 'layout.profile.failed.copy.sign.up.link': 'Failed to copy signup link.',
3077
- // components/shared/top-bar/app.launcher.ts
3078
- 'layout.launcher.apps': 'Apps',
3079
- 'layout.launcher.applications': 'Applications',
3080
- 'layout.launcher.no.apps': 'No apps available',
3081
- // components/shared/top-bar/app.company-branch-selector.ts
3082
- 'layout.company.branch.selector.title': 'Switch Company & Branch',
3083
- 'layout.company.branch.selector.select.company': 'Select Company',
3084
- 'layout.company.branch.selector.branch': 'Branch',
3085
- 'layout.company.branch.selector.select.branch': 'Select Branch',
3086
- 'layout.company.branch.selector.switch.button': 'Switch',
3087
- 'layout.company.branch.selector.no.company': 'No Company',
3088
- 'layout.company.branch.selector.branch.required': 'Branch Required',
3089
- 'layout.company.branch.selector.please.select.branch': 'Please select a branch',
3090
- // components/shared/top-bar/app.searchbar.ts
3091
- 'layout.topbar.search': 'Search',
3092
- 'layout.topbar.search.placeholder': 'Search for content...',
3093
- // components/layouts/classic/app.topbar.ts + components/layouts/modern/app.modern-topbar.ts
3094
- // + components/layouts/modern/app.modern-sidebar.ts
3095
- 'layout.topbar.toggle.menu': 'Toggle menu',
3096
- // components/shared/top-bar/app.topbar-actions.ts
3097
- 'layout.topbar.toggle.dark.mode': 'Toggle dark mode',
3098
- 'layout.topbar.open.theme.settings': 'Open theme settings',
3099
- 'layout.topbar.more.options': 'More options',
3100
- // components/shared/app.configurator.ts
3101
- 'layout.configurator.primary': 'Primary',
3102
- 'layout.configurator.surface': 'Surface',
3103
- 'layout.configurator.presets': 'Presets',
3104
- 'layout.configurator.menu.mode': 'Menu Mode',
3105
- 'layout.configurator.menu.mode.static': 'Static',
3106
- 'layout.configurator.menu.mode.overlay': 'Overlay',
3107
- 'layout.configurator.menu.mode.topbar': 'Topbar',
3108
- 'layout.configurator.layout.theme': 'Layout',
3109
- 'layout.configurator.layout.theme.classic': 'Classic',
3110
- 'layout.configurator.layout.theme.modern': 'Modern',
3111
- // components/shared/app.footer.ts
3112
- 'layout.footer.by': 'by',
3113
- // app-menu.config.ts (main app menu configuration)
3114
- 'menu.dashboard': 'Dashboard',
3115
- 'menu.administration': 'Administration',
3116
- 'menu.iam': 'IAM',
3117
- 'menu.storage': 'Storage',
3118
- 'menu.forms': 'Forms',
3119
- 'menu.email': 'Email',
3120
- 'menu.entity.builder': 'Entity Builder',
3121
- 'menu.event.manager': 'Event Manager',
3122
- 'menu.task.manager': 'Task Manager',
3123
- 'menu.ai.assistant': 'AI Assistant',
3124
- 'menu.notifications': 'Notifications',
3125
- 'menu.localization': 'Localization',
3126
- 'menu.components': 'Components',
3127
- 'menu.components.display': 'Display',
3128
- 'menu.components.menu': 'Menu',
3129
- 'menu.components.form': 'Form',
3130
- 'menu.components.overlay': 'Overlay',
3131
- 'menu.components.data': 'Data',
3132
- 'menu.components.panel': 'Panel',
3133
- 'menu.components.media': 'Media',
3134
- 'menu.components.misc': 'Directives',
3135
- // Those are layout part from notification module, but we put them here to avoid circular dependency between layout and notification module
3136
- // components/notification-bell/notification-bell.component.ts
3137
- 'notification.title': 'Notifications',
3138
- // components/notification-list/notification-list.component.ts
3139
- 'notification.mark.all.read': 'Mark all read',
3140
- 'notification.view.all': 'View all notifications',
3141
- 'notification.empty': 'No notifications',
3142
- // components/notification-item/notification-item.component.ts
3143
- 'notification.time.just.now': 'Just now',
3144
- 'notification.time.minutes.ago': '{{count}}m ago',
3145
- 'notification.time.hours.ago': '{{count}}h ago',
3146
- 'notification.time.days.ago': '{{count}}d ago',
3147
- // ng-ai-assistant/components/chat-widget-host/chat-widget-host.component.ts
3148
- 'ai-assistant.widget.title': 'Assistant',
3149
- 'ai-assistant.widget.placeholder': 'Ask me anything…',
3150
- 'ai-assistant.widget.empty': 'Ask me anything to get started',
3151
- 'ai-assistant.widget.pin': 'Pin',
3152
- 'ai-assistant.widget.pinned': 'Pinned',
3153
- 'ai-assistant.widget.error': 'Error: {{error}}',
4141
+ "layout.profile.title": "Profile",
4142
+ "layout.profile.profile.picture.alt": "Profile picture",
4143
+ "layout.profile.copy.sign.up.link": "Copy SignUp Link",
4144
+ "layout.profile.logout": "Logout",
4145
+ "layout.profile.guest": "Guest",
4146
+ "layout.profile.link.copied": "Link Copied",
4147
+ "layout.profile.sign.up.link.copied": "Sign Up Link copied.",
4148
+ "layout.profile.failed.copy.sign.up.link": "Failed to copy signup link.",
4149
+ "layout.launcher.apps": "Apps",
4150
+ "layout.launcher.applications": "Applications",
4151
+ "layout.launcher.no.apps": "No apps available",
4152
+ "layout.company.branch.selector.title": "Switch Company & Branch",
4153
+ "layout.company.branch.selector.select.company": "Select Company",
4154
+ "layout.company.branch.selector.branch": "Branch",
4155
+ "layout.company.branch.selector.select.branch": "Select Branch",
4156
+ "layout.company.branch.selector.switch.button": "Switch",
4157
+ "layout.company.branch.selector.no.company": "No Company",
4158
+ "layout.company.branch.selector.branch.required": "Branch Required",
4159
+ "layout.company.branch.selector.please.select.branch": "Please select a branch",
4160
+ "layout.topbar.search": "Search",
4161
+ "layout.topbar.search.placeholder": "Search for content...",
4162
+ "layout.topbar.toggle.menu": "Toggle menu",
4163
+ "layout.topbar.toggle.dark.mode": "Toggle dark mode",
4164
+ "layout.topbar.open.theme.settings": "Open theme settings",
4165
+ "layout.topbar.more.options": "More options",
4166
+ "layout.configurator.primary": "Primary",
4167
+ "layout.configurator.surface": "Surface",
4168
+ "layout.configurator.presets": "Presets",
4169
+ "layout.configurator.menu.mode": "Menu Mode",
4170
+ "layout.configurator.menu.mode.static": "Static",
4171
+ "layout.configurator.menu.mode.overlay": "Overlay",
4172
+ "layout.configurator.menu.mode.topbar": "Topbar",
4173
+ "layout.configurator.layout.theme": "Layout",
4174
+ "layout.configurator.layout.theme.classic": "Classic",
4175
+ "layout.configurator.layout.theme.modern": "Modern",
4176
+ "layout.footer.by": "by",
4177
+ "menu.dashboard": "Dashboard",
4178
+ "menu.administration": "Administration",
4179
+ "menu.iam": "IAM",
4180
+ "menu.storage": "Storage",
4181
+ "menu.forms": "Forms",
4182
+ "menu.email": "Email",
4183
+ "menu.entity.builder": "Entity Builder",
4184
+ "menu.event.manager": "Event Manager",
4185
+ "menu.task.manager": "Task Manager",
4186
+ "menu.ai.assistant": "AI Assistant",
4187
+ "menu.notifications": "Notifications",
4188
+ "menu.localization": "Localization",
4189
+ "menu.components": "Components",
4190
+ "menu.components.display": "Display",
4191
+ "menu.components.menu": "Menu",
4192
+ "menu.components.form": "Form",
4193
+ "menu.components.overlay": "Overlay",
4194
+ "menu.components.data": "Data",
4195
+ "menu.components.panel": "Panel",
4196
+ "menu.components.media": "Media",
4197
+ "menu.components.misc": "Directives",
4198
+ "notification.title": "Notifications",
4199
+ "notification.mark.all.read": "Mark all read",
4200
+ "notification.view.all": "View all notifications",
4201
+ "notification.empty": "No notifications",
4202
+ "notification.time.just.now": "Just now",
4203
+ "notification.time.minutes.ago": "{{count}}m ago",
4204
+ "notification.time.hours.ago": "{{count}}h ago",
4205
+ "notification.time.days.ago": "{{count}}d ago",
4206
+ "ai-assistant.widget.title": "Assistant",
4207
+ "ai-assistant.widget.placeholder": "Ask me anything…",
4208
+ "ai-assistant.widget.empty": "Ask me anything to get started",
4209
+ "ai-assistant.widget.pin": "Pin",
4210
+ "ai-assistant.widget.pinned": "Pinned",
4211
+ "ai-assistant.widget.error": "Error: {{error}}"
3154
4212
  };
4213
+ export { AppBrandLogo, AppClassicLayout, AppCompanyBranchSelector, AppConfigurator, AppFloatingConfigurator, AppFooter, AppLauncher, AppLayout, AppMenu, AppMenuitem, AppModernLayout, AppModernSidebar, AppModernTopbar, AppProfile, AppSearchbar, AppSidebar, AppTopbar, AppTopbarActions, 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, LayoutShellBase, NavyBlueTheme, applyTheme, buildBootstrapPreset, buildPreset, filterAppsByPermissions, filterMenuByPermissions };
3155
4214
 
3156
- // Components
3157
-
3158
- /**
3159
- * Generated bundle index. Do not edit.
3160
- */
3161
-
3162
- export { AppBrandLogo, AppClassicLayout, AppCompanyBranchSelector, AppConfigurator, AppFloatingConfigurator, AppFooter, AppLauncher, AppLayout, AppMenu, AppMenuitem, AppModernLayout, AppModernSidebar, AppModernTopbar, AppProfile, AppSearchbar, AppSidebar, AppTopbar, AppTopbarActions, 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, LayoutShellBase, NavyBlueTheme, buildPreset, filterAppsByPermissions, filterMenuByPermissions };
3163
- //# sourceMappingURL=flusys-ng-layout.mjs.map
4215
+ //# sourceMappingURL=flusys-ng-layout.mjs.map