@yuuvis/client-framework 3.0.0-beta.21.0 → 3.0.0-beta.21.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 (39) hide show
  1. package/autocomplete/README.md +1 -1
  2. package/common/README.md +1 -1
  3. package/fesm2022/yuuvis-client-framework-autocomplete.mjs +5 -236
  4. package/fesm2022/yuuvis-client-framework-autocomplete.mjs.map +1 -1
  5. package/fesm2022/yuuvis-client-framework-common.mjs +3 -1793
  6. package/fesm2022/yuuvis-client-framework-common.mjs.map +1 -1
  7. package/fesm2022/yuuvis-client-framework-list.mjs +3 -667
  8. package/fesm2022/yuuvis-client-framework-list.mjs.map +1 -1
  9. package/fesm2022/yuuvis-client-framework-master-details.mjs +3 -136
  10. package/fesm2022/yuuvis-client-framework-master-details.mjs.map +1 -1
  11. package/fesm2022/yuuvis-client-framework-overflow-hidden.mjs +3 -62
  12. package/fesm2022/yuuvis-client-framework-overflow-hidden.mjs.map +1 -1
  13. package/fesm2022/yuuvis-client-framework-overflow-menu.mjs +3 -129
  14. package/fesm2022/yuuvis-client-framework-overflow-menu.mjs.map +1 -1
  15. package/fesm2022/yuuvis-client-framework-popout.mjs +3 -239
  16. package/fesm2022/yuuvis-client-framework-popout.mjs.map +1 -1
  17. package/fesm2022/yuuvis-client-framework-split-view.mjs +3 -318
  18. package/fesm2022/yuuvis-client-framework-split-view.mjs.map +1 -1
  19. package/fesm2022/yuuvis-client-framework-widget-grid.mjs +3 -942
  20. package/fesm2022/yuuvis-client-framework-widget-grid.mjs.map +1 -1
  21. package/lib/assets/i18n/de.json +1 -55
  22. package/lib/assets/i18n/en.json +1 -55
  23. package/list/README.md +1 -1
  24. package/master-details/README.md +1 -1
  25. package/overflow-hidden/README.md +1 -1
  26. package/overflow-menu/README.md +1 -1
  27. package/package.json +6 -5
  28. package/popout/README.md +1 -1
  29. package/split-view/README.md +1 -1
  30. package/types/yuuvis-client-framework-autocomplete.d.ts +1 -89
  31. package/types/yuuvis-client-framework-common.d.ts +1 -536
  32. package/types/yuuvis-client-framework-list.d.ts +1 -380
  33. package/types/yuuvis-client-framework-master-details.d.ts +1 -69
  34. package/types/yuuvis-client-framework-overflow-hidden.d.ts +1 -28
  35. package/types/yuuvis-client-framework-overflow-menu.d.ts +1 -52
  36. package/types/yuuvis-client-framework-popout.d.ts +1 -106
  37. package/types/yuuvis-client-framework-split-view.d.ts +1 -197
  38. package/types/yuuvis-client-framework-widget-grid.d.ts +1 -299
  39. package/widget-grid/README.md +1 -46
