@unopsitg/ux 21.0.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.
Files changed (77) hide show
  1. package/README.md +56 -0
  2. package/assets/_animations.scss +47 -0
  3. package/assets/_breadcrumb.scss +71 -0
  4. package/assets/_card.scss +11 -0
  5. package/assets/_config.scss +95 -0
  6. package/assets/_content.scss +55 -0
  7. package/assets/_fonts.scss +1 -0
  8. package/assets/_footer.scss +17 -0
  9. package/assets/_main.scss +38 -0
  10. package/assets/_mask.scss +7 -0
  11. package/assets/_paginator.scss +16 -0
  12. package/assets/_responsive.scss +203 -0
  13. package/assets/_sass_variables.scss +2 -0
  14. package/assets/_search.scss +35 -0
  15. package/assets/_tags.scss +90 -0
  16. package/assets/_topbar.scss +250 -0
  17. package/assets/_utils.scss +19 -0
  18. package/assets/fonts/InterDisplay-Black.otf +0 -0
  19. package/assets/fonts/InterDisplay-BlackItalic.otf +0 -0
  20. package/assets/fonts/InterDisplay-Bold.otf +0 -0
  21. package/assets/fonts/InterDisplay-BoldItalic.otf +0 -0
  22. package/assets/fonts/InterDisplay-ExtraBold.otf +0 -0
  23. package/assets/fonts/InterDisplay-ExtraBoldItalic.otf +0 -0
  24. package/assets/fonts/InterDisplay-ExtraLight.otf +0 -0
  25. package/assets/fonts/InterDisplay-ExtraLightItalic.otf +0 -0
  26. package/assets/fonts/InterDisplay-Italic.otf +0 -0
  27. package/assets/fonts/InterDisplay-Light.otf +0 -0
  28. package/assets/fonts/InterDisplay-LightItalic.otf +0 -0
  29. package/assets/fonts/InterDisplay-Medium.otf +0 -0
  30. package/assets/fonts/InterDisplay-MediumItalic.otf +0 -0
  31. package/assets/fonts/InterDisplay-Regular.otf +0 -0
  32. package/assets/fonts/InterDisplay-SemiBold.otf +0 -0
  33. package/assets/fonts/InterDisplay-SemiBoldItalic.otf +0 -0
  34. package/assets/fonts/InterDisplay-Thin.otf +0 -0
  35. package/assets/fonts/InterDisplay-ThinItalic.otf +0 -0
  36. package/assets/layout.scss +20 -0
  37. package/assets/opp/AppLogo/AppLogo-onDark_H.svg +55 -0
  38. package/assets/opp/AppLogo/AppLogo-onDark_V.svg +55 -0
  39. package/assets/opp/AppLogo/AppLogo-onDark_compact.svg +48 -0
  40. package/assets/opp/AppLogo/AppLogo-onLight_H.svg +55 -0
  41. package/assets/opp/AppLogo/AppLogo-onLight_V.svg +55 -0
  42. package/assets/opp/AppLogo-dark-vertical.svg +55 -0
  43. package/assets/opp/drive-download-20260421T141232Z-3-001.zip +0 -0
  44. package/assets/opp/favicon/apple-touch-icon.png +0 -0
  45. package/assets/opp/favicon/favicon-96x96.png +0 -0
  46. package/assets/opp/favicon/favicon.ico +0 -0
  47. package/assets/opp/favicon/favicon.svg +17 -0
  48. package/assets/opp/favicon/web-app-manifest-192x192.png +0 -0
  49. package/assets/opp/favicon/web-app-manifest-512x512.png +0 -0
  50. package/assets/opp/logo-dark-horizontal.svg +55 -0
  51. package/assets/opp/logo-light-horizontal.svg +55 -0
  52. package/assets/opp/unops-logo/UNOPS logo_vertical_RGB.png +0 -0
  53. package/assets/opp/unops-logo/UNOPS logo_vertical_RGB_black.png +0 -0
  54. package/assets/opp/unops-logo/UNOPS logo_vertical_RGB_white.png +0 -0
  55. package/assets/opp/unops-logo/unops-logo_ondark.svg +19 -0
  56. package/assets/opp/unops-logo/unops-logo_onlight.svg +10 -0
  57. package/assets/sidebar/_sidebar.scss +7 -0
  58. package/assets/sidebar/_sidebar_compact.scss +176 -0
  59. package/assets/sidebar/_sidebar_drawer.scss +200 -0
  60. package/assets/sidebar/_sidebar_horizontal.scss +220 -0
  61. package/assets/sidebar/_sidebar_reveal.scss +176 -0
  62. package/assets/sidebar/_sidebar_slim.scss +117 -0
  63. package/assets/sidebar/_sidebar_theme_core.scss +147 -0
  64. package/assets/sidebar/_sidebar_themes.scss +4 -0
  65. package/assets/sidebar/_sidebar_vertical.scss +392 -0
  66. package/assets/sidebar/themes/_dark.scss +31 -0
  67. package/assets/sidebar/themes/_light.scss +16 -0
  68. package/assets/sidebar/themes/_primary.scss +41 -0
  69. package/assets/styles.scss +1 -0
  70. package/assets/tailwind.css +679 -0
  71. package/assets/variables/_common.scss +21 -0
  72. package/assets/variables/_dark.scss +13 -0
  73. package/assets/variables/_light.scss +13 -0
  74. package/fesm2022/unopsitg-ux.mjs +2596 -0
  75. package/fesm2022/unopsitg-ux.mjs.map +1 -0
  76. package/package.json +46 -0
  77. package/types/unopsitg-ux.d.ts +878 -0
