@yuuvis/client-shell 2.3.4 → 2.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{yuuvis-client-shell-settings.component-OPDWz2_-.mjs → yuuvis-client-shell-settings.component-DWoamL5p.mjs} +4 -4
- package/fesm2022/{yuuvis-client-shell-settings.component-OPDWz2_-.mjs.map → yuuvis-client-shell-settings.component-DWoamL5p.mjs.map} +1 -1
- package/fesm2022/yuuvis-client-shell.mjs +168 -125
- package/fesm2022/yuuvis-client-shell.mjs.map +1 -1
- package/lib/assets/i18n/de.json +2 -3
- package/lib/assets/i18n/en.json +2 -3
- package/lib/client-shell.component.d.ts +10 -20
- package/lib/components/app-header/app-header.component.d.ts +1 -0
- package/lib/components/sidebar-nav/sidebar-nav.component.d.ts +30 -2
- package/package.json +4 -4
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, signal, Component, Injectable,
|
|
2
|
+
import { inject, signal, Component, effect, untracked, Injectable, InjectionToken, computed, ChangeDetectionStrategy, ElementRef, HostListener, input, TemplateRef, Directive, NgModule } from '@angular/core';
|
|
3
3
|
import { MAT_DIALOG_DATA, MatDialogRef, MatDialog } from '@angular/material/dialog';
|
|
4
|
-
import * as
|
|
5
|
-
import { DmsService, TranslateService, TranslateModule, SystemType,
|
|
4
|
+
import * as i3 from '@yuuvis/client-core';
|
|
5
|
+
import { DmsService, TranslateService, TranslateModule, SystemType, DeviceService, ConfigService, SafeHtmlPipe, UserService, Utils, LocaleDatePipe, AuthService, UserRoles } from '@yuuvis/client-core';
|
|
6
6
|
import { AbstractContextAction, ACTION_ICON, SelectionRange } from '@yuuvis/client-framework/actions';
|
|
7
7
|
import { ShellService, ShellNotificationsService, CommandPaletteService } from '@yuuvis/client-shell-core';
|
|
8
|
-
import { switchMap, of, tap, filter, debounceTime } from 'rxjs';
|
|
9
|
-
import * as i1$
|
|
8
|
+
import { switchMap, of, tap, filter, debounceTime, map } from 'rxjs';
|
|
9
|
+
import * as i1$1 from '@angular/common';
|
|
10
10
|
import { CommonModule, AsyncPipe } from '@angular/common';
|
|
11
|
-
import * as
|
|
11
|
+
import * as i3$1 from '@angular/material/button';
|
|
12
12
|
import { MatButtonModule } from '@angular/material/button';
|
|
13
13
|
import * as i1 from '@angular/material/icon';
|
|
14
|
-
import { MatIconModule, MatIconRegistry
|
|
14
|
+
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
|
|
15
15
|
import * as i2 from '@angular/material/tooltip';
|
|
16
16
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
17
17
|
import { ConfirmService, BusyOverlayDirective, DialogComponent, LightDismissDirective, LayoutSettingsService } from '@yuuvis/client-framework/common';
|
|
18
|
-
import * as i4
|
|
18
|
+
import * as i4 from '@yuuvis/client-framework/list';
|
|
19
19
|
import { ListItemDirective, YuvListModule } from '@yuuvis/client-framework/list';
|
|
20
20
|
import { FlavorChipComponent } from '@yuuvis/client-framework/object-flavor';
|
|
21
21
|
import { YmtButtonDirective, YmtIconButtonDirective } from '@yuuvis/material';
|
|
22
|
-
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
|
|
23
22
|
import { MatProgressBar } from '@angular/material/progress-bar';
|
|
24
|
-
import * as
|
|
23
|
+
import * as i6 from '@angular/router';
|
|
25
24
|
import { Router, NavigationEnd, RouterModule } from '@angular/router';
|
|
26
25
|
import { SwPush } from '@angular/service-worker';
|
|
27
|
-
import * as
|
|
26
|
+
import * as i2$3 from '@yuuvis/client-framework/metadata-form-defaults';
|
|
28
27
|
import { YuvMetadataFormDefaultsModule } from '@yuuvis/client-framework/metadata-form-defaults';
|
|
29
|
-
import {
|
|
30
|
-
import
|
|
28
|
+
import { filter as filter$1, map as map$1, tap as tap$1, catchError, switchMap as switchMap$1 } from 'rxjs/operators';
|
|
29
|
+
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
|
|
30
|
+
import * as i2$1 from '@yuuvis/client-framework/overflow-hidden';
|
|
31
31
|
import { YuvOverflowHiddenModule } from '@yuuvis/client-framework/overflow-hidden';
|
|
32
|
+
import * as i2$2 from '@angular/material/menu';
|
|
33
|
+
import { MatMenuModule } from '@angular/material/menu';
|
|
34
|
+
import { MatDivider } from '@angular/material/divider';
|
|
32
35
|
import { CdkTrapFocus } from '@angular/cdk/a11y';
|
|
33
36
|
import { YUV_ICONS, YuvIconComponent } from '@yuuvis/client-framework/icons';
|
|
34
37
|
import { WidgetGridRegistry } from '@yuuvis/client-framework/widget-grid';
|
|
@@ -96,7 +99,7 @@ class ManageFlavorsComponent {
|
|
|
96
99
|
this.#refreshDmsObject();
|
|
97
100
|
}
|
|
98
101
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ManageFlavorsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
99
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ManageFlavorsComponent, isStandalone: true, selector: "yuv-manage-flavors", ngImport: i0, template: "<yuv-dialog [headertitel]=\"'yuv.shell.action.manage-flavors.title' | translate\">\n <main [yuvBusyOverlay]=\"busy()\">\n <p>{{ 'yuv.shell.action.manage-flavors.text' | translate }}</p>\n @if (appliedFlavors.length) {\n <h3>{{ 'yuv.shell.action.manage-flavors.applied.headline' | translate }}</h3>\n <ul>\n @for (f of appliedFlavors; track $index) {\n <li>\n <yuv-flavor-chip [flavor]=\"f\" yuvListItem></yuv-flavor-chip>\n <button mat-icon-button [matTooltip]=\"'yuv.shell.action.manage-flavors.applicable.button.remove.tooltip' | translate\" (click)=\"removeFlavor(f)\">\n <mat-icon>delete_forever</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n @if (applicableFlavors.length) {\n <h3>{{ 'yuv.shell.action.manage-flavors.applicable.headline' | translate }}</h3>\n <ul>\n @for (f of applicableFlavors; track $index) {\n <li>\n <yuv-flavor-chip [flavor]=\"f\" yuvListItem></yuv-flavor-chip>\n <button mat-icon-button [matTooltip]=\"'yuv.shell.action.manage-flavors.applied.button.apply.tooltip' | translate\" (click)=\"applyFlavor(f)\">\n <mat-icon>add</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n </main>\n <footer>\n <button ymtButton=\"secondary\" (click)=\"cancel()\">{{ 'yuv.shell.action.manage-flavors.button.cancel' | translate }}</button>\n </footer>\n</yuv-dialog>\n", styles: [":host main{display:flex;flex-flow:column;padding:var(--ymt-spacing-m);flex:1;overflow-y:auto}:host main yuv-flavor-chip{border-color:transparent;flex:1}:host main ul{padding:0;margin:0;list-style-type:none}:host main li{display:flex;gap:var(--ymt-spacing-xs);align-items:center;border:1px solid var(--ymt-outline-variant);margin-block-end:2px;padding:var(--ymt-spacing-2xs)}:host footer{flex:0 0 auto;margin-block-start:var(--ymt-spacing-m)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: BusyOverlayDirective, selector: "[yuvBusyOverlay]", inputs: ["yuvBusyOverlay", "yuvBusyOverlayConfig"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type:
|
|
102
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ManageFlavorsComponent, isStandalone: true, selector: "yuv-manage-flavors", ngImport: i0, template: "<yuv-dialog [headertitel]=\"'yuv.shell.action.manage-flavors.title' | translate\">\n <main [yuvBusyOverlay]=\"busy()\">\n <p>{{ 'yuv.shell.action.manage-flavors.text' | translate }}</p>\n @if (appliedFlavors.length) {\n <h3>{{ 'yuv.shell.action.manage-flavors.applied.headline' | translate }}</h3>\n <ul>\n @for (f of appliedFlavors; track $index) {\n <li>\n <yuv-flavor-chip [flavor]=\"f\" yuvListItem></yuv-flavor-chip>\n <button mat-icon-button [matTooltip]=\"'yuv.shell.action.manage-flavors.applicable.button.remove.tooltip' | translate\" (click)=\"removeFlavor(f)\">\n <mat-icon>delete_forever</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n @if (applicableFlavors.length) {\n <h3>{{ 'yuv.shell.action.manage-flavors.applicable.headline' | translate }}</h3>\n <ul>\n @for (f of applicableFlavors; track $index) {\n <li>\n <yuv-flavor-chip [flavor]=\"f\" yuvListItem></yuv-flavor-chip>\n <button mat-icon-button [matTooltip]=\"'yuv.shell.action.manage-flavors.applied.button.apply.tooltip' | translate\" (click)=\"applyFlavor(f)\">\n <mat-icon>add</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n </main>\n <footer>\n <button ymtButton=\"secondary\" (click)=\"cancel()\">{{ 'yuv.shell.action.manage-flavors.button.cancel' | translate }}</button>\n </footer>\n</yuv-dialog>\n", styles: [":host main{display:flex;flex-flow:column;padding:var(--ymt-spacing-m);flex:1;overflow-y:auto}:host main yuv-flavor-chip{border-color:transparent;flex:1}:host main ul{padding:0;margin:0;list-style-type:none}:host main li{display:flex;gap:var(--ymt-spacing-xs);align-items:center;border:1px solid var(--ymt-outline-variant);margin-block-end:2px;padding:var(--ymt-spacing-2xs)}:host footer{flex:0 0 auto;margin-block-start:var(--ymt-spacing-m)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: BusyOverlayDirective, selector: "[yuvBusyOverlay]", inputs: ["yuvBusyOverlay", "yuvBusyOverlayConfig"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "directive", type: ListItemDirective, selector: "[yuvListItem]", inputs: ["disabled", "active", "selected"] }, { kind: "component", type: FlavorChipComponent, selector: "yuv-flavor-chip", inputs: ["flavor", "enableRemove", "enableDescription"], outputs: ["flavorRemove"] }, { kind: "component", type: DialogComponent, selector: "yuv-dialog", inputs: ["headertitel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: YmtButtonDirective, selector: "button[ymtButton], a[ymtButton]", inputs: ["ymtButton", "disabled", "aria-disabled", "disableRipple", "disabledInteractive", "button-size"] }] }); }
|
|
100
103
|
}
|
|
101
104
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ManageFlavorsComponent, decorators: [{
|
|
102
105
|
type: Component,
|
|
@@ -157,11 +160,22 @@ class ManageFlavorsAction extends AbstractContextAction {
|
|
|
157
160
|
class ClientShellService {
|
|
158
161
|
#router;
|
|
159
162
|
#shell;
|
|
163
|
+
#appsEffect;
|
|
160
164
|
constructor() {
|
|
161
165
|
this.#router = inject(Router);
|
|
162
166
|
this.#shell = inject(ShellService);
|
|
163
167
|
this.appHeaderSlots = signal({});
|
|
164
168
|
this.apps = this.#shell.apps;
|
|
169
|
+
this.#appsEffect = effect(() => {
|
|
170
|
+
const apps = this.apps();
|
|
171
|
+
// apps may change due to i18n, so we need to update current app
|
|
172
|
+
if (this.currentApp()) {
|
|
173
|
+
untracked(() => {
|
|
174
|
+
const app = apps.find((a) => a.path === this.currentApp().path);
|
|
175
|
+
this.currentApp.set(app);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
});
|
|
165
179
|
this.currentApp = signal(undefined);
|
|
166
180
|
this.appHeader = signal(false);
|
|
167
181
|
this.#router.events
|
|
@@ -208,7 +222,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
208
222
|
|
|
209
223
|
class ShellLogoComponent {
|
|
210
224
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ShellLogoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
211
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ShellLogoComponent, isStandalone: true, selector: "yuv-shell-logo", host: { classAttribute: "shell-logo" }, ngImport: i0, template: "<a class=\"shell-logo__to-root\" routerLink=\"/\" [attr.aria-label]=\"'yuv.shell.logo.aria.label' | translate\">\n <mat-icon class=\"shell-logo__icon\" svgIcon=\"shellIcons:app_logo\"></mat-icon>\n</a>\n", styles: [":host.shell-logo{display:block;aspect-ratio:1/1}.shell-logo__to-root{all:unset;cursor:pointer;display:block;height:100%}.shell-logo__icon{width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type:
|
|
225
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ShellLogoComponent, isStandalone: true, selector: "yuv-shell-logo", host: { classAttribute: "shell-logo" }, ngImport: i0, template: "<a class=\"shell-logo__to-root\" routerLink=\"/\" [attr.aria-label]=\"'yuv.shell.logo.aria.label' | translate\">\n <mat-icon class=\"shell-logo__icon\" svgIcon=\"shellIcons:app_logo\"></mat-icon>\n</a>\n", styles: [":host.shell-logo{display:block;aspect-ratio:1/1}.shell-logo__to-root{all:unset;cursor:pointer;display:block;height:100%}.shell-logo__icon{width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i6.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] }); }
|
|
212
226
|
}
|
|
213
227
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ShellLogoComponent, decorators: [{
|
|
214
228
|
type: Component,
|
|
@@ -220,15 +234,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
220
234
|
class YuvAppHeaderComponent {
|
|
221
235
|
#clientShellService;
|
|
222
236
|
#shell;
|
|
237
|
+
#device;
|
|
223
238
|
#slots;
|
|
224
239
|
constructor() {
|
|
225
240
|
this.#clientShellService = inject(ClientShellService);
|
|
226
241
|
this.#shell = inject(ShellService);
|
|
242
|
+
this.#device = inject(DeviceService);
|
|
227
243
|
this.actionsSlot = null;
|
|
228
244
|
this.searchSlot = null;
|
|
229
245
|
this.notificationsSlot = null;
|
|
230
246
|
this.app = this.#clientShellService.currentApp;
|
|
231
247
|
this.#slots = this.#clientShellService.appHeaderSlots;
|
|
248
|
+
this.smallScreenLayout = this.#device.smallScreenLayout;
|
|
232
249
|
this.busy$ = this.#shell.isBusy$;
|
|
233
250
|
toObservable(this.#slots)
|
|
234
251
|
.pipe(debounceTime(1), takeUntilDestroyed())
|
|
@@ -239,23 +256,130 @@ class YuvAppHeaderComponent {
|
|
|
239
256
|
});
|
|
240
257
|
}
|
|
241
258
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: YuvAppHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
242
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: YuvAppHeaderComponent, isStandalone: true, selector: "yuv-app-header", ngImport: i0, template: "@let a = app();\n@if (a) {\n <header\n class=\"app-header\"\n [ngClass]=\"{\n 'has-actions': !!actionsSlot,\n 'has-search': !!searchSlot,\n 'has-notifications': !!notificationsSlot\n }\"\n aria-labelledby=\"yuv-app-header\"\n >\n @if (busy$ | async) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n <yuv-shell-logo />\n <yuv-overflow-hidden>\n <ng-template #yuvDefaultSlot>\n <a class=\"name\" [routerLink]=\"app()?.path\">\n <span class=\"title\" id=\"yuv-app-header\">\n {{ a.title }}\n <span class=\"claim\">{{ a.options?.appClaim }}</span>\n </span>\n </a>\n </ng-template>\n </yuv-overflow-hidden>\n <div class=\"actions\">\n <ng-container *ngTemplateOutlet=\"actionsSlot\"></ng-container>\n </div>\n <div class=\"search\">\n <ng-container *ngTemplateOutlet=\"searchSlot\"></ng-container>\n </div>\n <div class=\"notifications\">\n <ng-container *ngTemplateOutlet=\"notificationsSlot\"></ng-container>\n </div>\n </header>\n}\n", styles: [":host .app-header{position:relative;display:grid;grid-template-columns:var(--yuv-app-header-height, var(--ymt-sizing-6xl)) auto 1fr auto auto;grid-template-rows:var(--yuv-app-header-height, var(--ymt-sizing-6xl));grid-template-areas:\"logo name actions search notifications\";align-items:center;background-color:var(--ymt-bar-surface);color:var(--ymt-on-bar-surface);box-shadow:0 0 0 1px var(--ymt-outline-variant);box-sizing:border-box;padding-inline-end:var(--ymt-spacing-xl)}:host mat-progress-bar{position:absolute;inset-block-end:0}:host .name{grid-area:name;display:inline-block;padding-inline-end:var(--ymt-spacing-3xl);padding-inline-start:var(--ymt-spacing-m);color:currentColor;text-decoration:none;white-space:nowrap;min-width:128px}:host .name .title{display:flex;flex-direction:column;align-items:flex-start;gap:var(--ymt-spacing-2xs);font:var(--ymt-font-app-name);letter-spacing:var(--ymt-font-app-name-tracking);margin:0;line-height:1}:host .name .claim{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host .actions{grid-area:actions}:host .search{grid-area:search}:host .notifications{grid-area:notifications}:host .app-header.has-actions .actions{padding-inline-start:var(--ymt-spacing-xl);padding-inline-end:var(--ymt-spacing-l);border-inline-start:var(--ymt-outline-width) solid var(--ymt-outline-variant);border-inline-end:var(--ymt-outline-width) solid var(--ymt-outline-variant)}:host .app-header.has-search .search{padding-inline-start:var(--ymt-spacing-m)}:host .app-header.has-notifications .notifications{padding-inline-start:var(--ymt-spacing-xl)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$
|
|
259
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: YuvAppHeaderComponent, isStandalone: true, selector: "yuv-app-header", host: { properties: { "class.small-screen": "smallScreenLayout()" } }, ngImport: i0, template: "@let a = app();\n@if (a) {\n <header\n class=\"app-header\"\n [ngClass]=\"{\n 'has-actions': !!actionsSlot,\n 'has-search': !!searchSlot,\n 'has-notifications': !!notificationsSlot\n }\"\n aria-labelledby=\"yuv-app-header\"\n >\n @if (busy$ | async) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n <yuv-shell-logo />\n <yuv-overflow-hidden>\n <ng-template #yuvDefaultSlot>\n <a class=\"name\" [routerLink]=\"app()?.path\">\n <span class=\"title\" id=\"yuv-app-header\">\n {{ a.title }}\n <span class=\"claim\">{{ a.options?.appClaim }}</span>\n </span>\n </a>\n </ng-template>\n </yuv-overflow-hidden>\n <div class=\"actions\">\n <ng-container *ngTemplateOutlet=\"actionsSlot\"></ng-container>\n </div>\n <div class=\"search\">\n <ng-container *ngTemplateOutlet=\"searchSlot\"></ng-container>\n </div>\n <div class=\"notifications\">\n <ng-container *ngTemplateOutlet=\"notificationsSlot\"></ng-container>\n </div>\n </header>\n}\n", styles: [":host.small-screen .app-header{grid-template-areas:\"actions actions actions search notifications\"}:host.small-screen yuv-shell-logo,:host.small-screen .name{display:none}:host .app-header{position:relative;display:grid;grid-template-columns:var(--yuv-app-header-height, var(--ymt-sizing-6xl)) auto 1fr auto auto;grid-template-rows:var(--yuv-app-header-height, var(--ymt-sizing-6xl));grid-template-areas:\"logo name actions search notifications\";align-items:center;background-color:var(--ymt-bar-surface);color:var(--ymt-on-bar-surface);box-shadow:0 0 0 1px var(--ymt-outline-variant);box-sizing:border-box;padding-inline-end:var(--ymt-spacing-xl)}:host mat-progress-bar{position:absolute;inset-block-end:0}:host .name{grid-area:name;display:inline-block;padding-inline-end:var(--ymt-spacing-3xl);padding-inline-start:var(--ymt-spacing-m);color:currentColor;text-decoration:none;white-space:nowrap;min-width:128px}:host .name .title{display:flex;flex-direction:column;align-items:flex-start;gap:var(--ymt-spacing-2xs);font:var(--ymt-font-app-name);letter-spacing:var(--ymt-font-app-name-tracking);margin:0;line-height:1}:host .name .claim{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host .actions{grid-area:actions}:host .search{grid-area:search}:host .notifications{grid-area:notifications}:host .app-header.has-actions .actions{padding-inline-start:var(--ymt-spacing-xl);padding-inline-end:var(--ymt-spacing-l);border-inline-start:var(--ymt-outline-width) solid var(--ymt-outline-variant);border-inline-end:var(--ymt-outline-width) solid var(--ymt-outline-variant)}:host .app-header.has-search .search{padding-inline-start:var(--ymt-spacing-m)}:host .app-header.has-notifications .notifications{padding-inline-start:var(--ymt-spacing-xl)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "component", type: MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: YuvOverflowHiddenModule }, { kind: "component", type: i2$1.OverflowHiddenComponent, selector: "yuv-overflow-hidden" }, { kind: "component", type: ShellLogoComponent, selector: "yuv-shell-logo" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i6.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
|
|
243
260
|
}
|
|
244
261
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: YuvAppHeaderComponent, decorators: [{
|
|
245
262
|
type: Component,
|
|
246
|
-
args: [{ selector: 'yuv-app-header', imports: [CommonModule,
|
|
247
|
-
|
|
248
|
-
|
|
263
|
+
args: [{ selector: 'yuv-app-header', imports: [CommonModule, MatProgressBar, MatIconModule, YuvOverflowHiddenModule, ShellLogoComponent, RouterModule], host: {
|
|
264
|
+
'[class.small-screen]': 'smallScreenLayout()'
|
|
265
|
+
}, template: "@let a = app();\n@if (a) {\n <header\n class=\"app-header\"\n [ngClass]=\"{\n 'has-actions': !!actionsSlot,\n 'has-search': !!searchSlot,\n 'has-notifications': !!notificationsSlot\n }\"\n aria-labelledby=\"yuv-app-header\"\n >\n @if (busy$ | async) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n <yuv-shell-logo />\n <yuv-overflow-hidden>\n <ng-template #yuvDefaultSlot>\n <a class=\"name\" [routerLink]=\"app()?.path\">\n <span class=\"title\" id=\"yuv-app-header\">\n {{ a.title }}\n <span class=\"claim\">{{ a.options?.appClaim }}</span>\n </span>\n </a>\n </ng-template>\n </yuv-overflow-hidden>\n <div class=\"actions\">\n <ng-container *ngTemplateOutlet=\"actionsSlot\"></ng-container>\n </div>\n <div class=\"search\">\n <ng-container *ngTemplateOutlet=\"searchSlot\"></ng-container>\n </div>\n <div class=\"notifications\">\n <ng-container *ngTemplateOutlet=\"notificationsSlot\"></ng-container>\n </div>\n </header>\n}\n", styles: [":host.small-screen .app-header{grid-template-areas:\"actions actions actions search notifications\"}:host.small-screen yuv-shell-logo,:host.small-screen .name{display:none}:host .app-header{position:relative;display:grid;grid-template-columns:var(--yuv-app-header-height, var(--ymt-sizing-6xl)) auto 1fr auto auto;grid-template-rows:var(--yuv-app-header-height, var(--ymt-sizing-6xl));grid-template-areas:\"logo name actions search notifications\";align-items:center;background-color:var(--ymt-bar-surface);color:var(--ymt-on-bar-surface);box-shadow:0 0 0 1px var(--ymt-outline-variant);box-sizing:border-box;padding-inline-end:var(--ymt-spacing-xl)}:host mat-progress-bar{position:absolute;inset-block-end:0}:host .name{grid-area:name;display:inline-block;padding-inline-end:var(--ymt-spacing-3xl);padding-inline-start:var(--ymt-spacing-m);color:currentColor;text-decoration:none;white-space:nowrap;min-width:128px}:host .name .title{display:flex;flex-direction:column;align-items:flex-start;gap:var(--ymt-spacing-2xs);font:var(--ymt-font-app-name);letter-spacing:var(--ymt-font-app-name-tracking);margin:0;line-height:1}:host .name .claim{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host .actions{grid-area:actions}:host .search{grid-area:search}:host .notifications{grid-area:notifications}:host .app-header.has-actions .actions{padding-inline-start:var(--ymt-spacing-xl);padding-inline-end:var(--ymt-spacing-l);border-inline-start:var(--ymt-outline-width) solid var(--ymt-outline-variant);border-inline-end:var(--ymt-outline-width) solid var(--ymt-outline-variant)}:host .app-header.has-search .search{padding-inline-start:var(--ymt-spacing-m)}:host .app-header.has-notifications .notifications{padding-inline-start:var(--ymt-spacing-xl)}\n"] }]
|
|
249
266
|
}], ctorParameters: () => [] });
|
|
250
267
|
|
|
268
|
+
const SHELL_ACTION_BUTTONS_CONFIG = new InjectionToken('SHELL_ACTION_BUTTONS_CONFIG');
|
|
269
|
+
|
|
251
270
|
class SidebarNavComponent {
|
|
271
|
+
#shell;
|
|
272
|
+
#config;
|
|
273
|
+
#shellNotifications;
|
|
274
|
+
#matIconRegistryService;
|
|
275
|
+
#router;
|
|
276
|
+
#userService;
|
|
277
|
+
#device;
|
|
278
|
+
#shellApps;
|
|
279
|
+
constructor() {
|
|
280
|
+
this.#shell = inject(ShellService);
|
|
281
|
+
this.css = inject(ClientShellService);
|
|
282
|
+
this.customActionButtons = inject(SHELL_ACTION_BUTTONS_CONFIG, { optional: true });
|
|
283
|
+
this.#config = inject(ConfigService);
|
|
284
|
+
this.#shellNotifications = inject(ShellNotificationsService);
|
|
285
|
+
this.#matIconRegistryService = inject(MatIconRegistry);
|
|
286
|
+
this.safeHtmlPipe = inject(SafeHtmlPipe);
|
|
287
|
+
this.commandPalette = inject(CommandPaletteService);
|
|
288
|
+
this.#router = inject(Router);
|
|
289
|
+
this.translate = inject(TranslateService);
|
|
290
|
+
this.#userService = inject(UserService);
|
|
291
|
+
this.#device = inject(DeviceService);
|
|
292
|
+
this.APP_LOGOUT_EVENT_KEY = 'yuv.app.event.logout';
|
|
293
|
+
this._levels = {
|
|
294
|
+
info: 0,
|
|
295
|
+
success: 1,
|
|
296
|
+
warning: 2,
|
|
297
|
+
alert: 3
|
|
298
|
+
};
|
|
299
|
+
this.smallScreenLayout = this.#device.smallScreenLayout;
|
|
300
|
+
this.app = this.css.currentApp;
|
|
301
|
+
this.showNotifications = signal(false);
|
|
302
|
+
this.newNotifications = toSignal(this.#shellNotifications.shellNotifications$.pipe(tap((n) => this.showNotifications.set(n.length > 0)), map((notifications) => {
|
|
303
|
+
let maxLevel = 'info';
|
|
304
|
+
const count = notifications.filter((n) => !n.seen).length;
|
|
305
|
+
notifications.forEach((n) => (maxLevel = n.level && this._levels[n.level] > this._levels[maxLevel] ? n.level : maxLevel));
|
|
306
|
+
return count > 0 ? { maxLevel, count } : undefined;
|
|
307
|
+
})));
|
|
308
|
+
this.#shellApps = this.#shell.apps;
|
|
309
|
+
this.navApps = computed(() => this.#shellApps().filter((a) => !a.options?.hideFromNav));
|
|
310
|
+
this.registerIcons = computed(() => {
|
|
311
|
+
const apps = this.#shellApps();
|
|
312
|
+
const config = this.#shell.shellConfig();
|
|
313
|
+
const namespace = config.shellIconNamespace;
|
|
314
|
+
// find svg-icons to register and put them in an array
|
|
315
|
+
const customSvgIconsToRegister = apps
|
|
316
|
+
.filter(({ svgIcon }) => !!svgIcon)
|
|
317
|
+
.map(({ svgIcon, iconName }) => ({ svgIcon, iconName }));
|
|
318
|
+
customSvgIconsToRegister.push(config.appIcon);
|
|
319
|
+
// register svg-icons
|
|
320
|
+
const allRegistered = customSvgIconsToRegister.every((icon) => {
|
|
321
|
+
return namespace && icon && icon.svgIcon && icon.iconName
|
|
322
|
+
? !!this.#matIconRegistryService.addSvgIconLiteralInNamespace(namespace, icon.iconName, this.safeHtmlPipe.transform(icon.svgIcon))
|
|
323
|
+
: false;
|
|
324
|
+
});
|
|
325
|
+
return allRegistered;
|
|
326
|
+
});
|
|
327
|
+
this.translate.onLangChange.subscribe(() => this._setCommands(true));
|
|
328
|
+
window.addEventListener('storage', (evt) => {
|
|
329
|
+
if (evt.key === this.APP_LOGOUT_EVENT_KEY) {
|
|
330
|
+
this.appLogout(true);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
getAppTitle(a) {
|
|
335
|
+
return a.title || '';
|
|
336
|
+
}
|
|
337
|
+
handleCustomActionButtonClick(button) {
|
|
338
|
+
button.onClick({ router: this.#router, config: this.#config });
|
|
339
|
+
}
|
|
340
|
+
appLogout(triggeredFromOtherTab) {
|
|
341
|
+
if (!triggeredFromOtherTab) {
|
|
342
|
+
// send storage event to logout all other open tabs
|
|
343
|
+
window.localStorage.setItem(this.APP_LOGOUT_EVENT_KEY, `${Date.now()}`);
|
|
344
|
+
}
|
|
345
|
+
this.#userService.logout('');
|
|
346
|
+
}
|
|
347
|
+
_setCommands(update) {
|
|
348
|
+
const commands = this.#shellApps().map((a, i) => ({
|
|
349
|
+
id: `nav.app.${i}`,
|
|
350
|
+
label: this.translate.instant('yuv.shell.cmd.app.open', { title: a.title }),
|
|
351
|
+
callback: () => this.#router.navigate([a.path])
|
|
352
|
+
}));
|
|
353
|
+
commands.push({
|
|
354
|
+
id: `nav.shell.settings`,
|
|
355
|
+
label: this.translate.instant('yuv.shell.cmd.open.settings'),
|
|
356
|
+
callback: () => this.#router.navigate(['settings'])
|
|
357
|
+
});
|
|
358
|
+
commands.push({
|
|
359
|
+
id: `nav.shell.logout`,
|
|
360
|
+
label: this.translate.instant('yuv.shell.cmd.logout'),
|
|
361
|
+
callback: () => this.appLogout()
|
|
362
|
+
});
|
|
363
|
+
if (update)
|
|
364
|
+
this.commandPalette.updateCommands(commands);
|
|
365
|
+
else
|
|
366
|
+
this.commandPalette.registerCommands(commands);
|
|
367
|
+
}
|
|
368
|
+
ngOnInit() {
|
|
369
|
+
this._setCommands();
|
|
370
|
+
}
|
|
252
371
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidebarNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
253
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: SidebarNavComponent, isStandalone: true, selector: "yuv-sidebar-nav", ngImport: i0, template: "<nav class=\"sidenav\" [attr.aria-label]=\"('yuv.shell.side-nav.aria.label' | translate)\" >\n <div class=\"logo-container\">\n <ng-content select=\"[slot=shell-logo]\"></ng-content>\n </div>\n <div class=\"app-switcher-container\">\n <ng-content select=\"[slot=app-switcher]\"></ng-content>\n </div>\n <div class=\"actions-container\">\n <ng-content select=\"[slot=shell-actions]\"></ng-content>\n </div>\n</nav>\n", styles: [".sidenav{height:100%;width:var(--yuv-side-nav-width, var(--ymt-sizing-6xl));background:transparent;color:inherit;display:flex;flex-direction:column;justify-content:flex-start;align-items:center;gap:var(--ymt-spacing-s)}.sidenav .logo-container{width:100%}.sidenav .app-switcher-container{flex:1 1 0;overflow-y:auto;display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);scrollbar-width:none;-ms-overflow-style:none}.sidenav .app-switcher-container::-webkit-scrollbar{display:none}.sidenav .actions-container{justify-self:flex-end;border-top:var(--ymt-outline-width) solid var(--ymt-outline-variant);display:flex;flex-direction:column;gap:var(--ymt-spacing-s);padding:var(--ymt-spacing-m) 0}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
372
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: SidebarNavComponent, isStandalone: true, selector: "yuv-sidebar-nav", host: { properties: { "class.small-screen": "smallScreenLayout()" }, classAttribute: "sidenav" }, ngImport: i0, template: "<nav class=\"sidenav\" [attr.aria-label]=\"'yuv.shell.side-nav.aria.label' | translate\">\n @let iconsRegistered = registerIcons();\n @let showAppHeader = css.appHeader();\n\n @let a = app();\n @if (smallScreenLayout()) {\n <div class=\"menu\">\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>menu</mat-icon>\n </button>\n @if (a) {\n {{ a.title }}\n }\n </div>\n\n <mat-menu #menu=\"matMenu\" class=\"sort-menu\" [ariaLabel]=\"'yuv.sort.tooltip' | translate\">\n @for (a of navApps(); track a.path) {\n <button mat-menu-item [routerLink]=\"a.path\">\n @if (a.svgIcon) {\n @if (iconsRegistered) {\n <mat-icon [svgIcon]=\"'shellIcons:' + a.iconName\"></mat-icon>\n }\n } @else {\n <mat-icon>{{ a.iconName }}</mat-icon>\n }\n <span>{{ getAppTitle(a) }}</span>\n </button>\n }\n @if (showNotifications()) {\n <mat-divider></mat-divider>\n <button mat-menu-item [routerLink]=\"[{ outlets: { aside: 'notifications' } }]\" routerLinkActive=\"active\">\n <mat-icon>notifications</mat-icon>\n @if (newNotifications(); as note) {\n <div class=\"badge {{ note.maxLevel }}\">{{ note.count }}</div>\n }\n <span>{{ 'yuv.shell.notifications.title' | translate }}</span>\n </button>\n }\n <mat-divider></mat-divider>\n\n @for (customButton of customActionButtons; track customButton.iconName) {\n <button mat-menu-item (click)=\"handleCustomActionButtonClick(customButton)\">\n <mat-icon>{{ customButton.iconName }}</mat-icon>\n <span>{{ customButton.tooltip ? (customButton.tooltip | translate) : '' }}</span>\n </button>\n }\n <button mat-menu-item [routerLink]=\"['/settings']\" routerLinkActive=\"active\">\n <mat-icon>settings</mat-icon>\n <span>{{ 'yuv.shell.settings.title' | translate }}</span>\n </button>\n <button mat-menu-item (click)=\"appLogout()\">\n <mat-icon>power_settings_new</mat-icon>\n <span>{{ 'yuv.shell.cmd.logout' | translate }}</span>\n </button>\n </mat-menu>\n } @else {\n <div class=\"logo-container\">\n @if (iconsRegistered && !showAppHeader) {\n <yuv-shell-logo slot=\"shell-logo\" class=\"shell-nav__shell-logo\" />\n }\n </div>\n <div class=\"app-switcher-container\">\n <ul class=\"shell-nav__app-switcher\">\n @for (a of navApps(); track a.path) {\n <li class=\"shell-nav__app-switcher-item\" routerLinkActive=\"shell-nav__app-switcher-item--active\">\n <button class=\"shell-nav__app-switcher-link\" ymt-icon-button [routerLink]=\"a.path\" matTooltipPosition=\"after\" [matTooltip]=\"getAppTitle(a)\">\n @if (a.svgIcon) {\n @if (iconsRegistered) {\n <mat-icon [svgIcon]=\"'shellIcons:' + a.iconName\"></mat-icon>\n }\n } @else {\n <mat-icon>{{ a.iconName }}</mat-icon>\n }\n <!--@if (getNotifications(a.id)) {\n <div [ngClass]=\"'badge ' + getNotifications(a.id).maxLevel\">{{ getNotifications(a.id).count }}</div>\n }-->\n </button>\n </li>\n }\n </ul>\n </div>\n <div class=\"actions-container\">\n @if (showNotifications()) {\n <button\n class=\"shell-nav__actions-button\"\n ymt-icon-button\n [routerLink]=\"[{ outlets: { aside: 'notifications' } }]\"\n routerLinkActive=\"active\"\n matTooltipPosition=\"after\"\n [matTooltip]=\"'yuv.shell.notifications.title' | translate\"\n >\n <mat-icon>notifications</mat-icon>\n @if (newNotifications(); as note) {\n <div class=\"badge {{ note.maxLevel }}\">{{ note.count }}</div>\n }\n </button>\n }\n @for (customButton of customActionButtons; track customButton.iconName) {\n <button\n class=\"shell-nav__actions-button\"\n ymt-icon-button\n (click)=\"handleCustomActionButtonClick(customButton)\"\n matTooltipPosition=\"after\"\n [matTooltip]=\"customButton.tooltip ? (customButton.tooltip | translate) : ''\"\n >\n <mat-icon>{{ customButton.iconName }}</mat-icon>\n </button>\n }\n <button\n class=\"shell-nav__actions-button\"\n ymt-icon-button\n [routerLink]=\"['/settings']\"\n routerLinkActive=\"active\"\n [matTooltip]=\"'yuv.shell.settings.title' | translate\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n <button class=\"shell-nav__actions-button\" ymt-icon-button (click)=\"appLogout()\" [matTooltip]=\"'yuv.shell.cmd.logout' | translate\">\n <mat-icon>power_settings_new</mat-icon>\n </button>\n </div>\n }\n</nav>\n", styles: [":host{display:block}:host.small-screen nav .menu{display:flex;align-items:center;gap:var(--ymt-spacing-s);padding:var(--ymt-sizing-s) var(--ymt-sizing-m)}:host:not(.small-screen) .sidenav{height:100%;width:var(--yuv-side-nav-width, var(--ymt-sizing-6xl));background:transparent;color:inherit;display:flex;flex-direction:column;justify-content:flex-start;align-items:center;gap:var(--ymt-spacing-s)}:host:not(.small-screen) .sidenav .logo-container{width:100%}:host:not(.small-screen) .sidenav .app-switcher-container{flex:1 1 0;overflow-y:auto;display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);scrollbar-width:none;-ms-overflow-style:none}:host:not(.small-screen) .sidenav .app-switcher-container::-webkit-scrollbar{display:none}:host:not(.small-screen) .sidenav .actions-container{justify-self:flex-end;border-top:var(--ymt-outline-width) solid var(--ymt-outline-variant);display:flex;flex-direction:column;gap:var(--ymt-spacing-s);padding:var(--ymt-spacing-m) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-wrapper{display:contents}:host:not(.small-screen) .sidenav .shell-nav__app-switcher{display:contents;list-style:none}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item{position:relative;padding:var(--ymt-spacing-2xs) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:after{content:\"\";position:absolute;inset:0;top:var(--ymt-spacing-2xs);aspect-ratio:1/1;background-color:var(--mat-icon-button-state-layer-color, var(--mat-sys-on-surface-variant));border-radius:var(--ymt-corner-full);visibility:hidden;opacity:0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:before{content:\"\";height:var(--ymt-sizing-3xs);width:var(--ymt-sizing-xs);border-radius:var(--ymt-corner-full);position:absolute;left:50%;bottom:0;transform:translate3d(-50%,0,0);background-color:transparent;transition:background-color .1s ease-in-out}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:before{background-color:var(--ymt-primary)}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:after{visibility:visible;opacity:.08;pointer-events:none}:host:not(.small-screen) .sidenav .shell-nav__actions{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "component", type: ShellLogoComponent, selector: "yuv-shell-logo" }, { kind: "directive", type: YmtIconButtonDirective, selector: "button[ymtIconButton],button[ymt-icon-button],a[ymtIconButton],a[ymt-icon-button]", inputs: ["disabled", "disableRipple", "aria-disabled", "disabledInteractive", "icon-button-size"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i6.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i6.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
254
373
|
}
|
|
255
374
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SidebarNavComponent, decorators: [{
|
|
256
375
|
type: Component,
|
|
257
|
-
args: [{ selector: 'yuv-sidebar-nav', changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslateModule
|
|
258
|
-
|
|
376
|
+
args: [{ selector: 'yuv-sidebar-nav', changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslateModule, ShellLogoComponent, YmtIconButtonDirective,
|
|
377
|
+
MatMenuModule, MatButtonModule,
|
|
378
|
+
MatIconModule, MatTooltipModule, RouterModule, MatDivider], host: {
|
|
379
|
+
class: 'sidenav',
|
|
380
|
+
'[class.small-screen]': 'smallScreenLayout()'
|
|
381
|
+
}, template: "<nav class=\"sidenav\" [attr.aria-label]=\"'yuv.shell.side-nav.aria.label' | translate\">\n @let iconsRegistered = registerIcons();\n @let showAppHeader = css.appHeader();\n\n @let a = app();\n @if (smallScreenLayout()) {\n <div class=\"menu\">\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>menu</mat-icon>\n </button>\n @if (a) {\n {{ a.title }}\n }\n </div>\n\n <mat-menu #menu=\"matMenu\" class=\"sort-menu\" [ariaLabel]=\"'yuv.sort.tooltip' | translate\">\n @for (a of navApps(); track a.path) {\n <button mat-menu-item [routerLink]=\"a.path\">\n @if (a.svgIcon) {\n @if (iconsRegistered) {\n <mat-icon [svgIcon]=\"'shellIcons:' + a.iconName\"></mat-icon>\n }\n } @else {\n <mat-icon>{{ a.iconName }}</mat-icon>\n }\n <span>{{ getAppTitle(a) }}</span>\n </button>\n }\n @if (showNotifications()) {\n <mat-divider></mat-divider>\n <button mat-menu-item [routerLink]=\"[{ outlets: { aside: 'notifications' } }]\" routerLinkActive=\"active\">\n <mat-icon>notifications</mat-icon>\n @if (newNotifications(); as note) {\n <div class=\"badge {{ note.maxLevel }}\">{{ note.count }}</div>\n }\n <span>{{ 'yuv.shell.notifications.title' | translate }}</span>\n </button>\n }\n <mat-divider></mat-divider>\n\n @for (customButton of customActionButtons; track customButton.iconName) {\n <button mat-menu-item (click)=\"handleCustomActionButtonClick(customButton)\">\n <mat-icon>{{ customButton.iconName }}</mat-icon>\n <span>{{ customButton.tooltip ? (customButton.tooltip | translate) : '' }}</span>\n </button>\n }\n <button mat-menu-item [routerLink]=\"['/settings']\" routerLinkActive=\"active\">\n <mat-icon>settings</mat-icon>\n <span>{{ 'yuv.shell.settings.title' | translate }}</span>\n </button>\n <button mat-menu-item (click)=\"appLogout()\">\n <mat-icon>power_settings_new</mat-icon>\n <span>{{ 'yuv.shell.cmd.logout' | translate }}</span>\n </button>\n </mat-menu>\n } @else {\n <div class=\"logo-container\">\n @if (iconsRegistered && !showAppHeader) {\n <yuv-shell-logo slot=\"shell-logo\" class=\"shell-nav__shell-logo\" />\n }\n </div>\n <div class=\"app-switcher-container\">\n <ul class=\"shell-nav__app-switcher\">\n @for (a of navApps(); track a.path) {\n <li class=\"shell-nav__app-switcher-item\" routerLinkActive=\"shell-nav__app-switcher-item--active\">\n <button class=\"shell-nav__app-switcher-link\" ymt-icon-button [routerLink]=\"a.path\" matTooltipPosition=\"after\" [matTooltip]=\"getAppTitle(a)\">\n @if (a.svgIcon) {\n @if (iconsRegistered) {\n <mat-icon [svgIcon]=\"'shellIcons:' + a.iconName\"></mat-icon>\n }\n } @else {\n <mat-icon>{{ a.iconName }}</mat-icon>\n }\n <!--@if (getNotifications(a.id)) {\n <div [ngClass]=\"'badge ' + getNotifications(a.id).maxLevel\">{{ getNotifications(a.id).count }}</div>\n }-->\n </button>\n </li>\n }\n </ul>\n </div>\n <div class=\"actions-container\">\n @if (showNotifications()) {\n <button\n class=\"shell-nav__actions-button\"\n ymt-icon-button\n [routerLink]=\"[{ outlets: { aside: 'notifications' } }]\"\n routerLinkActive=\"active\"\n matTooltipPosition=\"after\"\n [matTooltip]=\"'yuv.shell.notifications.title' | translate\"\n >\n <mat-icon>notifications</mat-icon>\n @if (newNotifications(); as note) {\n <div class=\"badge {{ note.maxLevel }}\">{{ note.count }}</div>\n }\n </button>\n }\n @for (customButton of customActionButtons; track customButton.iconName) {\n <button\n class=\"shell-nav__actions-button\"\n ymt-icon-button\n (click)=\"handleCustomActionButtonClick(customButton)\"\n matTooltipPosition=\"after\"\n [matTooltip]=\"customButton.tooltip ? (customButton.tooltip | translate) : ''\"\n >\n <mat-icon>{{ customButton.iconName }}</mat-icon>\n </button>\n }\n <button\n class=\"shell-nav__actions-button\"\n ymt-icon-button\n [routerLink]=\"['/settings']\"\n routerLinkActive=\"active\"\n [matTooltip]=\"'yuv.shell.settings.title' | translate\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n <button class=\"shell-nav__actions-button\" ymt-icon-button (click)=\"appLogout()\" [matTooltip]=\"'yuv.shell.cmd.logout' | translate\">\n <mat-icon>power_settings_new</mat-icon>\n </button>\n </div>\n }\n</nav>\n", styles: [":host{display:block}:host.small-screen nav .menu{display:flex;align-items:center;gap:var(--ymt-spacing-s);padding:var(--ymt-sizing-s) var(--ymt-sizing-m)}:host:not(.small-screen) .sidenav{height:100%;width:var(--yuv-side-nav-width, var(--ymt-sizing-6xl));background:transparent;color:inherit;display:flex;flex-direction:column;justify-content:flex-start;align-items:center;gap:var(--ymt-spacing-s)}:host:not(.small-screen) .sidenav .logo-container{width:100%}:host:not(.small-screen) .sidenav .app-switcher-container{flex:1 1 0;overflow-y:auto;display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);scrollbar-width:none;-ms-overflow-style:none}:host:not(.small-screen) .sidenav .app-switcher-container::-webkit-scrollbar{display:none}:host:not(.small-screen) .sidenav .actions-container{justify-self:flex-end;border-top:var(--ymt-outline-width) solid var(--ymt-outline-variant);display:flex;flex-direction:column;gap:var(--ymt-spacing-s);padding:var(--ymt-spacing-m) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-wrapper{display:contents}:host:not(.small-screen) .sidenav .shell-nav__app-switcher{display:contents;list-style:none}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item{position:relative;padding:var(--ymt-spacing-2xs) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:after{content:\"\";position:absolute;inset:0;top:var(--ymt-spacing-2xs);aspect-ratio:1/1;background-color:var(--mat-icon-button-state-layer-color, var(--mat-sys-on-surface-variant));border-radius:var(--ymt-corner-full);visibility:hidden;opacity:0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:before{content:\"\";height:var(--ymt-sizing-3xs);width:var(--ymt-sizing-xs);border-radius:var(--ymt-corner-full);position:absolute;left:50%;bottom:0;transform:translate3d(-50%,0,0);background-color:transparent;transition:background-color .1s ease-in-out}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:before{background-color:var(--ymt-primary)}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:after{visibility:visible;opacity:.08;pointer-events:none}:host:not(.small-screen) .sidenav .shell-nav__actions{display:contents}\n"] }]
|
|
382
|
+
}], ctorParameters: () => [] });
|
|
259
383
|
|
|
260
384
|
class NotificationsPageComponent {
|
|
261
385
|
constructor() {
|
|
@@ -312,7 +436,7 @@ class NotificationsPageComponent {
|
|
|
312
436
|
});
|
|
313
437
|
}
|
|
314
438
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
315
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NotificationsPageComponent, isStandalone: true, selector: "yuv-notifications", host: { listeners: { "keydown": "onKeydown($event)" } }, ngImport: i0, template: "<div class=\"notifications\" (yuvLightDismiss)=\"close()\" cdkTrapFocus>\n <h2>{{ 'yuv.shell.notifications.title' | translate }}</h2>\n\n @if (notifications()?.length) {\n <yuv-list (itemSelect)=\"itemSelected($event)\" (itemFocus)=\"itemFocused($event)\">\n @for (n of notifications(); track n.id) {\n <div class=\"note {{ n.level }}\" [ngClass]=\"{ withRoute: n.targetRoute }\" yuvListItem>\n <div class=\"icon\"><yuv-icon [svg]=\"n.icon || icons.note\"></yuv-icon></div>\n <div class=\"received\">{{ n.timestamp | localeDate }}</div>\n <div class=\"title\">{{ n.title }}</div>\n <div class=\"description\">{{ n.description }}</div>\n <div class=\"meta\"></div>\n\n <div class=\"actions\">\n <button mat-icon-button (click)=\"remove(n.id)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n }\n </yuv-list>\n\n <div class=\"actions\">\n <button mat-icon-button [hidden]=\"!notifications()?.length\" class=\"icon secondary\" (click)=\"removeAll()\">\n {{ 'yuv.shell.notifications.button.remove.all' | translate }}<mat-icon>delete</mat-icon>\n </button>\n </div>\n } @else {\n <div class=\"empty\">\n <p>{{ 'yuv.shell.notifications.empty' | translate }}</p>\n </div>\n }\n\n <button mat-icon-button class=\"icon close\" (click)=\"close()\">\n <mat-icon>close</mat-icon>\n </button>\n</div>\n", styles: [":host{height:100%;display:flex}:host .notifications{background-color:var(--ymt-surface-panel);border-inline-end:1px solid var(--ymt-outline-variant);height:100%;overflow:hidden;box-sizing:border-box;padding:var(--ymt-spacing-m);display:grid;grid-template-rows:auto auto 1fr;grid-template-columns:1fr auto;grid-template-areas:\"title close\" \"actions actions\" \"list list\";gap:var(--ymt-spacing-m);max-width:30vw;min-width:300px;box-shadow:8px 0 8px #0000001a;animation:dialogAppear .2s ease-in-out}:host .notifications h2{color:var(--ymt-text-color-subtle);grid-area:title;margin:0;padding:0;align-self:center;text-overflow:ellipsis;overflow:hidden}:host .notifications .actions{grid-area:actions;display:flex;justify-content:end;--icon-size: 18px}:host .notifications .actions button{border-radius:.4em}:host .notifications .close{grid-area:close;--icon-size: 18px}:host .notifications yuv-list,:host .notifications .empty{grid-area:list}:host .notifications .empty{display:grid;align-items:center;justify-content:center;color:var(--ymt-text-color-subtle)}:host .notifications .note{--level-color: transparent;display:grid;grid-template-rows:auto auto auto auto;grid-template-columns:auto 1fr auto;grid-template-areas:\"icon received actions\" \"icon title title\" \"icon description description\" \"icon meta meta\";row-gap:var(--ymt-spacing-2xs);column-gap:var(--ymt-spacing-xs);align-items:center;padding:var(--ymt-spacing-xs);padding-inline-start:var(--ymt-spacing-m);margin-block-end:var(--ymt-spacing-xs);background-color:var(--ymt-surface-panel);outline:1px solid var(--ymt-outline-variant);outline-offset:-1px;position:relative;cursor:default}:host .notifications .note.withRoute{cursor:pointer}:host .notifications .note:before{content:\"\";width:4px;position:absolute;left:2px;top:2px;bottom:2px;border-radius:2px;background-color:var(--level-color)}:host .notifications .note.alert{--level-color: var(--ymt-danger)}:host .notifications .note.warning{--level-color: var(--ymt-warning)}:host .notifications .note.success{--level-color: var(--ymt-success)}:host .notifications .note:hover,:host .notifications .note[aria-current=true]{background-color:var(--ymt-focus-background)}:host .notifications .note:hover button,:host .notifications .note[aria-current=true] button{opacity:1}:host .notifications .note .icon{grid-area:icon;color:var(--ymt-text-color-subtle)}:host .notifications .note .title{overflow:hidden;text-overflow:ellipsis;font-weight:700;grid-area:title;color:var(--ymt-text-color)}:host .notifications .note .description{grid-area:description;overflow:hidden;text-overflow:ellipsis}:host .notifications .note .meta{grid-area:meta}:host .notifications .note .received{grid-area:received;color:var(--ymt-text-color-subtle)}:host .notifications .note button{grid-area:actions;padding:0;opacity:0}@keyframes dialogAppear{0%{opacity:0;transform:translate(calc(var(--ymt-spacing-m) * -1))}to{opacity:1;transform:translate(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$
|
|
439
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NotificationsPageComponent, isStandalone: true, selector: "yuv-notifications", host: { listeners: { "keydown": "onKeydown($event)" } }, ngImport: i0, template: "<div class=\"notifications\" (yuvLightDismiss)=\"close()\" cdkTrapFocus>\n <h2>{{ 'yuv.shell.notifications.title' | translate }}</h2>\n\n @if (notifications()?.length) {\n <yuv-list (itemSelect)=\"itemSelected($event)\" (itemFocus)=\"itemFocused($event)\">\n @for (n of notifications(); track n.id) {\n <div class=\"note {{ n.level }}\" [ngClass]=\"{ withRoute: n.targetRoute }\" yuvListItem>\n <div class=\"icon\"><yuv-icon [svg]=\"n.icon || icons.note\"></yuv-icon></div>\n <div class=\"received\">{{ n.timestamp | localeDate }}</div>\n <div class=\"title\">{{ n.title }}</div>\n <div class=\"description\">{{ n.description }}</div>\n <div class=\"meta\"></div>\n\n <div class=\"actions\">\n <button mat-icon-button (click)=\"remove(n.id)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n }\n </yuv-list>\n\n <div class=\"actions\">\n <button mat-icon-button [hidden]=\"!notifications()?.length\" class=\"icon secondary\" (click)=\"removeAll()\">\n {{ 'yuv.shell.notifications.button.remove.all' | translate }}<mat-icon>delete</mat-icon>\n </button>\n </div>\n } @else {\n <div class=\"empty\">\n <p>{{ 'yuv.shell.notifications.empty' | translate }}</p>\n </div>\n }\n\n <button mat-icon-button class=\"icon close\" (click)=\"close()\">\n <mat-icon>close</mat-icon>\n </button>\n</div>\n", styles: [":host{height:100%;display:flex}:host .notifications{background-color:var(--ymt-surface-panel);border-inline-end:1px solid var(--ymt-outline-variant);height:100%;overflow:hidden;box-sizing:border-box;padding:var(--ymt-spacing-m);display:grid;grid-template-rows:auto auto 1fr;grid-template-columns:1fr auto;grid-template-areas:\"title close\" \"actions actions\" \"list list\";gap:var(--ymt-spacing-m);max-width:30vw;min-width:300px;box-shadow:8px 0 8px #0000001a;animation:dialogAppear .2s ease-in-out}:host .notifications h2{color:var(--ymt-text-color-subtle);grid-area:title;margin:0;padding:0;align-self:center;text-overflow:ellipsis;overflow:hidden}:host .notifications .actions{grid-area:actions;display:flex;justify-content:end;--icon-size: 18px}:host .notifications .actions button{border-radius:.4em}:host .notifications .close{grid-area:close;--icon-size: 18px}:host .notifications yuv-list,:host .notifications .empty{grid-area:list}:host .notifications .empty{display:grid;align-items:center;justify-content:center;color:var(--ymt-text-color-subtle)}:host .notifications .note{--level-color: transparent;display:grid;grid-template-rows:auto auto auto auto;grid-template-columns:auto 1fr auto;grid-template-areas:\"icon received actions\" \"icon title title\" \"icon description description\" \"icon meta meta\";row-gap:var(--ymt-spacing-2xs);column-gap:var(--ymt-spacing-xs);align-items:center;padding:var(--ymt-spacing-xs);padding-inline-start:var(--ymt-spacing-m);margin-block-end:var(--ymt-spacing-xs);background-color:var(--ymt-surface-panel);outline:1px solid var(--ymt-outline-variant);outline-offset:-1px;position:relative;cursor:default}:host .notifications .note.withRoute{cursor:pointer}:host .notifications .note:before{content:\"\";width:4px;position:absolute;left:2px;top:2px;bottom:2px;border-radius:2px;background-color:var(--level-color)}:host .notifications .note.alert{--level-color: var(--ymt-danger)}:host .notifications .note.warning{--level-color: var(--ymt-warning)}:host .notifications .note.success{--level-color: var(--ymt-success)}:host .notifications .note:hover,:host .notifications .note[aria-current=true]{background-color:var(--ymt-focus-background)}:host .notifications .note:hover button,:host .notifications .note[aria-current=true] button{opacity:1}:host .notifications .note .icon{grid-area:icon;color:var(--ymt-text-color-subtle)}:host .notifications .note .title{overflow:hidden;text-overflow:ellipsis;font-weight:700;grid-area:title;color:var(--ymt-text-color)}:host .notifications .note .description{grid-area:description;overflow:hidden;text-overflow:ellipsis}:host .notifications .note .meta{grid-area:meta}:host .notifications .note .received{grid-area:received;color:var(--ymt-text-color-subtle)}:host .notifications .note button{grid-area:actions;padding:0;opacity:0}@keyframes dialogAppear{0%{opacity:0;transform:translate(calc(var(--ymt-spacing-m) * -1))}to{opacity:1;transform:translate(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: LightDismissDirective, selector: "[yuvLightDismiss]", outputs: ["yuvLightDismiss"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "pipe", type: LocaleDatePipe, name: "localeDate" }, { kind: "component", type: YuvIconComponent, selector: "yuv-icon", inputs: ["label", "svg", "svgSrc"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: YuvListModule }, { kind: "component", type: i4.ListComponent, selector: "yuv-list", inputs: ["preventChangeUntil", "multiselect", "selfHandleSelection", "autoSelect", "disableSelection"], outputs: ["itemSelect", "itemFocus"] }, { kind: "directive", type: i4.ListItemDirective, selector: "[yuvListItem]", inputs: ["disabled", "active", "selected"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] }); }
|
|
316
440
|
}
|
|
317
441
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotificationsPageComponent, decorators: [{
|
|
318
442
|
type: Component,
|
|
@@ -335,15 +459,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
335
459
|
const clientShellRoutes = [
|
|
336
460
|
{ path: 'dashboard', loadComponent: () => import('./yuuvis-client-shell-dashboard.component-DNpk_qVe.mjs').then((comp) => comp.DashboardPageComponent) },
|
|
337
461
|
{ path: 'notifications', component: NotificationsPageComponent, outlet: 'aside' },
|
|
338
|
-
{ path: 'settings', loadComponent: () => import('./yuuvis-client-shell-settings.component-
|
|
462
|
+
{ path: 'settings', loadComponent: () => import('./yuuvis-client-shell-settings.component-DWoamL5p.mjs').then((comp) => comp.SettingsPageComponent) },
|
|
339
463
|
// default route
|
|
340
464
|
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
|
|
341
465
|
// redirecting route
|
|
342
466
|
{ path: '**', redirectTo: '/' }
|
|
343
467
|
];
|
|
344
468
|
|
|
345
|
-
const SHELL_ACTION_BUTTONS_CONFIG = new InjectionToken('SHELL_ACTION_BUTTONS_CONFIG');
|
|
346
|
-
|
|
347
469
|
/**
|
|
348
470
|
* Base component for the client shell application. In your apps
|
|
349
471
|
* app.component.ts you can use this component as a base component
|
|
@@ -368,17 +490,14 @@ class ClientShellComponent {
|
|
|
368
490
|
#shell;
|
|
369
491
|
#device;
|
|
370
492
|
#swPush;
|
|
371
|
-
#matIconRegistryService;
|
|
372
493
|
#layoutSettings;
|
|
373
|
-
onFocusChange(event) {
|
|
374
|
-
// console.log('focused: ', document.activeElement);
|
|
375
|
-
}
|
|
376
494
|
onDragOver(event) {
|
|
377
495
|
event.stopPropagation();
|
|
378
496
|
event.preventDefault();
|
|
379
497
|
this.showUploadOverlay = !!this._dragContainsFiles(event);
|
|
380
498
|
}
|
|
381
|
-
|
|
499
|
+
// set shell apps from component input
|
|
500
|
+
#appsInputEffect;
|
|
382
501
|
#shellConfigEffect;
|
|
383
502
|
constructor() {
|
|
384
503
|
this.router = inject(Router);
|
|
@@ -387,71 +506,35 @@ class ClientShellComponent {
|
|
|
387
506
|
this.userService = inject(UserService);
|
|
388
507
|
this.#shell = inject(ShellService);
|
|
389
508
|
this.css = inject(ClientShellService);
|
|
390
|
-
this.shellNotifications = inject(ShellNotificationsService);
|
|
391
|
-
this.translate = inject(TranslateService);
|
|
392
|
-
this.commandPalette = inject(CommandPaletteService);
|
|
393
509
|
this.#device = inject(DeviceService);
|
|
394
510
|
this.#swPush = inject(SwPush);
|
|
395
|
-
this.#matIconRegistryService = inject(MatIconRegistry);
|
|
396
511
|
this.#layoutSettings = inject(LayoutSettingsService);
|
|
397
|
-
this.APP_LOGOUT_EVENT_KEY = 'yuv.app.event.logout';
|
|
398
|
-
this._levels = {
|
|
399
|
-
info: 0,
|
|
400
|
-
success: 1,
|
|
401
|
-
warning: 2,
|
|
402
|
-
alert: 3
|
|
403
|
-
};
|
|
404
512
|
this.safeHtmlPipe = inject(SafeHtmlPipe);
|
|
405
513
|
this.showUploadOverlay = false;
|
|
406
514
|
this.busy$ = this.#shell.isBusy$;
|
|
407
|
-
this.customActionButtons = inject(SHELL_ACTION_BUTTONS_CONFIG, { optional: true });
|
|
408
|
-
this.showNotifications = signal(false);
|
|
409
|
-
this.newNotifications = toSignal(this.shellNotifications.shellNotifications$.pipe(tap$1((n) => this.showNotifications.set(n.length > 0)), map((notifications) => {
|
|
410
|
-
let maxLevel = 'info';
|
|
411
|
-
const count = notifications.filter((n) => !n.seen).length;
|
|
412
|
-
notifications.forEach((n) => (maxLevel = n.level && this._levels[n.level] > this._levels[maxLevel] ? n.level : maxLevel));
|
|
413
|
-
return count > 0 ? { maxLevel, count } : undefined;
|
|
414
|
-
})));
|
|
415
515
|
this.apps = input.required();
|
|
416
|
-
|
|
417
|
-
this.#
|
|
516
|
+
// set shell apps from component input
|
|
517
|
+
this.#appsInputEffect = effect(() => this.#shell.setApps(this.apps()));
|
|
418
518
|
this.checkedForInitialRoute = false;
|
|
419
519
|
this.enableTenantSwitch = false;
|
|
520
|
+
this.smallScreenLayout = this.#device.smallScreenLayout;
|
|
420
521
|
this.config = input();
|
|
522
|
+
/**
|
|
523
|
+
* If set to true, the shell will adapt to small screen sizes (e.g. mobile phones).
|
|
524
|
+
* It also will propagate this setting to other components that might act aupon it.
|
|
525
|
+
* To do so, you can use the `DeviceService` and check for `smallScreenLayout` signal changes.
|
|
526
|
+
*/
|
|
527
|
+
this.supportsSmallScreens = input(false);
|
|
421
528
|
this.#shellConfigEffect = effect(() => {
|
|
422
529
|
const cfg = this.config();
|
|
423
530
|
if (cfg) {
|
|
424
531
|
this.#shell.setShellConfig(cfg);
|
|
425
532
|
}
|
|
426
533
|
});
|
|
427
|
-
this.registerIcons = computed(() => {
|
|
428
|
-
const apps = this.apps();
|
|
429
|
-
const config = this.#shell.shellConfig();
|
|
430
|
-
const namespace = config.shellIconNamespace;
|
|
431
|
-
// find svg-icons to register and put them in an array
|
|
432
|
-
const customSvgIconsToRegister = apps
|
|
433
|
-
.filter(({ svgIcon }) => !!svgIcon)
|
|
434
|
-
.map(({ svgIcon, iconName }) => ({ svgIcon, iconName }));
|
|
435
|
-
customSvgIconsToRegister.push(config.appIcon);
|
|
436
|
-
// register svg-icons
|
|
437
|
-
const allRegistered = customSvgIconsToRegister.every((icon) => {
|
|
438
|
-
return namespace && icon && icon.svgIcon && icon.iconName
|
|
439
|
-
? !!this.#matIconRegistryService.addSvgIconLiteralInNamespace(namespace, icon.iconName, this.safeHtmlPipe.transform(icon.svgIcon))
|
|
440
|
-
: false;
|
|
441
|
-
});
|
|
442
|
-
return allRegistered;
|
|
443
|
-
});
|
|
444
534
|
this.#layoutSettings.init();
|
|
445
|
-
this.translate.onLangChange.subscribe(() => this._setCommands(true));
|
|
446
|
-
// this.router.events.pipe(
|
|
447
|
-
// // filter(e => e instanceof NavigationStart)
|
|
448
|
-
// ).subscribe((e) => {
|
|
449
|
-
// console.log(e);
|
|
450
|
-
// });
|
|
451
535
|
this.router.events
|
|
452
|
-
.pipe(filter$1((e) => e instanceof NavigationEnd), map((e) => e))
|
|
536
|
+
.pipe(filter$1((e) => e instanceof NavigationEnd), map$1((e) => e))
|
|
453
537
|
.subscribe((e) => this._processRouterNavigationEnd(e));
|
|
454
|
-
this.#device.init();
|
|
455
538
|
this.userService.user$.subscribe((user) => {
|
|
456
539
|
if (user) {
|
|
457
540
|
this.checkedForInitialRoute = !(!this.user || this.user.id !== user.id);
|
|
@@ -459,14 +542,6 @@ class ClientShellComponent {
|
|
|
459
542
|
}
|
|
460
543
|
this.user = user;
|
|
461
544
|
});
|
|
462
|
-
window.addEventListener('storage', (evt) => {
|
|
463
|
-
if (evt.key === this.APP_LOGOUT_EVENT_KEY) {
|
|
464
|
-
this.appLogout(true);
|
|
465
|
-
}
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
getAppTitle(a) {
|
|
469
|
-
return a.title || '';
|
|
470
545
|
}
|
|
471
546
|
handleCustomActionButtonClick(button) {
|
|
472
547
|
button.onClick({ router: this.router, config: this.#config });
|
|
@@ -483,27 +558,6 @@ class ClientShellComponent {
|
|
|
483
558
|
return 0;
|
|
484
559
|
return event.dataTransfer ? Array.from(event.dataTransfer.items || []).filter((i) => i.kind === 'file' && i.type).length : 0;
|
|
485
560
|
}
|
|
486
|
-
_setCommands(update) {
|
|
487
|
-
const commands = this.apps().map((a, i) => ({
|
|
488
|
-
id: `nav.app.${i}`,
|
|
489
|
-
label: this.translate.instant('yuv.shell.cmd.app.open', { title: a.title }),
|
|
490
|
-
callback: () => this.router.navigate([a.path])
|
|
491
|
-
}));
|
|
492
|
-
commands.push({
|
|
493
|
-
id: `nav.shell.settings`,
|
|
494
|
-
label: this.translate.instant('yuv.shell.cmd.open.settings'),
|
|
495
|
-
callback: () => this.router.navigate(['settings'])
|
|
496
|
-
});
|
|
497
|
-
commands.push({
|
|
498
|
-
id: `nav.shell.logout`,
|
|
499
|
-
label: this.translate.instant('yuv.shell.cmd.logout'),
|
|
500
|
-
callback: () => this.appLogout()
|
|
501
|
-
});
|
|
502
|
-
if (update)
|
|
503
|
-
this.commandPalette.updateCommands(commands);
|
|
504
|
-
else
|
|
505
|
-
this.commandPalette.registerCommands(commands);
|
|
506
|
-
}
|
|
507
561
|
requestSubscription() {
|
|
508
562
|
if (!this.#swPush.isEnabled) {
|
|
509
563
|
return;
|
|
@@ -515,13 +569,6 @@ class ClientShellComponent {
|
|
|
515
569
|
}))
|
|
516
570
|
.subscribe();
|
|
517
571
|
}
|
|
518
|
-
appLogout(triggeredFromOtherTab) {
|
|
519
|
-
if (!triggeredFromOtherTab) {
|
|
520
|
-
// send storage event to logout all other open tabs
|
|
521
|
-
window.localStorage.setItem(this.APP_LOGOUT_EVENT_KEY, `${Date.now()}`);
|
|
522
|
-
}
|
|
523
|
-
this.userService.logout('');
|
|
524
|
-
}
|
|
525
572
|
_processRouterNavigationEnd(e) {
|
|
526
573
|
if (!this.checkedForInitialRoute) {
|
|
527
574
|
this.checkedForInitialRoute = true;
|
|
@@ -533,7 +580,7 @@ class ClientShellComponent {
|
|
|
533
580
|
// get persisted routes to decide where to redirect the logged in user to
|
|
534
581
|
this.auth
|
|
535
582
|
.getInitialRequestUri()
|
|
536
|
-
.pipe(switchMap$1((res) => this.auth.resetInitialRequestUri().pipe(map((_) => res))))
|
|
583
|
+
.pipe(switchMap$1((res) => this.auth.resetInitialRequestUri().pipe(map$1((_) => res))))
|
|
537
584
|
.subscribe((res) => {
|
|
538
585
|
const loginRes = res && !ignoreRoutes.includes(res.uri) ? res : null;
|
|
539
586
|
if (loginRes)
|
|
@@ -546,16 +593,16 @@ class ClientShellComponent {
|
|
|
546
593
|
return `${Utils.getBaseHref()}${r}`.replace('//', '/');
|
|
547
594
|
}
|
|
548
595
|
ngOnInit() {
|
|
596
|
+
this.#device.init(this.supportsSmallScreens());
|
|
549
597
|
this.#shell._init();
|
|
550
598
|
clientShellRoutes.forEach((route) => {
|
|
551
599
|
this.router.config.push(route);
|
|
552
600
|
});
|
|
553
601
|
this.router.resetConfig(this.router.config);
|
|
554
|
-
this._setCommands();
|
|
555
602
|
this.requestSubscription();
|
|
556
603
|
}
|
|
557
604
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ClientShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
558
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ClientShellComponent, isStandalone: true, selector: "yuv-client-shell", inputs: { apps: { classPropertyName: "apps", publicName: "apps", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "
|
|
605
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ClientShellComponent, isStandalone: true, selector: "yuv-client-shell", inputs: { apps: { classPropertyName: "apps", publicName: "apps", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, supportsSmallScreens: { classPropertyName: "supportsSmallScreens", publicName: "supportsSmallScreens", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "dragover": "onDragOver($event)" }, properties: { "class.small-screen": "smallScreenLayout()" } }, providers: [SafeHtmlPipe], ngImport: i0, template: "<yuv-metadata-default-templates></yuv-metadata-default-templates>\n\n<!-- @let iconsRegistered = registerIcons(); -->\n@let showAppHeader = css.appHeader();\n\n<!-- gloabl busy indicator if app header is present, it will deal with busy indication -->\n@if (!showAppHeader && (busy$ | async)) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n}\n@if (showAppHeader) {\n <yuv-app-header> </yuv-app-header>\n}\n<yuv-sidebar-nav></yuv-sidebar-nav>\n\n<main id=\"main-shell_content\" aria-label=\"main shell content\" [inert]=\"asideOutlet.isActivated\">\n <router-outlet></router-outlet>\n</main>\n\n<!-- outlet for aside modals like notifications -->\n<div class=\"asideOutlet\" [hidden]=\"!asideOutlet.isActivated\">\n <router-outlet name=\"aside\" #asideOutlet=\"outlet\"></router-outlet>\n</div>\n\n<div id=\"fi\" inert></div>\n", styles: [":host{display:block}:host.small-screen nav .menu{display:flex;align-items:center;gap:var(--ymt-spacing-s);padding:var(--ymt-sizing-s) var(--ymt-sizing-m)}:host:not(.small-screen) .sidenav{height:100%;width:var(--yuv-side-nav-width, var(--ymt-sizing-6xl));background:transparent;color:inherit;display:flex;flex-direction:column;justify-content:flex-start;align-items:center;gap:var(--ymt-spacing-s)}:host:not(.small-screen) .sidenav .logo-container{width:100%}:host:not(.small-screen) .sidenav .app-switcher-container{flex:1 1 0;overflow-y:auto;display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);scrollbar-width:none;-ms-overflow-style:none}:host:not(.small-screen) .sidenav .app-switcher-container::-webkit-scrollbar{display:none}:host:not(.small-screen) .sidenav .actions-container{justify-self:flex-end;border-top:var(--ymt-outline-width) solid var(--ymt-outline-variant);display:flex;flex-direction:column;gap:var(--ymt-spacing-s);padding:var(--ymt-spacing-m) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-wrapper{display:contents}:host:not(.small-screen) .sidenav .shell-nav__app-switcher{display:contents;list-style:none}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item{position:relative;padding:var(--ymt-spacing-2xs) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:after{content:\"\";position:absolute;inset:0;top:var(--ymt-spacing-2xs);aspect-ratio:1/1;background-color:var(--mat-icon-button-state-layer-color, var(--mat-sys-on-surface-variant));border-radius:var(--ymt-corner-full);visibility:hidden;opacity:0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:before{content:\"\";height:var(--ymt-sizing-3xs);width:var(--ymt-sizing-xs);border-radius:var(--ymt-corner-full);position:absolute;left:50%;bottom:0;transform:translate3d(-50%,0,0);background-color:transparent;transition:background-color .1s ease-in-out}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:before{background-color:var(--ymt-primary)}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:after{visibility:visible;opacity:.08;pointer-events:none}:host:not(.small-screen) .sidenav .shell-nav__actions{display:contents}:host{position:absolute;inset:0;overflow:hidden;background-color:var(--ymt-surface-app);display:grid;grid-template-rows:auto 1fr;grid-template-columns:auto 1fr;grid-template-areas:\"appheader appheader\" \"nav main\"}:host.small-screen{grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"appheader\" \"main\" \"nav\"}:host .progress-bar{position:absolute}:host yuv-app-header{grid-area:appheader}:host yuv-sidebar-nav{grid-area:nav}:host main{grid-area:main;overflow:hidden;color:var(--ymt-text-color)}:host .asideOutlet{position:absolute;inset:0;padding-inline-start:var(--yuv-side-nav-width, var(--ymt-sizing-6xl))}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i6.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: YuvMetadataFormDefaultsModule }, { kind: "component", type: i2$3.MetadataDefaultTemplatesComponent, selector: "yuv-metadata-default-templates" }, { kind: "component", type: YuvAppHeaderComponent, selector: "yuv-app-header" }, { kind: "component", type: SidebarNavComponent, selector: "yuv-sidebar-nav" }, { kind: "component", type: MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }] }); }
|
|
559
606
|
}
|
|
560
607
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ClientShellComponent, decorators: [{
|
|
561
608
|
type: Component,
|
|
@@ -566,16 +613,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
566
613
|
YuvMetadataFormDefaultsModule,
|
|
567
614
|
YuvAppHeaderComponent,
|
|
568
615
|
SidebarNavComponent,
|
|
569
|
-
MatIcon,
|
|
570
616
|
MatProgressBar,
|
|
571
|
-
MatTooltipModule
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
576
|
-
type: HostListener,
|
|
577
|
-
args: ['document:focusin', ['$event']]
|
|
578
|
-
}], onDragOver: [{
|
|
617
|
+
MatTooltipModule
|
|
618
|
+
], providers: [SafeHtmlPipe], host: {
|
|
619
|
+
'[class.small-screen]': 'smallScreenLayout()'
|
|
620
|
+
}, template: "<yuv-metadata-default-templates></yuv-metadata-default-templates>\n\n<!-- @let iconsRegistered = registerIcons(); -->\n@let showAppHeader = css.appHeader();\n\n<!-- gloabl busy indicator if app header is present, it will deal with busy indication -->\n@if (!showAppHeader && (busy$ | async)) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n}\n@if (showAppHeader) {\n <yuv-app-header> </yuv-app-header>\n}\n<yuv-sidebar-nav></yuv-sidebar-nav>\n\n<main id=\"main-shell_content\" aria-label=\"main shell content\" [inert]=\"asideOutlet.isActivated\">\n <router-outlet></router-outlet>\n</main>\n\n<!-- outlet for aside modals like notifications -->\n<div class=\"asideOutlet\" [hidden]=\"!asideOutlet.isActivated\">\n <router-outlet name=\"aside\" #asideOutlet=\"outlet\"></router-outlet>\n</div>\n\n<div id=\"fi\" inert></div>\n", styles: [":host{display:block}:host.small-screen nav .menu{display:flex;align-items:center;gap:var(--ymt-spacing-s);padding:var(--ymt-sizing-s) var(--ymt-sizing-m)}:host:not(.small-screen) .sidenav{height:100%;width:var(--yuv-side-nav-width, var(--ymt-sizing-6xl));background:transparent;color:inherit;display:flex;flex-direction:column;justify-content:flex-start;align-items:center;gap:var(--ymt-spacing-s)}:host:not(.small-screen) .sidenav .logo-container{width:100%}:host:not(.small-screen) .sidenav .app-switcher-container{flex:1 1 0;overflow-y:auto;display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);scrollbar-width:none;-ms-overflow-style:none}:host:not(.small-screen) .sidenav .app-switcher-container::-webkit-scrollbar{display:none}:host:not(.small-screen) .sidenav .actions-container{justify-self:flex-end;border-top:var(--ymt-outline-width) solid var(--ymt-outline-variant);display:flex;flex-direction:column;gap:var(--ymt-spacing-s);padding:var(--ymt-spacing-m) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-wrapper{display:contents}:host:not(.small-screen) .sidenav .shell-nav__app-switcher{display:contents;list-style:none}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item{position:relative;padding:var(--ymt-spacing-2xs) 0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:after{content:\"\";position:absolute;inset:0;top:var(--ymt-spacing-2xs);aspect-ratio:1/1;background-color:var(--mat-icon-button-state-layer-color, var(--mat-sys-on-surface-variant));border-radius:var(--ymt-corner-full);visibility:hidden;opacity:0}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item:before{content:\"\";height:var(--ymt-sizing-3xs);width:var(--ymt-sizing-xs);border-radius:var(--ymt-corner-full);position:absolute;left:50%;bottom:0;transform:translate3d(-50%,0,0);background-color:transparent;transition:background-color .1s ease-in-out}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:before{background-color:var(--ymt-primary)}:host:not(.small-screen) .sidenav .shell-nav__app-switcher-item--active:after{visibility:visible;opacity:.08;pointer-events:none}:host:not(.small-screen) .sidenav .shell-nav__actions{display:contents}:host{position:absolute;inset:0;overflow:hidden;background-color:var(--ymt-surface-app);display:grid;grid-template-rows:auto 1fr;grid-template-columns:auto 1fr;grid-template-areas:\"appheader appheader\" \"nav main\"}:host.small-screen{grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"appheader\" \"main\" \"nav\"}:host .progress-bar{position:absolute}:host yuv-app-header{grid-area:appheader}:host yuv-sidebar-nav{grid-area:nav}:host main{grid-area:main;overflow:hidden;color:var(--ymt-text-color)}:host .asideOutlet{position:absolute;inset:0;padding-inline-start:var(--yuv-side-nav-width, var(--ymt-sizing-6xl))}\n"] }]
|
|
621
|
+
}], ctorParameters: () => [], propDecorators: { onDragOver: [{
|
|
579
622
|
type: HostListener,
|
|
580
623
|
args: ['dragover', ['$event']]
|
|
581
624
|
}] } });
|