@@ -1,1801 +1,11 @@
1
- import * as i0 from '@angular/core';
2
- import { input, ChangeDetectionStrategy, Component, inject, computed, DestroyRef, viewChild, signal, afterNextRender, ElementRef, Input, Directive, output, EnvironmentInjector, ViewContainerRef, effect, NgZone, DOCUMENT, contentChildren, Injectable, forwardRef, Renderer2, NgModule, InjectionToken, makeEnvironmentProviders, RendererFactory2 } from '@angular/core';
3
- import { MatButtonModule } from '@angular/material/button';
4
- import * as i1 from '@angular/material/dialog';
5
- import { MatDialogActions, MatDialogTitle, MatDialogContent, MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
6
- import { TranslatePipe, AppCacheService, RetentionService, LocaleDatePipe } from '@yuuvis/client-core';
7
- import { YmtButtonDirective, YmtIconButtonDirective } from '@yuuvis/material';
8
- import * as i1$1 from '@angular/material/icon';
9
- import { MatIconModule } from '@angular/material/icon';
10
- import { TranslatePipe as TranslatePipe$1, TranslateService } from '@ngx-translate/core';
11
- import { MatProgressSpinner } from '@angular/material/progress-spinner';
12
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
13
- import { Subject, fromEvent, merge, timer, of, forkJoin, map as map$1 } from 'rxjs';
14
- import { debounceTime, tap, filter, switchMap, map, takeUntil } from 'rxjs/operators';
15
- import { NG_VALUE_ACCESSOR, NgControl, FormControlDirective, FormControlName, NgModel } from '@angular/forms';
16
- import { marker } from '@colsen1991/ngx-translate-extract-marker';
17
- import { coerceBooleanProperty } from '@angular/cdk/coercion';
18
-
19
- class DialogComponent {
20
- constructor() {
21
- this.headertitle = input(null, ...(ngDevMode ? [{ debugName: "headertitle" }] : /* istanbul ignore next */ []));
22
- /**
23
- * @deprecated use headertitle instead
24
- */
25
- this.headertitel = input(null, ...(ngDevMode ? [{ debugName: "headertitel" }] : /* istanbul ignore next */ []));
26
- }
27
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
28
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DialogComponent, isStandalone: true, selector: "yuv-dialog", inputs: { headertitle: { classPropertyName: "headertitle", publicName: "headertitle", isSignal: true, isRequired: false, transformFunction: null }, headertitel: { classPropertyName: "headertitel", publicName: "headertitel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
29
- @let title = headertitle() ?? headertitel();
30
- @if (title) {
31
- <h2 mat-dialog-title>{{ title }}</h2>
32
- }
33
- <mat-dialog-content>
34
- <ng-content select="main" />
35
- </mat-dialog-content>
36
-
37
- <mat-dialog-actions align="end" class="footer">
38
- <ng-content select="footer" />
39
- </mat-dialog-actions>
40
- `, isInline: true, styles: [":host{--_dialog-area-background: var(--dialog-area-background, light-dark(var(--ymt-surface), var(--ymt-surface-container-low)));--_dialog-area-divider-color: var(--dialog-area-divider-color, var(--ymt-outline-variant));height:100%;display:grid;grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"header\" \"content\" \"footer\"}:host h2{grid-area:header;background-color:var(--_dialog-area-background);border-block-end:1px solid var(--_dialog-area-divider-color)}:host .footer{background-color:var(--_dialog-area-background);border-block-start:1px solid var(--_dialog-area-divider-color);gap:calc(var(--ymt-font-body) / 2)}:host.not-separated{background-color:var(--ymt-surface-app)}:host.not-separated h2,:host.not-separated .footer{background-color:transparent;border:0}.mat-mdc-dialog-container{height:100%}mat-dialog-content:empty{display:contents}mat-dialog-content main{display:var(--ymt-dialog-content-display);grid-area:content;overflow:hidden}mat-dialog-actions{grid-area:footer}mat-dialog-actions:empty{display:contents}\n"], dependencies: [{ kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
41
- }
42
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogComponent, decorators: [{
43
- type: Component,
44
- args: [{ selector: 'yuv-dialog', imports: [MatDialogActions, MatDialogTitle, MatDialogContent], template: `
45
- @let title = headertitle() ?? headertitel();
46
- @if (title) {
47
- <h2 mat-dialog-title>{{ title }}</h2>
48
- }
49
- <mat-dialog-content>
50
- <ng-content select="main" />
51
- </mat-dialog-content>
52
-
53
- <mat-dialog-actions align="end" class="footer">
54
- <ng-content select="footer" />
55
- </mat-dialog-actions>
56
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--_dialog-area-background: var(--dialog-area-background, light-dark(var(--ymt-surface), var(--ymt-surface-container-low)));--_dialog-area-divider-color: var(--dialog-area-divider-color, var(--ymt-outline-variant));height:100%;display:grid;grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"header\" \"content\" \"footer\"}:host h2{grid-area:header;background-color:var(--_dialog-area-background);border-block-end:1px solid var(--_dialog-area-divider-color)}:host .footer{background-color:var(--_dialog-area-background);border-block-start:1px solid var(--_dialog-area-divider-color);gap:calc(var(--ymt-font-body) / 2)}:host.not-separated{background-color:var(--ymt-surface-app)}:host.not-separated h2,:host.not-separated .footer{background-color:transparent;border:0}.mat-mdc-dialog-container{height:100%}mat-dialog-content:empty{display:contents}mat-dialog-content main{display:var(--ymt-dialog-content-display);grid-area:content;overflow:hidden}mat-dialog-actions{grid-area:footer}mat-dialog-actions:empty{display:contents}\n"] }]
57
- }], propDecorators: { headertitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headertitle", required: false }] }], headertitel: [{ type: i0.Input, args: [{ isSignal: true, alias: "headertitel", required: false }] }] } });
58
-
59
- class ConfirmComponent {
60
- constructor() {
61
- this.dialogData = inject(MAT_DIALOG_DATA);
62
- this.buttonType = computed(() => {
63
- switch (this.dialogData.level) {
64
- case 'warning':
65
- return 'danger';
66
- // case 'warning':
67
- // return 'tertiary' as ButtonType;
68
- // case 'info':
69
- // return 'primary' as ButtonType;
70
- // case 'success':
71
- // return 'primary' as ButtonType;
72
- default:
73
- return 'primary';
74
- }
75
- }, ...(ngDevMode ? [{ debugName: "buttonType" }] : /* istanbul ignore next */ []));
76
- }
77
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ConfirmComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
78
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ConfirmComponent, isStandalone: true, selector: "yuv-confirm", ngImport: i0, template: "<yuv-dialog [headertitle]=\"dialogData.title || ''\">\n <main>{{ dialogData.message }}</main>\n <footer>\n @if (!dialogData.hideCancelButton) {\n <button ymtButton=\"secondary\" mat-dialog-close type=\"button\">\n {{ dialogData.cancelLabel || ('yuv.confirm.cancel' | translate) }}\n </button>\n }\n <button [ymtButton]=\"buttonType()\" type=\"button\" [mat-dialog-close]=\"true\">\n {{ dialogData.confirmLabel || ('yuv.confirm.confirm' | translate) }}\n </button>\n </footer>\n</yuv-dialog>\n", styles: [":host{display:contents}:host main{padding:var(--ymt-spacing-m)}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "component", type: DialogComponent, selector: "yuv-dialog", inputs: ["headertitle", "headertitel"] }, { kind: "directive", type: YmtButtonDirective, selector: "button[ymtButton], a[ymtButton]", inputs: ["ymtButton", "disabled", "aria-disabled", "disableRipple", "disabledInteractive", "button-size"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
79
- }
80
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ConfirmComponent, decorators: [{
81
- type: Component,
82
- args: [{ selector: 'yuv-confirm', imports: [MatButtonModule, TranslatePipe, MatDialogModule, DialogComponent, YmtButtonDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<yuv-dialog [headertitle]=\"dialogData.title || ''\">\n <main>{{ dialogData.message }}</main>\n <footer>\n @if (!dialogData.hideCancelButton) {\n <button ymtButton=\"secondary\" mat-dialog-close type=\"button\">\n {{ dialogData.cancelLabel || ('yuv.confirm.cancel' | translate) }}\n </button>\n }\n <button [ymtButton]=\"buttonType()\" type=\"button\" [mat-dialog-close]=\"true\">\n {{ dialogData.confirmLabel || ('yuv.confirm.confirm' | translate) }}\n </button>\n </footer>\n</yuv-dialog>\n", styles: [":host{display:contents}:host main{padding:var(--ymt-spacing-m)}\n"] }]
83
- }] });
84
-
85
- const DEFAULT_SCROLL_AMOUNT$1 = 150;
86
- /**
87
- * Wrapper component that adds left/right scroll buttons when its content overflows horizontally.
88
- * Buttons appear only when there is overflow in the respective direction.
89
- *
90
- * @example
91
- * <yuv-scroll-buttons>
92
- * <mat-chip-grid>...</mat-chip-grid>
93
- * </yuv-scroll-buttons>
94
- */
95
- class ScrollButtonsComponent {
96
- #destroyRef;
97
- #resizeObserver;
98
- constructor() {
99
- this.#destroyRef = inject(DestroyRef);
100
- this.scrollContainer = viewChild.required('scrollContainer');
101
- /** How many pixels to scroll per button click. */
102
- this.scrollAmount = input(DEFAULT_SCROLL_AMOUNT$1, ...(ngDevMode ? [{ debugName: "scrollAmount" }] : /* istanbul ignore next */ []));
103
- this.showLeftButton = signal(false, ...(ngDevMode ? [{ debugName: "showLeftButton" }] : /* istanbul ignore next */ []));
104
- this.showRightButton = signal(false, ...(ngDevMode ? [{ debugName: "showRightButton" }] : /* istanbul ignore next */ []));
105
- afterNextRender(() => {
106
- const element = this.scrollContainer().nativeElement;
107
- this.#resizeObserver = new ResizeObserver(() => this.#checkOverflow());
108
- this.#resizeObserver.observe(element);
109
- // Also observe mutations to detect when children (chips) are added/removed
110
- const mutationObserver = new MutationObserver(() => this.#checkOverflow());
111
- mutationObserver.observe(element, { childList: true, subtree: true });
112
- this.#destroyRef.onDestroy(() => {
113
- this.#resizeObserver?.disconnect();
114
- mutationObserver.disconnect();
115
- });
116
- this.#checkOverflow();
117
- });
118
- }
119
- onScroll() {
120
- this.#checkOverflow();
121
- }
122
- scrollLeft() {
123
- this.scrollContainer().nativeElement.scrollBy({ left: -this.scrollAmount(), behavior: 'smooth' });
124
- }
125
- scrollRight() {
126
- this.scrollContainer().nativeElement.scrollBy({ left: this.scrollAmount(), behavior: 'smooth' });
127
- }
128
- #checkOverflow() {
129
- const element = this.scrollContainer().nativeElement;
130
- const threshold = 1; // account for sub-pixel rounding
131
- this.showLeftButton.set(element.scrollLeft > threshold);
132
- this.showRightButton.set(element.scrollLeft + element.clientWidth < element.scrollWidth - threshold);
133
- }
134
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ScrollButtonsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
135
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ScrollButtonsComponent, isStandalone: true, selector: "yuv-scroll-buttons", inputs: { scrollAmount: { classPropertyName: "scrollAmount", publicName: "scrollAmount", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (showLeftButton()) {\n <button\n class=\"scroll-btn scroll-btn--left\"\n (click)=\"scrollLeft()\"\n [attr.aria-label]=\"'yuv.object-form-element.scroll.button.left' | translate\"\n >\n <mat-icon>chevron_left</mat-icon>\n </button>\n}\n<div class=\"scroll-content\" #scrollContainer (scroll)=\"onScroll()\">\n <ng-content />\n</div>\n@if (showRightButton()) {\n <button\n class=\"scroll-btn scroll-btn--right\"\n (click)=\"scrollRight()\"\n [attr.aria-label]=\"'yuv.object-form-element.scroll.button.right' | translate\"\n >\n <mat-icon>chevron_right</mat-icon>\n </button>\n}\n", styles: [":host{display:flex;align-items:center;flex:1;min-width:0}.scroll-content{flex:1;overflow-x:auto;scrollbar-width:none;min-width:0}.scroll-content ::ng-deep mat-chip-grid .mdc-evolution-chip-set__chips{flex-wrap:nowrap}.scroll-content ::ng-deep mat-chip-grid mat-chip-row{flex-shrink:0;max-width:none}.scroll-content ::ng-deep mat-chip-grid mat-chip-row .mdc-evolution-chip__cell--primary{max-width:none}.scroll-content ::ng-deep mat-chip-grid mat-chip-row .mdc-evolution-chip__text-label,.scroll-content ::ng-deep mat-chip-grid mat-chip-row .mat-mdc-chip-action-label{white-space:nowrap}.scroll-btn{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:0;color:var(--ymv-text-color-subtle);width:var(--ymv-sizing-m);height:var(--ymv-sizing-m);background-color:var(--ymv-surface-hover)}.scroll-btn:hover,.scroll-btn:focus-visible{color:var(--ymv-text-color)}.scroll-btn:focus-visible{outline:2px solid var(--ymv-primary-color);outline-offset:-2px}.scroll-btn mat-icon{font-size:20px;width:20px;height:20px}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: TranslatePipe$1, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
136
- }
137
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ScrollButtonsComponent, decorators: [{
138
- type: Component,
139
- args: [{ selector: 'yuv-scroll-buttons', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIconModule, TranslatePipe$1], template: "@if (showLeftButton()) {\n <button\n class=\"scroll-btn scroll-btn--left\"\n (click)=\"scrollLeft()\"\n [attr.aria-label]=\"'yuv.object-form-element.scroll.button.left' | translate\"\n >\n <mat-icon>chevron_left</mat-icon>\n </button>\n}\n<div class=\"scroll-content\" #scrollContainer (scroll)=\"onScroll()\">\n <ng-content />\n</div>\n@if (showRightButton()) {\n <button\n class=\"scroll-btn scroll-btn--right\"\n (click)=\"scrollRight()\"\n [attr.aria-label]=\"'yuv.object-form-element.scroll.button.right' | translate\"\n >\n <mat-icon>chevron_right</mat-icon>\n </button>\n}\n", styles: [":host{display:flex;align-items:center;flex:1;min-width:0}.scroll-content{flex:1;overflow-x:auto;scrollbar-width:none;min-width:0}.scroll-content ::ng-deep mat-chip-grid .mdc-evolution-chip-set__chips{flex-wrap:nowrap}.scroll-content ::ng-deep mat-chip-grid mat-chip-row{flex-shrink:0;max-width:none}.scroll-content ::ng-deep mat-chip-grid mat-chip-row .mdc-evolution-chip__cell--primary{max-width:none}.scroll-content ::ng-deep mat-chip-grid mat-chip-row .mdc-evolution-chip__text-label,.scroll-content ::ng-deep mat-chip-grid mat-chip-row .mat-mdc-chip-action-label{white-space:nowrap}.scroll-btn{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:0;color:var(--ymv-text-color-subtle);width:var(--ymv-sizing-m);height:var(--ymv-sizing-m);background-color:var(--ymv-surface-hover)}.scroll-btn:hover,.scroll-btn:focus-visible{color:var(--ymv-text-color)}.scroll-btn:focus-visible{outline:2px solid var(--ymv-primary-color);outline-offset:-2px}.scroll-btn mat-icon{font-size:20px;width:20px;height:20px}\n"] }]
140
- }], ctorParameters: () => [], propDecorators: { scrollContainer: [{ type: i0.ViewChild, args: ['scrollContainer', { isSignal: true }] }], scrollAmount: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollAmount", required: false }] }] } });
141
-
142
- /**
143
- * Directive putting focus on a 'focusable' child element.
144
- * By default the first focusable child will receive focus.
145
- */
146
- class AutofocusChildDirective {
147
- #elRef = inject(ElementRef);
148
- #targetIndex = 0;
149
- set yuvAutofocusChild(s) {
150
- const i = parseInt(s);
151
- this.#targetIndex = !isNaN(i) ? i : 0;
152
- }
153
- #getFirstFocusableChild() {
154
- const focusableElements = [
155
- ...this.#elRef.nativeElement.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])')
156
- ].filter((el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'));
157
- return focusableElements[this.#targetIndex];
158
- }
159
- ngAfterViewInit() {
160
- setTimeout(() => {
161
- const focusEl = this.#getFirstFocusableChild();
162
- if (focusEl)
163
- focusEl.focus();
164
- });
165
- }
166
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AutofocusChildDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
167
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AutofocusChildDirective, isStandalone: true, selector: "[yuvAutofocusChild]", inputs: { yuvAutofocusChild: "yuvAutofocusChild" }, ngImport: i0 }); }
168
- }
169
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AutofocusChildDirective, decorators: [{
170
- type: Directive,
171
- args: [{
172
- selector: '[yuvAutofocusChild]',
173
- standalone: true
174
- }]
175
- }], propDecorators: { yuvAutofocusChild: [{
176
- type: Input
177
- }] } });
178
-
179
- /**
180
- * Directive putting delayed focus on an element. If no
181
- * delay is set, the focus will be set immediately.
182
- *
183
- * You have to ensure that the element is focusable when adding this directive to it.
184
- */
185
- class AutofocusDelayedDirective {
186
- constructor() {
187
- this.#elRef = inject(ElementRef);
188
- /**
189
- * Sets the delay in milliseconds. If no delay is set, the focus will be set immediately.
190
- */
191
- this.yuvAutofocusDelayed = input(0, ...(ngDevMode ? [{ debugName: "yuvAutofocusDelayed" }] : /* istanbul ignore next */ []));
192
- this.#delay = computed(() => {
193
- const d = this.yuvAutofocusDelayed();
194
- return typeof d === 'string' ? parseInt(d) : d;
195
- }, ...(ngDevMode ? [{ debugName: "#delay" }] : /* istanbul ignore next */ []));
196
- }
197
- #elRef;
198
- #delay;
199
- ngAfterViewInit() {
200
- setTimeout(() => {
201
- this.#elRef.nativeElement.focus();
202
- }, this.#delay());
203
- }
204
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AutofocusDelayedDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
205
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: AutofocusDelayedDirective, isStandalone: true, selector: "[yuvAutofocusDelayed]", inputs: { yuvAutofocusDelayed: { classPropertyName: "yuvAutofocusDelayed", publicName: "yuvAutofocusDelayed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
206
- }
207
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AutofocusDelayedDirective, decorators: [{
208
- type: Directive,
209
- args: [{
210
- selector: '[yuvAutofocusDelayed]',
211
- standalone: true
212
- }]
213
- }], propDecorators: { yuvAutofocusDelayed: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvAutofocusDelayed", required: false }] }] } });
214
-
215
- class BusyOverlayComponent {
216
- constructor() {
217
- this.config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : /* istanbul ignore next */ []));
218
- this.error = input(undefined, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
219
- this.errorDismiss = output();
220
- }
221
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: BusyOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
222
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: BusyOverlayComponent, isStandalone: true, selector: "yuv-busy-overlay", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { errorDismiss: "errorDismiss" }, host: { properties: { "class.error": "!!error()", "class.blured": "!!config()?.blur", "style.--backdrop-background": "config()?.backdropColor || \"var(--ymt-surface-app)\"" } }, ngImport: i0, template: "<div class=\"backdrop\"></div>\n\n@let e = error();\n@if (e) {\n <div class=\"error\">\n <button ymtIconButton icon-button-size=\"small\" (click)=\"errorDismiss.emit()\">\n <mat-icon>close</mat-icon>\n </button>\n <p>{{ e }}</p>\n </div>\n} @else {\n <mat-progress-spinner class=\"ymt-progress-spinner--giant\" [mode]=\"'indeterminate'\"></mat-progress-spinner>\n}\n", styles: [":host{position:absolute;transition:opacity .2s;inset:0;display:grid;place-items:center;z-index:5}:host.blured{-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}:host{--mat-progress-spinner-active-indicator-color: currentColor}:host .error,:host mat-progress-spinner{opacity:0;animation:fadeIn .2s ease-in forwards;animation-delay:.2s}:host .error{display:flex;flex-direction:column;background-color:var(--ymt-danger-container);color:var(--ymt-on-danger-container);border:var(--ymt-danger);padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-xs);max-width:60ch;margin:var(--ymt-spacing-m);overflow-y:auto;align-items:end}:host .error p{margin:var(--ymt-spacing-m)}:host .backdrop{position:absolute;inset:0;background-color:rgb(from var(--backdrop-background) r g b/.5);animation:fadeIn .2s ease-in}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ 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: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
223
- }
224
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: BusyOverlayComponent, decorators: [{
225
- type: Component,
226
- args: [{ selector: 'yuv-busy-overlay', imports: [YmtIconButtonDirective, MatIconModule, MatProgressSpinner], host: {
227
- '[class.error]': '!!error()',
228
- '[class.blured]': '!!config()?.blur',
229
- '[style.--backdrop-background]': 'config()?.backdropColor || "var(--ymt-surface-app)"'
230
- }, template: "<div class=\"backdrop\"></div>\n\n@let e = error();\n@if (e) {\n <div class=\"error\">\n <button ymtIconButton icon-button-size=\"small\" (click)=\"errorDismiss.emit()\">\n <mat-icon>close</mat-icon>\n </button>\n <p>{{ e }}</p>\n </div>\n} @else {\n <mat-progress-spinner class=\"ymt-progress-spinner--giant\" [mode]=\"'indeterminate'\"></mat-progress-spinner>\n}\n", styles: [":host{position:absolute;transition:opacity .2s;inset:0;display:grid;place-items:center;z-index:5}:host.blured{-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}:host{--mat-progress-spinner-active-indicator-color: currentColor}:host .error,:host mat-progress-spinner{opacity:0;animation:fadeIn .2s ease-in forwards;animation-delay:.2s}:host .error{display:flex;flex-direction:column;background-color:var(--ymt-danger-container);color:var(--ymt-on-danger-container);border:var(--ymt-danger);padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-xs);max-width:60ch;margin:var(--ymt-spacing-m);overflow-y:auto;align-items:end}:host .error p{margin:var(--ymt-spacing-m)}:host .backdrop{position:absolute;inset:0;background-color:rgb(from var(--backdrop-background) r g b/.5);animation:fadeIn .2s ease-in}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
231
- }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], errorDismiss: [{ type: i0.Output, args: ["errorDismiss"] }] } });
1
+ export * from '@yuuvis/client-components/common';
232
2
 
233
3
  /**
234
- * A directive that will overlay its host component with a translucent background
235
- * and a loading spinner once the condition resolves with true. This is useful for example to
236
- * prevent user interaction while component data is loading or some processing is done.
237
- *
238
- * It'll also prevent the overlaid element from being interacted with.
239
- *
240
- * ```html
241
- * <div class="data-panel" [yuvBusyOverlay]="isLoadingData">...</div>
242
- * ```
243
- * Setting a `yuvBusyError` will replace the loading spinner by the provided error message.
244
- * You need to keep the `yuvBusyOverlay` condition true to show the error.
245
- * The error element rendered has a dismiss/close action that trigger the `yuvBusyErrorDismiss`
246
- * output event, that you then can use to set the busy condition to false.
247
- *
248
- * ```html
249
- * <div class="result-list"
250
- * [yuvBusyOverlay]="waitingForServerResponse"
251
- * (yuvBusyErrorDismiss)="waitingForServerResponse = false"
252
- * [yuvBusyError]="errorMessage">
253
- * ...
254
- * </div>
255
- * ```
256
- *
257
- * ```ts
258
- * // in your component code
259
- * this.waitingForServerResponse = true;
260
- * fetchData().subscribe({
261
- * next: (data) => {
262
- * ...
263
- * this.waitingForServerResponse = false;
264
- * }
265
- * error: (err) => {
266
- * this.errorMessage = 'Failed to load data from server: ' + err.message;
267
- * }
268
- * });
269
- * ```
270
- *
4
+ * @deprecated Import from `@yuuvis/client-components/common` instead.
5
+ * This entry point is a backward-compatibility shim and will be removed in a future release.
271
6
  */
272
- class BusyOverlayDirective {
273
- constructor() {
274
- this.#initialStylePosition = signal('initial', ...(ngDevMode ? [{ debugName: "#initialStylePosition" }] : /* istanbul ignore next */ []));
275
- this.stylePosition = signal(this.#initialStylePosition(), ...(ngDevMode ? [{ debugName: "stylePosition" }] : /* istanbul ignore next */ []));
276
- this.#elRef = inject(ElementRef);
277
- this.#environmentInjector = inject(EnvironmentInjector);
278
- this.#vcRef = inject(ViewContainerRef);
279
- /**
280
- * The Boolean expression to evaluate as the condition for showing the busy overlay
281
- */
282
- this.yuvBusyOverlay = input(false, ...(ngDevMode ? [{ debugName: "yuvBusyOverlay" }] : /* istanbul ignore next */ []));
283
- this.#yuvBusyOverlayEffect = effect(() => {
284
- const busy = this.yuvBusyOverlay();
285
- if (busy === true) {
286
- this.#addBusyOverlay();
287
- }
288
- else {
289
- this.#removeBusyOverlay();
290
- }
291
- }, ...(ngDevMode ? [{ debugName: "#yuvBusyOverlayEffect" }] : /* istanbul ignore next */ []));
292
- /**
293
- * Configuration options for the busy overlay. These include
294
- * e.g. backdrops background color and whether to blur the
295
- * overlaid content.
296
- */
297
- this.yuvBusyOverlayConfig = input(...(ngDevMode ? [undefined, { debugName: "yuvBusyOverlayConfig" }] : /* istanbul ignore next */ []));
298
- /**
299
- * Error message to display in the overlay. If set, the loading spinner is replaced by the error message.
300
- */
301
- this.yuvBusyError = input(...(ngDevMode ? [undefined, { debugName: "yuvBusyError" }] : /* istanbul ignore next */ []));
302
- this.#yuvBusyErrorEffect = effect(() => this.#busyOverlayComponentRef?.setInput('error', this.yuvBusyError()), ...(ngDevMode ? [{ debugName: "#yuvBusyErrorEffect" }] : /* istanbul ignore next */ []));
303
- /**
304
- * Event emitted when the error message's dismiss action is triggered.
305
- */
306
- this.yuvBusyErrorDismiss = output();
307
- this.#outputSubscriptions = [];
308
- }
309
- #initialStylePosition;
310
- #elRef;
311
- #environmentInjector;
312
- #vcRef;
313
- #yuvBusyOverlayEffect;
314
- #yuvBusyErrorEffect;
315
- #busyOverlayComponentRef;
316
- #outputSubscriptions;
317
- #attachOverlay() {
318
- if (this.#busyOverlayComponentRef) {
319
- // Already attached; just refresh inputs/outputs
320
- this.#applyInputs();
321
- return;
322
- }
323
- // Create the component inside the directive’s host
324
- this.#busyOverlayComponentRef = this.#vcRef.createComponent(BusyOverlayComponent, {
325
- environmentInjector: this.#environmentInjector
326
- });
327
- const node = this.#busyOverlayComponentRef.location.nativeElement;
328
- this.#elRef.nativeElement.appendChild(node);
329
- this.#applyInputs();
330
- this.#bindOutputs();
331
- this.#busyOverlayComponentRef.changeDetectorRef?.detectChanges?.();
332
- }
333
- #detachOverlay() {
334
- // Unsubscribe outputs first
335
- this.#outputSubscriptions.forEach((sub) => sub.unsubscribe());
336
- this.#outputSubscriptions = [];
337
- // Destroy componentRef and clear container
338
- if (this.#busyOverlayComponentRef) {
339
- this.#busyOverlayComponentRef.destroy();
340
- this.#busyOverlayComponentRef = undefined;
341
- }
342
- this.#vcRef.clear();
343
- }
344
- #applyInputs() {
345
- if (!this.#busyOverlayComponentRef)
346
- return;
347
- this.#busyOverlayComponentRef.setInput('config', this.yuvBusyOverlayConfig());
348
- this.#busyOverlayComponentRef.setInput('error', this.yuvBusyError());
349
- }
350
- #bindOutputs() {
351
- if (!this.#busyOverlayComponentRef)
352
- return;
353
- // Clean up old subscriptions if re-binding
354
- this.#outputSubscriptions.forEach((sub) => sub.unsubscribe());
355
- this.#outputSubscriptions = [];
356
- const errorDismissOutput = this.#busyOverlayComponentRef.instance.errorDismiss;
357
- this.#outputSubscriptions.push(errorDismissOutput.subscribe(() => this.yuvBusyErrorDismiss.emit()));
358
- }
359
- #addBusyOverlay() {
360
- this.stylePosition.set('relative');
361
- this.#attachOverlay();
362
- }
363
- #removeBusyOverlay() {
364
- if (!this.#busyOverlayComponentRef)
365
- return;
366
- const el = this.#busyOverlayComponentRef.location.nativeElement;
367
- if (el) {
368
- el.style.opacity = '0';
369
- setTimeout(() => {
370
- this.#detachOverlay();
371
- this.stylePosition.set(this.#initialStylePosition());
372
- }, 200);
373
- }
374
- }
375
- ngOnInit() {
376
- const initialPosition = getComputedStyle(this.#elRef.nativeElement).position;
377
- this.#initialStylePosition.set(initialPosition === 'static' || !initialPosition ? 'relative' : initialPosition);
378
- }
379
- ngOnDestroy() {
380
- this.#detachOverlay();
381
- }
382
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: BusyOverlayDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
383
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: BusyOverlayDirective, isStandalone: true, selector: "[yuvBusyOverlay]", inputs: { yuvBusyOverlay: { classPropertyName: "yuvBusyOverlay", publicName: "yuvBusyOverlay", isSignal: true, isRequired: false, transformFunction: null }, yuvBusyOverlayConfig: { classPropertyName: "yuvBusyOverlayConfig", publicName: "yuvBusyOverlayConfig", isSignal: true, isRequired: false, transformFunction: null }, yuvBusyError: { classPropertyName: "yuvBusyError", publicName: "yuvBusyError", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { yuvBusyErrorDismiss: "yuvBusyErrorDismiss" }, host: { properties: { "attr.aria-busy": "yuvBusyOverlay() ? \"true\" : \"false\"", "style.position": "stylePosition()" } }, ngImport: i0 }); }
384
- }
385
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: BusyOverlayDirective, decorators: [{
386
- type: Directive,
387
- args: [{
388
- selector: '[yuvBusyOverlay]',
389
- standalone: true,
390
- host: {
391
- '[attr.aria-busy]': 'yuvBusyOverlay() ? "true" : "false"',
392
- '[style.position]': 'stylePosition()'
393
- }
394
- }]
395
- }], propDecorators: { yuvBusyOverlay: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvBusyOverlay", required: false }] }], yuvBusyOverlayConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvBusyOverlayConfig", required: false }] }], yuvBusyError: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvBusyError", required: false }] }], yuvBusyErrorDismiss: [{ type: i0.Output, args: ["yuvBusyErrorDismiss"] }] } });
396
-
397
- /**
398
- * Fixes the issue of 'click' event beeing triggered on 'doubleclick' by defining new outputs that
399
- * distinguish between single and double click.
400
- */
401
- class ClickDoubleDirective {
402
- constructor() {
403
- this.debounceTime = input(200, ...(ngDevMode ? [{ debugName: "debounceTime" }] : /* istanbul ignore next */ []));
404
- this.doubleClick = output({ alias: 'click.double' });
405
- this.singleClick = output({ alias: 'click.single' });
406
- this.clicksSubject = new Subject();
407
- this.clicksSubject.pipe(takeUntilDestroyed(), debounceTime(this.debounceTime())).subscribe({
408
- next: (event) => {
409
- event.type === 'click' ? this.singleClick.emit(event) : this.doubleClick.emit(event);
410
- }
411
- });
412
- }
413
- clickEvent(event) {
414
- event.preventDefault();
415
- event.stopPropagation();
416
- this.clicksSubject.next(event);
417
- }
418
- doubleClickEvent(event) {
419
- event.preventDefault();
420
- event.stopPropagation();
421
- this.clicksSubject.next(event);
422
- }
423
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ClickDoubleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
424
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: ClickDoubleDirective, isStandalone: true, selector: "[click.single],[click.double]", inputs: { debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { doubleClick: "click.double", singleClick: "click.single" }, host: { listeners: { "click": "clickEvent($event)", "dblclick": "doubleClickEvent($event)" } }, ngImport: i0 }); }
425
- }
426
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ClickDoubleDirective, decorators: [{
427
- type: Directive,
428
- args: [{
429
- selector: '[click.single],[click.double]',
430
- host: {
431
- '(click)': 'clickEvent($event)',
432
- '(dblclick)': 'doubleClickEvent($event)'
433
- }
434
- }]
435
- }], ctorParameters: () => [], propDecorators: { debounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceTime", required: false }] }], doubleClick: [{ type: i0.Output, args: ["click.double"] }], singleClick: [{ type: i0.Output, args: ["click.single"] }] } });
436
-
437
- /**
438
- * Directive to watch the size of an element inside the DOM. Usefull for example to provide
439
- * a different layout (different components) depending on the available screen estate. You
440
- * should first try to use CSS container queries but somtimes you need a different set of
441
- * components to be loaded for a certain component size.
442
- *
443
- * Let's say you have components designed for bigger screens. You do not want to load them
444
- * if there is not enough space for them. So you rather load components that are designed to
445
- * take less space by providing the best user experience on smaller devices.
446
- *
447
- * ```html
448
- * <div yuvContainerSize (containerHeight)="onContainerResize($event)" (containerWidth)="onContainerResize($event)"></div>
449
- * ```
450
- *
451
- */
452
- class ContainerSizeDirective {
453
- constructor() {
454
- this.elRef = inject(ElementRef);
455
- this.ngZone = inject(NgZone);
456
- this.containerHeight = output();
457
- this.containerWidth = output();
458
- this._resizeObserver = new ResizeObserver((entries) => {
459
- const size = entries[0].borderBoxSize[0];
460
- if (!this._size || size.blockSize !== this._size.blockSize) {
461
- this._emit(size.blockSize, true);
462
- }
463
- if (!this._size || size.inlineSize !== this._size.inlineSize) {
464
- this._emit(size.inlineSize);
465
- }
466
- this._size = size;
467
- });
468
- this._resizeObserver.observe(this.elRef.nativeElement);
469
- }
470
- _emit(value, isHeight = false) {
471
- // ResizeObserver callback is not covered by change detection
472
- // so it has to be executed withing ngZone
473
- this.ngZone.run(() => {
474
- (isHeight ? this.containerHeight : this.containerWidth).emit(value);
475
- });
476
- }
477
- ngOnDestroy() {
478
- this._resizeObserver.unobserve(this.elRef.nativeElement);
479
- }
480
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ContainerSizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
481
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: ContainerSizeDirective, isStandalone: true, selector: "[yuvContainerSize]", outputs: { containerHeight: "containerHeight", containerWidth: "containerWidth" }, ngImport: i0 }); }
482
- }
483
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ContainerSizeDirective, decorators: [{
484
- type: Directive,
485
- args: [{
486
- selector: '[yuvContainerSize]',
487
- standalone: true
488
- }]
489
- }], ctorParameters: () => [], propDecorators: { containerHeight: [{ type: i0.Output, args: ["containerHeight"] }], containerWidth: [{ type: i0.Output, args: ["containerWidth"] }] } });
490
-
491
- /**
492
- * Directive for adding drag scroll behaviour to a container element. Elements that overlow will then
493
- * be 'scrollable' by dragging the list of children.
494
- *
495
- * @example
496
- * <div yuvDragScroll>
497
- * <div class="tile">#1</div>
498
- * <div class="tile">#2</div>
499
- * <div class="tile">#3</div>
500
- * ...
501
- * </div>
502
- */
503
- class DragScrollDirective {
504
- #document = inject(DOCUMENT);
505
- #element = inject(ElementRef);
506
- #dragging = false;
507
- #applyDraggingStyles(el, remove) {
508
- const draggingStyles = {
509
- cursor: 'grabbing'
510
- };
511
- Object.keys(draggingStyles).forEach((property) => {
512
- if (remove) {
513
- el.style.removeProperty(property);
514
- }
515
- else {
516
- el.style.setProperty(property, draggingStyles[property]);
517
- }
518
- });
519
- }
520
- ngAfterViewInit() {
521
- const nativeElement = this.#element.nativeElement;
522
- const mouseDown$ = fromEvent(nativeElement, 'mousedown');
523
- const mouseMove$ = fromEvent(this.#document, 'mousemove');
524
- const mouseUp$ = fromEvent(this.#document, 'mouseup').pipe(tap((e) => {
525
- if (this.#dragging) {
526
- e.preventDefault();
527
- e.stopPropagation();
528
- this.#dragging = false;
529
- this.#applyDraggingStyles(nativeElement, true);
530
- }
531
- }));
532
- const dragMove$ = mouseDown$.pipe(filter(() =>
533
- // only calculate new scroll position if the container is actually overflowing
534
- nativeElement.scrollHeight > nativeElement.clientHeight || nativeElement.scrollWidth > nativeElement.clientWidth), tap((startEvent) => {
535
- this.#applyDraggingStyles(nativeElement);
536
- startEvent.preventDefault();
537
- startEvent.stopPropagation();
538
- }), switchMap((startEvent) => {
539
- this.#dragging = true;
540
- const scrollPos = {
541
- left: nativeElement.scrollLeft,
542
- top: nativeElement.scrollTop
543
- };
544
- return mouseMove$.pipe(map((moveEvent) => {
545
- moveEvent.preventDefault();
546
- moveEvent.stopPropagation();
547
- return {
548
- startEvent,
549
- moveEvent,
550
- scrollPos
551
- };
552
- }), takeUntil(mouseUp$));
553
- }), tap(({ startEvent, moveEvent, scrollPos }) => {
554
- const diffX = moveEvent.clientX - startEvent.clientX;
555
- const diffY = moveEvent.clientY - startEvent.clientY;
556
- nativeElement.scrollLeft = scrollPos.left - diffX;
557
- nativeElement.scrollTop = scrollPos.top - diffY;
558
- }));
559
- dragMove$.subscribe();
560
- }
561
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DragScrollDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
562
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DragScrollDirective, isStandalone: true, selector: "[yuvDragScroll]", ngImport: i0 }); }
563
- }
564
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DragScrollDirective, decorators: [{
565
- type: Directive,
566
- args: [{
567
- selector: '[yuvDragScroll]'
568
- }]
569
- }] });
570
-
571
- class DragSelectDirective {
572
- constructor() {
573
- this.#selectStartX = 0;
574
- this.#selectStartY = 0;
575
- this.#selection = [];
576
- this.items = contentChildren(DragSelectItemDirective, ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
577
- this.#selectables = computed(() => this.items().map((item) => item.el), ...(ngDevMode ? [{ debugName: "#selectables" }] : /* istanbul ignore next */ []));
578
- this.yuvDragSelect = input(...(ngDevMode ? [undefined, { debugName: "yuvDragSelect" }] : /* istanbul ignore next */ []));
579
- this.dragSelectChange = output();
580
- this.dragSelect = output();
581
- this.#onPointerUp = () => {
582
- removeEventListener('pointermove', this.#resize);
583
- removeEventListener('pointerup', this.#onPointerUp);
584
- if (this.#selection.length)
585
- this.dragSelect.emit(this.#selection);
586
- if (this.#selector)
587
- this.#selector.remove();
588
- };
589
- this.#resize = (event) => {
590
- if (!this.#selector)
591
- return;
592
- const diffX = event.pageX - this.#selectStartX;
593
- const diffY = event.pageY - this.#selectStartY;
594
- this.#selector.style.left = diffX < 0 ? this.#selectStartX + diffX + 'px' : this.#selectStartX + 'px';
595
- this.#selector.style.top = diffY < 0 ? this.#selectStartY + diffY + 'px' : this.#selectStartY + 'px';
596
- this.#selector.style.height = Math.abs(diffY) + 'px';
597
- this.#selector.style.width = Math.abs(diffX) + 'px';
598
- this.#selector.style.border = `1px solid ${this.yuvDragSelect()?.selectorColor || 'var(--ymt-primary'}`;
599
- this.#checkSelected();
600
- };
601
- this.#checkSelected = () => {
602
- if (!this.#selector)
603
- return;
604
- const select = this.#selector.getBoundingClientRect();
605
- const { x, y, height, width } = select;
606
- if (!height || !width)
607
- return;
608
- const currSelectionLength = this.#selection.length;
609
- this.#selectables().forEach((selectable, idx) => {
610
- const r1 = { x: x + window.scrollX, y: y + window.scrollY, height, width };
611
- const r2 = selectable.getBoundingClientRect();
612
- this.#selection = this.#selection.filter((s) => s !== idx);
613
- if (this.#checkRectIntersection(r1, r2)) {
614
- this.#selection.push(idx);
615
- }
616
- });
617
- if (currSelectionLength !== this.#selection.length) {
618
- this.dragSelectChange.emit(this.#selection);
619
- }
620
- };
621
- this.#checkRectIntersection = (r1, r2) => {
622
- return !(r1.x + r1.width < r2.x || r2.x + r2.width < r1.x || r1.y + r1.height < r2.y || r2.y + r2.height < r1.y);
623
- };
624
- }
625
- #selector;
626
- #selectStartX;
627
- #selectStartY;
628
- #selection;
629
- onPointerDown(event) {
630
- if (this.yuvDragSelect()?.disabled || event.target.tagName === 'BUTTON')
631
- return;
632
- event.preventDefault();
633
- this.#selectStartX = event.pageX;
634
- this.#selectStartY = event.pageY;
635
- const div = document.createElement('div');
636
- div.style.position = 'absolute';
637
- div.style.width = '0';
638
- div.style.height = '0';
639
- div.style.left = this.#selectStartX + 'px';
640
- div.style.top = this.#selectStartY + 'px';
641
- div.classList.add('drag-select');
642
- this.#selector = div;
643
- document.body.append(this.#selector);
644
- this.#selection = [];
645
- addEventListener('pointermove', this.#resize);
646
- addEventListener('pointerup', this.#onPointerUp);
647
- }
648
- #selectables;
649
- #onPointerUp;
650
- #resize;
651
- #checkSelected;
652
- #checkRectIntersection;
653
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DragSelectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
654
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.2.9", type: DragSelectDirective, isStandalone: true, selector: "[yuvDragSelect]", inputs: { yuvDragSelect: { classPropertyName: "yuvDragSelect", publicName: "yuvDragSelect", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dragSelectChange: "dragSelectChange", dragSelect: "dragSelect" }, host: { listeners: { "pointerdown": "onPointerDown($event)" } }, queries: [{ propertyName: "items", predicate: DragSelectItemDirective, isSignal: true }], ngImport: i0 }); }
655
- }
656
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DragSelectDirective, decorators: [{
657
- type: Directive,
658
- args: [{
659
- selector: '[yuvDragSelect]',
660
- host: {
661
- '(pointerdown)': 'onPointerDown($event)'
662
- }
663
- }]
664
- }], propDecorators: { items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DragSelectItemDirective), { isSignal: true }] }], yuvDragSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvDragSelect", required: false }] }], dragSelectChange: [{ type: i0.Output, args: ["dragSelectChange"] }], dragSelect: [{ type: i0.Output, args: ["dragSelect"] }] } });
665
- class DragSelectItemDirective {
666
- constructor() {
667
- this.#elRef = inject(ElementRef);
668
- this.el = this.#elRef.nativeElement;
669
- }
670
- #elRef;
671
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DragSelectItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
672
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DragSelectItemDirective, isStandalone: true, selector: "[yuvDragSelectItem]", ngImport: i0 }); }
673
- }
674
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DragSelectItemDirective, decorators: [{
675
- type: Directive,
676
- args: [{
677
- selector: '[yuvDragSelectItem]',
678
- standalone: true
679
- }]
680
- }] });
681
-
682
- class FileDropService {
683
- constructor() {
684
- this.activeDropZone = signal(null, ...(ngDevMode ? [{ debugName: "activeDropZone" }] : /* istanbul ignore next */ []));
685
- }
686
- fileOver(id, active) {
687
- this.activeDropZone.set(active ? id : null);
688
- }
689
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileDropService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
690
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileDropService, providedIn: 'root' }); }
691
- }
692
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileDropService, decorators: [{
693
- type: Injectable,
694
- args: [{
695
- providedIn: 'root'
696
- }]
697
- }] });
698
-
699
- class FileDropZoneDirective {
700
- constructor() {
701
- this.#elRef = inject(ElementRef);
702
- this.#fileDropService = inject(FileDropService);
703
- // ID to track the element
704
- this.#id = crypto.randomUUID();
705
- this.active = computed(() => {
706
- const fileOver = this.#fileDropService.activeDropZone() === this.#id;
707
- this.fileDropOver.emit(fileOver);
708
- return fileOver;
709
- }, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
710
- this.#activeEffect = effect(() => {
711
- this.#toggleCover(this.active());
712
- }, ...(ngDevMode ? [{ debugName: "#activeEffect" }] : /* istanbul ignore next */ []));
713
- this.#defaultDragOverCoverStyles = {
714
- // outline: '2px dashed var(--ymt-primary)',
715
- display: 'flex',
716
- transition: 'background-color .3s ease-in-out',
717
- 'z-index': '500',
718
- 'align-items': 'center',
719
- 'justify-content': 'center',
720
- 'outline-offset': '-2px',
721
- 'background-color': 'rgb(from var(--ymt-primary) r g b / .5)'
722
- };
723
- this.#defaultDragOverCoverLabelStyles = {
724
- outline: '1px solid var(--ymt-primary)',
725
- padding: 'var(--ymt-spacing-m, 16px)',
726
- 'background-color': 'rgb(from var(--ymt-primary) r g b / 0.9)',
727
- color: 'var(--ymt-on-primary)'
728
- };
729
- this.yuvFileDropZone = input(...(ngDevMode ? [undefined, { debugName: "yuvFileDropZone" }] : /* istanbul ignore next */ []));
730
- this.fileDropDisabled = input(false, ...(ngDevMode ? [{ debugName: "fileDropDisabled" }] : /* istanbul ignore next */ []));
731
- this.fileDrop = output();
732
- this.fileDropOver = output();
733
- }
734
- #elRef;
735
- #fileDropService;
736
- #coverElement;
737
- // ID to track the element
738
- #id;
739
- #activeEffect;
740
- #defaultDragOverCoverStyles;
741
- #defaultDragOverCoverLabelStyles;
742
- onDrop(event) {
743
- if (this.fileDropDisabled())
744
- return;
745
- event.preventDefault();
746
- event.stopPropagation();
747
- this.#fileDropService.fileOver(this.#id, false);
748
- const dataTransfer = event.dataTransfer;
749
- if (dataTransfer) {
750
- if (dataTransfer?.items) {
751
- const files = [];
752
- for (let i = 0; i < dataTransfer.items.length; i++) {
753
- // If dropped items aren't files, reject them
754
- if (dataTransfer.items[i].kind === 'file') {
755
- const file = dataTransfer.items[i].getAsFile();
756
- if (file)
757
- files.push(file);
758
- }
759
- }
760
- dataTransfer.items.clear();
761
- this.fileDrop.emit(files);
762
- }
763
- else {
764
- const files = dataTransfer.files;
765
- dataTransfer.clearData();
766
- this.fileDrop.emit(Array.from(files));
767
- }
768
- }
769
- }
770
- onDragOver(event) {
771
- if (this.fileDropDisabled())
772
- return;
773
- event.stopPropagation();
774
- event.preventDefault();
775
- this.#fileDropService.fileOver(this.#id, true);
776
- }
777
- onDragLeave(event) {
778
- if (this.fileDropDisabled())
779
- return;
780
- if (event.target.getAttribute('id') === this.#coverElement?.getAttribute('id')) {
781
- this.#fileDropService.fileOver(this.#id, false);
782
- }
783
- }
784
- onBodyDragOver(event) {
785
- event.preventDefault();
786
- event.stopPropagation();
787
- }
788
- onBodyDrop(event) {
789
- event.preventDefault();
790
- }
791
- #toggleCover(active) {
792
- const el = this.#elRef.nativeElement;
793
- if (this.#coverElement) {
794
- el.style.position = 'initial';
795
- this.#coverElement.remove();
796
- this.#coverElement = undefined;
797
- }
798
- if (active) {
799
- el.style.position = 'relative';
800
- this.#coverElement = this.#createCoverElement();
801
- el.append(this.#coverElement);
802
- }
803
- }
804
- #createCoverElement() {
805
- const coverElement = document.createElement('div');
806
- coverElement.classList.add('yuv-file-drop-zone-cover');
807
- coverElement.setAttribute('id', this.#id);
808
- coverElement.style.position = 'absolute';
809
- coverElement.style.inset = '0';
810
- const styles = this.yuvFileDropZone()?.coverStyles || this.#defaultDragOverCoverStyles;
811
- Object.keys(styles).forEach((k) => {
812
- coverElement.style[k] = styles[k];
813
- });
814
- const label = this.yuvFileDropZone()?.label;
815
- if (label) {
816
- const coverLabelElement = document.createElement('div');
817
- coverElement.classList.add('yuv-file-drop-zone-label');
818
- coverLabelElement.innerText = label;
819
- const labelStyles = this.yuvFileDropZone()?.coverLabelStyles || this.#defaultDragOverCoverLabelStyles;
820
- Object.keys(labelStyles).forEach((k) => {
821
- coverLabelElement.style[k] = labelStyles[k];
822
- });
823
- coverLabelElement.style.pointerEvents = 'none';
824
- coverElement.append(coverLabelElement);
825
- }
826
- return coverElement;
827
- }
828
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileDropZoneDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
829
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: FileDropZoneDirective, isStandalone: true, selector: "[yuvFileDropZone]", inputs: { yuvFileDropZone: { classPropertyName: "yuvFileDropZone", publicName: "yuvFileDropZone", isSignal: true, isRequired: false, transformFunction: null }, fileDropDisabled: { classPropertyName: "fileDropDisabled", publicName: "fileDropDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileDrop: "fileDrop", fileDropOver: "fileDropOver" }, host: { listeners: { "drop": "onDrop($event)", "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "body:dragover": "onBodyDragOver($event)", "body:drop": "onBodyDrop($event)" } }, ngImport: i0 }); }
830
- }
831
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileDropZoneDirective, decorators: [{
832
- type: Directive,
833
- args: [{
834
- selector: '[yuvFileDropZone]',
835
- host: {
836
- '(drop)': 'onDrop($event)',
837
- '(dragover)': 'onDragOver($event)',
838
- '(dragleave)': 'onDragLeave($event)',
839
- '(body:dragover)': 'onBodyDragOver($event)',
840
- '(body:drop)': 'onBodyDrop($event)'
841
- }
842
- }]
843
- }], propDecorators: { yuvFileDropZone: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvFileDropZone", required: false }] }], fileDropDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileDropDisabled", required: false }] }], fileDrop: [{ type: i0.Output, args: ["fileDrop"] }], fileDropOver: [{ type: i0.Output, args: ["fileDropOver"] }] } });
844
-
845
- /**
846
- * Directive keeping track of the focus beeing within a component. Once the component or
847
- * any of its child components gain focus, a class of `focusWithin` will be set on the
848
- * host component in order to allow styling it while beeing focused. Furthermore you can
849
- * register callbacks once the component gets or looses focus.
850
- *
851
- * @example
852
- * // just set the css class
853
- * <some-component yuvFocusWithin></some-component>
854
- * // set the css class and listen to focus changes
855
- * <some-component (yuvFocusWithin)="onFocusEnter()" (yuvFocusWithinBlur)="onFocusLeave()"></some-component>
856
- */
857
- class FocusWithinDirective {
858
- onFocusIn(evt) {
859
- const hadFocusWithin = this.hasFocusWithin;
860
- this.hasFocusWithin = this.matchesFocusWithin();
861
- if (!hadFocusWithin && this.hasFocusWithin) {
862
- this.yuvFocusWithin.emit();
863
- }
864
- }
865
- onFocusOut(evt) {
866
- const hadFocusWithin = this.hasFocusWithin;
867
- this.hasFocusWithin = this.matchesFocusWithin();
868
- if (hadFocusWithin && !this.hasFocusWithin) {
869
- this.yuvFocusWithinBlur.emit();
870
- }
871
- }
872
- /**
873
- * @ignore
874
- */
875
- constructor(elRef) {
876
- this.elRef = elRef;
877
- this.eventCount = 0;
878
- this.hasFocusWithin = false;
879
- /**
880
- * Emitted once the component or any of its child components gains focus.
881
- */
882
- this.yuvFocusWithin = output();
883
- /**
884
- * Emitted once the component (incl. any of its child components) looses focus.
885
- */
886
- this.yuvFocusWithinBlur = output();
887
- }
888
- // Determine if the given node matches the given selector.
889
- // @see: https://www.bennadel.com/blog/3476-checking-to-see-if-an-element-has-a-css-pseudo-class-in-javascript.htm
890
- matchesFocusWithin() {
891
- const node = this.elRef.nativeElement;
892
- const nativeMatches = node.matches || node.msMatchesSelector;
893
- try {
894
- return nativeMatches.call(node, ':focus-within');
895
- }
896
- catch (error) {
897
- return false;
898
- }
899
- }
900
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FocusWithinDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
901
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FocusWithinDirective, isStandalone: true, selector: "[yuvFocusWithin]", outputs: { yuvFocusWithin: "yuvFocusWithin", yuvFocusWithinBlur: "yuvFocusWithinBlur" }, host: { listeners: { "focusin": "onFocusIn($event)", "focusout": "onFocusOut($event)" }, properties: { "class.focusWithin": "hasFocusWithin" } }, ngImport: i0 }); }
902
- }
903
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FocusWithinDirective, decorators: [{
904
- type: Directive,
905
- args: [{
906
- selector: '[yuvFocusWithin]',
907
- host: {
908
- '[class.focusWithin]': 'hasFocusWithin',
909
- '(focusin)': 'onFocusIn($event)',
910
- '(focusout)': 'onFocusOut($event)'
911
- }
912
- }]
913
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { yuvFocusWithin: [{ type: i0.Output, args: ["yuvFocusWithin"] }], yuvFocusWithinBlur: [{ type: i0.Output, args: ["yuvFocusWithinBlur"] }] } });
914
-
915
- /**
916
- * Directive for applying light dismiss actions. Adding this directive will trigger
917
- * the given function when the user clicks outside the component or hits escape.
918
- *
919
- * ```ts
920
- * <div class="notifications" (yuvLightDismiss)="close()">
921
- * ...
922
- * </div>
923
- * ```
924
- */
925
- class LightDismissDirective {
926
- constructor() {
927
- this.elRef = inject(ElementRef);
928
- this.yuvLightDismiss = output();
929
- }
930
- onKeydownHandler(event) {
931
- if (!event.defaultPrevented) {
932
- this.yuvLightDismiss.emit();
933
- }
934
- }
935
- onMousedown(event) {
936
- if (!this.elRef.nativeElement.contains(event.target)) {
937
- this.yuvLightDismiss.emit();
938
- }
939
- }
940
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LightDismissDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
941
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: LightDismissDirective, isStandalone: true, selector: "[yuvLightDismiss]", outputs: { yuvLightDismiss: "yuvLightDismiss" }, host: { listeners: { "document:keydown.escape": "onKeydownHandler($event)", "document:mousedown": "onMousedown($event)" } }, ngImport: i0 }); }
942
- }
943
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LightDismissDirective, decorators: [{
944
- type: Directive,
945
- args: [{
946
- selector: '[yuvLightDismiss]',
947
- host: {
948
- '(document:keydown.escape)': 'onKeydownHandler($event)',
949
- '(document:mousedown)': 'onMousedown($event)'
950
- }
951
- }]
952
- }], propDecorators: { yuvLightDismiss: [{ type: i0.Output, args: ["yuvLightDismiss"] }] } });
953
-
954
- class LongPressDirective {
955
- constructor() {
956
- this.elRef = inject(ElementRef);
957
- this.threshold = 500;
958
- this.yuvLongPress = input({
959
- enabled: false
960
- }, ...(ngDevMode ? [{ debugName: "yuvLongPress" }] : /* istanbul ignore next */ []));
961
- this.longpress = output();
962
- const mousedown = fromEvent(this.elRef.nativeElement, 'mousedown').pipe(takeUntilDestroyed(), filter((event) => event.button == 0), // Only allow left button (Primary button)
963
- map(() => true) // turn on threshold counter
964
- );
965
- const touchstart = fromEvent(this.elRef.nativeElement, 'touchstart').pipe(takeUntilDestroyed(), map(() => true));
966
- const touchEnd = fromEvent(this.elRef.nativeElement, 'touchend').pipe(takeUntilDestroyed(), map(() => false));
967
- const mouseup = fromEvent(window, 'mouseup').pipe(takeUntilDestroyed(), filter((event) => event.button == 0), // Only allow left button (Primary button)
968
- map(() => false) // reset threshold counter
969
- );
970
- merge(mousedown, mouseup, touchstart, touchEnd)
971
- .pipe(takeUntilDestroyed(), switchMap((state) => (state ? timer(this.threshold, 100) : of(null))), filter((value) => !!value))
972
- .subscribe(() => this.longpress.emit());
973
- }
974
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LongPressDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
975
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: LongPressDirective, isStandalone: true, selector: "[yuvLongPress]", inputs: { yuvLongPress: { classPropertyName: "yuvLongPress", publicName: "yuvLongPress", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { longpress: "longpress" }, ngImport: i0 }); }
976
- }
977
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LongPressDirective, decorators: [{
978
- type: Directive,
979
- args: [{
980
- selector: '[yuvLongPress]',
981
- standalone: true
982
- }]
983
- }], ctorParameters: () => [], propDecorators: { yuvLongPress: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvLongPress", required: false }] }], longpress: [{ type: i0.Output, args: ["longpress"] }] } });
984
-
985
- /* eslint-disable @typescript-eslint/no-unused-vars */
986
- /* eslint-disable @typescript-eslint/no-empty-function */
987
- // @see: https://netbasal.com/forwarding-form-controls-to-custom-control-components-in-angular-701e8406cc55
988
- class NoopValueAccessorDirective {
989
- writeValue(obj) { }
990
- registerOnChange(fn) { }
991
- registerOnTouched(fn) { }
992
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NoopValueAccessorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
993
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: NoopValueAccessorDirective, isStandalone: true, providers: [
994
- {
995
- provide: NG_VALUE_ACCESSOR,
996
- multi: true,
997
- useExisting: forwardRef(() => NoopValueAccessorDirective)
998
- }
999
- ], ngImport: i0 }); }
1000
- }
1001
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NoopValueAccessorDirective, decorators: [{
1002
- type: Directive,
1003
- args: [{
1004
- standalone: true,
1005
- providers: [
1006
- {
1007
- provide: NG_VALUE_ACCESSOR,
1008
- multi: true,
1009
- useExisting: forwardRef(() => NoopValueAccessorDirective)
1010
- }
1011
- ]
1012
- }]
1013
- }] });
1014
- function injectNgControl(cva) {
1015
- const ngControl = inject(NgControl, { self: true, optional: true });
1016
- if (!ngControl)
1017
- return null;
1018
- if (ngControl instanceof FormControlDirective || ngControl instanceof FormControlName || ngControl instanceof NgModel) {
1019
- if (cva)
1020
- ngControl.valueAccessor = cva;
1021
- return ngControl;
1022
- }
1023
- throw new Error('...');
1024
- }
1025
-
1026
- const DEFAULT_SCROLL_AMOUNT = 150;
1027
- const THRESHOLD = 1;
1028
- /**
1029
- * Directive that adds left/right scroll buttons around the host element when its content overflows horizontally.
1030
- * The directive restructures the DOM by wrapping the host inside a scroll container with navigation buttons.
1031
- *
1032
- * @example
1033
- * <mat-chip-grid yuvScrollButtons>...</mat-chip-grid>
1034
- *
1035
- * @example
1036
- * <div yuvScrollButtons [yuvScrollButtonsAmount]="200">...</div>
1037
- */
1038
- class ScrollButtonsDirective {
1039
- #renderer;
1040
- #elRef;
1041
- #destroyRef;
1042
- #leftBtn;
1043
- #rightBtn;
1044
- #scrollContent;
1045
- constructor() {
1046
- this.#renderer = inject(Renderer2);
1047
- this.#elRef = inject(ElementRef);
1048
- this.translate = inject(TranslateService);
1049
- this.#destroyRef = inject(DestroyRef);
1050
- /** How many pixels to scroll per button click. */
1051
- // eslint-disable-next-line @typescript-eslint/member-ordering
1052
- this.yuvScrollButtonsAmount = input(DEFAULT_SCROLL_AMOUNT, ...(ngDevMode ? [{ debugName: "yuvScrollButtonsAmount" }] : /* istanbul ignore next */ []));
1053
- afterNextRender(() => this.#init());
1054
- }
1055
- #init() {
1056
- const host = this.#elRef.nativeElement;
1057
- const parent = host.parentNode;
1058
- const renderer = this.#renderer;
1059
- // Create wrapper
1060
- const wrapper = renderer.createElement('div');
1061
- this.#applyStyles(wrapper, {
1062
- display: 'flex',
1063
- 'align-items': 'center',
1064
- flex: '1',
1065
- 'min-width': '0'
1066
- });
1067
- // Create scroll content container
1068
- this.#scrollContent = renderer.createElement('div');
1069
- this.#applyStyles(this.#scrollContent, {
1070
- flex: '1',
1071
- 'overflow-x': 'auto',
1072
- 'scrollbar-width': 'none',
1073
- 'min-width': '0'
1074
- });
1075
- // Create buttons
1076
- this.#leftBtn = this.#createButton('chevron_left');
1077
- this.#rightBtn = this.#createButton('chevron_right');
1078
- // Restructure DOM: replace host with wrapper, move host into scroll content
1079
- renderer.insertBefore(parent, wrapper, host);
1080
- renderer.appendChild(this.#scrollContent, host);
1081
- renderer.appendChild(wrapper, this.#leftBtn);
1082
- renderer.appendChild(wrapper, this.#scrollContent);
1083
- renderer.appendChild(wrapper, this.#rightBtn);
1084
- // Apply chip-specific overrides
1085
- this.#applyChipOverrides(host);
1086
- // Hide buttons initially
1087
- this.#setButtonVisible(this.#leftBtn, false);
1088
- this.#setButtonVisible(this.#rightBtn, false);
1089
- // Set up listeners
1090
- this.#scrollContent.addEventListener('scroll', () => this.#checkOverflow(), { passive: true });
1091
- const resizeObserver = new ResizeObserver(() => this.#checkOverflow());
1092
- resizeObserver.observe(this.#scrollContent);
1093
- const mutationObserver = new MutationObserver(() => this.#checkOverflow());
1094
- mutationObserver.observe(this.#scrollContent, { childList: true, subtree: true });
1095
- this.#leftBtn.addEventListener('click', () => {
1096
- this.#scrollContent.scrollBy({ left: -this.yuvScrollButtonsAmount(), behavior: 'smooth' });
1097
- });
1098
- this.#rightBtn.addEventListener('click', () => {
1099
- this.#scrollContent.scrollBy({ left: this.yuvScrollButtonsAmount(), behavior: 'smooth' });
1100
- });
1101
- this.#destroyRef.onDestroy(() => {
1102
- resizeObserver.disconnect();
1103
- mutationObserver.disconnect();
1104
- });
1105
- this.#checkOverflow();
1106
- }
1107
- #createButton(icon) {
1108
- const btn = this.#renderer.createElement('button');
1109
- btn.setAttribute('aria-label', icon === 'chevron_left'
1110
- ? this.translate.instant('yuv.object-form-element.scroll.button.left')
1111
- : this.translate.instant('yuv.object-form-element.scroll.button.right'));
1112
- this.#applyStyles(btn, {
1113
- 'flex-shrink': '0',
1114
- display: 'inline-flex',
1115
- 'align-items': 'center',
1116
- 'justify-content': 'center',
1117
- background: 'none',
1118
- border: 'none',
1119
- cursor: 'pointer',
1120
- padding: '0',
1121
- color: 'var(--ymt-text-color-subtle)',
1122
- width: 'var(--ymt-sizing-m)',
1123
- height: 'var(--ymt-sizing-m)',
1124
- backgroundColor: 'var(--ymt-surface-hover)'
1125
- });
1126
- const iconEl = this.#renderer.createElement('span');
1127
- iconEl.classList.add('material-symbols-sharp');
1128
- this.#applyStyles(iconEl, {
1129
- 'font-size': '1.25rem', //calc(var(--ymt-sizing-s) * 0.8)
1130
- width: 'var(--ymt-sizing-s)',
1131
- height: 'var(--ymt-sizing-s)'
1132
- });
1133
- iconEl.textContent = icon;
1134
- this.#renderer.appendChild(btn, iconEl);
1135
- const highlight = () => {
1136
- btn.style.color = 'var(--ymt-text-color)';
1137
- };
1138
- const reset = () => {
1139
- btn.style.color = 'var(--ymt-text-color-subtle)';
1140
- };
1141
- btn.addEventListener('mouseenter', highlight);
1142
- btn.addEventListener('mouseleave', reset);
1143
- btn.addEventListener('focusin', highlight);
1144
- btn.addEventListener('focusout', reset);
1145
- // Focus-visible outline via CSS class
1146
- btn.addEventListener('focus', () => {
1147
- if (btn.matches(':focus-visible')) {
1148
- btn.style.outline = '2px solid var(--ymt-primary-color)';
1149
- btn.style.outlineOffset = '-2px';
1150
- }
1151
- });
1152
- btn.addEventListener('blur', () => {
1153
- btn.style.outline = '';
1154
- btn.style.outlineOffset = '';
1155
- });
1156
- return btn;
1157
- }
1158
- #applyChipOverrides(host) {
1159
- const chipSet = host.querySelector('.mdc-evolution-chip-set__chips');
1160
- if (chipSet) {
1161
- chipSet.style.flexWrap = 'nowrap';
1162
- }
1163
- // Use MutationObserver to apply overrides to chip rows as they appear
1164
- const applyRowOverrides = () => {
1165
- host.querySelectorAll('mat-chip-row, .mat-mdc-chip-row').forEach((row) => {
1166
- const rowElement = row;
1167
- rowElement.style.flexShrink = '0';
1168
- rowElement.style.maxWidth = 'none';
1169
- const primaryCell = rowElement.querySelector('.mdc-evolution-chip__cell--primary');
1170
- if (primaryCell) {
1171
- primaryCell.style.maxWidth = 'none';
1172
- }
1173
- rowElement
1174
- .querySelectorAll('.mdc-evolution-chip__text-label, .mat-mdc-chip-action-label')
1175
- .forEach((label) => {
1176
- label.style.whiteSpace = 'nowrap';
1177
- });
1178
- });
1179
- };
1180
- applyRowOverrides();
1181
- const observer = new MutationObserver(() => applyRowOverrides());
1182
- observer.observe(host, { childList: true, subtree: true });
1183
- this.#destroyRef.onDestroy(() => observer.disconnect());
1184
- }
1185
- #setButtonVisible(btn, visible) {
1186
- btn.style.display = visible ? 'inline-flex' : 'none';
1187
- }
1188
- #checkOverflow() {
1189
- const scrollElement = this.#scrollContent;
1190
- this.#setButtonVisible(this.#leftBtn, scrollElement.scrollLeft > THRESHOLD);
1191
- this.#setButtonVisible(this.#rightBtn, scrollElement.scrollLeft + scrollElement.clientWidth < scrollElement.scrollWidth - THRESHOLD);
1192
- }
1193
- #applyStyles(element, styles) {
1194
- Object.entries(styles).forEach(([prop, value]) => {
1195
- this.#renderer.setStyle(element, prop, value);
1196
- });
1197
- }
1198
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ScrollButtonsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1199
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: ScrollButtonsDirective, isStandalone: true, selector: "[yuvScrollButtons]", inputs: { yuvScrollButtonsAmount: { classPropertyName: "yuvScrollButtonsAmount", publicName: "yuvScrollButtonsAmount", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
1200
- }
1201
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ScrollButtonsDirective, decorators: [{
1202
- type: Directive,
1203
- args: [{
1204
- selector: '[yuvScrollButtons]'
1205
- }]
1206
- }], ctorParameters: () => [], propDecorators: { yuvScrollButtonsAmount: [{ type: i0.Input, args: [{ isSignal: true, alias: "yuvScrollButtonsAmount", required: false }] }] } });
1207
-
1208
- const directives = [
1209
- BusyOverlayDirective,
1210
- FocusWithinDirective,
1211
- FileDropZoneDirective,
1212
- ClickDoubleDirective,
1213
- LightDismissDirective,
1214
- ContainerSizeDirective,
1215
- LongPressDirective,
1216
- DragSelectDirective,
1217
- DragScrollDirective,
1218
- NoopValueAccessorDirective,
1219
- AutofocusChildDirective,
1220
- AutofocusDelayedDirective,
1221
- ScrollButtonsDirective
1222
- ];
1223
- const components = [ConfirmComponent, ScrollButtonsComponent];
1224
- class YuvCommonModule {
1225
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: YuvCommonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1226
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: YuvCommonModule, imports: [BusyOverlayDirective,
1227
- FocusWithinDirective,
1228
- FileDropZoneDirective,
1229
- ClickDoubleDirective,
1230
- LightDismissDirective,
1231
- ContainerSizeDirective,
1232
- LongPressDirective,
1233
- DragSelectDirective,
1234
- DragScrollDirective,
1235
- NoopValueAccessorDirective,
1236
- AutofocusChildDirective,
1237
- AutofocusDelayedDirective,
1238
- ScrollButtonsDirective, ConfirmComponent, ScrollButtonsComponent], exports: [BusyOverlayDirective,
1239
- FocusWithinDirective,
1240
- FileDropZoneDirective,
1241
- ClickDoubleDirective,
1242
- LightDismissDirective,
1243
- ContainerSizeDirective,
1244
- LongPressDirective,
1245
- DragSelectDirective,
1246
- DragScrollDirective,
1247
- NoopValueAccessorDirective,
1248
- AutofocusChildDirective,
1249
- AutofocusDelayedDirective,
1250
- ScrollButtonsDirective, ConfirmComponent, ScrollButtonsComponent] }); }
1251
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: YuvCommonModule, imports: [components] }); }
1252
- }
1253
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: YuvCommonModule, decorators: [{
1254
- type: NgModule,
1255
- args: [{
1256
- imports: [...directives, ...components],
1257
- exports: [...directives, ...components]
1258
- }]
1259
- }] });
1260
-
1261
- function getFocusableChildren(element) {
1262
- return [...Array.from(element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])'))].filter((el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'));
1263
- }
1264
- function getFirstFocusableChild(element) {
1265
- return getFocusableChildren(element)[0];
1266
- }
1267
-
1268
- /**
1269
- * Service to store and retrieve layout settings. Those
1270
- * settings are stored on the users device because in
1271
- * general layout settings like panel widths are highly
1272
- * dependent on the current device.
1273
- */
1274
- class LayoutSettingsService {
1275
- constructor() {
1276
- this.#STORAGE_PREFIX = 'yuv.layout:';
1277
- /**
1278
- * @deprecated Theme storage is now handled by `ThemeService`.
1279
- */
1280
- this.#MODE_STORAGE_KEY = 'app.layout.mode';
1281
- this.DEFAULT_SPLIT_VIEW_GUTTER_SIZE = 16;
1282
- /**
1283
- * @deprecated Use `ThemeService.mode` instead.
1284
- */
1285
- this.themeMode = signal('system', ...(ngDevMode ? [{ debugName: "themeMode" }] : /* istanbul ignore next */ []));
1286
- /**
1287
- * @deprecated Use `ThemeService.mode` instead.
1288
- */
1289
- this.mode = this.themeMode.asReadonly();
1290
- }
1291
- #STORAGE_PREFIX;
1292
- /**
1293
- * @deprecated Theme storage is now handled by `ThemeService`.
1294
- */
1295
- #MODE_STORAGE_KEY;
1296
- /**
1297
- * @deprecated Theme initialization is now handled by `ThemeService` internally.
1298
- */
1299
- init() {
1300
- // set configured mode
1301
- const modeSettings = this.getSettings(this.#MODE_STORAGE_KEY);
1302
- this.applyLayoutMode(modeSettings?.mode || undefined);
1303
- }
1304
- /**
1305
- * @deprecated Use `ThemeService.toggleTheme()` instead.
1306
- */
1307
- setMode(mode) {
1308
- this.saveSettings(this.#MODE_STORAGE_KEY, { mode });
1309
- }
1310
- saveSettings(key, settings) {
1311
- if (typeof settings === 'object') {
1312
- localStorage.setItem(this.#getKey(key), JSON.stringify(settings));
1313
- return true;
1314
- }
1315
- else
1316
- return false;
1317
- }
1318
- getSettings(key) {
1319
- try {
1320
- const v = localStorage.getItem(this.#getKey(key));
1321
- return v ? JSON.parse(v) : undefined;
1322
- }
1323
- catch (e) {
1324
- console.error('Error while parsing layout settings', e);
1325
- return undefined;
1326
- }
1327
- }
1328
- #getKey(key) {
1329
- return `${this.#STORAGE_PREFIX}${key}`;
1330
- }
1331
- /**
1332
- * Clears all layout settings.
1333
- */
1334
- clearSettings() {
1335
- Object.keys(localStorage).forEach((key) => {
1336
- if (key.startsWith(this.#STORAGE_PREFIX)) {
1337
- localStorage.removeItem(key);
1338
- }
1339
- });
1340
- }
1341
- /**
1342
- * @deprecated Use `ThemeService.toggleTheme()` instead.
1343
- */
1344
- applyLayoutMode(mode) {
1345
- const body = document.getElementsByTagName('body')[0];
1346
- body.style.colorScheme = mode === 'dark' || mode === 'light' ? mode : 'inherit';
1347
- this.themeMode.set(mode || 'system');
1348
- }
1349
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1350
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutSettingsService, providedIn: 'root' }); }
1351
- }
1352
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutSettingsService, decorators: [{
1353
- type: Injectable,
1354
- args: [{
1355
- providedIn: 'root'
1356
- }]
1357
- }] });
1358
-
1359
- /**
1360
- * Providing an error message when a translation string for an object wasn't found
1361
- */
1362
- class FormTranslateService {
1363
- constructor() {
1364
- this.translate = inject(TranslateService);
1365
- }
1366
- /**
1367
- * Set the error label if a translation for this string wasn't provided
1368
- * @param error - error message about a missed translation key
1369
- * @param params
1370
- */
1371
- getErrorLabel(error, params) {
1372
- switch (error) {
1373
- case 'daterange':
1374
- return this.translate.instant('yuv.object-form-element.error.daterange.invalid', params);
1375
- case 'daterangeorder':
1376
- return this.translate.instant('yuv.object-form-element.error.daterangeorder.invalid', params);
1377
- case 'numberrange':
1378
- return this.translate.instant('yuv.object-form-element.error.numberrange.invalid', params);
1379
- case 'numberrangeorder':
1380
- return this.translate.instant('yuv.object-form-element.error.numberrangeorder.invalid', params);
1381
- case 'number':
1382
- return this.translate.instant('yuv.object-form-element.error.number', params);
1383
- case 'precision':
1384
- return this.translate.instant('yuv.object-form-element.error.number.precision', params);
1385
- case 'scale':
1386
- return this.translate.instant('yuv.object-form-element.error.number.scale', params);
1387
- case 'regex':
1388
- return this.translate.instant('yuv.object-form-element.error.string.regex.nomatch', params);
1389
- case 'pattern':
1390
- return this.translate.instant('yuv.object-form-element.error.string.regex.nomatch', params);
1391
- case 'classificationemail':
1392
- return this.translate.instant('yuv.object-form-element.error.string.classification.email', params);
1393
- case 'classificationphone':
1394
- return this.translate.instant('yuv.object-form-element.error.string.classification.phone', params);
1395
- case 'classificationurl':
1396
- return this.translate.instant('yuv.object-form-element.error.string.classification.url', params);
1397
- case 'onlyWhitespaces':
1398
- return this.translate.instant('yuv.object-form-element.error.string.whitespaces', params);
1399
- case 'datecontrol':
1400
- return this.translate.instant('yuv.object-form-element.error.date.invalid', params);
1401
- case 'required':
1402
- return this.translate.instant('yuv.object-form-element.error.required', params);
1403
- case 'maxlength':
1404
- return this.translate.instant('yuv.object-form-element.error.maxlength', params);
1405
- case 'minlength':
1406
- return this.translate.instant('yuv.object-form-element.error.minlength', params);
1407
- case 'minmax':
1408
- return this.translate.instant('yuv.object-form-element.error.minmax', params);
1409
- case 'minvalue':
1410
- return this.translate.instant('yuv.object-form-element.error.minvalue', params);
1411
- case 'maxvalue':
1412
- return this.translate.instant('yuv.object-form-element.error.maxvalue', params);
1413
- default:
1414
- return this.translate.instant(error, params);
1415
- }
1416
- }
1417
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormTranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1418
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormTranslateService, providedIn: 'root' }); }
1419
- }
1420
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormTranslateService, decorators: [{
1421
- type: Injectable,
1422
- args: [{
1423
- providedIn: 'root'
1424
- }]
1425
- }] });
1426
-
1427
- var ThemeStorageKeys;
1428
- (function (ThemeStorageKeys) {
1429
- ThemeStorageKeys["THEME_MODE"] = "yuv.theme:app.theme.mode";
1430
- ThemeStorageKeys["CUSTOM_THEME"] = "yuv.theme:app.theme.custom";
1431
- })(ThemeStorageKeys || (ThemeStorageKeys = {}));
1432
- const DEFAULT_THEME_KEY = 'yuv-default';
1433
- const DEFAULT_THEME = {
1434
- key: DEFAULT_THEME_KEY,
1435
- label: 'yuv.theme.default.label',
1436
- description: 'yuv.theme.default.description',
1437
- hasLightTheme: true,
1438
- hasDarkTheme: true
1439
- };
1440
- const HIGH_CONTRAST_THEME_KEY = 'yuv-high-contrast';
1441
- const HIGH_CONTRAST_THEME = {
1442
- key: HIGH_CONTRAST_THEME_KEY,
1443
- label: 'yuv.theme.highContrast.label',
1444
- description: 'yuv.theme.highContrast.description',
1445
- hasLightTheme: true,
1446
- hasDarkTheme: true
1447
- };
1448
-
1449
- const YUV_CUSTOM_THEME = new InjectionToken('CUSTOM_THEME', { factory: () => [HIGH_CONTRAST_THEME] });
1450
- const provideYuvCustomTheme = (customTheme) => makeEnvironmentProviders([{ provide: YUV_CUSTOM_THEME, useValue: [HIGH_CONTRAST_THEME, ...customTheme] }]);
1451
-
1452
- class ThemeService {
1453
- #rendererFactory;
1454
- #customThemesToken;
1455
- #document;
1456
- #storage;
1457
- #MODE_STORAGE_KEY;
1458
- #CUSTOM_THEME_STORAGE_KEY;
1459
- #renderer;
1460
- #mode;
1461
- #currentTheme;
1462
- #disableMode;
1463
- // Effect to apply classes and save to localStorage
1464
- #setThemeModeEffect;
1465
- constructor() {
1466
- this.#rendererFactory = inject(RendererFactory2);
1467
- this.#customThemesToken = inject(YUV_CUSTOM_THEME, { optional: true });
1468
- this.#document = inject(DOCUMENT);
1469
- this.#storage = inject(AppCacheService);
1470
- this.#MODE_STORAGE_KEY = ThemeStorageKeys.THEME_MODE;
1471
- this.#CUSTOM_THEME_STORAGE_KEY = ThemeStorageKeys.CUSTOM_THEME;
1472
- this.#renderer = this.#rendererFactory.createRenderer(null, null);
1473
- this.#mode = signal('light', ...(ngDevMode ? [{ debugName: "#mode" }] : /* istanbul ignore next */ []));
1474
- this.mode = this.#mode.asReadonly();
1475
- this.customTheme = signal(DEFAULT_THEME, ...(ngDevMode ? [{ debugName: "customTheme" }] : /* istanbul ignore next */ []));
1476
- this.customThemes = computed(() => {
1477
- const customThemesToken = this.#customThemesToken || [];
1478
- return customThemesToken.map((theme) => {
1479
- const { key, label, description, hasLightTheme, hasDarkTheme } = theme;
1480
- return {
1481
- key,
1482
- label,
1483
- description,
1484
- hasLightTheme: hasLightTheme ?? true,
1485
- hasDarkTheme: hasDarkTheme ?? false
1486
- };
1487
- });
1488
- }, ...(ngDevMode ? [{ debugName: "customThemes" }] : /* istanbul ignore next */ []));
1489
- this.#currentTheme = signal(DEFAULT_THEME_KEY, ...(ngDevMode ? [{ debugName: "#currentTheme" }] : /* istanbul ignore next */ []));
1490
- this.currentTheme = this.#currentTheme.asReadonly();
1491
- this.#disableMode = signal(false, ...(ngDevMode ? [{ debugName: "#disableMode" }] : /* istanbul ignore next */ []));
1492
- this.disableMode = this.#disableMode.asReadonly();
1493
- // Effect to apply classes and save to localStorage
1494
- this.#setThemeModeEffect = effect(() => {
1495
- const currentTheme = this.#mode();
1496
- const body = this.#document.getElementsByTagName('body')[0];
1497
- this.setMode(currentTheme);
1498
- // Theme class
1499
- if (currentTheme === 'dark') {
1500
- this.#renderer.setAttribute(body, 'style', 'color-scheme: dark');
1501
- }
1502
- else if (currentTheme === 'light') {
1503
- this.#renderer.setAttribute(body, 'style', 'color-scheme: light');
1504
- }
1505
- else {
1506
- this.#renderer.removeAttribute(body, 'style');
1507
- }
1508
- }, ...(ngDevMode ? [{ debugName: "#setThemeModeEffect" }] : /* istanbul ignore next */ []));
1509
- this.#initializeModes();
1510
- }
1511
- #saveSettings(key, settings) {
1512
- if (typeof settings === 'object') {
1513
- this.#storage.setItem(key, JSON.stringify(settings)).subscribe();
1514
- return true;
1515
- }
1516
- else
1517
- return false;
1518
- }
1519
- #deleteSettings(key) {
1520
- this.#storage.removeItem(key).subscribe();
1521
- return true;
1522
- }
1523
- #checkPrefersContrast() {
1524
- const prefersHighContrast = window.matchMedia('(prefers-contrast: more)').matches;
1525
- }
1526
- #initializeModes() {
1527
- // Theme initialization
1528
- forkJoin([this.#storage.getItem(this.#MODE_STORAGE_KEY), this.#storage.getItem(this.#CUSTOM_THEME_STORAGE_KEY)])
1529
- .pipe(map$1(([savedTheme, savedCustomTheme]) => {
1530
- if (savedTheme) {
1531
- try {
1532
- this.#mode.set(JSON.parse(savedTheme).mode);
1533
- }
1534
- catch (error) {
1535
- this.#mode.set(this.#mode());
1536
- }
1537
- }
1538
- else {
1539
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
1540
- this.#mode.set(prefersDark ? 'dark' : 'light');
1541
- }
1542
- // Contrast initialization
1543
- this.#checkPrefersContrast();
1544
- // Custom Theme initialization
1545
- if (savedCustomTheme) {
1546
- try {
1547
- const customThemeKey = JSON.parse(savedCustomTheme).customTheme;
1548
- this.setCustomTheme(customThemeKey);
1549
- }
1550
- catch (error) {
1551
- // ignore
1552
- }
1553
- }
1554
- }))
1555
- .subscribe();
1556
- }
1557
- setMode(mode) {
1558
- this.#saveSettings(this.#MODE_STORAGE_KEY, { mode });
1559
- }
1560
- setCustomTheme(key) {
1561
- // Reset contrast to system on custom theme change
1562
- const previousTheme = this.customTheme();
1563
- const body = this.#document.getElementsByTagName('body')[0];
1564
- previousTheme && previousTheme.key && this.#renderer.removeClass(body, previousTheme.key);
1565
- const selectedTheme = this.customThemes().find((theme) => theme.key === key);
1566
- if (selectedTheme) {
1567
- this.customTheme.set(selectedTheme);
1568
- this.#currentTheme.set(selectedTheme.key);
1569
- selectedTheme.key && this.#renderer.addClass(body, selectedTheme.key);
1570
- // Check if custom theme has light or dark mode
1571
- if ((selectedTheme.hasDarkTheme && selectedTheme.hasLightTheme) || (!selectedTheme.hasDarkTheme && !selectedTheme.hasLightTheme)) {
1572
- // If both, do nothing further with this (enable group)
1573
- this.#disableMode.set(false);
1574
- }
1575
- else {
1576
- // Else disable mode button group and set the corresponding mode active
1577
- if (selectedTheme.hasDarkTheme) {
1578
- this.#mode.set('dark');
1579
- }
1580
- else if (selectedTheme.hasLightTheme) {
1581
- this.#mode.set('light');
1582
- }
1583
- this.#disableMode.set(true);
1584
- }
1585
- this.#saveSettings(this.#CUSTOM_THEME_STORAGE_KEY, { customTheme: key });
1586
- }
1587
- else {
1588
- if (key === DEFAULT_THEME_KEY) {
1589
- this.#disableMode.set(false);
1590
- this.#renderer.removeClass(body, HIGH_CONTRAST_THEME_KEY);
1591
- this.#deleteSettings(this.#CUSTOM_THEME_STORAGE_KEY);
1592
- }
1593
- // Default theme or no theme found
1594
- }
1595
- }
1596
- toggleTheme(theme) {
1597
- this.#mode.set(theme);
1598
- }
1599
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1600
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ThemeService, providedIn: 'root' }); }
1601
- }
1602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ThemeService, decorators: [{
1603
- type: Injectable,
1604
- args: [{
1605
- providedIn: 'root'
1606
- }]
1607
- }], ctorParameters: () => [] });
1608
-
1609
- marker('yuv.theme.default.label');
1610
- marker('yuv.theme.default.description');
1611
- marker('yuv.theme.highContrast.label');
1612
- marker('yuv.theme.highContrast.description');
1613
-
1614
- // eslint-disable-next-line @angular-eslint/component-class-suffix
1615
- class AbstractMatFormField {
1616
- constructor() {
1617
- this.stateChanges = new Subject();
1618
- this.elRef = inject(ElementRef);
1619
- this.#value = signal(null, ...(ngDevMode ? [{ debugName: "#value" }] : /* istanbul ignore next */ []));
1620
- this.#id = signal('', ...(ngDevMode ? [{ debugName: "#id" }] : /* istanbul ignore next */ []));
1621
- this.#placeholder = signal('', ...(ngDevMode ? [{ debugName: "#placeholder" }] : /* istanbul ignore next */ []));
1622
- this.ngControl = null;
1623
- this.#focused = signal(false, ...(ngDevMode ? [{ debugName: "#focused" }] : /* istanbul ignore next */ []));
1624
- this.#empty = signal(false, ...(ngDevMode ? [{ debugName: "#empty" }] : /* istanbul ignore next */ []));
1625
- this.#required = signal(false, ...(ngDevMode ? [{ debugName: "#required" }] : /* istanbul ignore next */ []));
1626
- this.#disabled = signal(false, ...(ngDevMode ? [{ debugName: "#disabled" }] : /* istanbul ignore next */ []));
1627
- this.focusHandled = signal(false, ...(ngDevMode ? [{ debugName: "focusHandled" }] : /* istanbul ignore next */ []));
1628
- this.#errorState = signal(false, ...(ngDevMode ? [{ debugName: "#errorState" }] : /* istanbul ignore next */ []));
1629
- }
1630
- onFocusIn() {
1631
- this.focused = true;
1632
- }
1633
- onFocusout() {
1634
- this.focused = false;
1635
- }
1636
- #value;
1637
- set value(v) {
1638
- this.#value.set(v);
1639
- this.empty = !v;
1640
- this.stateChanges.next();
1641
- }
1642
- get value() {
1643
- return this.#value();
1644
- }
1645
- #id;
1646
- set id(v) {
1647
- this.#id.set(v);
1648
- this.stateChanges.next();
1649
- }
1650
- get id() {
1651
- return this.#id();
1652
- }
1653
- #placeholder;
1654
- set placeholder(v) {
1655
- this.#placeholder.set(v);
1656
- this.stateChanges.next();
1657
- }
1658
- get placeholder() {
1659
- return this.#placeholder();
1660
- }
1661
- #focused;
1662
- set focused(f) {
1663
- this.#focused.set(f);
1664
- this.stateChanges.next();
1665
- }
1666
- get focused() {
1667
- return this.#focused();
1668
- }
1669
- // #shouldLabelFloat = signal<boolean>(false);
1670
- // set shouldLabelFloat(f: boolean) {
1671
- // this.#shouldLabelFloat.set(f);
1672
- // this.stateChanges.next();
1673
- // }
1674
- get shouldLabelFloat() {
1675
- return this.focused || !this.empty || !!this.ngControl?.invalid;
1676
- }
1677
- #empty;
1678
- set empty(f) {
1679
- this.#empty.set(f);
1680
- this.stateChanges.next();
1681
- }
1682
- get empty() {
1683
- return this.#empty();
1684
- }
1685
- #required;
1686
- set required(f) {
1687
- this.#required.set(coerceBooleanProperty(f));
1688
- this.stateChanges.next();
1689
- }
1690
- get required() {
1691
- return this.#required();
1692
- }
1693
- #disabled;
1694
- set disabled(f) {
1695
- this.#disabled.set(coerceBooleanProperty(f));
1696
- this.stateChanges.next();
1697
- }
1698
- get disabled() {
1699
- return this.#disabled();
1700
- }
1701
- #errorState;
1702
- set errorState(f) {
1703
- this.#errorState.set(coerceBooleanProperty(f));
1704
- this.stateChanges.next();
1705
- }
1706
- get errorState() {
1707
- return this.#errorState();
1708
- }
1709
- setDescribedByIds(ids) {
1710
- // this.describedBy = ids.join(' ');
1711
- }
1712
- onContainerClick(event) {
1713
- if (this.focusHandled())
1714
- return;
1715
- const fe = getFocusableChildren(this.elRef.nativeElement);
1716
- if (fe[0] && !fe.includes(event.target)) {
1717
- fe[0].focus();
1718
- }
1719
- }
1720
- onNgOnDestroy() {
1721
- this.stateChanges.complete();
1722
- }
1723
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AbstractMatFormField, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1724
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AbstractMatFormField, isStandalone: true, selector: "ng-component", inputs: { placeholder: "placeholder", required: "required", disabled: "disabled" }, host: { listeners: { "focusin": "onFocusIn()", "focusout": "onFocusout()" } }, ngImport: i0, template: '', isInline: true }); }
1725
- }
1726
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AbstractMatFormField, decorators: [{
1727
- type: Component,
1728
- args: [{
1729
- template: '',
1730
- host: {
1731
- '(focusin)': 'onFocusIn()',
1732
- '(focusout)': 'onFocusout()'
1733
- }
1734
- }]
1735
- }], propDecorators: { placeholder: [{
1736
- type: Input
1737
- }], required: [{
1738
- type: Input
1739
- }], disabled: [{
1740
- type: Input
1741
- }] } });
1742
-
1743
- var DialogSize;
1744
- (function (DialogSize) {
1745
- DialogSize["SMALL"] = "small";
1746
- DialogSize["MEDIUM"] = "medium";
1747
- DialogSize["LARGE"] = "large";
1748
- DialogSize["EXTRA_LARGE"] = "extra-large";
1749
- DialogSize["FULL_SCREEN"] = "full-screen";
1750
- })(DialogSize || (DialogSize = {}));
1751
-
1752
- class RetentionBadgeComponent {
1753
- constructor() {
1754
- this.#retention = inject(RetentionService);
1755
- this.dmsObject = input.required(...(ngDevMode ? [{ debugName: "dmsObject" }] : /* istanbul ignore next */ []));
1756
- this.retentionData = computed(() => {
1757
- return this.#retention.getRetentionState(this.dmsObject());
1758
- }, ...(ngDevMode ? [{ debugName: "retentionData" }] : /* istanbul ignore next */ []));
1759
- }
1760
- #retention;
1761
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RetentionBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1762
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RetentionBadgeComponent, isStandalone: true, selector: "yuv-retention-badge", inputs: { dmsObject: { classPropertyName: "dmsObject", publicName: "dmsObject", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@let rd = retentionData();\n@if (rd.underRetention) {\n <div class=\"badge\">\n <span class=\"badge__icon\">\n <mat-icon class=\"ymt-icon--size-s\">lock_clock</mat-icon>\n </span>\n <span class=\"badge__label\">\n <span class=\"badge__label-truncated\">\n {{\n 'yuv.retention-badge.retain'\n | translate\n : {\n from: rd.start | localeDate: 'shortDate',\n until: rd.end | localeDate: 'shortDate'\n }\n }}\n </span>\n </span>\n </div>\n}\n", styles: [":host{display:contents}:host .badge{font:var(--ymt-font-body-subtle);color:var(--badge-retention-color, var(--ymt-on-surface));display:inline-flex;overflow:hidden;padding:var(--ymt-spacing-2xs) var(--ymt-spacing-s) var(--ymt-spacing-2xs) var(--ymt-spacing-xs);background-color:var(--badge-retention-background, var(--ymt-primary-container));border-radius:var(--ymt-corner-full);gap:var(--ymt-spacing-s)}:host .badge__icon{flex-shrink:0;color:var(--badge-retention-icon-color, var(--ymt-on-primary-container));display:flex;align-items:center;justify-content:center}:host .badge__label{color:var(--badge-retention-color, var(--ymt-on-surface));align-self:stretch;display:flex;align-items:center;justify-content:center;border-top-right-radius:var(--ymt-corner-full);border-bottom-right-radius:var(--ymt-corner-full);overflow:hidden}:host .badge__label-truncated{text-overflow:ellipsis;overflow:hidden;white-space:nowrap;display:block}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: LocaleDatePipe, name: "localeDate" }] }); }
1763
- }
1764
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RetentionBadgeComponent, decorators: [{
1765
- type: Component,
1766
- args: [{ selector: 'yuv-retention-badge', standalone: true, imports: [TranslatePipe, LocaleDatePipe, MatIconModule], template: "@let rd = retentionData();\n@if (rd.underRetention) {\n <div class=\"badge\">\n <span class=\"badge__icon\">\n <mat-icon class=\"ymt-icon--size-s\">lock_clock</mat-icon>\n </span>\n <span class=\"badge__label\">\n <span class=\"badge__label-truncated\">\n {{\n 'yuv.retention-badge.retain'\n | translate\n : {\n from: rd.start | localeDate: 'shortDate',\n until: rd.end | localeDate: 'shortDate'\n }\n }}\n </span>\n </span>\n </div>\n}\n", styles: [":host{display:contents}:host .badge{font:var(--ymt-font-body-subtle);color:var(--badge-retention-color, var(--ymt-on-surface));display:inline-flex;overflow:hidden;padding:var(--ymt-spacing-2xs) var(--ymt-spacing-s) var(--ymt-spacing-2xs) var(--ymt-spacing-xs);background-color:var(--badge-retention-background, var(--ymt-primary-container));border-radius:var(--ymt-corner-full);gap:var(--ymt-spacing-s)}:host .badge__icon{flex-shrink:0;color:var(--badge-retention-icon-color, var(--ymt-on-primary-container));display:flex;align-items:center;justify-content:center}:host .badge__label{color:var(--badge-retention-color, var(--ymt-on-surface));align-self:stretch;display:flex;align-items:center;justify-content:center;border-top-right-radius:var(--ymt-corner-full);border-bottom-right-radius:var(--ymt-corner-full);overflow:hidden}:host .badge__label-truncated{text-overflow:ellipsis;overflow:hidden;white-space:nowrap;display:block}\n"] }]
1767
- }], propDecorators: { dmsObject: [{ type: i0.Input, args: [{ isSignal: true, alias: "dmsObject", required: true }] }] } });
1768
-
1769
- class ConfirmService {
1770
- #dialog = inject(MatDialog);
1771
- confirm(data) {
1772
- return this.#dialog
1773
- .open(ConfirmComponent, { data })
1774
- .afterClosed()
1775
- .pipe(map((result) => !!result));
1776
- }
1777
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ConfirmService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1778
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ConfirmService, providedIn: 'root' }); }
1779
- }
1780
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ConfirmService, decorators: [{
1781
- type: Injectable,
1782
- args: [{
1783
- providedIn: 'root'
1784
- }]
1785
- }] });
1786
-
1787
- class HaloFocusComponent {
1788
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HaloFocusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1789
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: HaloFocusComponent, isStandalone: true, selector: "yuv-halo-focus", ngImport: i0, template: "<p>halo-focus works!</p>\n", styles: [""] }); }
1790
- }
1791
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HaloFocusComponent, decorators: [{
1792
- type: Component,
1793
- args: [{ selector: 'yuv-halo-focus', imports: [], template: "<p>halo-focus works!</p>\n" }]
1794
- }] });
1795
7
 
1796
8
  /**
1797
9
  * Generated bundle index. Do not edit.
1798
10
  */
1799
-
1800
- export { AbstractMatFormField, AutofocusChildDirective, AutofocusDelayedDirective, BusyOverlayComponent, BusyOverlayDirective, ClickDoubleDirective, ConfirmComponent, ConfirmService, ContainerSizeDirective, DEFAULT_THEME, DEFAULT_THEME_KEY, DialogComponent, DialogSize, DragScrollDirective, DragSelectDirective, DragSelectItemDirective, FileDropZoneDirective, FocusWithinDirective, FormTranslateService, HIGH_CONTRAST_THEME, HIGH_CONTRAST_THEME_KEY, HaloFocusComponent, LayoutSettingsService, LightDismissDirective, LongPressDirective, NoopValueAccessorDirective, RetentionBadgeComponent, ScrollButtonsComponent, ScrollButtonsDirective, ThemeService, ThemeStorageKeys, YUV_CUSTOM_THEME, YuvCommonModule, getFirstFocusableChild, getFocusableChildren, injectNgControl, provideYuvCustomTheme };
1801
11
  //# sourceMappingURL=yuuvis-client-framework-common.mjs.map