@@ -0,0 +1,2596 @@
1
+ import { definePreset, updatePreset, updateSurfacePalette, $t } from '@primeuix/themes';
2
+ import SoftBase from '@primeuix/themes/aura';
3
+ import CrispBase from '@primeuix/themes/lara';
4
+ import ContrastBase from '@primeuix/themes/nora';
5
+ import * as i0 from '@angular/core';
6
+ import { InjectionToken, signal, inject, computed, effect, Injectable, Component, PLATFORM_ID, model, booleanAttribute, Input, input, HostListener, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core';
7
+ import * as i1 from '@angular/router';
8
+ import { Router, NavigationEnd, RouterModule } from '@angular/router';
9
+ import * as i1$1 from '@angular/common';
10
+ import { CommonModule, isPlatformBrowser } from '@angular/common';
11
+ import { BehaviorSubject, filter, Subject, takeUntil } from 'rxjs';
12
+ import * as i5 from '@angular/forms';
13
+ import { FormsModule } from '@angular/forms';
14
+ import { PrimeNG } from 'primeng/config';
15
+ import * as i4 from 'primeng/drawer';
16
+ import { DrawerModule } from 'primeng/drawer';
17
+ import * as i5$1 from 'primeng/radiobutton';
18
+ import { RadioButtonModule } from 'primeng/radiobutton';
19
+ import * as i3 from 'primeng/selectbutton';
20
+ import { SelectButtonModule } from 'primeng/selectbutton';
21
+ import { ToggleSwitchModule } from 'primeng/toggleswitch';
22
+ import * as i6 from 'primeng/button';
23
+ import { ButtonModule } from 'primeng/button';
24
+ import * as i2 from 'primeng/divider';
25
+ import { DividerModule } from 'primeng/divider';
26
+ import * as i4$1 from 'primeng/inputtext';
27
+ import { InputTextModule } from 'primeng/inputtext';
28
+ import * as i3$1 from 'primeng/select';
29
+ import { SelectModule } from 'primeng/select';
30
+ import * as i3$2 from 'primeng/autofocus';
31
+ import { AutoFocusModule } from 'primeng/autofocus';
32
+ import * as i1$2 from 'primeng/dialog';
33
+ import { DialogModule } from 'primeng/dialog';
34
+ import * as i4$2 from 'primeng/ripple';
35
+ import { RippleModule } from 'primeng/ripple';
36
+ import * as i3$3 from 'primeng/tooltip';
37
+ import { TooltipModule } from 'primeng/tooltip';
38
+ import * as i10 from 'primeng/avatar';
39
+ import { AvatarModule } from 'primeng/avatar';
40
+ import * as i8 from 'primeng/badge';
41
+ import { BadgeModule } from 'primeng/badge';
42
+ import * as i5$2 from 'primeng/iconfield';
43
+ import { IconFieldModule } from 'primeng/iconfield';
44
+ import * as i6$1 from 'primeng/inputicon';
45
+ import { InputIconModule } from 'primeng/inputicon';
46
+ import * as i9 from 'primeng/overlaybadge';
47
+ import { OverlayBadgeModule } from 'primeng/overlaybadge';
48
+ import * as i3$4 from 'primeng/styleclass';
49
+ import { StyleClassModule } from 'primeng/styleclass';
50
+
51
+ /**
52
+ * Brand theme presets built on top of PrimeUIX base presets.
53
+ *
54
+ * Upstream mapping (for PrimeNG docs, changelogs, and debugging):
55
+ * Soft <- @primeuix/themes/aura
56
+ * Crisp <- @primeuix/themes/lara
57
+ * Contrast <- @primeuix/themes/nora
58
+ */
59
+ const brandPrimitives = {
60
+ deepsea: { 50: '#F1F5F9', 100: '#E2E8F0', 200: '#CBD5E1', 300: '#94A3B8', 400: '#64748B', 500: '#475569', 600: '#334155', 700: '#1E293B', 800: '#0F172A', 900: '#0A101D', 950: '#03080c' },
61
+ gray: { 50: '#f0f3f4', 100: '#ECF0F5', 200: '#d8dadf', 300: '#b9bac1', 400: '#858c99', 500: '#808393', 600: '#808284', 700: '#6a6b6d', 800: '#535455', 900: '#3c3d3e', 950: '#262627' },
62
+ red: { 50: '#f6cac6', 100: '#f0a9a4', 200: '#eb8982', 300: '#e56960', 400: '#e0493e', 500: '#da291c', 600: '#b92318', 700: '#991d14', 800: '#78170f', 900: '#57100b', 950: '#370a07' },
63
+ orange: { 50: '#f9d6c3', 100: '#f6be9f', 200: '#f2a57a', 300: '#ef8d56', 400: '#eb7432', 500: '#e85c0e', 600: '#c54e0c', 700: '#a2400a', 800: '#803308', 900: '#5d2506', 950: '#3a1704' },
64
+ yellow: { 50: '#fff0c5', 100: '#ffe7a1', 200: '#ffdd7e', 300: '#ffd45b', 400: '#ffcb38', 500: '#ffc215', 600: '#d9a512', 700: '#b3880f', 800: '#8c6b0c', 900: '#664e08', 950: '#403105' },
65
+ lemon: { 50: '#fdfad0', 100: '#fcf7b4', 200: '#fbf398', 300: '#faf07c', 400: '#f9ed60', 500: '#f8ea44', 600: '#d3c73a', 700: '#aea430', 800: '#888125', 900: '#635e1b', 950: '#3e3b11' },
66
+ lime: { 50: '#f0f5bf', 100: '#e7ef99', 200: '#dfe873', 300: '#d6e24d', 400: '#cddc26', 500: '#c4d600', 600: '#a7b600', 700: '#899600', 800: '#6c7600', 900: '#4e5600', 950: '#313600' },
67
+ babygreen: { 50: '#e5f2cf', 100: '#d5e9b1', 200: '#c5e194', 300: '#b6d977', 400: '#a6d15a', 500: '#96c93d', 600: '#80ab34', 700: '#698d2b', 800: '#536f22', 900: '#3c5018', 950: '#26320f' },
68
+ green: { 50: '#d2e7cd', 100: '#b7d9af', 200: '#9dca92', 300: '#82bc74', 400: '#67ad56', 500: '#4c9f38', 600: '#418730', 700: '#356f27', 800: '#2a571f', 900: '#1e4016', 950: '#13280e' },
69
+ olive: { 50: '#d1e0d5', 100: '#b5cdbc', 200: '#99baa3', 300: '#7da889', 400: '#619570', 500: '#458257', 600: '#3b6f4a', 700: '#305b3d', 800: '#264830', 900: '#1c3423', 950: '#112116' },
70
+ teal: { 50: '#bfeae5', 100: '#99ddd5', 200: '#73d0c6', 300: '#4dc3b6', 400: '#26b6a7', 500: '#00a997', 600: '#009080', 700: '#00766a', 800: '#005d53', 900: '#00443c', 950: '#002a26' },
71
+ ocean: { 50: '#d3f0f7', 100: '#b8e7f3', 200: '#9edeee', 300: '#83d5e9', 400: '#69cce5', 500: '#4ec3e0', 600: '#42a6be', 700: '#37899d', 800: '#2b6b7b', 900: '#1f4e5a', 950: '#143138' },
72
+ blue: { 50: '#DEEFFF', 100: '#C9E8FF', 150: '#99D3ED', 200: '#73c3e6', 300: '#4db3df', 400: '#26a2d8', 500: '#0092d1', 600: '#007cb2', 700: '#006692', 800: '#005073', 900: '#003a54', 950: '#002534' },
73
+ darkblue: { 50: '#D0EEFF', 100: '#B7E2F9', 200: '#73abc7', 300: '#4d94b8', 400: '#267da9', 500: '#00669a', 600: '#005783', 700: '#00476c', 800: '#003855', 900: '#00293e', 950: '#001a27' },
74
+ midnight: { 50: '#bfd2dd', 100: '#99b6c8', 200: '#739bb4', 300: '#4d809f', 400: '#26648b', 500: '#004976', 600: '#003e64', 700: '#003353', 800: '#002841', 850: '#001E31', 900: '#001d2f', 950: '#00121e' },
75
+ cherry: { 50: '#e6c7d9', 100: '#d6a5c2', 200: '#c783ab', 300: '#b86294', 400: '#a8407d', 500: '#991e66', 600: '#821a57', 700: '#6b1547', 800: '#541138', 900: '#3d0c29', 950: '#26081a' }
76
+ };
77
+ const brandOverrides = {
78
+ primitive: {
79
+ ...brandPrimitives,
80
+ slate: brandPrimitives.gray,
81
+ zinc: brandPrimitives.deepsea,
82
+ neutral: brandPrimitives.gray,
83
+ stone: brandPrimitives.gray,
84
+ emerald: brandPrimitives.green,
85
+ amber: brandPrimitives.yellow,
86
+ cyan: brandPrimitives.ocean,
87
+ sky: brandPrimitives.blue,
88
+ indigo: brandPrimitives.darkblue,
89
+ violet: brandPrimitives.midnight,
90
+ purple: brandPrimitives.cherry,
91
+ fuchsia: brandPrimitives.cherry,
92
+ pink: brandPrimitives.cherry,
93
+ rose: brandPrimitives.red
94
+ },
95
+ semantic: {
96
+ fontFamily: '"Noto Sans", sans-serif',
97
+ primary: {
98
+ 50: '{darkblue.50}',
99
+ 100: '{darkblue.100}',
100
+ 200: '{darkblue.200}',
101
+ 300: '{darkblue.300}',
102
+ 400: '{darkblue.400}',
103
+ 500: '{darkblue.500}',
104
+ 600: '{darkblue.600}',
105
+ 700: '{darkblue.700}',
106
+ 800: '{darkblue.800}',
107
+ 900: '{darkblue.900}',
108
+ 950: '{darkblue.950}'
109
+ },
110
+ colorScheme: {
111
+ light: {
112
+ surface: {
113
+ 0: '#ffffff',
114
+ 50: '{deepsea.50}',
115
+ 100: '{deepsea.100}',
116
+ 200: '{deepsea.200}',
117
+ 300: '{deepsea.300}',
118
+ 400: '{deepsea.400}',
119
+ 500: '{deepsea.500}',
120
+ 600: '{deepsea.600}',
121
+ 700: '{deepsea.700}',
122
+ 800: '{deepsea.800}',
123
+ 900: '{deepsea.900}',
124
+ 950: '{deepsea.950}'
125
+ }
126
+ },
127
+ dark: {
128
+ surface: {
129
+ 0: '#ffffff',
130
+ 50: '{darkblue.50}',
131
+ 100: '{darkblue.100}',
132
+ 200: '{darkblue.200}',
133
+ 300: '{darkblue.200}',
134
+ 400: '{darkblue.300}',
135
+ 500: '{darkblue.400}',
136
+ 600: '{darkblue.500}',
137
+ 700: '{darkblue.600}',
138
+ 800: '{darkblue.700}',
139
+ 900: '{darkblue.900}',
140
+ 950: '{darkblue.950}'
141
+ }
142
+ }
143
+ }
144
+ },
145
+ components: {
146
+ button: {
147
+ colorScheme: {
148
+ light: {
149
+ root: {
150
+ primary: {
151
+ background: '{primary.600}',
152
+ borderColor: '{primary.600}'
153
+ }
154
+ }
155
+ }
156
+ }
157
+ },
158
+ datatable: {
159
+ colorScheme: {
160
+ light: {
161
+ row: {
162
+ hoverBackground: '{surface.100}',
163
+ hoverColor: '{surface.950}'
164
+ }
165
+ },
166
+ dark: {
167
+ row: {
168
+ hoverBackground: '{surface.900}',
169
+ hoverColor: '{surface.0}'
170
+ }
171
+ }
172
+ }
173
+ },
174
+ paginator: {
175
+ root: {
176
+ background: 'transparent'
177
+ }
178
+ },
179
+ tag: {
180
+ root: {
181
+ padding: '0.25rem 0.5rem',
182
+ fontWeight: '500'
183
+ },
184
+ colorScheme: {
185
+ light: {
186
+ secondary: { color: '{surface.800}' },
187
+ success: { color: '{green.800}' },
188
+ active: { color: '{green.800}' },
189
+ inactive: { color: '{gray.800}' },
190
+ info: { color: '{blue.800}' },
191
+ warn: { color: '{orange.800}' },
192
+ error: { color: '{red.800}' }
193
+ },
194
+ dark: {
195
+ secondary: { color: '{surface.100}' },
196
+ success: { color: '{green.100}' },
197
+ active: { color: '{green.100}' },
198
+ info: { color: '{blue.100}' },
199
+ inactive: { color: '{gray.100}' },
200
+ warn: { color: '{orange.100}' },
201
+ error: { color: '{red.100}' }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ };
207
+ const BrandSoft = definePreset(SoftBase, brandOverrides);
208
+ const BrandCrisp = definePreset(CrispBase, brandOverrides);
209
+ const BrandContrast = definePreset(ContrastBase, brandOverrides);
210
+ const brandPresets = {
211
+ Soft: BrandSoft,
212
+ Crisp: BrandCrisp,
213
+ Contrast: BrandContrast
214
+ };
215
+
216
+ const MENU_MODEL = new InjectionToken('UNOPS_UX_MENU_MODEL', {
217
+ factory: () => {
218
+ throw new Error('MENU_MODEL is not provided. Add { provide: MENU_MODEL, useFactory: ... } or useValue to app.config.ts providers.');
219
+ }
220
+ });
221
+ const SIDEBAR_LOGO = new InjectionToken('UNOPS_UX_SIDEBAR_LOGO', {
222
+ factory: () => ({
223
+ expanded: 'assets/opp/AppLogo/AppLogo-onDark_H.svg',
224
+ compact: 'assets/opp/AppLogo/AppLogo-onDark_compact.svg',
225
+ alt: 'UNOPS'
226
+ })
227
+ });
228
+ const TOPBAR_MOBILE_LOGO = new InjectionToken('UNOPS_UX_TOPBAR_MOBILE_LOGO', {
229
+ factory: () => ({
230
+ dark: 'assets/opp/AppLogo/AppLogo-onDark_H.svg',
231
+ light: 'assets/opp/AppLogo/AppLogo-onLight_H.svg',
232
+ alt: 'UNOPS'
233
+ })
234
+ });
235
+
236
+ class LayoutService {
237
+ layoutConfig = signal({
238
+ preset: 'Soft',
239
+ primary: 'blue',
240
+ surface: null,
241
+ darkTheme: true,
242
+ menuMode: 'static',
243
+ menuTheme: 'primary',
244
+ cardStyle: 'transparent'
245
+ }, ...(ngDevMode ? [{ debugName: "layoutConfig" }] : []));
246
+ layoutState = signal({
247
+ staticMenuInactive: false,
248
+ overlayMenuActive: false,
249
+ rightMenuVisible: false,
250
+ configSidebarVisible: false,
251
+ mobileMenuActive: false,
252
+ searchBarActive: false,
253
+ sidebarExpanded: false,
254
+ sidebarPinned: true,
255
+ menuHoverActive: false,
256
+ activePath: null,
257
+ anchored: false
258
+ }, ...(ngDevMode ? [{ debugName: "layoutState" }] : []));
259
+ router = inject(Router);
260
+ currentUrl = signal('', ...(ngDevMode ? [{ debugName: "currentUrl" }] : []));
261
+ isDarkTheme = computed(() => this.layoutConfig().darkTheme, ...(ngDevMode ? [{ debugName: "isDarkTheme" }] : []));
262
+ isSlim = computed(() => this.layoutConfig().menuMode === 'slim', ...(ngDevMode ? [{ debugName: "isSlim" }] : []));
263
+ isHorizontal = computed(() => this.layoutConfig().menuMode === 'horizontal', ...(ngDevMode ? [{ debugName: "isHorizontal" }] : []));
264
+ isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay', ...(ngDevMode ? [{ debugName: "isOverlay" }] : []));
265
+ isCompact = computed(() => this.layoutConfig().menuMode === 'compact', ...(ngDevMode ? [{ debugName: "isCompact" }] : []));
266
+ isStatic = computed(() => this.layoutConfig().menuMode === 'static', ...(ngDevMode ? [{ debugName: "isStatic" }] : []));
267
+ isReveal = computed(() => this.layoutConfig().menuMode === 'reveal', ...(ngDevMode ? [{ debugName: "isReveal" }] : []));
268
+ isDrawer = computed(() => this.layoutConfig().menuMode === 'drawer', ...(ngDevMode ? [{ debugName: "isDrawer" }] : []));
269
+ isSidebarPinned = computed(() => this.layoutState().sidebarPinned, ...(ngDevMode ? [{ debugName: "isSidebarPinned" }] : []));
270
+ isRail = computed(() => !this.layoutState().sidebarPinned && this.isStatic(), ...(ngDevMode ? [{ debugName: "isRail" }] : []));
271
+ hasOverlaySubmenu = computed(() => this.isSlim() || this.isCompact() || this.isHorizontal(), ...(ngDevMode ? [{ debugName: "hasOverlaySubmenu" }] : []));
272
+ hasOpenOverlay = computed(() => this.layoutState().overlayMenuActive || this.hasOpenOverlaySubmenu(), ...(ngDevMode ? [{ debugName: "hasOpenOverlay" }] : []));
273
+ hasOpenOverlaySubmenu = computed(() => {
274
+ return this.hasOverlaySubmenu() && !!this.layoutState().activePath;
275
+ }, ...(ngDevMode ? [{ debugName: "hasOpenOverlaySubmenu" }] : []));
276
+ isSidebarStateChanged = computed(() => {
277
+ const layoutConfig = this.layoutConfig();
278
+ return layoutConfig.menuMode === 'horizontal' || layoutConfig.menuMode === 'slim' || layoutConfig.menuMode === 'slim-plus';
279
+ }, ...(ngDevMode ? [{ debugName: "isSidebarStateChanged" }] : []));
280
+ initialized = false;
281
+ previousMenuMode = undefined;
282
+ constructor() {
283
+ effect(() => {
284
+ const config = this.layoutConfig();
285
+ if (!this.initialized || !config) {
286
+ this.initialized = true;
287
+ return;
288
+ }
289
+ this.handleDarkModeTransition(config);
290
+ });
291
+ effect(() => {
292
+ this.updateMenuState();
293
+ });
294
+ }
295
+ updateMenuState() {
296
+ const menuMode = this.layoutConfig().menuMode;
297
+ if (this.previousMenuMode === undefined) {
298
+ this.previousMenuMode = menuMode;
299
+ return;
300
+ }
301
+ if (this.previousMenuMode === menuMode) {
302
+ return;
303
+ }
304
+ this.previousMenuMode = menuMode;
305
+ const isOverlaySubmenu = menuMode === 'slim' || menuMode === 'slim-plus' || menuMode === 'horizontal' || menuMode === 'compact';
306
+ this.layoutState.update((prev) => ({
307
+ ...prev,
308
+ staticMenuInactive: false,
309
+ overlayMenuActive: false,
310
+ mobileMenuActive: false,
311
+ sidebarExpanded: false,
312
+ menuHoverActive: false,
313
+ anchored: false,
314
+ activePath: this.isDesktop() ? (isOverlaySubmenu ? null : this.router.url) : prev.activePath
315
+ }));
316
+ }
317
+ handleDarkModeTransition(config) {
318
+ const supportsViewTransition = 'startViewTransition' in document;
319
+ if (supportsViewTransition) {
320
+ this.startViewTransition(config);
321
+ }
322
+ else {
323
+ this.toggleDarkMode(config);
324
+ }
325
+ }
326
+ startViewTransition(config) {
327
+ const transition = document.startViewTransition(() => {
328
+ this.toggleDarkMode(config);
329
+ });
330
+ }
331
+ toggleDarkMode(config) {
332
+ const _config = config || this.layoutConfig();
333
+ if (_config.darkTheme) {
334
+ document.documentElement.classList.add('app-dark');
335
+ }
336
+ else {
337
+ document.documentElement.classList.remove('app-dark');
338
+ }
339
+ }
340
+ toggleMenu() {
341
+ if (this.isDesktop()) {
342
+ this.toggleSidebarPin();
343
+ }
344
+ else {
345
+ this.layoutState.update((prev) => ({ ...prev, mobileMenuActive: !prev.mobileMenuActive }));
346
+ }
347
+ }
348
+ toggleSidebarPin() {
349
+ this.layoutState.update((prev) => ({
350
+ ...prev,
351
+ sidebarPinned: !prev.sidebarPinned,
352
+ sidebarExpanded: false
353
+ }));
354
+ }
355
+ changeMenuMode(mode) {
356
+ this.layoutConfig.update((prev) => ({ ...prev, menuMode: mode }));
357
+ this.layoutState.update((prev) => ({ ...prev, staticMenuInactive: false, overlayMenuActive: false, mobileMenuActive: false, sidebarExpanded: false, sidebarPinned: true, menuHoverActive: false, anchored: false }));
358
+ if (this.isDesktop()) {
359
+ this.layoutState.update((prev) => ({ ...prev, activePath: this.hasOverlaySubmenu() ? null : this.router.url }));
360
+ }
361
+ }
362
+ toggleRightMenu() {
363
+ this.layoutState.update((prev) => ({
364
+ ...prev,
365
+ rightMenuVisible: !prev.rightMenuVisible
366
+ }));
367
+ }
368
+ toggleConfigSidebar() {
369
+ this.layoutState.update((prev) => ({
370
+ ...prev,
371
+ configSidebarVisible: !prev.configSidebarVisible
372
+ }));
373
+ }
374
+ toggleSearchBar() {
375
+ this.layoutState.update((prev) => ({
376
+ ...prev,
377
+ searchBarActive: !prev.searchBarActive
378
+ }));
379
+ }
380
+ isDesktop() {
381
+ return window.innerWidth > 991;
382
+ }
383
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
384
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: LayoutService, providedIn: 'root' });
385
+ }
386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: LayoutService, decorators: [{
387
+ type: Injectable,
388
+ args: [{
389
+ providedIn: 'root'
390
+ }]
391
+ }], ctorParameters: () => [] });
392
+
393
+ class AppBreadcrumb {
394
+ router;
395
+ _breadcrumbs$ = new BehaviorSubject([]);
396
+ breadcrumbs$ = this._breadcrumbs$.asObservable();
397
+ constructor(router) {
398
+ this.router = router;
399
+ this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event) => {
400
+ const root = this.router.routerState.snapshot.root;
401
+ const breadcrumbs = [];
402
+ this.addBreadcrumb(root, [], breadcrumbs);
403
+ this._breadcrumbs$.next(breadcrumbs);
404
+ });
405
+ }
406
+ addBreadcrumb(route, parentUrl, breadcrumbs) {
407
+ const routeUrl = parentUrl.concat(route.url.map((url) => url.path));
408
+ const breadcrumb = route.data['breadcrumb'];
409
+ const parentBreadcrumb = route.parent && route.parent.data ? route.parent.data['breadcrumb'] : null;
410
+ if (breadcrumb && breadcrumb !== parentBreadcrumb) {
411
+ breadcrumbs.push({
412
+ label: route.data['breadcrumb'],
413
+ url: '/' + routeUrl.join('/')
414
+ });
415
+ }
416
+ if (route.firstChild) {
417
+ this.addBreadcrumb(route.firstChild, routeUrl, breadcrumbs);
418
+ }
419
+ }
420
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppBreadcrumb, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component });
421
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AppBreadcrumb, isStandalone: true, selector: "[app-breadcrumb]", ngImport: i0, template: `<nav class="layout-breadcrumb" aria-label="Breadcrumb">
422
+ <ol>
423
+ @for (item of breadcrumbs$ | async; track item.url; let last = $last) {
424
+ <li class="text-sm font-medium text-surface-700 dark:text-surface-100">
425
+ @if (!last && item.url) {
426
+ <a [routerLink]="item.url" class="text-surface-700 dark:text-surface-100 cursor-pointer">{{ item.label }}</a>
427
+ } @else {
428
+ {{ item.label }}
429
+ }
430
+ </li>
431
+ @if (!last) {
432
+ <li class="text-sm font-medium text-surface-400 dark:text-surface-400">/</li>
433
+ }
434
+ }
435
+ </ol>
436
+ </nav> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] });
437
+ }
438
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppBreadcrumb, decorators: [{
439
+ type: Component,
440
+ args: [{
441
+ selector: '[app-breadcrumb]',
442
+ imports: [CommonModule, RouterModule],
443
+ template: `<nav class="layout-breadcrumb" aria-label="Breadcrumb">
444
+ <ol>
445
+ @for (item of breadcrumbs$ | async; track item.url; let last = $last) {
446
+ <li class="text-sm font-medium text-surface-700 dark:text-surface-100">
447
+ @if (!last && item.url) {
448
+ <a [routerLink]="item.url" class="text-surface-700 dark:text-surface-100 cursor-pointer">{{ item.label }}</a>
449
+ } @else {
450
+ {{ item.label }}
451
+ }
452
+ </li>
453
+ @if (!last) {
454
+ <li class="text-sm font-medium text-surface-400 dark:text-surface-400">/</li>
455
+ }
456
+ }
457
+ </ol>
458
+ </nav> `
459
+ }]
460
+ }], ctorParameters: () => [{ type: i1.Router }] });
461
+
462
+ class AppConfigurator {
463
+ simple = false;
464
+ location = 'app';
465
+ router = inject(Router);
466
+ config = inject(PrimeNG);
467
+ layoutService = inject(LayoutService);
468
+ platformId = inject(PLATFORM_ID);
469
+ primeng = inject(PrimeNG);
470
+ presetKeys = Object.keys(brandPresets);
471
+ themeOptions = [
472
+ { name: 'Light', value: false },
473
+ { name: 'Dark', value: true }
474
+ ];
475
+ menuThemeOptions = [];
476
+ ngOnInit() {
477
+ if (isPlatformBrowser(this.platformId)) {
478
+ this.onPresetChange(this.layoutService.layoutConfig().preset);
479
+ }
480
+ this.updateMenuThemeOptions();
481
+ }
482
+ updateMenuThemeOptions() {
483
+ if (this.darkTheme()) {
484
+ this.menuThemeOptions = [
485
+ { name: 'Dark', value: 'dark' },
486
+ { name: 'Primary', value: 'primary' }
487
+ ];
488
+ }
489
+ else {
490
+ this.menuThemeOptions = [
491
+ { name: 'Light', value: 'light' },
492
+ { name: 'Dark', value: 'dark' },
493
+ { name: 'Primary', value: 'primary' }
494
+ ];
495
+ }
496
+ }
497
+ surfaces = [
498
+ {
499
+ name: 'gray',
500
+ palette: {
501
+ 0: '#ffffff',
502
+ 50: '#e5e6e6',
503
+ 100: '#d5d6d7',
504
+ 200: '#c6c7c8',
505
+ 300: '#b6b8b9',
506
+ 400: '#a7a8aa',
507
+ 500: '#97999b',
508
+ 600: '#808284',
509
+ 700: '#6a6b6d',
510
+ 800: '#535455',
511
+ 900: '#3c3d3e',
512
+ 950: '#262627'
513
+ }
514
+ },
515
+ {
516
+ name: 'darkblue',
517
+ palette: {
518
+ 0: '#ffffff',
519
+ 50: '#D0EEFF',
520
+ 100: '#B7E2F9',
521
+ 200: '#73abc7',
522
+ 300: '#73abc7',
523
+ 400: '#4d94b8',
524
+ 500: '#267da9',
525
+ 600: '#00669a',
526
+ 700: '#005783',
527
+ 800: '#00476c',
528
+ 900: '#00293e',
529
+ 950: '#001a27'
530
+ }
531
+ }
532
+ ];
533
+ selectedPrimaryColor = computed(() => {
534
+ return this.layoutService.layoutConfig().primary;
535
+ }, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
536
+ selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "selectedSurfaceColor" }] : []));
537
+ selectedPreset = computed(() => this.layoutService.layoutConfig().preset, ...(ngDevMode ? [{ debugName: "selectedPreset" }] : []));
538
+ menuMode = computed(() => {
539
+ const mode = this.layoutService.layoutConfig().menuMode;
540
+ const pinned = this.layoutService.layoutState().sidebarPinned;
541
+ return mode === 'static' && !pinned ? 'rail' : mode;
542
+ }, ...(ngDevMode ? [{ debugName: "menuMode" }] : []));
543
+ cardStyle = model(this.layoutService.layoutConfig().cardStyle, ...(ngDevMode ? [{ debugName: "cardStyle" }] : []));
544
+ get configSidebarVisible() {
545
+ return this.layoutService.layoutState().configSidebarVisible;
546
+ }
547
+ set configSidebarVisible(val) {
548
+ this.layoutService.layoutState.update((prev) => ({ ...prev, configSidebarVisible: val }));
549
+ }
550
+ darkTheme = computed(() => this.layoutService.layoutConfig().darkTheme, ...(ngDevMode ? [{ debugName: "darkTheme" }] : []));
551
+ selectedSurface = computed(() => this.layoutService.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "selectedSurface" }] : []));
552
+ cardStyleOptions = [
553
+ { name: 'Transparent', value: 'transparent' },
554
+ { name: 'Filled', value: 'filled' }
555
+ ];
556
+ primaryColors = computed(() => {
557
+ const presetPalette = brandPresets[this.layoutService.layoutConfig().preset].primitive;
558
+ const colors = ['red', 'orange', 'yellow', 'lemon', 'lime', 'babygreen', 'green', 'olive', 'teal', 'ocean', 'blue', 'darkblue', 'midnight', 'cherry'];
559
+ const palettes = [{ name: 'noir', palette: {} }];
560
+ colors.forEach((color) => {
561
+ palettes.push({
562
+ name: color,
563
+ palette: presetPalette?.[color]
564
+ });
565
+ });
566
+ return palettes;
567
+ }, ...(ngDevMode ? [{ debugName: "primaryColors" }] : []));
568
+ menuTheme = computed(() => this.layoutService.layoutConfig().menuTheme, ...(ngDevMode ? [{ debugName: "menuTheme" }] : []));
569
+ getPresetExt() {
570
+ const color = this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {};
571
+ if (color.name === 'noir') {
572
+ return {
573
+ semantic: {
574
+ primary: {
575
+ 50: '{surface.50}',
576
+ 100: '{surface.100}',
577
+ 200: '{surface.200}',
578
+ 300: '{surface.300}',
579
+ 400: '{surface.400}',
580
+ 500: '{surface.500}',
581
+ 600: '{surface.600}',
582
+ 700: '{surface.700}',
583
+ 800: '{surface.800}',
584
+ 900: '{surface.900}',
585
+ 950: '{surface.950}'
586
+ },
587
+ colorScheme: {
588
+ light: {
589
+ primary: {
590
+ color: '{primary.950}',
591
+ contrastColor: '#ffffff',
592
+ hoverColor: '{primary.800}',
593
+ activeColor: '{primary.700}'
594
+ },
595
+ highlight: {
596
+ background: '{primary.950}',
597
+ focusBackground: '{primary.700}',
598
+ color: '#ffffff',
599
+ focusColor: '#ffffff'
600
+ }
601
+ },
602
+ dark: {
603
+ primary: {
604
+ color: '{primary.50}',
605
+ contrastColor: '{primary.950}',
606
+ hoverColor: '{primary.200}',
607
+ activeColor: '{primary.300}'
608
+ },
609
+ highlight: {
610
+ background: '{primary.50}',
611
+ focusBackground: '{primary.300}',
612
+ color: '{primary.950}',
613
+ focusColor: '{primary.950}'
614
+ }
615
+ }
616
+ }
617
+ }
618
+ };
619
+ }
620
+ else {
621
+ return {
622
+ semantic: {
623
+ primary: color.palette,
624
+ colorScheme: {
625
+ light: {
626
+ primary: {
627
+ color: '{primary.500}',
628
+ contrastColor: '#ffffff',
629
+ hoverColor: '{primary.600}',
630
+ activeColor: '{primary.700}'
631
+ },
632
+ highlight: {
633
+ background: '{primary.50}',
634
+ focusBackground: '{primary.100}',
635
+ color: '{primary.700}',
636
+ focusColor: '{primary.800}'
637
+ }
638
+ },
639
+ dark: {
640
+ primary: {
641
+ color: '{primary.400}',
642
+ contrastColor: '{surface.900}',
643
+ hoverColor: '{primary.300}',
644
+ activeColor: '{primary.200}'
645
+ },
646
+ highlight: {
647
+ background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
648
+ focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
649
+ color: 'rgba(255,255,255,.87)',
650
+ focusColor: 'rgba(255,255,255,.87)'
651
+ }
652
+ }
653
+ }
654
+ }
655
+ };
656
+ }
657
+ }
658
+ updateColors(event, type, color) {
659
+ if (type === 'primary') {
660
+ this.layoutService.layoutConfig.update((state) => ({
661
+ ...state,
662
+ primary: color.name
663
+ }));
664
+ }
665
+ else if (type === 'surface') {
666
+ this.layoutService.layoutConfig.update((state) => ({
667
+ ...state,
668
+ surface: color.name
669
+ }));
670
+ }
671
+ this.applyTheme(type, color);
672
+ event.stopPropagation();
673
+ }
674
+ applyTheme(type, color) {
675
+ if (type === 'primary') {
676
+ updatePreset(this.getPresetExt());
677
+ }
678
+ else if (type === 'surface') {
679
+ updateSurfacePalette(color.palette);
680
+ }
681
+ }
682
+ onPresetChange(event) {
683
+ this.layoutService.layoutConfig.update((state) => ({
684
+ ...state,
685
+ preset: event
686
+ }));
687
+ const preset = brandPresets[event];
688
+ const surfacePalette = this.surfaces.find((s) => s.name === this.selectedSurfaceColor())?.palette;
689
+ $t().preset(preset).preset(this.getPresetExt()).surfacePalette(surfacePalette).use({ useDefaultOptions: true });
690
+ }
691
+ onCardStyleChange(value) {
692
+ this.layoutService.layoutConfig.update((state) => ({
693
+ ...state,
694
+ cardStyle: value
695
+ }));
696
+ }
697
+ onMenuThemeChange(value) {
698
+ this.layoutService.layoutConfig.update((state) => ({
699
+ ...state,
700
+ menuTheme: value
701
+ }));
702
+ }
703
+ setMenuMode(mode) {
704
+ const isRail = mode === 'rail';
705
+ const actualMode = isRail ? 'static' : mode;
706
+ this.layoutService.layoutConfig.update((state) => ({
707
+ ...state,
708
+ menuMode: actualMode
709
+ }));
710
+ this.layoutService.layoutState.update((state) => ({
711
+ ...state,
712
+ staticMenuInactive: false,
713
+ overlayMenuActive: false,
714
+ mobileMenuActive: false,
715
+ sidebarExpanded: false,
716
+ sidebarPinned: !isRail,
717
+ menuHoverActive: false,
718
+ anchored: false
719
+ }));
720
+ }
721
+ toggleDarkMode() {
722
+ this.executeDarkModeToggle();
723
+ }
724
+ executeDarkModeToggle() {
725
+ this.layoutService.layoutConfig.update((state) => ({
726
+ ...state,
727
+ darkTheme: !state.darkTheme
728
+ }));
729
+ if (this.darkTheme()) {
730
+ this.setMenuTheme('dark');
731
+ }
732
+ this.updateMenuThemeOptions();
733
+ }
734
+ toggleConfigSidebar() {
735
+ this.layoutService.layoutState.update((val) => ({ ...val, configSidebarVisible: !val.configSidebarVisible }));
736
+ }
737
+ setMenuTheme(theme) {
738
+ this.layoutService.layoutConfig.update((state) => ({
739
+ ...state,
740
+ menuTheme: theme
741
+ }));
742
+ }
743
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppConfigurator, deps: [], target: i0.ɵɵFactoryTarget.Component });
744
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AppConfigurator, isStandalone: true, selector: "app-configurator", inputs: { simple: { classPropertyName: "simple", publicName: "simple", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, location: { classPropertyName: "location", publicName: "location", isSignal: false, isRequired: false, transformFunction: null }, cardStyle: { classPropertyName: "cardStyle", publicName: "cardStyle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardStyle: "cardStyleChange" }, ngImport: i0, template: `
745
+ <p-drawer [(visible)]="configSidebarVisible" position="right" [transitionOptions]="'.3s cubic-bezier(0, 0, 0.2, 1)'" styleClass="layout-config-sidebar w-80" header="Settings" appendTo="body">
746
+ <div class="flex flex-col gap-6">
747
+ <div>
748
+ <span class="text-lg text-muted-color font-semibold">Primary</span>
749
+ <div class="pt-2 flex gap-2 flex-wrap">
750
+ @for (primaryColor of primaryColors(); track primaryColor.name) {
751
+ <button
752
+ type="button"
753
+ [title]="primaryColor.name"
754
+ (click)="updateColors($event, 'primary', primaryColor)"
755
+ class="w-6 h-6 cursor-pointer hover:shadow-lg rounded duration-150 flex items-center justify-center"
756
+ [style]="{
757
+ 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
758
+ }"
759
+ >
760
+ <i *ngIf="primaryColor.name === selectedPrimaryColor()" class="pi pi-check text-white"></i>
761
+ </button>
762
+ }
763
+ </div>
764
+ </div>
765
+
766
+ <div>
767
+ <span class="text-lg text-muted-color font-semibold">Surface</span>
768
+ <div class="pt-2 flex gap-2 flex-wrap">
769
+ @for (surface of surfaces; track surface.name) {
770
+ <button
771
+ type="button"
772
+ [title]="surface.name"
773
+ (click)="updateColors($event, 'surface', surface)"
774
+ class="w-6 h-6 cursor-pointer hover:shadow-lg rounded duration-150 flex items-center justify-center"
775
+ [style]="{
776
+ 'background-color': surface?.palette?.['500']
777
+ }"
778
+ >
779
+ <i *ngIf="selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : darkTheme() ? surface.name === 'darkblue' : surface.name === 'gray'" class="pi pi-check text-white"></i>
780
+ </button>
781
+ }
782
+ </div>
783
+ </div>
784
+ <div>
785
+ <div class="flex flex-col gap-2">
786
+ <span class="text-lg text-muted-color font-semibold">Presets</span>
787
+ <p-selectbutton [options]="presetKeys" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false"></p-selectbutton>
788
+ </div>
789
+ </div>
790
+ <div>
791
+ <div class="flex flex-col gap-2">
792
+ <span class="text-lg text-muted-color font-semibold">Color Scheme</span>
793
+ <p-selectbutton [ngModel]="darkTheme()" (ngModelChange)="toggleDarkMode()" [options]="themeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false"></p-selectbutton>
794
+ </div>
795
+ </div>
796
+ <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
797
+ <span class="text-lg text-muted-color font-semibold">Card Style</span>
798
+ <p-selectbutton [ngModel]="cardStyle()" (ngModelChange)="onCardStyleChange($event)" [options]="cardStyleOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
799
+ </div>
800
+
801
+ <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
802
+ <span class="text-lg text-muted-color font-semibold">Menu Theme</span>
803
+ <p-selectbutton [ngModel]="menuTheme()" (ngModelChange)="onMenuThemeChange($event)" [options]="menuThemeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
804
+ </div>
805
+
806
+ <div *ngIf="!simple && location === 'app'">
807
+ <div class="flex flex-col gap-2">
808
+ <span class="text-lg text-muted-color font-semibold">Menu Type</span>
809
+ <div class="flex flex-wrap flex-col gap-3">
810
+ <div class="flex">
811
+ <div class="flex items-center gap-2 w-6/12">
812
+ <p-radiobutton name="menuMode" value="static" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('static')" inputId="static"></p-radiobutton>
813
+ <label for="static">Static</label>
814
+ </div>
815
+ <div class="flex items-center gap-2 w-6/12">
816
+ <p-radiobutton name="menuMode" value="rail" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('rail')" inputId="rail"></p-radiobutton>
817
+ <label for="rail">Rail</label>
818
+ </div>
819
+ </div>
820
+ <div class="flex">
821
+ <div class="flex items-center gap-2 w-6/12">
822
+ <p-radiobutton name="menuMode" value="overlay" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('overlay')" inputId="overlay"></p-radiobutton>
823
+ <label for="overlay">Overlay</label>
824
+ </div>
825
+ <div class="flex items-center gap-2 w-6/12">
826
+ <p-radiobutton name="menuMode" value="slim" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('slim')" inputId="slim"></p-radiobutton>
827
+ <label for="slim">Slim</label>
828
+ </div>
829
+ </div>
830
+ <div class="flex">
831
+ <div class="flex items-center gap-2 w-6/12">
832
+ <p-radiobutton name="menuMode" value="compact" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('compact')" inputId="compact"></p-radiobutton>
833
+ <label for="compact">Compact</label>
834
+ </div>
835
+ <div class="flex items-center gap-2 w-6/12">
836
+ <p-radiobutton name="menuMode" value="reveal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('reveal')" inputId="reveal"></p-radiobutton>
837
+ <label for="reveal">Reveal</label>
838
+ </div>
839
+ </div>
840
+ <div class="flex">
841
+ <div class="flex items-center gap-2 w-6/12">
842
+ <p-radiobutton name="menuMode" value="drawer" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('drawer')" inputId="drawer"></p-radiobutton>
843
+ <label for="drawer">Drawer</label>
844
+ </div>
845
+ <div class="flex items-center gap-2 w-6/12">
846
+ <p-radiobutton name="menuMode" value="horizontal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('horizontal')" inputId="horizontal"></p-radiobutton>
847
+ <label for="horizontal">Horizontal</label>
848
+ </div>
849
+ </div>
850
+ </div>
851
+ </div>
852
+ </div>
853
+ </div>
854
+ </p-drawer>
855
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i3.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i5$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "styleClass", "autofocus", "binary", "variant", "size"], outputs: ["onClick", "onFocus", "onBlur"] }] });
856
+ }
857
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppConfigurator, decorators: [{
858
+ type: Component,
859
+ args: [{
860
+ selector: 'app-configurator',
861
+ imports: [CommonModule, FormsModule, SelectButtonModule, DrawerModule, ToggleSwitchModule, RadioButtonModule],
862
+ template: `
863
+ <p-drawer [(visible)]="configSidebarVisible" position="right" [transitionOptions]="'.3s cubic-bezier(0, 0, 0.2, 1)'" styleClass="layout-config-sidebar w-80" header="Settings" appendTo="body">
864
+ <div class="flex flex-col gap-6">
865
+ <div>
866
+ <span class="text-lg text-muted-color font-semibold">Primary</span>
867
+ <div class="pt-2 flex gap-2 flex-wrap">
868
+ @for (primaryColor of primaryColors(); track primaryColor.name) {
869
+ <button
870
+ type="button"
871
+ [title]="primaryColor.name"
872
+ (click)="updateColors($event, 'primary', primaryColor)"
873
+ class="w-6 h-6 cursor-pointer hover:shadow-lg rounded duration-150 flex items-center justify-center"
874
+ [style]="{
875
+ 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
876
+ }"
877
+ >
878
+ <i *ngIf="primaryColor.name === selectedPrimaryColor()" class="pi pi-check text-white"></i>
879
+ </button>
880
+ }
881
+ </div>
882
+ </div>
883
+
884
+ <div>
885
+ <span class="text-lg text-muted-color font-semibold">Surface</span>
886
+ <div class="pt-2 flex gap-2 flex-wrap">
887
+ @for (surface of surfaces; track surface.name) {
888
+ <button
889
+ type="button"
890
+ [title]="surface.name"
891
+ (click)="updateColors($event, 'surface', surface)"
892
+ class="w-6 h-6 cursor-pointer hover:shadow-lg rounded duration-150 flex items-center justify-center"
893
+ [style]="{
894
+ 'background-color': surface?.palette?.['500']
895
+ }"
896
+ >
897
+ <i *ngIf="selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : darkTheme() ? surface.name === 'darkblue' : surface.name === 'gray'" class="pi pi-check text-white"></i>
898
+ </button>
899
+ }
900
+ </div>
901
+ </div>
902
+ <div>
903
+ <div class="flex flex-col gap-2">
904
+ <span class="text-lg text-muted-color font-semibold">Presets</span>
905
+ <p-selectbutton [options]="presetKeys" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false"></p-selectbutton>
906
+ </div>
907
+ </div>
908
+ <div>
909
+ <div class="flex flex-col gap-2">
910
+ <span class="text-lg text-muted-color font-semibold">Color Scheme</span>
911
+ <p-selectbutton [ngModel]="darkTheme()" (ngModelChange)="toggleDarkMode()" [options]="themeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false"></p-selectbutton>
912
+ </div>
913
+ </div>
914
+ <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
915
+ <span class="text-lg text-muted-color font-semibold">Card Style</span>
916
+ <p-selectbutton [ngModel]="cardStyle()" (ngModelChange)="onCardStyleChange($event)" [options]="cardStyleOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
917
+ </div>
918
+
919
+ <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
920
+ <span class="text-lg text-muted-color font-semibold">Menu Theme</span>
921
+ <p-selectbutton [ngModel]="menuTheme()" (ngModelChange)="onMenuThemeChange($event)" [options]="menuThemeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
922
+ </div>
923
+
924
+ <div *ngIf="!simple && location === 'app'">
925
+ <div class="flex flex-col gap-2">
926
+ <span class="text-lg text-muted-color font-semibold">Menu Type</span>
927
+ <div class="flex flex-wrap flex-col gap-3">
928
+ <div class="flex">
929
+ <div class="flex items-center gap-2 w-6/12">
930
+ <p-radiobutton name="menuMode" value="static" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('static')" inputId="static"></p-radiobutton>
931
+ <label for="static">Static</label>
932
+ </div>
933
+ <div class="flex items-center gap-2 w-6/12">
934
+ <p-radiobutton name="menuMode" value="rail" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('rail')" inputId="rail"></p-radiobutton>
935
+ <label for="rail">Rail</label>
936
+ </div>
937
+ </div>
938
+ <div class="flex">
939
+ <div class="flex items-center gap-2 w-6/12">
940
+ <p-radiobutton name="menuMode" value="overlay" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('overlay')" inputId="overlay"></p-radiobutton>
941
+ <label for="overlay">Overlay</label>
942
+ </div>
943
+ <div class="flex items-center gap-2 w-6/12">
944
+ <p-radiobutton name="menuMode" value="slim" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('slim')" inputId="slim"></p-radiobutton>
945
+ <label for="slim">Slim</label>
946
+ </div>
947
+ </div>
948
+ <div class="flex">
949
+ <div class="flex items-center gap-2 w-6/12">
950
+ <p-radiobutton name="menuMode" value="compact" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('compact')" inputId="compact"></p-radiobutton>
951
+ <label for="compact">Compact</label>
952
+ </div>
953
+ <div class="flex items-center gap-2 w-6/12">
954
+ <p-radiobutton name="menuMode" value="reveal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('reveal')" inputId="reveal"></p-radiobutton>
955
+ <label for="reveal">Reveal</label>
956
+ </div>
957
+ </div>
958
+ <div class="flex">
959
+ <div class="flex items-center gap-2 w-6/12">
960
+ <p-radiobutton name="menuMode" value="drawer" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('drawer')" inputId="drawer"></p-radiobutton>
961
+ <label for="drawer">Drawer</label>
962
+ </div>
963
+ <div class="flex items-center gap-2 w-6/12">
964
+ <p-radiobutton name="menuMode" value="horizontal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('horizontal')" inputId="horizontal"></p-radiobutton>
965
+ <label for="horizontal">Horizontal</label>
966
+ </div>
967
+ </div>
968
+ </div>
969
+ </div>
970
+ </div>
971
+ </div>
972
+ </p-drawer>
973
+ `
974
+ }]
975
+ }], propDecorators: { simple: [{
976
+ type: Input,
977
+ args: [{ transform: booleanAttribute }]
978
+ }], location: [{
979
+ type: Input
980
+ }], cardStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardStyle", required: false }] }, { type: i0.Output, args: ["cardStyleChange"] }] } });
981
+
982
+ class AppFooter {
983
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
984
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppFooter, isStandalone: true, selector: "[app-footer]", ngImport: i0, template: `
985
+ <footer class="layout-footer">
986
+ <span class="footer-copyright">&#169; UNOPS 2026</span>
987
+ </footer>
988
+ `, isInline: true });
989
+ }
990
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, decorators: [{
991
+ type: Component,
992
+ args: [{
993
+ selector: '[app-footer]',
994
+ template: `
995
+ <footer class="layout-footer">
996
+ <span class="footer-copyright">&#169; UNOPS 2026</span>
997
+ </footer>
998
+ `
999
+ }]
1000
+ }] });
1001
+
1002
+ class AppRightMenu {
1003
+ layoutService = inject(LayoutService);
1004
+ cards = [
1005
+ { label: '*****24', value: { id: 1, name: '*****24', code: 'A1' } },
1006
+ { label: '*****75', value: { id: 2, name: '*****75', code: 'A2' } }
1007
+ ];
1008
+ selectedCard;
1009
+ amountValue = '';
1010
+ get rightMenuVisible() {
1011
+ return this.layoutService.layoutState().rightMenuVisible;
1012
+ }
1013
+ set rightMenuVisible(_val) {
1014
+ this.layoutService.layoutState.update((prev) => ({ ...prev, rightMenuVisible: _val }));
1015
+ }
1016
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppRightMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
1017
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppRightMenu, isStandalone: true, selector: "[app-rightmenu]", ngImport: i0, template: ` <p-drawer header="Menu" [(visible)]="rightMenuVisible" position="right" styleClass="layout-rightmenu w-full! sm:w-xl!">
1018
+ <div>
1019
+ <h2 class="title-h7 text-left">Activity</h2>
1020
+ <div class="flex flex-col mt-7">
1021
+ <div class="flex gap-6">
1022
+ <div class="flex flex-col items-center">
1023
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1024
+ <i class="pi pi-dollar text-blue-600 text-2xl!"></i>
1025
+ </span>
1026
+ <span class="min-h-14 w-px bg-(--surface-border)"></span>
1027
+ </div>
1028
+ <div class="mt-2">
1029
+ <h5 class="label-large">New Sale</h5>
1030
+ <p class="md:label-small mt-1">Richard Jones has purchased a blue t-shirt for <b class="body-small text-surface-950 dark:text-surface-0">$79</b></p>
1031
+ </div>
1032
+ </div>
1033
+ <div class="flex gap-6">
1034
+ <div class="flex flex-col items-center">
1035
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1036
+ <i class="pi pi-download text-orange-600 text-2xl!"></i>
1037
+ </span>
1038
+ <span class="min-h-14 w-px bg-(--surface-border)"></span>
1039
+ </div>
1040
+ <div class="mt-2">
1041
+ <h5 class="label-large">Withdrawal Initiated</h5>
1042
+ <p class="md:label-small mt-1">Your request for withdrawal of <b class="body-small text-surface-950 dark:text-surface-0">$2500</b> has been initiated.</p>
1043
+ </div>
1044
+ </div>
1045
+ <div class="flex gap-6">
1046
+ <div class="flex flex-col items-center">
1047
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1048
+ <i class="pi pi-question-circle text-violet-600 text-2xl!"></i>
1049
+ </span>
1050
+ <span class="min-h-14 w-px bg-(--surface-border)"></span>
1051
+ </div>
1052
+ <div class="mt-2">
1053
+ <h5 class="label-large">Question Received</h5>
1054
+ <p class="md:label-small mt-1">Jane Davis has posted a <b class="body-small text-surface-950 dark:text-surface-0">new question</b> about your product.</p>
1055
+ </div>
1056
+ </div>
1057
+ <div class="flex gap-6">
1058
+ <div class="flex flex-col items-center">
1059
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1060
+ <i class="pi pi-comment text-blue-600 text-2xl!"></i>
1061
+ </span>
1062
+ </div>
1063
+ <div class="mt-2">
1064
+ <h5 class="label-large">Comment Received</h5>
1065
+ <p class="md:label-small mt-1">Claire Smith has upvoted your store along with a <b class="body-small text-surface-950 dark:text-surface-0">comment.</b></p>
1066
+ </div>
1067
+ </div>
1068
+ </div>
1069
+ </div>
1070
+ <p-divider class="my-10!" />
1071
+ <div>
1072
+ <h2 class="title-h7 text-left">Quick Withdraw</h2>
1073
+ <div class="flex flex-col gap-3.5 mt-7 mb-6">
1074
+ <input pInputText type="text" [(ngModel)]="amountValue" placeholder="Amount" />
1075
+ <p-select [(ngModel)]="selectedCard" [options]="cards" optionLabel="label" placeholder="Select a Card" class="w-full" />
1076
+ </div>
1077
+ <button pButton label="Confirm" severity="success" class="w-full!"></button>
1078
+ </div>
1079
+ <p-divider class="my-10!" />
1080
+ <div>
1081
+ <h2 class="title-h7 text-left">Shipment Tracking</h2>
1082
+ <p class="body-small mt-1 text-left">Track your ongoing shipments to customers.</p>
1083
+ <img class="w-full h-full max-h-60 object-cover border border-surface rounded-2xl mt-4" src="layout/images/sidebar-right/staticmap.png" alt="unops-ng_ux" />
1084
+ </div>
1085
+ </p-drawer>`, isInline: true, dependencies: [{ kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i2.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i6.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }] });
1086
+ }
1087
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppRightMenu, decorators: [{
1088
+ type: Component,
1089
+ args: [{
1090
+ selector: '[app-rightmenu]',
1091
+ imports: [DrawerModule, DividerModule, SelectModule, InputTextModule, FormsModule, ButtonModule],
1092
+ template: ` <p-drawer header="Menu" [(visible)]="rightMenuVisible" position="right" styleClass="layout-rightmenu w-full! sm:w-xl!">
1093
+ <div>
1094
+ <h2 class="title-h7 text-left">Activity</h2>
1095
+ <div class="flex flex-col mt-7">
1096
+ <div class="flex gap-6">
1097
+ <div class="flex flex-col items-center">
1098
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1099
+ <i class="pi pi-dollar text-blue-600 text-2xl!"></i>
1100
+ </span>
1101
+ <span class="min-h-14 w-px bg-(--surface-border)"></span>
1102
+ </div>
1103
+ <div class="mt-2">
1104
+ <h5 class="label-large">New Sale</h5>
1105
+ <p class="md:label-small mt-1">Richard Jones has purchased a blue t-shirt for <b class="body-small text-surface-950 dark:text-surface-0">$79</b></p>
1106
+ </div>
1107
+ </div>
1108
+ <div class="flex gap-6">
1109
+ <div class="flex flex-col items-center">
1110
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1111
+ <i class="pi pi-download text-orange-600 text-2xl!"></i>
1112
+ </span>
1113
+ <span class="min-h-14 w-px bg-(--surface-border)"></span>
1114
+ </div>
1115
+ <div class="mt-2">
1116
+ <h5 class="label-large">Withdrawal Initiated</h5>
1117
+ <p class="md:label-small mt-1">Your request for withdrawal of <b class="body-small text-surface-950 dark:text-surface-0">$2500</b> has been initiated.</p>
1118
+ </div>
1119
+ </div>
1120
+ <div class="flex gap-6">
1121
+ <div class="flex flex-col items-center">
1122
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1123
+ <i class="pi pi-question-circle text-violet-600 text-2xl!"></i>
1124
+ </span>
1125
+ <span class="min-h-14 w-px bg-(--surface-border)"></span>
1126
+ </div>
1127
+ <div class="mt-2">
1128
+ <h5 class="label-large">Question Received</h5>
1129
+ <p class="md:label-small mt-1">Jane Davis has posted a <b class="body-small text-surface-950 dark:text-surface-0">new question</b> about your product.</p>
1130
+ </div>
1131
+ </div>
1132
+ <div class="flex gap-6">
1133
+ <div class="flex flex-col items-center">
1134
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1135
+ <i class="pi pi-comment text-blue-600 text-2xl!"></i>
1136
+ </span>
1137
+ </div>
1138
+ <div class="mt-2">
1139
+ <h5 class="label-large">Comment Received</h5>
1140
+ <p class="md:label-small mt-1">Claire Smith has upvoted your store along with a <b class="body-small text-surface-950 dark:text-surface-0">comment.</b></p>
1141
+ </div>
1142
+ </div>
1143
+ </div>
1144
+ </div>
1145
+ <p-divider class="my-10!" />
1146
+ <div>
1147
+ <h2 class="title-h7 text-left">Quick Withdraw</h2>
1148
+ <div class="flex flex-col gap-3.5 mt-7 mb-6">
1149
+ <input pInputText type="text" [(ngModel)]="amountValue" placeholder="Amount" />
1150
+ <p-select [(ngModel)]="selectedCard" [options]="cards" optionLabel="label" placeholder="Select a Card" class="w-full" />
1151
+ </div>
1152
+ <button pButton label="Confirm" severity="success" class="w-full!"></button>
1153
+ </div>
1154
+ <p-divider class="my-10!" />
1155
+ <div>
1156
+ <h2 class="title-h7 text-left">Shipment Tracking</h2>
1157
+ <p class="body-small mt-1 text-left">Track your ongoing shipments to customers.</p>
1158
+ <img class="w-full h-full max-h-60 object-cover border border-surface rounded-2xl mt-4" src="layout/images/sidebar-right/staticmap.png" alt="unops-ng_ux" />
1159
+ </div>
1160
+ </p-drawer>`
1161
+ }]
1162
+ }] });
1163
+
1164
+ class AppSearch {
1165
+ layoutService = inject(LayoutService);
1166
+ toggleSearchBar() {
1167
+ this.layoutService.layoutState.update((value) => ({ ...value, searchBarActive: !value.searchBarActive }));
1168
+ }
1169
+ get searchBarActive() {
1170
+ return this.layoutService.layoutState().searchBarActive;
1171
+ }
1172
+ set searchBarActive(_val) {
1173
+ this.layoutService.layoutState.update((prev) => ({ ...prev, searchBarActive: _val }));
1174
+ }
1175
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSearch, deps: [], target: i0.ɵɵFactoryTarget.Component });
1176
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppSearch, isStandalone: true, selector: "[app-search]", ngImport: i0, template: ` <p-dialog [(visible)]="searchBarActive" [breakpoints]="{ '992px': '75vw', '576px': '90vw' }" modal dismissableMask styleClass="w-1/2 search-container">
1177
+ <ng-template #headless>
1178
+ <div class="w-full">
1179
+ <i class="pi pi-search"></i>
1180
+ <input pInputText type="text" [pAutoFocus]="true" class="p-inputtext search-input" placeholder="Search" (keydown.enter)="toggleSearchBar()" />
1181
+ </div>
1182
+ </ng-template>
1183
+ </p-dialog>`, isInline: true, dependencies: [{ kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$2.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: AutoFocusModule }, { kind: "directive", type: i3$2.AutoFocus, selector: "[pAutoFocus]", inputs: ["pAutoFocus"] }] });
1184
+ }
1185
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSearch, decorators: [{
1186
+ type: Component,
1187
+ args: [{
1188
+ selector: '[app-search]',
1189
+ imports: [DialogModule, InputTextModule, AutoFocusModule],
1190
+ template: ` <p-dialog [(visible)]="searchBarActive" [breakpoints]="{ '992px': '75vw', '576px': '90vw' }" modal dismissableMask styleClass="w-1/2 search-container">
1191
+ <ng-template #headless>
1192
+ <div class="w-full">
1193
+ <i class="pi pi-search"></i>
1194
+ <input pInputText type="text" [pAutoFocus]="true" class="p-inputtext search-input" placeholder="Search" (keydown.enter)="toggleSearchBar()" />
1195
+ </div>
1196
+ </ng-template>
1197
+ </p-dialog>`
1198
+ }]
1199
+ }] });
1200
+
1201
+ class AppMenuitem {
1202
+ layoutService = inject(LayoutService);
1203
+ router = inject(Router);
1204
+ hoverExpandTimer = null;
1205
+ item = input(null, ...(ngDevMode ? [{ debugName: "item" }] : []));
1206
+ root = input(true, ...(ngDevMode ? [{ debugName: "root" }] : []));
1207
+ parentPath = input(null, ...(ngDevMode ? [{ debugName: "parentPath" }] : []));
1208
+ preventAutoActivate = input(false, ...(ngDevMode ? [{ debugName: "preventAutoActivate" }] : []));
1209
+ isDisabled = computed(() => this.item()?.disabled ?? false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
1210
+ isSlim = computed(() => this.layoutService.layoutConfig().menuMode === 'slim', ...(ngDevMode ? [{ debugName: "isSlim" }] : []));
1211
+ isHorizontal = computed(() => this.layoutService.layoutConfig().menuMode === 'horizontal', ...(ngDevMode ? [{ debugName: "isHorizontal" }] : []));
1212
+ isCompact = computed(() => this.layoutService.layoutConfig().menuMode === 'compact', ...(ngDevMode ? [{ debugName: "isCompact" }] : []));
1213
+ isRailCollapsed = computed(() => this.layoutService.isRail() && !this.layoutService.layoutState().sidebarExpanded, ...(ngDevMode ? [{ debugName: "isRailCollapsed" }] : []));
1214
+ isVisible = computed(() => this.item()?.visible !== false, ...(ngDevMode ? [{ debugName: "isVisible" }] : []));
1215
+ hasChildren = computed(() => !!this.item()?.items && (this.item()?.items?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "hasChildren" }] : []));
1216
+ hasCommand = computed(() => typeof this.item()?.command === 'function', ...(ngDevMode ? [{ debugName: "hasCommand" }] : []));
1217
+ hasRouterLink = computed(() => !!this.item()?.routerLink, ...(ngDevMode ? [{ debugName: "hasRouterLink" }] : []));
1218
+ fullPath = computed(() => {
1219
+ const itemPath = this.item()?.path;
1220
+ if (!itemPath)
1221
+ return this.parentPath();
1222
+ const parent = this.parentPath();
1223
+ if (parent && !itemPath.startsWith(parent)) {
1224
+ return parent + itemPath;
1225
+ }
1226
+ return itemPath;
1227
+ }, ...(ngDevMode ? [{ debugName: "fullPath" }] : []));
1228
+ menuHoverActive = computed(() => this.layoutService.layoutState().menuHoverActive, ...(ngDevMode ? [{ debugName: "menuHoverActive" }] : []));
1229
+ isActive = computed(() => {
1230
+ const activePath = this.layoutService.layoutState().activePath;
1231
+ if (this.item()?.path) {
1232
+ return activePath?.startsWith(this.fullPath() ?? '') ?? false;
1233
+ }
1234
+ return false;
1235
+ }, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
1236
+ isRouteWithin = computed(() => {
1237
+ this.layoutService.currentUrl();
1238
+ if (!this.root() || !this.hasChildren())
1239
+ return false;
1240
+ return this.hasMatchingChildRoute(this.item());
1241
+ }, ...(ngDevMode ? [{ debugName: "isRouteWithin" }] : []));
1242
+ initialized = signal(false, ...(ngDevMode ? [{ debugName: "initialized" }] : []));
1243
+ constructor() {
1244
+ effect(() => {
1245
+ this.updateActivePath();
1246
+ });
1247
+ }
1248
+ defaultMatchOptions = {
1249
+ paths: 'exact',
1250
+ queryParams: 'ignored',
1251
+ matrixParams: 'ignored',
1252
+ fragment: 'ignored'
1253
+ };
1254
+ itemMatchOptions(item) {
1255
+ return item?.routerLinkActiveOptions ?? this.defaultMatchOptions;
1256
+ }
1257
+ updateActivePath() {
1258
+ if (this.layoutService.hasOverlaySubmenu() && this.layoutService.isDesktop()) {
1259
+ return;
1260
+ }
1261
+ if (this.preventAutoActivate()) {
1262
+ return;
1263
+ }
1264
+ const item = this.item();
1265
+ const parentPath = this.parentPath();
1266
+ if (item?.routerLink && !item?.items) {
1267
+ const isRouteActive = this.router.isActive(item.routerLink[0], this.itemMatchOptions(item));
1268
+ if (isRouteActive && parentPath) {
1269
+ this.layoutService.layoutState.update((val) => ({
1270
+ ...val,
1271
+ activePath: parentPath
1272
+ }));
1273
+ }
1274
+ }
1275
+ }
1276
+ ngAfterViewInit() {
1277
+ setTimeout(() => {
1278
+ this.initialized.set(true);
1279
+ });
1280
+ }
1281
+ itemClick(event) {
1282
+ if (this.isDisabled()) {
1283
+ event.preventDefault();
1284
+ return;
1285
+ }
1286
+ if (this.hasCommand()) {
1287
+ this.item()?.command?.({ originalEvent: event, item: this.item() ?? undefined });
1288
+ }
1289
+ if (this.hasChildren()) {
1290
+ if (this.isActive()) {
1291
+ const deactivateHover = this.root() && this.layoutService.hasOverlaySubmenu() && this.layoutService.isDesktop();
1292
+ this.layoutService.layoutState.update((val) => ({
1293
+ ...val,
1294
+ activePath: this.parentPath(),
1295
+ menuHoverActive: deactivateHover ? false : val.menuHoverActive
1296
+ }));
1297
+ }
1298
+ else {
1299
+ this.layoutService.layoutState.update((val) => ({
1300
+ ...val,
1301
+ activePath: this.fullPath(),
1302
+ menuHoverActive: true
1303
+ }));
1304
+ }
1305
+ }
1306
+ else {
1307
+ this.layoutService.layoutState.update((val) => ({
1308
+ ...val,
1309
+ overlayMenuActive: false,
1310
+ mobileMenuActive: false,
1311
+ menuHoverActive: false
1312
+ }));
1313
+ if (this.layoutService.hasOverlaySubmenu() && this.layoutService.isDesktop()) {
1314
+ this.layoutService.layoutState.update((val) => ({
1315
+ ...val,
1316
+ activePath: null
1317
+ }));
1318
+ }
1319
+ }
1320
+ if (this.layoutService.isDesktop() && this.layoutService.isRail() && !this.layoutService.layoutState().sidebarExpanded) {
1321
+ this.layoutService.toggleSidebarPin();
1322
+ }
1323
+ }
1324
+ onMouseEnter() {
1325
+ if (this.layoutService.isDesktop() && this.root() && this.hasChildren() && this.menuHoverActive() && !this.isActive()) {
1326
+ this.layoutService.layoutState.update((val) => ({
1327
+ ...val,
1328
+ activePath: this.fullPath(),
1329
+ menuHoverActive: true
1330
+ }));
1331
+ }
1332
+ }
1333
+ onMouseLeave() { }
1334
+ ngOnDestroy() {
1335
+ this.clearHoverTimer();
1336
+ }
1337
+ scheduleRailExpand() {
1338
+ this.clearHoverTimer();
1339
+ this.hoverExpandTimer = setTimeout(() => {
1340
+ this.layoutService.layoutState.update((val) => ({
1341
+ ...val,
1342
+ sidebarExpanded: true
1343
+ }));
1344
+ }, 500);
1345
+ }
1346
+ clearHoverTimer() {
1347
+ if (this.hoverExpandTimer !== null) {
1348
+ clearTimeout(this.hoverExpandTimer);
1349
+ this.hoverExpandTimer = null;
1350
+ }
1351
+ }
1352
+ hasMatchingChildRoute(item) {
1353
+ if (!item)
1354
+ return false;
1355
+ if (item.routerLink) {
1356
+ return this.router.isActive(item.routerLink[0], this.itemMatchOptions(item));
1357
+ }
1358
+ return item.items?.some((child) => this.hasMatchingChildRoute(child)) ?? false;
1359
+ }
1360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppMenuitem, deps: [], target: i0.ɵɵFactoryTarget.Component });
1361
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AppMenuitem, isStandalone: true, selector: "[app-menuitem]", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, root: { classPropertyName: "root", publicName: "root", isSignal: true, isRequired: false, transformFunction: null }, parentPath: { classPropertyName: "parentPath", publicName: "parentPath", isSignal: true, isRequired: false, transformFunction: null }, preventAutoActivate: { classPropertyName: "preventAutoActivate", publicName: "preventAutoActivate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.active-menuitem": "isActive()", "class.layout-root-menuitem": "root()", "class.route-active-within": "isRouteWithin()" } }, ngImport: i0, template: `
1362
+ @if (root() && isVisible() && hasChildren()) {
1363
+ <div class="layout-menuitem-root-text">{{ item()?.label }}</div>
1364
+ }
1365
+ @if ((!hasRouterLink() || hasChildren()) && isVisible()) {
1366
+ <a
1367
+ [attr.href]="item()?.url"
1368
+ (click)="itemClick($event)"
1369
+ (mouseenter)="onMouseEnter()"
1370
+ (mouseleave)="onMouseLeave()"
1371
+ [ngClass]="item()?.class"
1372
+ [attr.target]="item()?.target"
1373
+ tabindex="0"
1374
+ pRipple
1375
+ [pTooltip]="item()?.label"
1376
+ [tooltipDisabled]="!((isCompact() || isRailCollapsed()) && !isActive() && root())"
1377
+ >
1378
+ <i [ngClass]="item()?.icon" class="layout-menuitem-icon"></i>
1379
+ <span class="layout-menuitem-text label-small text-inherit">{{ item()?.label }}</span>
1380
+ @if (hasChildren()) {
1381
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1382
+ }
1383
+ </a>
1384
+ }
1385
+ @if (hasRouterLink() && !hasChildren() && isVisible()) {
1386
+ <a
1387
+ (click)="itemClick($event)"
1388
+ (mouseenter)="onMouseEnter()"
1389
+ (mouseleave)="onMouseLeave()"
1390
+ [ngClass]="item()?.class"
1391
+ [routerLink]="item()?.routerLink"
1392
+ routerLinkActive="active-route"
1393
+ [routerLinkActiveOptions]="item()?.routerLinkActiveOptions || { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }"
1394
+ [fragment]="item()?.fragment"
1395
+ [queryParamsHandling]="item()?.queryParamsHandling"
1396
+ [preserveFragment]="item()?.preserveFragment"
1397
+ [skipLocationChange]="item()?.skipLocationChange"
1398
+ [replaceUrl]="item()?.replaceUrl"
1399
+ [state]="item()?.state"
1400
+ [queryParams]="item()?.queryParams"
1401
+ [attr.target]="item()?.target"
1402
+ tabindex="0"
1403
+ [pTooltip]="item()?.label"
1404
+ [tooltipDisabled]="!((isCompact() || isRailCollapsed()) && !isActive() && root())"
1405
+ >
1406
+ <i [ngClass]="item()?.icon" class="layout-menuitem-icon"></i>
1407
+ <span class="layout-menuitem-text label-small text-inherit">{{ item()?.label }}</span>
1408
+ @if (hasChildren()) {
1409
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1410
+ }
1411
+ </a>
1412
+ }
1413
+ @if (hasChildren() && isVisible()) {
1414
+ <ul [animate.enter]="initialized() ? 'p-submenu-enter' : null" [animate.leave]="'p-submenu-leave'" [class.layout-root-submenulist]="root()">
1415
+ @for (child of item()?.items; track child?.label) {
1416
+ <li app-menuitem [item]="child" [root]="false" [parentPath]="fullPath()" [preventAutoActivate]="preventAutoActivate() || !!item()?.preventAutoActivate" [class]="child?.['badgeClass']"></li>
1417
+ }
1418
+ </ul>
1419
+ }
1420
+ `, isInline: true, styles: [".p-submenu-enter{animation:p-animate-submenu-expand .15s cubic-bezier(.4,0,.2,1) forwards;overflow:hidden}.p-submenu-leave{animation:p-animate-submenu-collapse .15s cubic-bezier(.4,0,.2,1) forwards;overflow:hidden}@keyframes p-animate-submenu-expand{0%{max-height:0}to{max-height:1000px}}@keyframes p-animate-submenu-collapse{0%{max-height:1000px}to{max-height:0}}\n"], dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "root", "parentPath", "preventAutoActivate"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i4$2.Ripple, selector: "[pRipple]" }] });
1421
+ }
1422
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppMenuitem, decorators: [{
1423
+ type: Component,
1424
+ args: [{ selector: '[app-menuitem]', imports: [CommonModule, RouterModule, TooltipModule, RippleModule], template: `
1425
+ @if (root() && isVisible() && hasChildren()) {
1426
+ <div class="layout-menuitem-root-text">{{ item()?.label }}</div>
1427
+ }
1428
+ @if ((!hasRouterLink() || hasChildren()) && isVisible()) {
1429
+ <a
1430
+ [attr.href]="item()?.url"
1431
+ (click)="itemClick($event)"
1432
+ (mouseenter)="onMouseEnter()"
1433
+ (mouseleave)="onMouseLeave()"
1434
+ [ngClass]="item()?.class"
1435
+ [attr.target]="item()?.target"
1436
+ tabindex="0"
1437
+ pRipple
1438
+ [pTooltip]="item()?.label"
1439
+ [tooltipDisabled]="!((isCompact() || isRailCollapsed()) && !isActive() && root())"
1440
+ >
1441
+ <i [ngClass]="item()?.icon" class="layout-menuitem-icon"></i>
1442
+ <span class="layout-menuitem-text label-small text-inherit">{{ item()?.label }}</span>
1443
+ @if (hasChildren()) {
1444
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1445
+ }
1446
+ </a>
1447
+ }
1448
+ @if (hasRouterLink() && !hasChildren() && isVisible()) {
1449
+ <a
1450
+ (click)="itemClick($event)"
1451
+ (mouseenter)="onMouseEnter()"
1452
+ (mouseleave)="onMouseLeave()"
1453
+ [ngClass]="item()?.class"
1454
+ [routerLink]="item()?.routerLink"
1455
+ routerLinkActive="active-route"
1456
+ [routerLinkActiveOptions]="item()?.routerLinkActiveOptions || { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }"
1457
+ [fragment]="item()?.fragment"
1458
+ [queryParamsHandling]="item()?.queryParamsHandling"
1459
+ [preserveFragment]="item()?.preserveFragment"
1460
+ [skipLocationChange]="item()?.skipLocationChange"
1461
+ [replaceUrl]="item()?.replaceUrl"
1462
+ [state]="item()?.state"
1463
+ [queryParams]="item()?.queryParams"
1464
+ [attr.target]="item()?.target"
1465
+ tabindex="0"
1466
+ [pTooltip]="item()?.label"
1467
+ [tooltipDisabled]="!((isCompact() || isRailCollapsed()) && !isActive() && root())"
1468
+ >
1469
+ <i [ngClass]="item()?.icon" class="layout-menuitem-icon"></i>
1470
+ <span class="layout-menuitem-text label-small text-inherit">{{ item()?.label }}</span>
1471
+ @if (hasChildren()) {
1472
+ <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1473
+ }
1474
+ </a>
1475
+ }
1476
+ @if (hasChildren() && isVisible()) {
1477
+ <ul [animate.enter]="initialized() ? 'p-submenu-enter' : null" [animate.leave]="'p-submenu-leave'" [class.layout-root-submenulist]="root()">
1478
+ @for (child of item()?.items; track child?.label) {
1479
+ <li app-menuitem [item]="child" [root]="false" [parentPath]="fullPath()" [preventAutoActivate]="preventAutoActivate() || !!item()?.preventAutoActivate" [class]="child?.['badgeClass']"></li>
1480
+ }
1481
+ </ul>
1482
+ }
1483
+ `, host: {
1484
+ '[class.active-menuitem]': 'isActive()',
1485
+ '[class.layout-root-menuitem]': 'root()',
1486
+ '[class.route-active-within]': 'isRouteWithin()'
1487
+ }, styles: [".p-submenu-enter{animation:p-animate-submenu-expand .15s cubic-bezier(.4,0,.2,1) forwards;overflow:hidden}.p-submenu-leave{animation:p-animate-submenu-collapse .15s cubic-bezier(.4,0,.2,1) forwards;overflow:hidden}@keyframes p-animate-submenu-expand{0%{max-height:0}to{max-height:1000px}}@keyframes p-animate-submenu-collapse{0%{max-height:1000px}to{max-height:0}}\n"] }]
1488
+ }], ctorParameters: () => [], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: false }] }], root: [{ type: i0.Input, args: [{ isSignal: true, alias: "root", required: false }] }], parentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentPath", required: false }] }], preventAutoActivate: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventAutoActivate", required: false }] }] } });
1489
+
1490
+ class AppMenu {
1491
+ /** Injected menu tree from the host application. */
1492
+ menuItems = inject(MENU_MODEL);
1493
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
1494
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AppMenu, isStandalone: true, selector: "[app-menu]", ngImport: i0, template: `<ul class="layout-menu">
1495
+ @for (item of menuItems; track $index) {
1496
+ @if (!item.separator) {
1497
+ <li app-menuitem [item]="item" [root]="true"></li>
1498
+ } @else {
1499
+ <li class="menu-separator"></li>
1500
+ }
1501
+ }
1502
+ </ul> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "root", "parentPath", "preventAutoActivate"] }, { kind: "ngmodule", type: RouterModule }] });
1503
+ }
1504
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppMenu, decorators: [{
1505
+ type: Component,
1506
+ args: [{
1507
+ selector: '[app-menu]',
1508
+ imports: [CommonModule, AppMenuitem, RouterModule],
1509
+ template: `<ul class="layout-menu">
1510
+ @for (item of menuItems; track $index) {
1511
+ @if (!item.separator) {
1512
+ <li app-menuitem [item]="item" [root]="true"></li>
1513
+ } @else {
1514
+ <li class="menu-separator"></li>
1515
+ }
1516
+ }
1517
+ </ul> `
1518
+ }]
1519
+ }] });
1520
+
1521
+ class AppTopbar {
1522
+ layoutService = inject(LayoutService);
1523
+ mobileLogoConfig = inject(TOPBAR_MOBILE_LOGO);
1524
+ mobileLogo = computed(() => this.layoutService.isDarkTheme() ? this.mobileLogoConfig.dark : this.mobileLogoConfig.light, ...(ngDevMode ? [{ debugName: "mobileLogo" }] : []));
1525
+ desktopLogo = computed(() => this.layoutService.isDarkTheme() ? this.mobileLogoConfig.dark : this.mobileLogoConfig.light, ...(ngDevMode ? [{ debugName: "desktopLogo" }] : []));
1526
+ isDarkTheme = computed(() => this.layoutService.isDarkTheme(), ...(ngDevMode ? [{ debugName: "isDarkTheme" }] : []));
1527
+ isSidebarPinned = computed(() => this.layoutService.isSidebarPinned(), ...(ngDevMode ? [{ debugName: "isSidebarPinned" }] : []));
1528
+ searchActive = signal(false, ...(ngDevMode ? [{ debugName: "searchActive" }] : []));
1529
+ profileMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "profileMenuOpen" }] : []));
1530
+ shouldFocusSearch = false;
1531
+ menuButton;
1532
+ searchInput;
1533
+ profileItem;
1534
+ profilePanel;
1535
+ notificationsBars = signal([
1536
+ {
1537
+ id: 'inbox',
1538
+ label: 'Inbox',
1539
+ badge: '2'
1540
+ },
1541
+ {
1542
+ id: 'general',
1543
+ label: 'General'
1544
+ },
1545
+ {
1546
+ id: 'archived',
1547
+ label: 'Archived'
1548
+ }
1549
+ ], ...(ngDevMode ? [{ debugName: "notificationsBars" }] : []));
1550
+ notifications = signal([
1551
+ {
1552
+ id: 'inbox',
1553
+ data: [
1554
+ {
1555
+ image: 'demo/images/avatar/avatar-square-m-2.jpg',
1556
+ name: 'Michael Lee',
1557
+ description: 'You have a new message from the support team regarding your recent inquiry.',
1558
+ time: '1 hour ago',
1559
+ new: true
1560
+ },
1561
+ {
1562
+ image: 'demo/images/avatar/avatar-square-f-1.jpg',
1563
+ name: 'Alice Johnson',
1564
+ description: 'Your report has been successfully submitted and is under review.',
1565
+ time: '10 minutes ago',
1566
+ new: true
1567
+ },
1568
+ {
1569
+ image: 'demo/images/avatar/avatar-square-f-2.jpg',
1570
+ name: 'Emily Davis',
1571
+ description: 'The project deadline has been updated to September 30th. Please check the details.',
1572
+ time: 'Yesterday at 4:35 PM',
1573
+ new: false
1574
+ }
1575
+ ]
1576
+ },
1577
+ {
1578
+ id: 'general',
1579
+ data: [
1580
+ {
1581
+ image: 'demo/images/avatar/avatar-square-f-1.jpg',
1582
+ name: 'Alice Johnson',
1583
+ description: 'Reminder: Your subscription is about to expire in 3 days. Renew now to avoid interruption.',
1584
+ time: '30 minutes ago',
1585
+ new: true
1586
+ },
1587
+ {
1588
+ image: 'demo/images/avatar/avatar-square-m-2.jpg',
1589
+ name: 'Michael Lee',
1590
+ description: 'The server maintenance has been completed successfully. No further downtime is expected.',
1591
+ time: 'Yesterday at 2:15 PM',
1592
+ new: false
1593
+ }
1594
+ ]
1595
+ },
1596
+ {
1597
+ id: 'archived',
1598
+ data: [
1599
+ {
1600
+ image: 'demo/images/avatar/avatar-square-m-1.jpg',
1601
+ name: 'Lucas Brown',
1602
+ description: 'Your appointment with Dr. Anderson has been confirmed for October 12th at 10:00 AM.',
1603
+ time: '1 week ago',
1604
+ new: true
1605
+ },
1606
+ {
1607
+ image: 'demo/images/avatar/avatar-square-f-2.jpg',
1608
+ name: 'Emily Davis',
1609
+ description: 'The document you uploaded has been successfully archived for future reference.',
1610
+ time: '2 weeks ago',
1611
+ new: false
1612
+ }
1613
+ ]
1614
+ }
1615
+ ], ...(ngDevMode ? [{ debugName: "notifications" }] : []));
1616
+ languages = signal([
1617
+ { code: 'en', label: 'English', flag: '🇬🇧' },
1618
+ { code: 'fr', label: 'French', flag: '🇫🇷' },
1619
+ { code: 'es', label: 'Spanish', flag: '🇪🇸' }
1620
+ ], ...(ngDevMode ? [{ debugName: "languages" }] : []));
1621
+ selectedLanguage = signal('en', ...(ngDevMode ? [{ debugName: "selectedLanguage" }] : []));
1622
+ selectedNotificationBar = model(this.notificationsBars()[0].id ?? 'inbox', ...(ngDevMode ? [{ debugName: "selectedNotificationBar" }] : []));
1623
+ selectedNotificationsBarData = computed(() => this.notifications().find((f) => f.id === this.selectedNotificationBar()).data, ...(ngDevMode ? [{ debugName: "selectedNotificationsBarData" }] : []));
1624
+ onMenuButtonClick() {
1625
+ this.layoutService.toggleMenu();
1626
+ }
1627
+ toggleSidebarPin() {
1628
+ this.layoutService.toggleSidebarPin();
1629
+ }
1630
+ toggleDarkMode() {
1631
+ this.layoutService.layoutConfig.update((state) => ({
1632
+ ...state,
1633
+ darkTheme: !state.darkTheme
1634
+ }));
1635
+ }
1636
+ showRightMenu() {
1637
+ this.layoutService.toggleRightMenu();
1638
+ }
1639
+ onConfigButtonClick() {
1640
+ this.layoutService.toggleConfigSidebar();
1641
+ }
1642
+ selectLanguage(code) {
1643
+ this.selectedLanguage.set(code);
1644
+ }
1645
+ toggleProfileMenu(event) {
1646
+ event.stopPropagation();
1647
+ this.profileMenuOpen.update((open) => !open);
1648
+ }
1649
+ onDocumentClick(event) {
1650
+ if (!this.profileMenuOpen())
1651
+ return;
1652
+ const target = event.target;
1653
+ const insideProfile = this.profileItem?.nativeElement?.contains(target);
1654
+ if (!insideProfile) {
1655
+ this.profileMenuOpen.set(false);
1656
+ }
1657
+ }
1658
+ openSearch() {
1659
+ this.searchActive.set(true);
1660
+ this.shouldFocusSearch = true;
1661
+ }
1662
+ closeSearch() {
1663
+ this.searchActive.set(false);
1664
+ }
1665
+ ngAfterViewChecked() {
1666
+ if (this.shouldFocusSearch && this.searchInput) {
1667
+ this.searchInput.nativeElement.focus();
1668
+ this.shouldFocusSearch = false;
1669
+ }
1670
+ }
1671
+ toggleSearchBar() {
1672
+ this.layoutService.toggleSearchBar();
1673
+ }
1674
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppTopbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
1675
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AppTopbar, isStandalone: true, selector: "[app-topbar]", inputs: { selectedNotificationBar: { classPropertyName: "selectedNotificationBar", publicName: "selectedNotificationBar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedNotificationBar: "selectedNotificationBarChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "menuButton", first: true, predicate: ["menubutton"], descendants: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }, { propertyName: "profileItem", first: true, predicate: ["profileItem"], descendants: true }, { propertyName: "profilePanel", first: true, predicate: ["profilePanel"], descendants: true }], ngImport: i0, template: `<div class="layout-topbar">
1676
+ <button type="button" class="mobile-menu-button" aria-label="Toggle navigation menu" (click)="onMenuButtonClick()">
1677
+ <i class="pi pi-bars"></i>
1678
+ </button>
1679
+ <div class="topbar-left">
1680
+ <button
1681
+ type="button"
1682
+ class="topbar-menu-toggle"
1683
+ [class.active]="isSidebarPinned()"
1684
+ [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
1685
+ (click)="toggleSidebarPin()"
1686
+ >
1687
+ <i class="pi pi-bars"></i>
1688
+ </button>
1689
+ <a class="topbar-logo" [routerLink]="['/']">
1690
+ <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
1691
+ </a>
1692
+ <span class="topbar-logo-separator"></span>
1693
+ <div app-breadcrumb></div>
1694
+ @if (searchActive()) {
1695
+ <div class="flex items-center gap-2 ml-auto">
1696
+ <p-iconfield class="w-48 sm:w-80">
1697
+ <p-inputicon styleClass="pi pi-search" />
1698
+ <input #searchInput type="text" pInputText placeholder="Search..." aria-label="Search" class="w-full !py-2 !text-sm" (keydown.escape)="closeSearch()" />
1699
+ </p-iconfield>
1700
+ <button type="button" class="flex items-center justify-center w-8 h-8 rounded-md cursor-pointer hover:bg-emphasis transition-colors" aria-label="Close search" (click)="closeSearch()">
1701
+ <i class="pi pi-times text-sm"></i>
1702
+ </button>
1703
+ </div>
1704
+ }
1705
+ </div>
1706
+
1707
+ <a class="mobile-logo" [routerLink]="['/landing']">
1708
+ <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
1709
+ </a>
1710
+
1711
+ <div class="topbar-right">
1712
+ <ul class="topbar-menu">
1713
+ <li class="right-sidebar-item" [class.hidden]="searchActive()">
1714
+ <a class="right-sidebar-button" aria-label="Open search" (click)="openSearch()">
1715
+ <i class="pi pi-search"></i>
1716
+ </a>
1717
+ </li>
1718
+ <li class="right-sidebar-item" [class.hidden]="searchActive()">
1719
+ <a
1720
+ class="right-sidebar-button"
1721
+ [attr.aria-label]="isDarkTheme() ? 'Switch to light mode' : 'Switch to dark mode'"
1722
+ (click)="toggleDarkMode()"
1723
+ >
1724
+ <i [class]="isDarkTheme() ? 'pi pi-sun' : 'pi pi-moon'"></i>
1725
+ </a>
1726
+ </li>
1727
+ <li class="right-sidebar-item static sm:relative z-50">
1728
+ <a class="right-sidebar-button" aria-label="Notifications" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveActiveClass="animate-fadeout" leaveToClass="hidden" [hideOnOutsideClick]="true">
1729
+ <span class="w-2 h-2 rounded-full bg-red-500 absolute top-2 right-2.5"></span>
1730
+ <i class="pi pi-bell"></i>
1731
+ </a>
1732
+ <div
1733
+ class="list-none m-0 rounded-2xl border border-surface fixed sm:absolute bg-surface-0 dark:bg-surface-900 overflow-hidden hidden origin-top w-[calc(100vw-2rem)] sm:w-88 mt-2 z-50 top-auto left-4 sm:left-auto sm:right-0 shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
1734
+ >
1735
+ <div class="p-4 flex items-center justify-between border-b border-surface">
1736
+ <span class="label-small text-surface-950 dark:text-surface-0">Notifications</span>
1737
+ <button pRipple class="py-1 px-2 text-surface-950 dark:text-surface-0 label-x-small hover:bg-emphasis border border-surface rounded-lg shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05)] transition-all">Mark all as read</button>
1738
+ </div>
1739
+ <div class="flex items-center border-b border-surface">
1740
+ @for (item of notificationsBars(); track item.id; let i = $index) {
1741
+ <button
1742
+ [ngClass]="{ 'border-surface-950 dark:border-surface-0': selectedNotificationBar() === item.id, 'border-transparent': selectedNotificationBar() !== item.id }"
1743
+ class="px-3.5 py-2 inline-flex items-center border-b gap-2"
1744
+ (click)="selectedNotificationBar.set(item.id)"
1745
+ >
1746
+ <span [ngClass]="{ 'text-surface-950 dark:text-surface-0': selectedNotificationBar() === item.id }" class="label-small">{{ item.label }}</span>
1747
+ <p-badge *ngIf="item?.badge" [value]="item.badge" severity="success" size="small" class="rounded-md!" />
1748
+ </button>
1749
+ }
1750
+ </div>
1751
+ <ul class="flex flex-col divide-y divide-(--surface-border) max-h-80 overflow-auto">
1752
+ @for (item of selectedNotificationsBarData(); track item.name; let i = $index) {
1753
+ <li>
1754
+ <div class="flex items-center gap-3 px-4 sm:px-6 py-3.5 cursor-pointer hover:bg-emphasis transition-all">
1755
+ <p-overlay-badge value="" severity="danger" class="inline-flex">
1756
+ <p-avatar size="large">
1757
+ <img [src]="item.image" [alt]="item.name" class="rounded-lg" />
1758
+ </p-avatar>
1759
+ </p-overlay-badge>
1760
+ <div class="flex items-center gap-3">
1761
+ <div class="flex flex-col">
1762
+ <span class="label-small text-left text-surface-950 dark:text-surface-0">{{ item.name }}</span>
1763
+ <span class="label-xsmall text-left line-clamp-1">{{ item.description }}</span>
1764
+ <span class="label-xsmall text-left">{{ item.time }}</span>
1765
+ </div>
1766
+ <p-badge *ngIf="item.new" value="" severity="success" />
1767
+ </div>
1768
+ </div>
1769
+ <span *ngIf="i !== notifications().length - 1"></span>
1770
+ </li>
1771
+ }
1772
+ </ul>
1773
+ </div>
1774
+ </li>
1775
+ <li class="right-sidebar-item static sm:relative">
1776
+ <a class="right-sidebar-button relative z-50" aria-label="Change language" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveActiveClass="animate-fadeout" leaveToClass="hidden" [hideOnOutsideClick]="true">
1777
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1778
+ <path d="m5 8 6 6"/>
1779
+ <path d="m4 14 6-6 2-3"/>
1780
+ <path d="M2 5h12"/>
1781
+ <path d="M7 2h1"/>
1782
+ <path d="m22 22-5-10-5 10"/>
1783
+ <path d="M14 18h6"/>
1784
+ </svg>
1785
+ </a>
1786
+ <div
1787
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 hidden origin-top w-44 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
1788
+ >
1789
+ <ul class="flex flex-col gap-1">
1790
+ @for (lang of languages(); track lang.code) {
1791
+ <li>
1792
+ <a
1793
+ class="label-small dark:text-surface-400 flex gap-2.5 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer"
1794
+ [class.text-surface-950]="selectedLanguage() === lang.code"
1795
+ [class.dark:text-surface-0]="selectedLanguage() === lang.code"
1796
+ [class.font-semibold]="selectedLanguage() === lang.code"
1797
+ (click)="selectLanguage(lang.code)"
1798
+ >
1799
+ <span class="text-lg">{{ lang.flag }}</span>
1800
+ <span>{{ lang.label }}</span>
1801
+ </a>
1802
+ </li>
1803
+ }
1804
+ </ul>
1805
+ </div>
1806
+ </li>
1807
+ <li class="profile-item static sm:relative" #profileItem>
1808
+ <a class="right-sidebar-button relative z-50" aria-label="User profile menu" (click)="toggleProfileMenu($event)">
1809
+ <p-avatar icon="pi pi-user" styleClass="w-10! h-10!" />
1810
+ </a>
1811
+ <div
1812
+ #profilePanel
1813
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 origin-top w-52 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
1814
+ [class.hidden]="!profileMenuOpen()"
1815
+ [class.animate-scalein]="profileMenuOpen()"
1816
+ >
1817
+ <ul class="flex flex-col gap-1">
1818
+ <div class="mobile-profile-actions">
1819
+ <li>
1820
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false); openSearch()">
1821
+ <i class="pi pi-search"></i>
1822
+ <span>Search</span>
1823
+ </a>
1824
+ </li>
1825
+ <li>
1826
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false); toggleDarkMode()">
1827
+ <i [class]="isDarkTheme() ? 'pi pi-sun' : 'pi pi-moon'"></i>
1828
+ <span>{{ isDarkTheme() ? 'Light Mode' : 'Dark Mode' }}</span>
1829
+ </a>
1830
+ </li>
1831
+ <li>
1832
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer relative" (click)="profileMenuOpen.set(false)">
1833
+ <i class="pi pi-bell"></i>
1834
+ <span>Notifications</span>
1835
+ <span class="w-2 h-2 rounded-full bg-red-500 ml-auto"></span>
1836
+ </a>
1837
+ </li>
1838
+ <li class="border-b border-surface pb-1 mb-1">
1839
+ <span class="label-xsmall px-2.5 py-1 text-surface-400">Language</span>
1840
+ @for (lang of languages(); track lang.code) {
1841
+ <a
1842
+ class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer"
1843
+ [class.text-surface-950]="selectedLanguage() === lang.code"
1844
+ [class.dark:text-surface-0]="selectedLanguage() === lang.code"
1845
+ [class.font-semibold]="selectedLanguage() === lang.code"
1846
+ (click)="profileMenuOpen.set(false); selectLanguage(lang.code)"
1847
+ >
1848
+ <span class="text-lg">{{ lang.flag }}</span>
1849
+ <span>{{ lang.label }}</span>
1850
+ @if (selectedLanguage() === lang.code) {
1851
+ <i class="pi pi-check ml-auto text-xs"></i>
1852
+ }
1853
+ </a>
1854
+ }
1855
+ </li>
1856
+ </div>
1857
+ <li>
1858
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
1859
+ <i class="pi pi-user"></i>
1860
+ <span>Profile</span>
1861
+ </a>
1862
+ </li>
1863
+ <li>
1864
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
1865
+ <i class="pi pi-cog"></i>
1866
+ <span>Settings</span>
1867
+ </a>
1868
+ </li>
1869
+ <li>
1870
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
1871
+ <i class="pi pi-calendar"></i>
1872
+ <span>Calendar</span>
1873
+ </a>
1874
+ </li>
1875
+ <li>
1876
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
1877
+ <i class="pi pi-inbox"></i>
1878
+ <span>Inbox</span>
1879
+ </a>
1880
+ </li>
1881
+ <li class="border-t border-surface mt-1 pt-1">
1882
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
1883
+ <i class="pi pi-power-off"></i>
1884
+ <span>Log out</span>
1885
+ </a>
1886
+ </li>
1887
+ </ul>
1888
+ </div>
1889
+ </li>
1890
+ </ul>
1891
+ </div>
1892
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i3$4.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppBreadcrumb, selector: "[app-breadcrumb]" }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i5$2.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i6$1.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i4$2.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i8.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "ngmodule", type: OverlayBadgeModule }, { kind: "component", type: i9.OverlayBadge, selector: "p-overlayBadge, p-overlay-badge, p-overlaybadge", inputs: ["styleClass", "style", "badgeSize", "severity", "value", "badgeDisabled", "size"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i10.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }] });
1893
+ }
1894
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppTopbar, decorators: [{
1895
+ type: Component,
1896
+ args: [{
1897
+ selector: '[app-topbar]',
1898
+ imports: [RouterModule, CommonModule, StyleClassModule, AppBreadcrumb, InputTextModule, ButtonModule, IconFieldModule, InputIconModule, RippleModule, BadgeModule, OverlayBadgeModule, AvatarModule],
1899
+ template: `<div class="layout-topbar">
1900
+ <button type="button" class="mobile-menu-button" aria-label="Toggle navigation menu" (click)="onMenuButtonClick()">
1901
+ <i class="pi pi-bars"></i>
1902
+ </button>
1903
+ <div class="topbar-left">
1904
+ <button
1905
+ type="button"
1906
+ class="topbar-menu-toggle"
1907
+ [class.active]="isSidebarPinned()"
1908
+ [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
1909
+ (click)="toggleSidebarPin()"
1910
+ >
1911
+ <i class="pi pi-bars"></i>
1912
+ </button>
1913
+ <a class="topbar-logo" [routerLink]="['/']">
1914
+ <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
1915
+ </a>
1916
+ <span class="topbar-logo-separator"></span>
1917
+ <div app-breadcrumb></div>
1918
+ @if (searchActive()) {
1919
+ <div class="flex items-center gap-2 ml-auto">
1920
+ <p-iconfield class="w-48 sm:w-80">
1921
+ <p-inputicon styleClass="pi pi-search" />
1922
+ <input #searchInput type="text" pInputText placeholder="Search..." aria-label="Search" class="w-full !py-2 !text-sm" (keydown.escape)="closeSearch()" />
1923
+ </p-iconfield>
1924
+ <button type="button" class="flex items-center justify-center w-8 h-8 rounded-md cursor-pointer hover:bg-emphasis transition-colors" aria-label="Close search" (click)="closeSearch()">
1925
+ <i class="pi pi-times text-sm"></i>
1926
+ </button>
1927
+ </div>
1928
+ }
1929
+ </div>
1930
+
1931
+ <a class="mobile-logo" [routerLink]="['/landing']">
1932
+ <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
1933
+ </a>
1934
+
1935
+ <div class="topbar-right">
1936
+ <ul class="topbar-menu">
1937
+ <li class="right-sidebar-item" [class.hidden]="searchActive()">
1938
+ <a class="right-sidebar-button" aria-label="Open search" (click)="openSearch()">
1939
+ <i class="pi pi-search"></i>
1940
+ </a>
1941
+ </li>
1942
+ <li class="right-sidebar-item" [class.hidden]="searchActive()">
1943
+ <a
1944
+ class="right-sidebar-button"
1945
+ [attr.aria-label]="isDarkTheme() ? 'Switch to light mode' : 'Switch to dark mode'"
1946
+ (click)="toggleDarkMode()"
1947
+ >
1948
+ <i [class]="isDarkTheme() ? 'pi pi-sun' : 'pi pi-moon'"></i>
1949
+ </a>
1950
+ </li>
1951
+ <li class="right-sidebar-item static sm:relative z-50">
1952
+ <a class="right-sidebar-button" aria-label="Notifications" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveActiveClass="animate-fadeout" leaveToClass="hidden" [hideOnOutsideClick]="true">
1953
+ <span class="w-2 h-2 rounded-full bg-red-500 absolute top-2 right-2.5"></span>
1954
+ <i class="pi pi-bell"></i>
1955
+ </a>
1956
+ <div
1957
+ class="list-none m-0 rounded-2xl border border-surface fixed sm:absolute bg-surface-0 dark:bg-surface-900 overflow-hidden hidden origin-top w-[calc(100vw-2rem)] sm:w-88 mt-2 z-50 top-auto left-4 sm:left-auto sm:right-0 shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
1958
+ >
1959
+ <div class="p-4 flex items-center justify-between border-b border-surface">
1960
+ <span class="label-small text-surface-950 dark:text-surface-0">Notifications</span>
1961
+ <button pRipple class="py-1 px-2 text-surface-950 dark:text-surface-0 label-x-small hover:bg-emphasis border border-surface rounded-lg shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05)] transition-all">Mark all as read</button>
1962
+ </div>
1963
+ <div class="flex items-center border-b border-surface">
1964
+ @for (item of notificationsBars(); track item.id; let i = $index) {
1965
+ <button
1966
+ [ngClass]="{ 'border-surface-950 dark:border-surface-0': selectedNotificationBar() === item.id, 'border-transparent': selectedNotificationBar() !== item.id }"
1967
+ class="px-3.5 py-2 inline-flex items-center border-b gap-2"
1968
+ (click)="selectedNotificationBar.set(item.id)"
1969
+ >
1970
+ <span [ngClass]="{ 'text-surface-950 dark:text-surface-0': selectedNotificationBar() === item.id }" class="label-small">{{ item.label }}</span>
1971
+ <p-badge *ngIf="item?.badge" [value]="item.badge" severity="success" size="small" class="rounded-md!" />
1972
+ </button>
1973
+ }
1974
+ </div>
1975
+ <ul class="flex flex-col divide-y divide-(--surface-border) max-h-80 overflow-auto">
1976
+ @for (item of selectedNotificationsBarData(); track item.name; let i = $index) {
1977
+ <li>
1978
+ <div class="flex items-center gap-3 px-4 sm:px-6 py-3.5 cursor-pointer hover:bg-emphasis transition-all">
1979
+ <p-overlay-badge value="" severity="danger" class="inline-flex">
1980
+ <p-avatar size="large">
1981
+ <img [src]="item.image" [alt]="item.name" class="rounded-lg" />
1982
+ </p-avatar>
1983
+ </p-overlay-badge>
1984
+ <div class="flex items-center gap-3">
1985
+ <div class="flex flex-col">
1986
+ <span class="label-small text-left text-surface-950 dark:text-surface-0">{{ item.name }}</span>
1987
+ <span class="label-xsmall text-left line-clamp-1">{{ item.description }}</span>
1988
+ <span class="label-xsmall text-left">{{ item.time }}</span>
1989
+ </div>
1990
+ <p-badge *ngIf="item.new" value="" severity="success" />
1991
+ </div>
1992
+ </div>
1993
+ <span *ngIf="i !== notifications().length - 1"></span>
1994
+ </li>
1995
+ }
1996
+ </ul>
1997
+ </div>
1998
+ </li>
1999
+ <li class="right-sidebar-item static sm:relative">
2000
+ <a class="right-sidebar-button relative z-50" aria-label="Change language" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveActiveClass="animate-fadeout" leaveToClass="hidden" [hideOnOutsideClick]="true">
2001
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
2002
+ <path d="m5 8 6 6"/>
2003
+ <path d="m4 14 6-6 2-3"/>
2004
+ <path d="M2 5h12"/>
2005
+ <path d="M7 2h1"/>
2006
+ <path d="m22 22-5-10-5 10"/>
2007
+ <path d="M14 18h6"/>
2008
+ </svg>
2009
+ </a>
2010
+ <div
2011
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 hidden origin-top w-44 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
2012
+ >
2013
+ <ul class="flex flex-col gap-1">
2014
+ @for (lang of languages(); track lang.code) {
2015
+ <li>
2016
+ <a
2017
+ class="label-small dark:text-surface-400 flex gap-2.5 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer"
2018
+ [class.text-surface-950]="selectedLanguage() === lang.code"
2019
+ [class.dark:text-surface-0]="selectedLanguage() === lang.code"
2020
+ [class.font-semibold]="selectedLanguage() === lang.code"
2021
+ (click)="selectLanguage(lang.code)"
2022
+ >
2023
+ <span class="text-lg">{{ lang.flag }}</span>
2024
+ <span>{{ lang.label }}</span>
2025
+ </a>
2026
+ </li>
2027
+ }
2028
+ </ul>
2029
+ </div>
2030
+ </li>
2031
+ <li class="profile-item static sm:relative" #profileItem>
2032
+ <a class="right-sidebar-button relative z-50" aria-label="User profile menu" (click)="toggleProfileMenu($event)">
2033
+ <p-avatar icon="pi pi-user" styleClass="w-10! h-10!" />
2034
+ </a>
2035
+ <div
2036
+ #profilePanel
2037
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 origin-top w-52 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
2038
+ [class.hidden]="!profileMenuOpen()"
2039
+ [class.animate-scalein]="profileMenuOpen()"
2040
+ >
2041
+ <ul class="flex flex-col gap-1">
2042
+ <div class="mobile-profile-actions">
2043
+ <li>
2044
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false); openSearch()">
2045
+ <i class="pi pi-search"></i>
2046
+ <span>Search</span>
2047
+ </a>
2048
+ </li>
2049
+ <li>
2050
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false); toggleDarkMode()">
2051
+ <i [class]="isDarkTheme() ? 'pi pi-sun' : 'pi pi-moon'"></i>
2052
+ <span>{{ isDarkTheme() ? 'Light Mode' : 'Dark Mode' }}</span>
2053
+ </a>
2054
+ </li>
2055
+ <li>
2056
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer relative" (click)="profileMenuOpen.set(false)">
2057
+ <i class="pi pi-bell"></i>
2058
+ <span>Notifications</span>
2059
+ <span class="w-2 h-2 rounded-full bg-red-500 ml-auto"></span>
2060
+ </a>
2061
+ </li>
2062
+ <li class="border-b border-surface pb-1 mb-1">
2063
+ <span class="label-xsmall px-2.5 py-1 text-surface-400">Language</span>
2064
+ @for (lang of languages(); track lang.code) {
2065
+ <a
2066
+ class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer"
2067
+ [class.text-surface-950]="selectedLanguage() === lang.code"
2068
+ [class.dark:text-surface-0]="selectedLanguage() === lang.code"
2069
+ [class.font-semibold]="selectedLanguage() === lang.code"
2070
+ (click)="profileMenuOpen.set(false); selectLanguage(lang.code)"
2071
+ >
2072
+ <span class="text-lg">{{ lang.flag }}</span>
2073
+ <span>{{ lang.label }}</span>
2074
+ @if (selectedLanguage() === lang.code) {
2075
+ <i class="pi pi-check ml-auto text-xs"></i>
2076
+ }
2077
+ </a>
2078
+ }
2079
+ </li>
2080
+ </div>
2081
+ <li>
2082
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
2083
+ <i class="pi pi-user"></i>
2084
+ <span>Profile</span>
2085
+ </a>
2086
+ </li>
2087
+ <li>
2088
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
2089
+ <i class="pi pi-cog"></i>
2090
+ <span>Settings</span>
2091
+ </a>
2092
+ </li>
2093
+ <li>
2094
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
2095
+ <i class="pi pi-calendar"></i>
2096
+ <span>Calendar</span>
2097
+ </a>
2098
+ </li>
2099
+ <li>
2100
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
2101
+ <i class="pi pi-inbox"></i>
2102
+ <span>Inbox</span>
2103
+ </a>
2104
+ </li>
2105
+ <li class="border-t border-surface mt-1 pt-1">
2106
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
2107
+ <i class="pi pi-power-off"></i>
2108
+ <span>Log out</span>
2109
+ </a>
2110
+ </li>
2111
+ </ul>
2112
+ </div>
2113
+ </li>
2114
+ </ul>
2115
+ </div>
2116
+ </div>`
2117
+ }]
2118
+ }], propDecorators: { menuButton: [{
2119
+ type: ViewChild,
2120
+ args: ['menubutton']
2121
+ }], searchInput: [{
2122
+ type: ViewChild,
2123
+ args: ['searchInput']
2124
+ }], profileItem: [{
2125
+ type: ViewChild,
2126
+ args: ['profileItem']
2127
+ }], profilePanel: [{
2128
+ type: ViewChild,
2129
+ args: ['profilePanel']
2130
+ }], selectedNotificationBar: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedNotificationBar", required: false }] }, { type: i0.Output, args: ["selectedNotificationBarChange"] }], onDocumentClick: [{
2131
+ type: HostListener,
2132
+ args: ['document:click', ['$event']]
2133
+ }] } });
2134
+
2135
+ const BREAKPOINT = 992;
2136
+ class AppSidebar {
2137
+ layoutService = inject(LayoutService);
2138
+ router = inject(Router);
2139
+ el = inject(ElementRef);
2140
+ menuContainer;
2141
+ sidebarRef;
2142
+ timeout = null;
2143
+ observer = null;
2144
+ outsideClickListener = null;
2145
+ mediaQuery = window.matchMedia(`(min-width: ${BREAKPOINT}px)`);
2146
+ destroy$ = new Subject();
2147
+ isHorizontal = computed(() => this.layoutService.isHorizontal(), ...(ngDevMode ? [{ debugName: "isHorizontal" }] : []));
2148
+ constructor() {
2149
+ effect(() => {
2150
+ const hasOpenOverlay = this.layoutService.hasOpenOverlay();
2151
+ const mobileMenuActive = this.layoutService.layoutState().mobileMenuActive;
2152
+ if (this.layoutService.isDesktop()) {
2153
+ if (hasOpenOverlay) {
2154
+ this.bindOutsideClickListener();
2155
+ }
2156
+ else {
2157
+ this.unbindOutsideClickListener();
2158
+ }
2159
+ }
2160
+ else {
2161
+ if (mobileMenuActive) {
2162
+ this.bindOutsideClickListener();
2163
+ }
2164
+ else {
2165
+ this.unbindOutsideClickListener();
2166
+ }
2167
+ }
2168
+ });
2169
+ effect(() => {
2170
+ const hasOpenOverlaySubmenu = this.layoutService.hasOpenOverlaySubmenu();
2171
+ if (this.layoutService.isDesktop()) {
2172
+ if (hasOpenOverlaySubmenu) {
2173
+ setTimeout(() => this.setupIntersectionObserver());
2174
+ }
2175
+ else {
2176
+ this.unbindObserver();
2177
+ }
2178
+ }
2179
+ });
2180
+ }
2181
+ ngOnInit() {
2182
+ this.router.events
2183
+ .pipe(filter((event) => event instanceof NavigationEnd), takeUntil(this.destroy$))
2184
+ .subscribe((event) => {
2185
+ const navEvent = event;
2186
+ this.onRouteChange(navEvent.urlAfterRedirects);
2187
+ });
2188
+ this.onRouteChange(this.router.url);
2189
+ this.mediaQuery.addEventListener('change', this.screenChangeListener);
2190
+ }
2191
+ ngOnDestroy() {
2192
+ this.destroy$.next();
2193
+ this.destroy$.complete();
2194
+ this.unbindOutsideClickListener();
2195
+ this.unbindObserver();
2196
+ this.mediaQuery.removeEventListener('change', this.screenChangeListener);
2197
+ }
2198
+ onRouteChange(path) {
2199
+ this.layoutService.currentUrl.set(path);
2200
+ let newActivePath;
2201
+ if (this.layoutService.hasOverlaySubmenu() && this.layoutService.isDesktop()) {
2202
+ newActivePath = null;
2203
+ }
2204
+ else {
2205
+ newActivePath = path;
2206
+ }
2207
+ this.layoutService.layoutState.update((val) => ({
2208
+ ...val,
2209
+ activePath: newActivePath,
2210
+ overlayMenuActive: false,
2211
+ staticMenuMobileActive: false,
2212
+ menuHoverActive: false
2213
+ }));
2214
+ }
2215
+ screenChangeListener = () => {
2216
+ if (this.layoutService.hasOverlaySubmenu()) {
2217
+ this.layoutService.layoutState.update((val) => ({
2218
+ ...val,
2219
+ activePath: this.layoutService.isDesktop() ? null : this.router.url,
2220
+ menuHoverActive: false
2221
+ }));
2222
+ this.unbindOutsideClickListener();
2223
+ this.unbindObserver();
2224
+ }
2225
+ };
2226
+ bindOutsideClickListener() {
2227
+ if (!this.outsideClickListener) {
2228
+ this.outsideClickListener = (event) => {
2229
+ if (this.isOutsideClicked(event)) {
2230
+ if (this.layoutService.isDesktop()) {
2231
+ this.layoutService.layoutState.update((val) => ({
2232
+ ...val,
2233
+ overlayMenuActive: false
2234
+ }));
2235
+ if (this.layoutService.hasOverlaySubmenu()) {
2236
+ this.layoutService.layoutState.update((val) => ({
2237
+ ...val,
2238
+ activePath: null,
2239
+ menuHoverActive: false
2240
+ }));
2241
+ }
2242
+ }
2243
+ else {
2244
+ this.layoutService.layoutState.update((val) => ({
2245
+ ...val,
2246
+ mobileMenuActive: false
2247
+ }));
2248
+ }
2249
+ }
2250
+ };
2251
+ document.addEventListener('click', this.outsideClickListener);
2252
+ }
2253
+ }
2254
+ unbindOutsideClickListener() {
2255
+ if (this.outsideClickListener) {
2256
+ document.removeEventListener('click', this.outsideClickListener);
2257
+ this.outsideClickListener = null;
2258
+ }
2259
+ }
2260
+ isOutsideClicked(event) {
2261
+ const topbarButtonEl = document.querySelector('.mobile-menu-button');
2262
+ const sidebarEl = this.el.nativeElement;
2263
+ return !(sidebarEl?.isSameNode(event.target) ||
2264
+ sidebarEl?.contains(event.target) ||
2265
+ topbarButtonEl?.isSameNode(event.target) ||
2266
+ topbarButtonEl?.contains(event.target));
2267
+ }
2268
+ onMouseEnter() { }
2269
+ onMouseLeave() {
2270
+ if (!this.layoutService.isDesktop() || !this.layoutService.isRail())
2271
+ return;
2272
+ if (this.layoutService.layoutState().sidebarPinned)
2273
+ return;
2274
+ if (this.timeout)
2275
+ clearTimeout(this.timeout);
2276
+ this.timeout = setTimeout(() => {
2277
+ this.layoutService.layoutState.update((val) => ({
2278
+ ...val,
2279
+ sidebarExpanded: false
2280
+ }));
2281
+ }, 300);
2282
+ }
2283
+ onAnchorToggle() {
2284
+ this.layoutService.layoutState.update((state) => ({
2285
+ ...state,
2286
+ anchored: !state.anchored
2287
+ }));
2288
+ }
2289
+ onMenuScroll() {
2290
+ if (this.menuContainer?.nativeElement) {
2291
+ if (this.layoutService.isHorizontal()) {
2292
+ const scrollLeft = this.menuContainer.nativeElement.scrollLeft;
2293
+ this.menuContainer.nativeElement.style.setProperty('--menu-scroll-x', `-${scrollLeft}px`);
2294
+ }
2295
+ else {
2296
+ const scrollTop = this.menuContainer.nativeElement.scrollTop;
2297
+ this.menuContainer.nativeElement.style.setProperty('--menu-scroll-y', `-${scrollTop}px`);
2298
+ }
2299
+ }
2300
+ if (this.layoutService.hasOverlaySubmenu() && this.layoutService.isDesktop()) {
2301
+ this.layoutService.layoutState.update((val) => ({
2302
+ ...val,
2303
+ activePath: null,
2304
+ menuHoverActive: false
2305
+ }));
2306
+ }
2307
+ }
2308
+ setupIntersectionObserver() {
2309
+ if (!this.menuContainer?.nativeElement)
2310
+ return;
2311
+ if (this.observer) {
2312
+ this.observer.disconnect();
2313
+ }
2314
+ const activeMenuItem = this.menuContainer.nativeElement.querySelector('.layout-root-menuitem.active-menuitem');
2315
+ if (!activeMenuItem)
2316
+ return;
2317
+ this.observer = new IntersectionObserver((entries) => {
2318
+ entries.forEach((entry) => {
2319
+ if (this.layoutService.isDesktop() &&
2320
+ !entry.isIntersecting &&
2321
+ this.layoutService.hasOverlaySubmenu() &&
2322
+ this.layoutService.layoutState().activePath) {
2323
+ this.layoutService.layoutState.update((val) => ({
2324
+ ...val,
2325
+ activePath: null
2326
+ }));
2327
+ }
2328
+ });
2329
+ }, {
2330
+ root: this.menuContainer.nativeElement,
2331
+ threshold: 0
2332
+ });
2333
+ this.observer.observe(activeMenuItem);
2334
+ }
2335
+ unbindObserver() {
2336
+ if (this.observer) {
2337
+ this.observer.disconnect();
2338
+ this.observer = null;
2339
+ }
2340
+ }
2341
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2342
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppSidebar, isStandalone: true, selector: "[app-sidebar]", viewQueries: [{ propertyName: "menuContainer", first: true, predicate: ["menuContainer"], descendants: true }, { propertyName: "sidebarRef", first: true, predicate: ["sidebarRef"], descendants: true }], ngImport: i0, template: `<nav class="layout-sidebar" aria-label="Main navigation" (mouseleave)="onMouseLeave()">
2343
+ <div #menuContainer class="layout-menu-container" (scroll)="onMenuScroll()">
2344
+ <div app-menu></div>
2345
+ </div>
2346
+ <div app-topbar *ngIf="isHorizontal()"></div>
2347
+ </nav>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: AppMenu, selector: "[app-menu]" }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: AppTopbar, selector: "[app-topbar]", inputs: ["selectedNotificationBar"], outputs: ["selectedNotificationBarChange"] }] });
2348
+ }
2349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSidebar, decorators: [{
2350
+ type: Component,
2351
+ args: [{
2352
+ selector: '[app-sidebar]',
2353
+ imports: [CommonModule, AppMenu, RouterModule, AppTopbar],
2354
+ template: `<nav class="layout-sidebar" aria-label="Main navigation" (mouseleave)="onMouseLeave()">
2355
+ <div #menuContainer class="layout-menu-container" (scroll)="onMenuScroll()">
2356
+ <div app-menu></div>
2357
+ </div>
2358
+ <div app-topbar *ngIf="isHorizontal()"></div>
2359
+ </nav>`
2360
+ }]
2361
+ }], ctorParameters: () => [], propDecorators: { menuContainer: [{
2362
+ type: ViewChild,
2363
+ args: ['menuContainer']
2364
+ }], sidebarRef: [{
2365
+ type: ViewChild,
2366
+ args: ['sidebarRef']
2367
+ }] } });
2368
+
2369
+ class AppLayout {
2370
+ layoutService = inject(LayoutService);
2371
+ constructor() {
2372
+ effect(() => {
2373
+ const state = this.layoutService.layoutState();
2374
+ if (state.mobileMenuActive) {
2375
+ document.body.classList.add('blocked-scroll');
2376
+ }
2377
+ else {
2378
+ document.body.classList.remove('blocked-scroll');
2379
+ }
2380
+ });
2381
+ }
2382
+ containerClass = computed(() => {
2383
+ const layoutConfig = this.layoutService.layoutConfig();
2384
+ const layoutState = this.layoutService.layoutState();
2385
+ return {
2386
+ 'layout-overlay': layoutConfig.menuMode === 'overlay',
2387
+ 'layout-static': layoutConfig.menuMode === 'static',
2388
+ 'layout-slim': layoutConfig.menuMode === 'slim',
2389
+ 'layout-horizontal': layoutConfig.menuMode === 'horizontal',
2390
+ 'layout-compact': layoutConfig.menuMode === 'compact',
2391
+ 'layout-reveal': layoutConfig.menuMode === 'reveal',
2392
+ 'layout-drawer': layoutConfig.menuMode === 'drawer',
2393
+ 'layout-overlay-active': layoutState.overlayMenuActive,
2394
+ 'layout-mobile-active': layoutState.mobileMenuActive,
2395
+ 'layout-sidebar-expanded': layoutState.sidebarExpanded,
2396
+ 'layout-sidebar-rail': !layoutState.sidebarPinned && layoutConfig.menuMode === 'static',
2397
+ 'layout-sidebar-anchored': layoutState.anchored,
2398
+ [`layout-card-${layoutConfig.cardStyle}`]: true,
2399
+ [`layout-sidebar-${layoutConfig.menuTheme}`]: true
2400
+ };
2401
+ }, ...(ngDevMode ? [{ debugName: "containerClass" }] : []));
2402
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
2403
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppLayout, isStandalone: true, selector: "app-layout", ngImport: i0, template: `<div class="layout-wrapper" [ngClass]="containerClass()">
2404
+ <div app-topbar></div>
2405
+ <div class="layout-body">
2406
+ <div app-sidebar></div>
2407
+ <div class="layout-content-wrapper">
2408
+ <div class="layout-content-wrapper-inside">
2409
+ <main class="layout-content">
2410
+ <div app-breadcrumb></div>
2411
+ <router-outlet></router-outlet>
2412
+ </main>
2413
+ <div app-footer></div>
2414
+ </div>
2415
+ </div>
2416
+ </div>
2417
+ <app-configurator />
2418
+ <div app-search></div>
2419
+ <div app-rightmenu></div>
2420
+ <div class="layout-mask"></div>
2421
+ </div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppTopbar, selector: "[app-topbar]", inputs: ["selectedNotificationBar"], outputs: ["selectedNotificationBarChange"] }, { kind: "component", type: AppSidebar, selector: "[app-sidebar]" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator", inputs: ["simple", "location", "cardStyle"], outputs: ["cardStyleChange"] }, { kind: "component", type: AppBreadcrumb, selector: "[app-breadcrumb]" }, { kind: "component", type: AppFooter, selector: "[app-footer]" }, { kind: "component", type: AppSearch, selector: "[app-search]" }, { kind: "component", type: AppRightMenu, selector: "[app-rightmenu]" }] });
2422
+ }
2423
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppLayout, decorators: [{
2424
+ type: Component,
2425
+ args: [{
2426
+ selector: 'app-layout',
2427
+ imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppConfigurator, AppBreadcrumb, AppFooter, AppSearch, AppRightMenu],
2428
+ template: `<div class="layout-wrapper" [ngClass]="containerClass()">
2429
+ <div app-topbar></div>
2430
+ <div class="layout-body">
2431
+ <div app-sidebar></div>
2432
+ <div class="layout-content-wrapper">
2433
+ <div class="layout-content-wrapper-inside">
2434
+ <main class="layout-content">
2435
+ <div app-breadcrumb></div>
2436
+ <router-outlet></router-outlet>
2437
+ </main>
2438
+ <div app-footer></div>
2439
+ </div>
2440
+ </div>
2441
+ </div>
2442
+ <app-configurator />
2443
+ <div app-search></div>
2444
+ <div app-rightmenu></div>
2445
+ <div class="layout-mask"></div>
2446
+ </div> `
2447
+ }]
2448
+ }], ctorParameters: () => [] });
2449
+
2450
+ class AuthLayout {
2451
+ layoutService = inject(LayoutService);
2452
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AuthLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
2453
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AuthLayout, isStandalone: true, selector: "auth-layout", ngImport: i0, template: `
2454
+ <main>
2455
+ <router-outlet></router-outlet>
2456
+ </main>
2457
+ <button class="layout-config-button config-link" (click)="layoutService.toggleConfigSidebar()">
2458
+ <i class="pi pi-cog"></i>
2459
+ </button>
2460
+ <app-configurator location="landing" />
2461
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator", inputs: ["simple", "location", "cardStyle"], outputs: ["cardStyleChange"] }] });
2462
+ }
2463
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AuthLayout, decorators: [{
2464
+ type: Component,
2465
+ args: [{
2466
+ selector: 'auth-layout',
2467
+ imports: [RouterModule, AppConfigurator],
2468
+ template: `
2469
+ <main>
2470
+ <router-outlet></router-outlet>
2471
+ </main>
2472
+ <button class="layout-config-button config-link" (click)="layoutService.toggleConfigSidebar()">
2473
+ <i class="pi pi-cog"></i>
2474
+ </button>
2475
+ <app-configurator location="landing" />
2476
+ `
2477
+ }]
2478
+ }] });
2479
+
2480
+ let aiCardBgFilterId = 0;
2481
+ /**
2482
+ * Animated brand-tinted background for AI insight cards: shifting host background
2483
+ * plus soft blurred SVG blobs with staggered fill animation.
2484
+ *
2485
+ * Project card content with `<ng-content />` so it stacks above the SVG layer.
2486
+ */
2487
+ class AiCardBgComponent {
2488
+ blurFilterId = `uxAiBlur_${++aiCardBgFilterId}`;
2489
+ get blurFilterUrl() {
2490
+ return `url(#${this.blurFilterId})`;
2491
+ }
2492
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiCardBgComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2493
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AiCardBgComponent, isStandalone: true, selector: "ux-ai-card-bg", host: { properties: { "attr.data-ai-bg": "true" }, classAttribute: "ux-ai-card-bg block box-border" }, ngImport: i0, template: `
2494
+ <svg
2495
+ class="ux-ai-card-bg__svg"
2496
+ viewBox="0 0 400 300"
2497
+ fill="none"
2498
+ xmlns="http://www.w3.org/2000/svg"
2499
+ aria-hidden="true"
2500
+ >
2501
+ <defs>
2502
+ <filter [attr.id]="blurFilterId" x="-50%" y="-50%" width="200%" height="200%">
2503
+ <feGaussianBlur stdDeviation="30" />
2504
+ </filter>
2505
+ </defs>
2506
+ <ellipse
2507
+ class="ux-ai-fg ux-ai-fg--1"
2508
+ cx="60"
2509
+ cy="50"
2510
+ rx="120"
2511
+ ry="100"
2512
+ opacity="0.35"
2513
+ [attr.filter]="blurFilterUrl"
2514
+ />
2515
+ <ellipse
2516
+ class="ux-ai-fg ux-ai-fg--2"
2517
+ cx="320"
2518
+ cy="80"
2519
+ rx="100"
2520
+ ry="80"
2521
+ opacity="0.3"
2522
+ [attr.filter]="blurFilterUrl"
2523
+ />
2524
+ <ellipse
2525
+ class="ux-ai-fg ux-ai-fg--3"
2526
+ cx="200"
2527
+ cy="240"
2528
+ rx="140"
2529
+ ry="90"
2530
+ opacity="0.25"
2531
+ [attr.filter]="blurFilterUrl"
2532
+ />
2533
+ </svg>
2534
+ <ng-content />
2535
+ `, isInline: true, styles: [":host{--ux-ai-bg-from: #cce5ff;--ux-ai-bg-to:rgb(255, 236, 254);--ux-ai-fg-from:rgb(188, 214, 255);--ux-ai-fg-to:rgb(246, 216, 255);position:relative;overflow:hidden;isolation:isolate;contain:paint;animation:ux-ai-bg-move 5s ease-in-out infinite;will-change:background-color}:host-context([class*=\"app-dark\"]){--ux-ai-bg-from:rgb(0, 32, 69);--ux-ai-bg-to:rgb(47, 18, 116);--ux-ai-fg-from:rgb(0, 20, 47);--ux-ai-fg-to:rgb(76, 1, 71)}:host>:not(svg){position:relative;z-index:1}:host svg.ux-ai-card-bg__svg{position:absolute;inset:0;z-index:0;height:100%;width:100%;max-height:100%;overflow:hidden;pointer-events:none}.ux-ai-fg{will-change:fill;animation:ux-ai-fg-move 6s ease-in-out infinite}.ux-ai-fg--1{animation-delay:0s}.ux-ai-fg--2{animation-delay:2s}.ux-ai-fg--3{animation-delay:4s}@keyframes ux-ai-bg-move{0%,to{background-color:var(--ux-ai-bg-from)}50%{background-color:var(--ux-ai-bg-to)}}@keyframes ux-ai-fg-move{0%,to{fill:var(--ux-ai-fg-from)}50%{fill:var(--ux-ai-fg-to)}}@media(prefers-reduced-motion:reduce){:host{animation:none;background-color:var(--ux-ai-bg-from)}.ux-ai-fg{animation:none;fill:var(--ux-ai-fg-from)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2536
+ }
2537
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiCardBgComponent, decorators: [{
2538
+ type: Component,
2539
+ args: [{ selector: 'ux-ai-card-bg', changeDetection: ChangeDetectionStrategy.OnPush, host: {
2540
+ class: 'ux-ai-card-bg block box-border',
2541
+ '[attr.data-ai-bg]': 'true'
2542
+ }, template: `
2543
+ <svg
2544
+ class="ux-ai-card-bg__svg"
2545
+ viewBox="0 0 400 300"
2546
+ fill="none"
2547
+ xmlns="http://www.w3.org/2000/svg"
2548
+ aria-hidden="true"
2549
+ >
2550
+ <defs>
2551
+ <filter [attr.id]="blurFilterId" x="-50%" y="-50%" width="200%" height="200%">
2552
+ <feGaussianBlur stdDeviation="30" />
2553
+ </filter>
2554
+ </defs>
2555
+ <ellipse
2556
+ class="ux-ai-fg ux-ai-fg--1"
2557
+ cx="60"
2558
+ cy="50"
2559
+ rx="120"
2560
+ ry="100"
2561
+ opacity="0.35"
2562
+ [attr.filter]="blurFilterUrl"
2563
+ />
2564
+ <ellipse
2565
+ class="ux-ai-fg ux-ai-fg--2"
2566
+ cx="320"
2567
+ cy="80"
2568
+ rx="100"
2569
+ ry="80"
2570
+ opacity="0.3"
2571
+ [attr.filter]="blurFilterUrl"
2572
+ />
2573
+ <ellipse
2574
+ class="ux-ai-fg ux-ai-fg--3"
2575
+ cx="200"
2576
+ cy="240"
2577
+ rx="140"
2578
+ ry="90"
2579
+ opacity="0.25"
2580
+ [attr.filter]="blurFilterUrl"
2581
+ />
2582
+ </svg>
2583
+ <ng-content />
2584
+ `, styles: [":host{--ux-ai-bg-from: #cce5ff;--ux-ai-bg-to:rgb(255, 236, 254);--ux-ai-fg-from:rgb(188, 214, 255);--ux-ai-fg-to:rgb(246, 216, 255);position:relative;overflow:hidden;isolation:isolate;contain:paint;animation:ux-ai-bg-move 5s ease-in-out infinite;will-change:background-color}:host-context([class*=\"app-dark\"]){--ux-ai-bg-from:rgb(0, 32, 69);--ux-ai-bg-to:rgb(47, 18, 116);--ux-ai-fg-from:rgb(0, 20, 47);--ux-ai-fg-to:rgb(76, 1, 71)}:host>:not(svg){position:relative;z-index:1}:host svg.ux-ai-card-bg__svg{position:absolute;inset:0;z-index:0;height:100%;width:100%;max-height:100%;overflow:hidden;pointer-events:none}.ux-ai-fg{will-change:fill;animation:ux-ai-fg-move 6s ease-in-out infinite}.ux-ai-fg--1{animation-delay:0s}.ux-ai-fg--2{animation-delay:2s}.ux-ai-fg--3{animation-delay:4s}@keyframes ux-ai-bg-move{0%,to{background-color:var(--ux-ai-bg-from)}50%{background-color:var(--ux-ai-bg-to)}}@keyframes ux-ai-fg-move{0%,to{fill:var(--ux-ai-fg-from)}50%{fill:var(--ux-ai-fg-to)}}@media(prefers-reduced-motion:reduce){:host{animation:none;background-color:var(--ux-ai-bg-from)}.ux-ai-fg{animation:none;fill:var(--ux-ai-fg-from)}}\n"] }]
2585
+ }] });
2586
+
2587
+ /*
2588
+ * Public API Surface of @unopsitg/ux
2589
+ */
2590
+
2591
+ /**
2592
+ * Generated bundle index. Do not edit.
2593
+ */
2594
+
2595
+ export { AiCardBgComponent, AppBreadcrumb, AppConfigurator, AppFooter, AppLayout, AppMenu, AppMenuitem, AppRightMenu, AppSearch, AppSidebar, AppTopbar, AuthLayout, BrandContrast, BrandCrisp, BrandSoft, LayoutService, MENU_MODEL, SIDEBAR_LOGO, TOPBAR_MOBILE_LOGO, brandPresets, brandPrimitives };
2596
+ //# sourceMappingURL=unopsitg-ux.mjs.map