@quadrel-enterprise-ui/framework 20.27.4 → 20.27.6-beta.242.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/quadrel-enterprise-ui-framework.mjs +535 -237
- package/fesm2022/quadrel-enterprise-ui-framework.mjs.map +1 -1
- package/index.d.ts +94 -35
- package/package.json +1 -1
- package/src/assets/i18n/de.json +1 -0
- package/src/assets/i18n/en.json +1 -0
- package/src/assets/i18n/fr.json +1 -0
- package/src/assets/i18n/it.json +1 -0
- package/fesm2022/quadrel-enterprise-ui-framework-dialog-confirm.module-BQLiEzSo.mjs +0 -42
- package/fesm2022/quadrel-enterprise-ui-framework-dialog-confirm.module-BQLiEzSo.mjs.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ElementRef, Directive, InjectionToken, HostBinding, Input, ViewEncapsulation, Component, Injectable, Injector, HostListener, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, NgModule, EventEmitter, Output, Renderer2, Pipe, ViewContainerRef, NO_ERRORS_SCHEMA, DestroyRef, SecurityContext, NgZone, ViewChildren, forwardRef, ContentChildren, ContentChild, isDevMode, QueryList, TemplateRef, CUSTOM_ELEMENTS_SCHEMA, provideAppInitializer } from '@angular/core';
|
|
3
|
-
import { Dialog, DialogRef, DialogModule } from '@angular/cdk/dialog';
|
|
2
|
+
import { inject, ElementRef, Directive, InjectionToken, HostBinding, Input, ViewEncapsulation, Component, Injectable, Injector, HostListener, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, NgModule, EventEmitter, Output, Renderer2, Pipe, ViewContainerRef, NO_ERRORS_SCHEMA, DestroyRef, SecurityContext, NgZone, ViewChildren, forwardRef, ContentChildren, ContentChild, afterNextRender, isDevMode, QueryList, TemplateRef, CUSTOM_ELEMENTS_SCHEMA, provideAppInitializer } from '@angular/core';
|
|
3
|
+
import { Dialog, DialogRef, DIALOG_DATA, DialogModule } from '@angular/cdk/dialog';
|
|
4
4
|
import * as i1 from '@angular/common';
|
|
5
5
|
import { CommonModule, NgFor, NgIf, NgClass, NgTemplateOutlet, AsyncPipe } from '@angular/common';
|
|
6
|
-
import { BehaviorSubject, pairwise,
|
|
6
|
+
import { BehaviorSubject, pairwise, map as map$1, Subject, throwError, of, ReplaySubject, merge, fromEvent, isObservable, NEVER, Observable, EMPTY, shareReplay, Subscription, distinctUntilChanged as distinctUntilChanged$1, debounce, timer, switchMap, startWith as startWith$1, debounceTime as debounceTime$1, takeUntil as takeUntil$1, firstValueFrom, combineLatest, concat, take as take$1, tap as tap$1, from, delay, first, scan, queueScheduler, filter as filter$1, concatMap as concatMap$1, exhaustMap, finalize, forkJoin, delayWhen, combineLatestWith, withLatestFrom as withLatestFrom$1, async } from 'rxjs';
|
|
7
7
|
import { map, takeUntil, take, filter, catchError, debounceTime, startWith, distinctUntilChanged, concatMap, skip, tap, auditTime, withLatestFrom, observeOn, switchMap as switchMap$1, pairwise as pairwise$1, mergeMap, delay as delay$1 } from 'rxjs/operators';
|
|
8
8
|
import { v4 } from 'uuid';
|
|
9
9
|
import * as i3 from '@ngx-translate/core';
|
|
@@ -1143,13 +1143,41 @@ var QdDialogSize;
|
|
|
1143
1143
|
QdDialogSize["FullWidth"] = "100%";
|
|
1144
1144
|
})(QdDialogSize || (QdDialogSize = {}));
|
|
1145
1145
|
|
|
1146
|
+
/**
|
|
1147
|
+
* Tracks which dialogs currently hold unsaved changes.
|
|
1148
|
+
*
|
|
1149
|
+
* Pending changes are always bound to the dialog that owns them. A dialog is
|
|
1150
|
+
* identified by its `DialogRef`, so two dialogs open at the same time never
|
|
1151
|
+
* read or overwrite each other's state.
|
|
1152
|
+
*
|
|
1153
|
+
* #### Notes & Best Practices:
|
|
1154
|
+
*
|
|
1155
|
+
* - A dialog can hold several sources of pending changes, for example one per
|
|
1156
|
+
* form. The dialog counts as changed while at least one source is marked.
|
|
1157
|
+
* - Every source that marks changes must clear them again when it is destroyed.
|
|
1158
|
+
* - A dialog without any marked source reports no pending changes. This is why
|
|
1159
|
+
* the discard confirmation dialog can always close itself.
|
|
1160
|
+
* - Owners are held weakly, so a dialog that is destroyed without clearing its
|
|
1161
|
+
* sources cannot keep them alive.
|
|
1162
|
+
*/
|
|
1146
1163
|
class QdDialogChangeGuardService {
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1164
|
+
_pendingChangeSourcesByOwner = new WeakMap();
|
|
1165
|
+
hasPendingChanges(owner) {
|
|
1166
|
+
const sources = this._pendingChangeSourcesByOwner.get(owner);
|
|
1167
|
+
return !!sources?.size;
|
|
1168
|
+
}
|
|
1169
|
+
markPendingChanges(source, owner) {
|
|
1170
|
+
const sources = this._pendingChangeSourcesByOwner.get(owner) ?? new Set();
|
|
1171
|
+
sources.add(source);
|
|
1172
|
+
this._pendingChangeSourcesByOwner.set(owner, sources);
|
|
1173
|
+
}
|
|
1174
|
+
clearPendingChanges(source, owner) {
|
|
1175
|
+
const sources = this._pendingChangeSourcesByOwner.get(owner);
|
|
1176
|
+
if (!sources)
|
|
1177
|
+
return;
|
|
1178
|
+
sources.delete(source);
|
|
1179
|
+
if (!sources.size)
|
|
1180
|
+
this._pendingChangeSourcesByOwner.delete(owner);
|
|
1153
1181
|
}
|
|
1154
1182
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogChangeGuardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1155
1183
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogChangeGuardService, providedIn: 'root' });
|
|
@@ -1161,6 +1189,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
1161
1189
|
}]
|
|
1162
1190
|
}] });
|
|
1163
1191
|
|
|
1192
|
+
/**
|
|
1193
|
+
* DI token that provides the component rendered inside the discard-confirmation
|
|
1194
|
+
* dialog.
|
|
1195
|
+
*
|
|
1196
|
+
* The discard dialog renders `<qd-dialog>`, `<qd-dialog>` opens the discard
|
|
1197
|
+
* dialog through this service, and the service opens the discard dialog
|
|
1198
|
+
* component — a compile-time template cycle. Binding the component through this
|
|
1199
|
+
* token (instead of the service importing it directly) breaks that cycle, so
|
|
1200
|
+
* the dialog can be bundled eagerly instead of lazy-loaded. This is the one
|
|
1201
|
+
* place a token is genuinely required.
|
|
1202
|
+
*/
|
|
1203
|
+
const QD_DISCARD_CONFIRM_DIALOG_COMPONENT_TOKEN = new InjectionToken('QD_DISCARD_CONFIRM_DIALOG_COMPONENT_TOKEN');
|
|
1204
|
+
|
|
1164
1205
|
const GENERIC_DEFAULTS = {
|
|
1165
1206
|
title: { i18n: 'i18n.qd.dialog.confirm.title' },
|
|
1166
1207
|
confirm: { i18n: 'i18n.qd.dialog.confirm.proceed' },
|
|
@@ -1174,6 +1215,7 @@ const CANCEL_DEFAULTS = {
|
|
|
1174
1215
|
};
|
|
1175
1216
|
class QdConfirmationDialogOpenerService {
|
|
1176
1217
|
dialog = inject(Dialog);
|
|
1218
|
+
confirmDialogComponent = inject(QD_DISCARD_CONFIRM_DIALOG_COMPONENT_TOKEN);
|
|
1177
1219
|
showConfirmDialog(config, testId = 'dialog-confirm') {
|
|
1178
1220
|
const resolved = {
|
|
1179
1221
|
title: config.title ?? GENERIC_DEFAULTS.title,
|
|
@@ -1181,14 +1223,12 @@ class QdConfirmationDialogOpenerService {
|
|
|
1181
1223
|
confirm: config.confirm ?? GENERIC_DEFAULTS.confirm,
|
|
1182
1224
|
cancel: config.cancel ?? GENERIC_DEFAULTS.cancel
|
|
1183
1225
|
};
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
return dialogRef.closed.pipe(map$1(result => !!result));
|
|
1191
|
-
}));
|
|
1226
|
+
const dialogRef = this.open(this.confirmDialogComponent, {
|
|
1227
|
+
title: resolved.title,
|
|
1228
|
+
dialogSize: QdDialogSize.Small,
|
|
1229
|
+
data: { ...resolved, testId }
|
|
1230
|
+
});
|
|
1231
|
+
return dialogRef.closed.pipe(map$1(result => !!result));
|
|
1192
1232
|
}
|
|
1193
1233
|
showDiscardConfirmDialog(message, testId = 'cancel-confirmation') {
|
|
1194
1234
|
return this.showConfirmDialog({
|
|
@@ -1458,6 +1498,15 @@ class QdDialogComponent {
|
|
|
1458
1498
|
changeDetectorRef = inject(ChangeDetectorRef);
|
|
1459
1499
|
dialogChangeGuard = inject(QdDialogChangeGuardService);
|
|
1460
1500
|
dialogService = inject(QdDialogService);
|
|
1501
|
+
/**
|
|
1502
|
+
* A static test ID for integration tests can be set. <br />
|
|
1503
|
+
* The value for the HTML attribute [data-test-id].
|
|
1504
|
+
*
|
|
1505
|
+
* Every element of the dialog derives its own test ID from this root,
|
|
1506
|
+
* for example `<root>-close-button`. A nested dialog passes its own root
|
|
1507
|
+
* down, so a stacked dialog stays reachable without structural selectors.
|
|
1508
|
+
*/
|
|
1509
|
+
testId = 'dialog';
|
|
1461
1510
|
body;
|
|
1462
1511
|
infoBanners;
|
|
1463
1512
|
get hasSectionsClass() {
|
|
@@ -1468,6 +1517,7 @@ class QdDialogComponent {
|
|
|
1468
1517
|
hasInfoBanner;
|
|
1469
1518
|
_destroyed$ = new Subject();
|
|
1470
1519
|
_pageDialogCanCloseFn = null;
|
|
1520
|
+
_isConfirmingClose = false;
|
|
1471
1521
|
get isFullWidth() {
|
|
1472
1522
|
return this.config.dialogSize === QdDialogSize.FullWidth;
|
|
1473
1523
|
}
|
|
@@ -1509,10 +1559,14 @@ class QdDialogComponent {
|
|
|
1509
1559
|
this.dialogRef.close();
|
|
1510
1560
|
}
|
|
1511
1561
|
confirmAndClose() {
|
|
1562
|
+
if (this._isConfirmingClose)
|
|
1563
|
+
return;
|
|
1564
|
+
this._isConfirmingClose = true;
|
|
1512
1565
|
this.dialog
|
|
1513
1566
|
.showDiscardConfirmDialog(this.config.cancel?.confirmationMessage, 'dialog-cancel-confirmation')
|
|
1514
1567
|
.pipe(take(1))
|
|
1515
1568
|
.subscribe(confirmed => {
|
|
1569
|
+
this._isConfirmingClose = false;
|
|
1516
1570
|
if (!confirmed)
|
|
1517
1571
|
return;
|
|
1518
1572
|
this.config.cancel?.handler?.();
|
|
@@ -1533,7 +1587,7 @@ class QdDialogComponent {
|
|
|
1533
1587
|
});
|
|
1534
1588
|
}
|
|
1535
1589
|
handleLegacyDialogClose() {
|
|
1536
|
-
if (!this.dialogChangeGuard.hasPendingChanges)
|
|
1590
|
+
if (!this.dialogChangeGuard.hasPendingChanges(this.dialogRef))
|
|
1537
1591
|
return this.runCancelHandlerAndClose();
|
|
1538
1592
|
this.confirmAndClose();
|
|
1539
1593
|
}
|
|
@@ -1550,12 +1604,15 @@ class QdDialogComponent {
|
|
|
1550
1604
|
});
|
|
1551
1605
|
}
|
|
1552
1606
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1553
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdDialogComponent, isStandalone: false, selector: "qd-dialog", host: { properties: { "class.full-width": "isFullWidth", "class.has-sections": "this.hasSectionsClass" } }, providers: [QdDialogComponent], viewQueries: [{ propertyName: "body", first: true, predicate: ["body"], descendants: true }, { propertyName: "infoBanners", first: true, predicate: ["banners"], descendants: true }], ngImport: i0, template: "<div *ngIf=\"!isFullWidth\" class=\"dialog-header\">\n <div class=\"title-container\">\n {{ config?.title?.i18n | translate }}\n </div>\n <button\n *ngIf=\"!config?.hideCloseHeaderButton\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n >\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n </button>\n</div>\n\n<button\n *ngIf=\"!config?.hideCloseHeaderButton && isFullWidth\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n>\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n</button>\n\n<div class=\"wrapper-body\">\n <div [class.has-dialog-info-banners]=\"hasInfoBanner\" #banners>\n <ng-content select=\"qd-page-info-banner\"></ng-content>\n </div>\n <div class=\"body\" [class.full-width]=\"isFullWidth\" #body>\n <ng-content></ng-content>\n </div>\n</div>\n\n<div class=\"qd-dialog-actions\">\n <button
|
|
1607
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdDialogComponent, isStandalone: false, selector: "qd-dialog", inputs: { testId: ["data-test-id", "testId"] }, host: { properties: { "class.full-width": "isFullWidth", "class.has-sections": "this.hasSectionsClass" } }, providers: [QdDialogComponent], viewQueries: [{ propertyName: "body", first: true, predicate: ["body"], descendants: true }, { propertyName: "infoBanners", first: true, predicate: ["banners"], descendants: true }], ngImport: i0, template: "<div *ngIf=\"!isFullWidth\" class=\"dialog-header\">\n <div class=\"title-container\">\n {{ config?.title?.i18n | translate }}\n </div>\n <button\n *ngIf=\"!config?.hideCloseHeaderButton\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n [attr.aria-label]=\"'i18n.qd.dialog.action.close' | translate\"\n [data-test-id]=\"testId + '-close-button'\"\n >\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n </button>\n</div>\n\n<button\n *ngIf=\"!config?.hideCloseHeaderButton && isFullWidth\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n [attr.aria-label]=\"'i18n.qd.dialog.action.close' | translate\"\n [data-test-id]=\"testId + '-close-button'\"\n>\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n</button>\n\n<div class=\"wrapper-body\">\n <div [class.has-dialog-info-banners]=\"hasInfoBanner\" #banners>\n <ng-content select=\"qd-page-info-banner\"></ng-content>\n </div>\n <div class=\"body\" [class.full-width]=\"isFullWidth\" #body>\n <ng-content></ng-content>\n </div>\n</div>\n\n<div class=\"qd-dialog-actions\">\n <button\n *ngIf=\"config.cancel\"\n qdButton\n qdButtonGhost\n color=\"secondary\"\n (click)=\"close()\"\n [data-test-id]=\"testId + '-cancel-button'\"\n >\n {{ config?.cancel?.label?.i18n ?? \"i18n.qd.dialog.action.cancel\" | translate }}\n </button>\n <button *ngIf=\"config.primary\" qdButton (click)=\"primaryActionClicked()\" [data-test-id]=\"testId + '-primary-button'\">\n {{ config?.primary?.label?.i18n ?? \"i18n.qd.dialog.action.primary\" | translate }}\n </button>\n <ng-content select=\"qd-dialog-action\"></ng-content>\n</div>\n", styles: ["qd-dialog{display:flex;width:100%;height:100%;flex-direction:column;background:#efefef}qd-dialog.full-width{position:relative;width:calc(100% - 3rem)!important;height:calc(100% - 3rem)!important;margin:1.5rem}qd-dialog.full-width .close{position:absolute;z-index:9999;top:1.25rem;right:1rem;color:#454545;cursor:pointer}.dialog-header{display:flex;height:3.125rem;flex-direction:row;align-items:center;justify-content:space-between;padding:.5rem 1.5rem;border-bottom:2px solid rgb(213,213,213);background:#fff}.dialog-header .title-container{display:block;color:#171717;font-weight:500}.dialog-header .close{cursor:pointer}.wrapper-body{overflow:auto;flex:1;background:#efefef}.wrapper-body .has-dialog-info-banners{padding:1rem 1.25rem .5rem;border-bottom:rgb(213,213,213) solid .0625rem;background-color:#fff}.wrapper-body .has-dialog-info-banners qd-page-info-banner:last-child{margin-bottom:0}.wrapper-body .body.full-width{height:100%}.wrapper-body .body:not(.full-width){max-height:80vh;padding:1rem 1.5rem}.wrapper-body .body:not(.full-width) qd-section{padding-right:1.5rem;padding-left:1.5rem}.wrapper-body .body:not(.full-width) qd-text-section{margin-right:0;margin-left:0}qd-dialog.has-sections .wrapper-body .body:not(.full-width){padding:0}.qd-dialog-actions{display:flex;justify-content:flex-end;padding:.5rem 1.5rem;border-top:2px solid rgb(213,213,213);background:#efefef;column-gap:.625rem}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdIconButtonComponent, selector: "button[qdIconButton], a[qdIconButton], button[qd-icon-button]", inputs: ["color", "data-test-id"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon", "status"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None });
|
|
1554
1608
|
}
|
|
1555
1609
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogComponent, decorators: [{
|
|
1556
1610
|
type: Component,
|
|
1557
|
-
args: [{ selector: 'qd-dialog', host: { '[class.full-width]': 'isFullWidth' }, encapsulation: ViewEncapsulation.None, providers: [QdDialogComponent], standalone: false, template: "<div *ngIf=\"!isFullWidth\" class=\"dialog-header\">\n <div class=\"title-container\">\n {{ config?.title?.i18n | translate }}\n </div>\n <button\n *ngIf=\"!config?.hideCloseHeaderButton\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n >\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n </button>\n</div>\n\n<button\n *ngIf=\"!config?.hideCloseHeaderButton && isFullWidth\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n>\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n</button>\n\n<div class=\"wrapper-body\">\n <div [class.has-dialog-info-banners]=\"hasInfoBanner\" #banners>\n <ng-content select=\"qd-page-info-banner\"></ng-content>\n </div>\n <div class=\"body\" [class.full-width]=\"isFullWidth\" #body>\n <ng-content></ng-content>\n </div>\n</div>\n\n<div class=\"qd-dialog-actions\">\n <button
|
|
1558
|
-
}], propDecorators: {
|
|
1611
|
+
args: [{ selector: 'qd-dialog', host: { '[class.full-width]': 'isFullWidth' }, encapsulation: ViewEncapsulation.None, providers: [QdDialogComponent], standalone: false, template: "<div *ngIf=\"!isFullWidth\" class=\"dialog-header\">\n <div class=\"title-container\">\n {{ config?.title?.i18n | translate }}\n </div>\n <button\n *ngIf=\"!config?.hideCloseHeaderButton\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n [attr.aria-label]=\"'i18n.qd.dialog.action.close' | translate\"\n [data-test-id]=\"testId + '-close-button'\"\n >\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n </button>\n</div>\n\n<button\n *ngIf=\"!config?.hideCloseHeaderButton && isFullWidth\"\n qdIconButton\n class=\"close\"\n color=\"secondary\"\n tabindex=\"-1\"\n (click)=\"close()\"\n [attr.aria-label]=\"'i18n.qd.dialog.action.close' | translate\"\n [data-test-id]=\"testId + '-close-button'\"\n>\n <qd-icon icon=\"timesLargeLight\"></qd-icon>\n</button>\n\n<div class=\"wrapper-body\">\n <div [class.has-dialog-info-banners]=\"hasInfoBanner\" #banners>\n <ng-content select=\"qd-page-info-banner\"></ng-content>\n </div>\n <div class=\"body\" [class.full-width]=\"isFullWidth\" #body>\n <ng-content></ng-content>\n </div>\n</div>\n\n<div class=\"qd-dialog-actions\">\n <button\n *ngIf=\"config.cancel\"\n qdButton\n qdButtonGhost\n color=\"secondary\"\n (click)=\"close()\"\n [data-test-id]=\"testId + '-cancel-button'\"\n >\n {{ config?.cancel?.label?.i18n ?? \"i18n.qd.dialog.action.cancel\" | translate }}\n </button>\n <button *ngIf=\"config.primary\" qdButton (click)=\"primaryActionClicked()\" [data-test-id]=\"testId + '-primary-button'\">\n {{ config?.primary?.label?.i18n ?? \"i18n.qd.dialog.action.primary\" | translate }}\n </button>\n <ng-content select=\"qd-dialog-action\"></ng-content>\n</div>\n", styles: ["qd-dialog{display:flex;width:100%;height:100%;flex-direction:column;background:#efefef}qd-dialog.full-width{position:relative;width:calc(100% - 3rem)!important;height:calc(100% - 3rem)!important;margin:1.5rem}qd-dialog.full-width .close{position:absolute;z-index:9999;top:1.25rem;right:1rem;color:#454545;cursor:pointer}.dialog-header{display:flex;height:3.125rem;flex-direction:row;align-items:center;justify-content:space-between;padding:.5rem 1.5rem;border-bottom:2px solid rgb(213,213,213);background:#fff}.dialog-header .title-container{display:block;color:#171717;font-weight:500}.dialog-header .close{cursor:pointer}.wrapper-body{overflow:auto;flex:1;background:#efefef}.wrapper-body .has-dialog-info-banners{padding:1rem 1.25rem .5rem;border-bottom:rgb(213,213,213) solid .0625rem;background-color:#fff}.wrapper-body .has-dialog-info-banners qd-page-info-banner:last-child{margin-bottom:0}.wrapper-body .body.full-width{height:100%}.wrapper-body .body:not(.full-width){max-height:80vh;padding:1rem 1.5rem}.wrapper-body .body:not(.full-width) qd-section{padding-right:1.5rem;padding-left:1.5rem}.wrapper-body .body:not(.full-width) qd-text-section{margin-right:0;margin-left:0}qd-dialog.has-sections .wrapper-body .body:not(.full-width){padding:0}.qd-dialog-actions{display:flex;justify-content:flex-end;padding:.5rem 1.5rem;border-top:2px solid rgb(213,213,213);background:#efefef;column-gap:.625rem}\n"] }]
|
|
1612
|
+
}], propDecorators: { testId: [{
|
|
1613
|
+
type: Input,
|
|
1614
|
+
args: ['data-test-id']
|
|
1615
|
+
}], body: [{
|
|
1559
1616
|
type: ViewChild,
|
|
1560
1617
|
args: ['body']
|
|
1561
1618
|
}], infoBanners: [{
|
|
@@ -1579,7 +1636,7 @@ class QdDialogAuthSessionEndComponent {
|
|
|
1579
1636
|
this.dialogRef.close({ doLogoutViaIdentityBroker: true });
|
|
1580
1637
|
}
|
|
1581
1638
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogAuthSessionEndComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1582
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdDialogAuthSessionEndComponent, isStandalone: false, selector: "qd-dialog-auth-session-end", ngImport: i0, template: "<qd-dialog>\n <qd-text-section>\n <qd-text-section-paragraph>\n {{ \"i18n.qd.authSupport.dialog.content\" | translate }}\n </qd-text-section-paragraph>\n </qd-text-section>\n\n <qd-dialog-action>\n <button qdButton color=\"secondary\" (click)=\"backToEPortal()\">\n {{ \"i18n.qd.authSupport.dialog.action.backToEPortal\" | translate }}\n </button>\n <button qdButton (click)=\"reAuthenticate()\">\n {{ \"i18n.qd.authSupport.dialog.action.reAuthenticate\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", dependencies: [{ kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "component", type: QdTextSectionComponent, selector: "qd-text-section" }, { kind: "component", type: QdTextSectionParagraphComponent, selector: "qd-text-section-paragraph" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
1639
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdDialogAuthSessionEndComponent, isStandalone: false, selector: "qd-dialog-auth-session-end", ngImport: i0, template: "<qd-dialog>\n <qd-text-section>\n <qd-text-section-paragraph>\n {{ \"i18n.qd.authSupport.dialog.content\" | translate }}\n </qd-text-section-paragraph>\n </qd-text-section>\n\n <qd-dialog-action>\n <button qdButton color=\"secondary\" (click)=\"backToEPortal()\">\n {{ \"i18n.qd.authSupport.dialog.action.backToEPortal\" | translate }}\n </button>\n <button qdButton (click)=\"reAuthenticate()\">\n {{ \"i18n.qd.authSupport.dialog.action.reAuthenticate\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", dependencies: [{ kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "component", type: QdTextSectionComponent, selector: "qd-text-section" }, { kind: "component", type: QdTextSectionParagraphComponent, selector: "qd-text-section-paragraph" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
1583
1640
|
}
|
|
1584
1641
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogAuthSessionEndComponent, decorators: [{
|
|
1585
1642
|
type: Component,
|
|
@@ -11547,6 +11604,7 @@ class QdInputComponent {
|
|
|
11547
11604
|
controlContainer = inject(ControlContainer, { optional: true, host: true, skipSelf: true });
|
|
11548
11605
|
eventBrokerService = inject(QdEventBrokerService, { optional: true });
|
|
11549
11606
|
numberInputService = inject(QdNumberInputService);
|
|
11607
|
+
translateService = inject(TranslateService);
|
|
11550
11608
|
/**
|
|
11551
11609
|
* The form control name can be assigned here if you want to use Reactive Forms.
|
|
11552
11610
|
*/
|
|
@@ -11703,7 +11761,7 @@ class QdInputComponent {
|
|
|
11703
11761
|
const displayValue = this.inputType === 'number'
|
|
11704
11762
|
? this.numberInputService.formatNumberForViewonly(this._value.value)
|
|
11705
11763
|
: this._value.value;
|
|
11706
|
-
return [`${displayValue}${this._value.unit ? ' ' + this._value.unit : ''}`];
|
|
11764
|
+
return [`${displayValue}${this._value.unit ? ' ' + this.getUnitLabel(this._value.unit) : ''}`];
|
|
11707
11765
|
}
|
|
11708
11766
|
ngOnInit() {
|
|
11709
11767
|
this.subscribeToOptions();
|
|
@@ -11767,41 +11825,47 @@ class QdInputComponent {
|
|
|
11767
11825
|
if (input?.validity?.badInput)
|
|
11768
11826
|
return;
|
|
11769
11827
|
const value = event.target.value;
|
|
11770
|
-
if (this.inputType === 'number' && value && !this.
|
|
11771
|
-
this.control?.setErrors({
|
|
11772
|
-
...this.control.errors,
|
|
11773
|
-
invalidCharacters: this.numberInputService.invalidCharactersErrorKey
|
|
11774
|
-
});
|
|
11828
|
+
if (this.inputType === 'number' && value && !this.handleNumberInput(value))
|
|
11775
11829
|
return;
|
|
11776
|
-
}
|
|
11777
|
-
if (this.inputType === 'number' && value) {
|
|
11778
|
-
const valueWithoutGroups = value.split(this.numberInputService.groupSeparator).join('');
|
|
11779
|
-
const filtered = this.numberInputService.filterValue(valueWithoutGroups);
|
|
11780
|
-
const hasInvalidChars = filtered !== valueWithoutGroups;
|
|
11781
|
-
if (hasInvalidChars) {
|
|
11782
|
-
this._value = { ...this._value, value: filtered };
|
|
11783
|
-
if (this.control) {
|
|
11784
|
-
this.control.setErrors({
|
|
11785
|
-
...this.control.errors,
|
|
11786
|
-
invalidCharacters: this.numberInputService.invalidCharactersErrorKey
|
|
11787
|
-
});
|
|
11788
|
-
}
|
|
11789
|
-
this._isUserTyped = true;
|
|
11790
|
-
this._onTouch();
|
|
11791
|
-
this.emitValue();
|
|
11792
|
-
return;
|
|
11793
|
-
}
|
|
11794
|
-
if (this.control?.errors?.['invalidCharacters']) {
|
|
11795
|
-
const remainingErrors = Object.fromEntries(Object.entries(this.control.errors).filter(([key]) => key !== 'invalidCharacters'));
|
|
11796
|
-
this.control.setErrors(Object.keys(remainingErrors).length ? remainingErrors : null);
|
|
11797
|
-
}
|
|
11798
|
-
}
|
|
11799
11830
|
this._isUserTyped = true;
|
|
11800
11831
|
this._value = { ...this._value, value };
|
|
11832
|
+
this._displayValue = String(value ?? '');
|
|
11801
11833
|
this.loadOptionsForTypedValue(value);
|
|
11802
11834
|
this.emitValue();
|
|
11803
11835
|
this._onTouch();
|
|
11804
11836
|
}
|
|
11837
|
+
handleNumberInput(value) {
|
|
11838
|
+
if (!this.numberInputService.isValidNumber(value)) {
|
|
11839
|
+
this.setInvalidCharactersError();
|
|
11840
|
+
return false;
|
|
11841
|
+
}
|
|
11842
|
+
const valueWithoutGroups = value.split(this.numberInputService.groupSeparator).join('');
|
|
11843
|
+
const filtered = this.numberInputService.filterValue(valueWithoutGroups);
|
|
11844
|
+
if (filtered !== valueWithoutGroups) {
|
|
11845
|
+
this._value = { ...this._value, value: filtered };
|
|
11846
|
+
this.setInvalidCharactersError();
|
|
11847
|
+
this._isUserTyped = true;
|
|
11848
|
+
this._onTouch();
|
|
11849
|
+
this.emitValue();
|
|
11850
|
+
return false;
|
|
11851
|
+
}
|
|
11852
|
+
this.clearInvalidCharactersError();
|
|
11853
|
+
return true;
|
|
11854
|
+
}
|
|
11855
|
+
setInvalidCharactersError() {
|
|
11856
|
+
if (!this.control)
|
|
11857
|
+
return;
|
|
11858
|
+
this.control.setErrors({
|
|
11859
|
+
...this.control.errors,
|
|
11860
|
+
invalidCharacters: this.numberInputService.invalidCharactersErrorKey
|
|
11861
|
+
});
|
|
11862
|
+
}
|
|
11863
|
+
clearInvalidCharactersError() {
|
|
11864
|
+
if (!this.control?.errors?.['invalidCharacters'])
|
|
11865
|
+
return;
|
|
11866
|
+
const remainingErrors = Object.fromEntries(Object.entries(this.control.errors).filter(([key]) => key !== 'invalidCharacters'));
|
|
11867
|
+
this.control.setErrors(Object.keys(remainingErrors).length ? remainingErrors : null);
|
|
11868
|
+
}
|
|
11805
11869
|
handleOptionSelected(option) {
|
|
11806
11870
|
this._value = { ...this._value, value: option.i18n };
|
|
11807
11871
|
this.popover?.close();
|
|
@@ -11903,6 +11967,10 @@ class QdInputComponent {
|
|
|
11903
11967
|
this.popover?.close();
|
|
11904
11968
|
}
|
|
11905
11969
|
}
|
|
11970
|
+
getUnitLabel(unit) {
|
|
11971
|
+
const unitOption = this.config?.units?.find(option => option.value === unit);
|
|
11972
|
+
return unitOption ? this.translateService.instant(unitOption.i18n) : unit;
|
|
11973
|
+
}
|
|
11906
11974
|
emitValue() {
|
|
11907
11975
|
const emittable = this.inputType === 'number' ? this.numberInputService.parseValue(this._value.value) : this._value.value;
|
|
11908
11976
|
if (this.hasUnits) {
|
|
@@ -18841,6 +18909,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
18841
18909
|
}]
|
|
18842
18910
|
}] });
|
|
18843
18911
|
|
|
18912
|
+
class QdDialogConfirmComponent {
|
|
18913
|
+
dialogRef = inject(DialogRef);
|
|
18914
|
+
data = inject(DIALOG_DATA);
|
|
18915
|
+
config = this.data;
|
|
18916
|
+
testId = this.data?.testId ?? 'dialog-confirm';
|
|
18917
|
+
close() {
|
|
18918
|
+
this.dialogRef.close(false);
|
|
18919
|
+
}
|
|
18920
|
+
confirm() {
|
|
18921
|
+
this.dialogRef.close(true);
|
|
18922
|
+
}
|
|
18923
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogConfirmComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18924
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdDialogConfirmComponent, isStandalone: false, selector: "qd-dialog-confirm", ngImport: i0, template: "<qd-dialog [data-test-id]=\"testId\">\n <qd-text-section>\n <qd-text-section-paragraph>\n {{ config.message.i18n | translate }}\n </qd-text-section-paragraph>\n </qd-text-section>\n\n <qd-dialog-action>\n <button qdButton qdButtonGhost color=\"secondary\" (click)=\"close()\" [data-test-id]=\"testId + '-close'\">\n {{ config.cancel.i18n | translate }}\n </button>\n\n <button qdButton color=\"error\" (click)=\"confirm()\" [data-test-id]=\"testId + '-proceed'\">\n {{ config.confirm.i18n | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", dependencies: [{ kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdTextSectionComponent, selector: "qd-text-section" }, { kind: "component", type: QdTextSectionParagraphComponent, selector: "qd-text-section-paragraph" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
18925
|
+
}
|
|
18926
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogConfirmComponent, decorators: [{
|
|
18927
|
+
type: Component,
|
|
18928
|
+
args: [{ selector: 'qd-dialog-confirm', standalone: false, template: "<qd-dialog [data-test-id]=\"testId\">\n <qd-text-section>\n <qd-text-section-paragraph>\n {{ config.message.i18n | translate }}\n </qd-text-section-paragraph>\n </qd-text-section>\n\n <qd-dialog-action>\n <button qdButton qdButtonGhost color=\"secondary\" (click)=\"close()\" [data-test-id]=\"testId + '-close'\">\n {{ config.cancel.i18n | translate }}\n </button>\n\n <button qdButton color=\"error\" (click)=\"confirm()\" [data-test-id]=\"testId + '-proceed'\">\n {{ config.confirm.i18n | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n" }]
|
|
18929
|
+
}] });
|
|
18930
|
+
|
|
18844
18931
|
/**
|
|
18845
18932
|
* **QdDialogRecordStepperComponent** provides a pagination for the fullscreen version of **QdDialog**. <br />
|
|
18846
18933
|
* It is possible to navigate through a list of data provided by the dialog.
|
|
@@ -19103,7 +19190,7 @@ class QdDialogConfirmationComponent {
|
|
|
19103
19190
|
});
|
|
19104
19191
|
}
|
|
19105
19192
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogConfirmationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19106
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdDialogConfirmationComponent, isStandalone: false, selector: "qd-dialog-confirmation", inputs: { config: "config", testId: ["data-test-id", "testId"] }, ngImport: i0, template: "<qd-dialog [attr.data-test-id]=\"testId\">\n <div class=\"loading-overlay\" *ngIf=\"isLoading\">\n <qd-spinner></qd-spinner>\n </div>\n\n <div class=\"headline\" *ngIf=\"isInfoState\">{{ config.info.headline.i18n | translate }}</div>\n\n <div class=\"headline\" *ngIf=\"isSuccessState\">\n {{ (config.success.headline.i18n | translate) || (config.info.headline.i18n | translate) }}\n </div>\n\n <div class=\"headline\" *ngIf=\"isErrorState && config.error.headline.i18n\">\n {{ config.error.headline.i18n | translate }}\n </div>\n\n <div class=\"info\">\n <ng-container *ngIf=\"isInfoState\"> {{ config.info.info.i18n | translate }} </ng-container>\n\n <ng-container *ngIf=\"isSuccessState\">\n <qd-icon class=\"success\" [icon]=\"'checkCircleSolid'\"></qd-icon>\n <span>{{ (config.success.info.i18n | translate) || (config.info.info.i18n | translate) }} </span>\n </ng-container>\n\n <ng-container *ngIf=\"isErrorState && config.error?.info?.i18n\">\n <qd-icon class=\"error\" [icon]=\"'timesCircleSolid'\"></qd-icon>\n <span>{{ config.error?.info?.i18n | translate }}</span>\n </ng-container>\n </div>\n\n <div class=\"confirm-content\">\n <ng-container *ngIf=\"isInfoState\">\n <ng-content select=\"[qdDialogConfirmInfo]\"></ng-content>\n <qd-checkboxes\n [attr.data-test-id]=\"testId + '-checkbox'\"\n *ngIf=\"useCheckbox\"\n [config]=\"checkboxConfig\"\n [(values)]=\"confirmChecked\"\n ></qd-checkboxes>\n </ng-container>\n\n <ng-container *ngIf=\"isSuccessState\">\n <ng-content select=\"[qdDialogConfirmSuccess]\"></ng-content>\n </ng-container>\n\n <ng-container *ngIf=\"isErrorState\">\n <ng-content select=\"[qdDialogConfirmError]\"></ng-content>\n </ng-container>\n\n <qd-notifications [context]=\"notificationContext\"></qd-notifications>\n </div>\n\n <qd-dialog-action>\n <ng-container *ngIf=\"isInfoState\">\n <button qdButton qdButtonGhost (click)=\"close()\" [disabled]=\"isLoading\">\n {{ config.buttons.cancel.i18n | translate }}\n </button>\n\n <button qdButton color=\"primary\" (click)=\"confirm()\" [disabled]=\"(useCheckbox && !isConfirmed) || isLoading\">\n {{ config.buttons.next.i18n | translate }}\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"isSuccessState\">\n <button qdButton color=\"primary\" (click)=\"submit()\">\n {{ (config.buttons.success?.i18n | translate) || (config.buttons.next.i18n | translate) }}\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"isErrorState\">\n <button qdButton color=\"primary\" (click)=\"submit()\">\n {{ (config.buttons.error?.i18n | translate) || (config.buttons.next.i18n | translate) }}\n </button>\n </ng-container>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [":host .headline{color:#171717;font-size:1rem;font-weight:700;line-height:1.5rem;margin-bottom:.75rem}:host .info{color:#171717;font-size:1rem;font-weight:400;line-height:1.5rem;display:flex}:host .info ::ng-deep .qd-icon{width:.875rem;margin-right:.75rem;font-size:1.25rem;font-weight:300}:host .info ::ng-deep .qd-icon.success{color:#00813a}:host .info ::ng-deep .qd-icon.error{color:#c70023}:host .loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background-color:#0003}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdCheckboxesComponent, selector: "qd-checkboxes", inputs: ["formControlName", "values", "config", "data-test-id"], outputs: ["valuesChange", "clickHint", "clickReadonly", "clickViewonly"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon", "status"] }, { kind: "component", type: QdNotificationsComponent, selector: "qd-notifications", inputs: ["context"] }, { kind: "component", type: QdSpinnerComponent, selector: "qd-spinner" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
19193
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdDialogConfirmationComponent, isStandalone: false, selector: "qd-dialog-confirmation", inputs: { config: "config", testId: ["data-test-id", "testId"] }, ngImport: i0, template: "<qd-dialog [attr.data-test-id]=\"testId\">\n <div class=\"loading-overlay\" *ngIf=\"isLoading\">\n <qd-spinner></qd-spinner>\n </div>\n\n <div class=\"headline\" *ngIf=\"isInfoState\">{{ config.info.headline.i18n | translate }}</div>\n\n <div class=\"headline\" *ngIf=\"isSuccessState\">\n {{ (config.success.headline.i18n | translate) || (config.info.headline.i18n | translate) }}\n </div>\n\n <div class=\"headline\" *ngIf=\"isErrorState && config.error.headline.i18n\">\n {{ config.error.headline.i18n | translate }}\n </div>\n\n <div class=\"info\">\n <ng-container *ngIf=\"isInfoState\"> {{ config.info.info.i18n | translate }} </ng-container>\n\n <ng-container *ngIf=\"isSuccessState\">\n <qd-icon class=\"success\" [icon]=\"'checkCircleSolid'\"></qd-icon>\n <span>{{ (config.success.info.i18n | translate) || (config.info.info.i18n | translate) }} </span>\n </ng-container>\n\n <ng-container *ngIf=\"isErrorState && config.error?.info?.i18n\">\n <qd-icon class=\"error\" [icon]=\"'timesCircleSolid'\"></qd-icon>\n <span>{{ config.error?.info?.i18n | translate }}</span>\n </ng-container>\n </div>\n\n <div class=\"confirm-content\">\n <ng-container *ngIf=\"isInfoState\">\n <ng-content select=\"[qdDialogConfirmInfo]\"></ng-content>\n <qd-checkboxes\n [attr.data-test-id]=\"testId + '-checkbox'\"\n *ngIf=\"useCheckbox\"\n [config]=\"checkboxConfig\"\n [(values)]=\"confirmChecked\"\n ></qd-checkboxes>\n </ng-container>\n\n <ng-container *ngIf=\"isSuccessState\">\n <ng-content select=\"[qdDialogConfirmSuccess]\"></ng-content>\n </ng-container>\n\n <ng-container *ngIf=\"isErrorState\">\n <ng-content select=\"[qdDialogConfirmError]\"></ng-content>\n </ng-container>\n\n <qd-notifications [context]=\"notificationContext\"></qd-notifications>\n </div>\n\n <qd-dialog-action>\n <ng-container *ngIf=\"isInfoState\">\n <button qdButton qdButtonGhost (click)=\"close()\" [disabled]=\"isLoading\">\n {{ config.buttons.cancel.i18n | translate }}\n </button>\n\n <button qdButton color=\"primary\" (click)=\"confirm()\" [disabled]=\"(useCheckbox && !isConfirmed) || isLoading\">\n {{ config.buttons.next.i18n | translate }}\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"isSuccessState\">\n <button qdButton color=\"primary\" (click)=\"submit()\">\n {{ (config.buttons.success?.i18n | translate) || (config.buttons.next.i18n | translate) }}\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"isErrorState\">\n <button qdButton color=\"primary\" (click)=\"submit()\">\n {{ (config.buttons.error?.i18n | translate) || (config.buttons.next.i18n | translate) }}\n </button>\n </ng-container>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [":host .headline{color:#171717;font-size:1rem;font-weight:700;line-height:1.5rem;margin-bottom:.75rem}:host .info{color:#171717;font-size:1rem;font-weight:400;line-height:1.5rem;display:flex}:host .info ::ng-deep .qd-icon{width:.875rem;margin-right:.75rem;font-size:1.25rem;font-weight:300}:host .info ::ng-deep .qd-icon.success{color:#00813a}:host .info ::ng-deep .qd-icon.error{color:#c70023}:host .loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background-color:#0003}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdCheckboxesComponent, selector: "qd-checkboxes", inputs: ["formControlName", "values", "config", "data-test-id"], outputs: ["valuesChange", "clickHint", "clickReadonly", "clickViewonly"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon", "status"] }, { kind: "component", type: QdNotificationsComponent, selector: "qd-notifications", inputs: ["context"] }, { kind: "component", type: QdSpinnerComponent, selector: "qd-spinner" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
19107
19194
|
}
|
|
19108
19195
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogConfirmationComponent, decorators: [{
|
|
19109
19196
|
type: Component,
|
|
@@ -19118,7 +19205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
19118
19205
|
|
|
19119
19206
|
class QdPageDialogWithBreadcrumbsComponent {
|
|
19120
19207
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageDialogWithBreadcrumbsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19121
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdPageDialogWithBreadcrumbsComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: "<qd-dialog>\n <div class=\"wrapper\">\n <qd-breadcrumbs></qd-breadcrumbs>\n <div class=\"router-outlet-wrapper\">\n <router-outlet name=\"dialog\"></router-outlet>\n </div>\n </div>\n</qd-dialog>\n", styles: [".wrapper{display:flex;height:100%;flex-flow:column}qd-breadcrumbs{z-index:99;flex:0 1 auto;padding-right:2.25rem;padding-bottom:.75rem;margin-bottom:-.75rem}.router-outlet-wrapper{position:relative;height:100%;flex:1 1 auto}\n"], dependencies: [{ kind: "directive", type: i2$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: QdBreadcrumbsComponent, selector: "qd-breadcrumbs" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }] });
|
|
19208
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdPageDialogWithBreadcrumbsComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: "<qd-dialog>\n <div class=\"wrapper\">\n <qd-breadcrumbs></qd-breadcrumbs>\n <div class=\"router-outlet-wrapper\">\n <router-outlet name=\"dialog\"></router-outlet>\n </div>\n </div>\n</qd-dialog>\n", styles: [".wrapper{display:flex;height:100%;flex-flow:column}qd-breadcrumbs{z-index:99;flex:0 1 auto;padding-right:2.25rem;padding-bottom:.75rem;margin-bottom:-.75rem}.router-outlet-wrapper{position:relative;height:100%;flex:1 1 auto}\n"], dependencies: [{ kind: "directive", type: i2$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: QdBreadcrumbsComponent, selector: "qd-breadcrumbs" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }] });
|
|
19122
19209
|
}
|
|
19123
19210
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageDialogWithBreadcrumbsComponent, decorators: [{
|
|
19124
19211
|
type: Component,
|
|
@@ -19129,10 +19216,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
19129
19216
|
* Tracks pending changes inside a QdDialog by comparing the current form value with the initial value.
|
|
19130
19217
|
* Supports both Reactive Forms (FormGroupDirective) and Template-Driven Forms (NgForm).
|
|
19131
19218
|
*
|
|
19132
|
-
* Relevant when closing the dialog (X icon or Cancel button):
|
|
19219
|
+
* Relevant when closing the dialog (X icon, ESC or Cancel button):
|
|
19133
19220
|
* - if the current value equals the initial value, closing will not trigger a pending-changes warning
|
|
19134
19221
|
* - the form is reset to pristine, so the dialog behaves like “unchanged”
|
|
19135
19222
|
*
|
|
19223
|
+
* #### Notes & Best Practices:
|
|
19224
|
+
*
|
|
19225
|
+
* - Pending changes are reported for the dialog that hosts the form. Other open
|
|
19226
|
+
* dialogs are never affected.
|
|
19227
|
+
* - Outside a dialog there is nothing to guard. The form is still reset to
|
|
19228
|
+
* pristine, but no pending changes are reported.
|
|
19229
|
+
*
|
|
19136
19230
|
* #### **Usage (Reactive Form)**
|
|
19137
19231
|
*
|
|
19138
19232
|
* ```html
|
|
@@ -19146,6 +19240,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
19146
19240
|
class QdPendingChangesGuardDirective {
|
|
19147
19241
|
ngForm = inject(NgForm, { optional: true });
|
|
19148
19242
|
formGroupDirective = inject(FormGroupDirective, { optional: true });
|
|
19243
|
+
dialogRef = inject(DialogRef, { optional: true });
|
|
19149
19244
|
changeGuard = inject(QdDialogChangeGuardService);
|
|
19150
19245
|
statusSubscription;
|
|
19151
19246
|
initialValue;
|
|
@@ -19165,19 +19260,25 @@ class QdPendingChangesGuardDirective {
|
|
|
19165
19260
|
});
|
|
19166
19261
|
}
|
|
19167
19262
|
}
|
|
19263
|
+
ngOnDestroy() {
|
|
19264
|
+
this.statusSubscription?.unsubscribe();
|
|
19265
|
+
this.reportPendingChanges(false);
|
|
19266
|
+
}
|
|
19168
19267
|
updateStatus() {
|
|
19169
19268
|
if (!this.formGroup)
|
|
19170
19269
|
return;
|
|
19171
19270
|
const isPristine = isEqual(this.formGroup.value, this.initialValue);
|
|
19172
|
-
|
|
19173
|
-
this.changeGuard.setPendingChangesStatus(hasChanges);
|
|
19271
|
+
this.reportPendingChanges(!isPristine);
|
|
19174
19272
|
if (isPristine && this.formGroup.dirty) {
|
|
19175
19273
|
this.formGroup.markAsPristine();
|
|
19176
19274
|
}
|
|
19177
19275
|
}
|
|
19178
|
-
|
|
19179
|
-
this.
|
|
19180
|
-
|
|
19276
|
+
reportPendingChanges(hasChanges) {
|
|
19277
|
+
if (!this.dialogRef)
|
|
19278
|
+
return;
|
|
19279
|
+
if (hasChanges)
|
|
19280
|
+
return this.changeGuard.markPendingChanges(this, this.dialogRef);
|
|
19281
|
+
this.changeGuard.clearPendingChanges(this, this.dialogRef);
|
|
19181
19282
|
}
|
|
19182
19283
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPendingChangesGuardDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
19183
19284
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: QdPendingChangesGuardDirective, isStandalone: false, selector: "[qdPendingChangesGuard]", ngImport: i0 });
|
|
@@ -19195,6 +19296,7 @@ class QdDialogModule {
|
|
|
19195
19296
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: QdDialogModule, declarations: [QdDialogActionComponent,
|
|
19196
19297
|
QdDialogAuthSessionEndComponent,
|
|
19197
19298
|
QdDialogComponent,
|
|
19299
|
+
QdDialogConfirmComponent,
|
|
19198
19300
|
QdDialogConfirmationComponent,
|
|
19199
19301
|
QdDialogConfirmationErrorDirective,
|
|
19200
19302
|
QdDialogConfirmationInfoDirective,
|
|
@@ -19224,6 +19326,10 @@ class QdDialogModule {
|
|
|
19224
19326
|
{
|
|
19225
19327
|
provide: QD_PAGE_DIALOG_WITH_BREADCRUMBS_HOST,
|
|
19226
19328
|
useValue: QdPageDialogWithBreadcrumbsComponent
|
|
19329
|
+
},
|
|
19330
|
+
{
|
|
19331
|
+
provide: QD_DISCARD_CONFIRM_DIALOG_COMPONENT_TOKEN,
|
|
19332
|
+
useValue: QdDialogConfirmComponent
|
|
19227
19333
|
}
|
|
19228
19334
|
], imports: [CommonModule,
|
|
19229
19335
|
TranslateModule,
|
|
@@ -19259,6 +19365,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
19259
19365
|
QdDialogActionComponent,
|
|
19260
19366
|
QdDialogAuthSessionEndComponent,
|
|
19261
19367
|
QdDialogComponent,
|
|
19368
|
+
QdDialogConfirmComponent,
|
|
19262
19369
|
QdDialogConfirmationComponent,
|
|
19263
19370
|
QdDialogConfirmationErrorDirective,
|
|
19264
19371
|
QdDialogConfirmationInfoDirective,
|
|
@@ -19281,6 +19388,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
19281
19388
|
{
|
|
19282
19389
|
provide: QD_PAGE_DIALOG_WITH_BREADCRUMBS_HOST,
|
|
19283
19390
|
useValue: QdPageDialogWithBreadcrumbsComponent
|
|
19391
|
+
},
|
|
19392
|
+
{
|
|
19393
|
+
provide: QD_DISCARD_CONFIRM_DIALOG_COMPONENT_TOKEN,
|
|
19394
|
+
useValue: QdDialogConfirmComponent
|
|
19284
19395
|
}
|
|
19285
19396
|
]
|
|
19286
19397
|
}]
|
|
@@ -19716,7 +19827,7 @@ class QdFileCollectorDialogComponent {
|
|
|
19716
19827
|
return !!fileUpload.error || fileUpload.progress === 100;
|
|
19717
19828
|
}
|
|
19718
19829
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFileCollectorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19719
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdFileCollectorDialogComponent, isStandalone: false, selector: "qd-file-collector-dialog", providers: [QdFileCollectorService], ngImport: i0, template: "<qd-dialog>\n <qd-file-collector-dialog-item\n *ngFor=\"let fileUpload of this.fileUploads; let i = index\"\n [progress]=\"fileUpload.progress\"\n [collectedFile]=\"fileUpload.collectedFile\"\n [error]=\"fileUpload.error\"\n [data-test-id]=\"'file-collector-dialog-item-' + i\"\n ></qd-file-collector-dialog-item>\n <qd-dialog-action>\n <button\n qdButton\n (click)=\"close()\"\n [disabled]=\"isCloseButtonDisabled()\"\n data-test-id=\"file-collector-dialog-button-finish\"\n >\n {{ \"i18n.qd.fileCollector.button.finish\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }, { kind: "component", type: QdFileCollectorDialogItemComponent, selector: "qd-file-collector-dialog-item", inputs: ["progress", "collectedFile", "error", "data-test-id"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
19830
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdFileCollectorDialogComponent, isStandalone: false, selector: "qd-file-collector-dialog", providers: [QdFileCollectorService], ngImport: i0, template: "<qd-dialog>\n <qd-file-collector-dialog-item\n *ngFor=\"let fileUpload of this.fileUploads; let i = index\"\n [progress]=\"fileUpload.progress\"\n [collectedFile]=\"fileUpload.collectedFile\"\n [error]=\"fileUpload.error\"\n [data-test-id]=\"'file-collector-dialog-item-' + i\"\n ></qd-file-collector-dialog-item>\n <qd-dialog-action>\n <button\n qdButton\n (click)=\"close()\"\n [disabled]=\"isCloseButtonDisabled()\"\n data-test-id=\"file-collector-dialog-button-finish\"\n >\n {{ \"i18n.qd.fileCollector.button.finish\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "component", type: QdFileCollectorDialogItemComponent, selector: "qd-file-collector-dialog-item", inputs: ["progress", "collectedFile", "error", "data-test-id"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
19720
19831
|
}
|
|
19721
19832
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFileCollectorDialogComponent, decorators: [{
|
|
19722
19833
|
type: Component,
|
|
@@ -19919,7 +20030,7 @@ class QdFileDeleteDialogComponent {
|
|
|
19919
20030
|
this.dialogRef.close(true);
|
|
19920
20031
|
}
|
|
19921
20032
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFileDeleteDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19922
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdFileDeleteDialogComponent, isStandalone: false, selector: "qd-file-delete-dialog", ngImport: i0, template: "<qd-dialog>\n {{ deleteHint }}\n\n <qd-dialog-action>\n <button (click)=\"cancel()\" qdButton qdButtonGhost color=\"secondary\" data-test-id=\"file-delete-dialog-button-cancel\">\n {{ \"i18n.qd.fileCollector.deleteConfirmation.button.cancel\" | translate }}\n </button>\n\n <button (click)=\"delete()\" qdButton color=\"error\" data-test-id=\"file-delete-dialog-button-delete\">\n {{ \"i18n.qd.fileCollector.deleteConfirmation.button.delete\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [":host{overflow-wrap:break-word}\n"], dependencies: [{ kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
20033
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdFileDeleteDialogComponent, isStandalone: false, selector: "qd-file-delete-dialog", ngImport: i0, template: "<qd-dialog>\n {{ deleteHint }}\n\n <qd-dialog-action>\n <button (click)=\"cancel()\" qdButton qdButtonGhost color=\"secondary\" data-test-id=\"file-delete-dialog-button-cancel\">\n {{ \"i18n.qd.fileCollector.deleteConfirmation.button.cancel\" | translate }}\n </button>\n\n <button (click)=\"delete()\" qdButton color=\"error\" data-test-id=\"file-delete-dialog-button-delete\">\n {{ \"i18n.qd.fileCollector.deleteConfirmation.button.delete\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [":host{overflow-wrap:break-word}\n"], dependencies: [{ kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
19923
20034
|
}
|
|
19924
20035
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFileDeleteDialogComponent, decorators: [{
|
|
19925
20036
|
type: Component,
|
|
@@ -21231,16 +21342,16 @@ class QdFilterItemFreeTextComponent {
|
|
|
21231
21342
|
itemIndex = 0;
|
|
21232
21343
|
testId;
|
|
21233
21344
|
enterClick = new EventEmitter();
|
|
21234
|
-
item;
|
|
21235
|
-
i18n;
|
|
21236
21345
|
get dataTestId() {
|
|
21237
21346
|
return this.testId;
|
|
21238
21347
|
}
|
|
21348
|
+
item;
|
|
21349
|
+
i18n;
|
|
21350
|
+
inputValue = '';
|
|
21351
|
+
_destroyed$ = new Subject();
|
|
21239
21352
|
get value() {
|
|
21240
21353
|
return (this.item && this.item.item) || '';
|
|
21241
21354
|
}
|
|
21242
|
-
inputValue = '';
|
|
21243
|
-
_destroyed$ = new Subject();
|
|
21244
21355
|
ngOnInit() {
|
|
21245
21356
|
this.filterService
|
|
21246
21357
|
.getCategoryById$(this.filterId, this.categoryIndex)
|
|
@@ -21252,6 +21363,8 @@ class QdFilterItemFreeTextComponent {
|
|
|
21252
21363
|
.getItemById$(this.filterId, this.categoryIndex, this.itemIndex)
|
|
21253
21364
|
.pipe(takeUntil$1(this._destroyed$))
|
|
21254
21365
|
.subscribe(item => {
|
|
21366
|
+
if ((item?.item ?? '') !== this.inputValue)
|
|
21367
|
+
this.inputValue = '';
|
|
21255
21368
|
this.item = item;
|
|
21256
21369
|
});
|
|
21257
21370
|
}
|
|
@@ -28277,6 +28390,7 @@ class QdTableModule {
|
|
|
28277
28390
|
TranslateModule, i1$1.StoreFeatureModule, QdButtonModule,
|
|
28278
28391
|
QdChipModule,
|
|
28279
28392
|
QdDataFacetsModule,
|
|
28393
|
+
QdDialogModule,
|
|
28280
28394
|
QdIconModule,
|
|
28281
28395
|
QdPopoverModule,
|
|
28282
28396
|
QdStatusIndicatorModule,
|
|
@@ -28289,6 +28403,7 @@ class QdTableModule {
|
|
|
28289
28403
|
QdButtonModule,
|
|
28290
28404
|
QdChipModule,
|
|
28291
28405
|
QdDataFacetsModule,
|
|
28406
|
+
QdDialogModule,
|
|
28292
28407
|
QdIconModule,
|
|
28293
28408
|
QdPopoverModule,
|
|
28294
28409
|
QdStatusIndicatorModule,
|
|
@@ -28306,6 +28421,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
28306
28421
|
QdButtonModule,
|
|
28307
28422
|
QdChipModule,
|
|
28308
28423
|
QdDataFacetsModule,
|
|
28424
|
+
QdDialogModule,
|
|
28309
28425
|
QdIconModule,
|
|
28310
28426
|
QdPopoverModule,
|
|
28311
28427
|
QdStatusIndicatorModule,
|
|
@@ -28711,7 +28827,7 @@ class QdContextSelectDialogComponent {
|
|
|
28711
28827
|
});
|
|
28712
28828
|
}
|
|
28713
28829
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdContextSelectDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
28714
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdContextSelectDialogComponent, isStandalone: false, selector: "qd-context-select-dialog", providers: [QdSearchService], ngImport: i0, template: "<qd-dialog>\n <div class=\"search\" *ngIf=\"data.config.hasSearch\">\n <qd-search [configData]=\"searchConfig\"></qd-search>\n </div>\n <qd-table\n [data]=\"tableData\"\n [config]=\"tableConfig\"\n (selectedRowsOutput)=\"select($event)\"\n data-test-id=\"context-select-dialog-table\"\n ></qd-table>\n <qd-dialog-action>\n <button qdButton qdButtonGhost color=\"secondary\" (click)=\"cancel()\">\n {{ \"i18n.qd.context.dialog.cancelButtonLabel\" | translate }}\n </button>\n <button qdButton (click)=\"confirm()\" data-test-id=\"context-select-dialog-submit\">\n {{ \"i18n.qd.context.dialog.confirmButtonLabel\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [".search{display:flex;justify-content:end;padding-bottom:.9375rem}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }, { kind: "component", type: QdTableComponent, selector: "qd-table", inputs: ["config", "data", "data-test-id"], outputs: ["secondaryActionOutput", "selectedRowsOutput", "emptyStateActionOutput"] }, { kind: "component", type: QdSearchComponent, selector: "qd-search", inputs: ["configData"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
28830
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdContextSelectDialogComponent, isStandalone: false, selector: "qd-context-select-dialog", providers: [QdSearchService], ngImport: i0, template: "<qd-dialog>\n <div class=\"search\" *ngIf=\"data.config.hasSearch\">\n <qd-search [configData]=\"searchConfig\"></qd-search>\n </div>\n <qd-table\n [data]=\"tableData\"\n [config]=\"tableConfig\"\n (selectedRowsOutput)=\"select($event)\"\n data-test-id=\"context-select-dialog-table\"\n ></qd-table>\n <qd-dialog-action>\n <button qdButton qdButtonGhost color=\"secondary\" (click)=\"cancel()\">\n {{ \"i18n.qd.context.dialog.cancelButtonLabel\" | translate }}\n </button>\n <button qdButton (click)=\"confirm()\" data-test-id=\"context-select-dialog-submit\">\n {{ \"i18n.qd.context.dialog.confirmButtonLabel\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [".search{display:flex;justify-content:end;padding-bottom:.9375rem}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "component", type: QdTableComponent, selector: "qd-table", inputs: ["config", "data", "data-test-id"], outputs: ["secondaryActionOutput", "selectedRowsOutput", "emptyStateActionOutput"] }, { kind: "component", type: QdSearchComponent, selector: "qd-search", inputs: ["configData"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
28715
28831
|
}
|
|
28716
28832
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdContextSelectDialogComponent, decorators: [{
|
|
28717
28833
|
type: Component,
|
|
@@ -28995,8 +29111,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
28995
29111
|
|
|
28996
29112
|
class QdPageCommitActionExecutor {
|
|
28997
29113
|
static execute(action, values, context) {
|
|
28998
|
-
const { formGroupManager, navigationInterceptor, destroyed$, onAfterSnapshot } = context;
|
|
28999
|
-
const captured = formGroupManager.
|
|
29114
|
+
const { formGroupManager, navigationInterceptor, destroyed$, onAfterSnapshot, scheduleFrameworkRebuildStop } = context;
|
|
29115
|
+
const captured = formGroupManager.createCurrentValuesSnapshot();
|
|
29000
29116
|
let result$;
|
|
29001
29117
|
navigationInterceptor.executeWithBypass(() => {
|
|
29002
29118
|
const handlerResult = action.handler(values);
|
|
@@ -29004,7 +29120,9 @@ class QdPageCommitActionExecutor {
|
|
|
29004
29120
|
result$ = handlerResult;
|
|
29005
29121
|
});
|
|
29006
29122
|
const applySuccess = () => {
|
|
29007
|
-
formGroupManager.
|
|
29123
|
+
formGroupManager.updateSavedSnapshotFromValues(captured);
|
|
29124
|
+
formGroupManager.startAdoptingFrameworkRebuild();
|
|
29125
|
+
scheduleFrameworkRebuildStop?.();
|
|
29008
29126
|
navigationInterceptor.executeWithBypass(() => {
|
|
29009
29127
|
try {
|
|
29010
29128
|
onAfterSnapshot?.();
|
|
@@ -29048,136 +29166,170 @@ class QdPageCommitActionExecutor {
|
|
|
29048
29166
|
}
|
|
29049
29167
|
|
|
29050
29168
|
/**
|
|
29051
|
-
*
|
|
29052
|
-
*
|
|
29053
|
-
*
|
|
29054
|
-
*
|
|
29055
|
-
*
|
|
29056
|
-
*
|
|
29057
|
-
* -
|
|
29058
|
-
* -
|
|
29059
|
-
* -
|
|
29060
|
-
* -
|
|
29061
|
-
*
|
|
29062
|
-
* ####
|
|
29063
|
-
*
|
|
29064
|
-
*
|
|
29065
|
-
*
|
|
29066
|
-
*
|
|
29067
|
-
*
|
|
29068
|
-
*
|
|
29069
|
-
*
|
|
29070
|
-
*
|
|
29071
|
-
*
|
|
29072
|
-
*
|
|
29073
|
-
*
|
|
29074
|
-
* **
|
|
29075
|
-
*
|
|
29076
|
-
*
|
|
29077
|
-
*
|
|
29078
|
-
*
|
|
29079
|
-
*
|
|
29080
|
-
* ```ts
|
|
29081
|
-
* service.takeFormGroupsSnapshot();
|
|
29082
|
-
* ```
|
|
29083
|
-
*
|
|
29084
|
-
* **Detect Changes**
|
|
29085
|
-
* ```ts
|
|
29086
|
-
* service.$hasValuesChanged().subscribe(changed => {
|
|
29087
|
-
* if (changed) {
|
|
29088
|
-
* console.log('Form data has changed');
|
|
29089
|
-
* }
|
|
29090
|
-
* });
|
|
29091
|
-
* ```
|
|
29092
|
-
*
|
|
29093
|
-
* **Restore from Snapshot**
|
|
29094
|
-
* ```ts
|
|
29095
|
-
* service.restoreFormGroupsFromSnapshot();
|
|
29096
|
-
* ```
|
|
29097
|
-
*
|
|
29098
|
-
* **Check if All Forms Are Valid**
|
|
29099
|
-
* ```ts
|
|
29100
|
-
* service.$areFormGroupsValid().subscribe(valid => {
|
|
29101
|
-
* if (valid) {
|
|
29102
|
-
* console.log('All forms valid!');
|
|
29103
|
-
* }
|
|
29104
|
-
* });
|
|
29105
|
-
* ```
|
|
29106
|
-
*
|
|
29107
|
-
* #### Notes:
|
|
29108
|
-
* - FormArrays are not replaced during restore. Their length is adjusted and their content reset.
|
|
29109
|
-
* - Control cloning is recursive and structure-preserving.
|
|
29110
|
-
* - No structural information is stored in the snapshot; only data.
|
|
29169
|
+
* Central tracker for all reactive forms on a single QdPage.
|
|
29170
|
+
*
|
|
29171
|
+
* Its main job is to answer one question: **has the user changed anything since the last save?**
|
|
29172
|
+
* The page uses that answer to warn before leaving with unsaved changes.
|
|
29173
|
+
*
|
|
29174
|
+
* #### What it does
|
|
29175
|
+
* - **Registry** — holds every form of the page and gives access to it.
|
|
29176
|
+
* - **Change detection** — keeps a snapshot of the saved values and compares the live form against it.
|
|
29177
|
+
* - **Restore** — resets the forms back to the snapshot when the user discards changes.
|
|
29178
|
+
* - **Validity** — reports whether all forms are valid and can cancel in-flight async validators.
|
|
29179
|
+
*
|
|
29180
|
+
* #### Adopting framework rebuilds
|
|
29181
|
+
* After a save, the consumer often rebuilds the form itself — a `FormArray` cleared and pushed
|
|
29182
|
+
* again, or a control replaced via `setControl`. A plain value compare would wrongly read that as a
|
|
29183
|
+
* user edit. To prevent that:
|
|
29184
|
+
*
|
|
29185
|
+
* - **What it does** — right after each save or snapshot write, the framework briefly *adopts its
|
|
29186
|
+
* own rebuild* (`startAdoptingFrameworkRebuild`) and folds those control replacements into the
|
|
29187
|
+
* snapshot, so they count as already saved.
|
|
29188
|
+
* - **Why it is needed** — otherwise a rebuilt value (text `"99"` becoming number `99`) would look
|
|
29189
|
+
* like an unsaved change.
|
|
29190
|
+
* - **When it ends** — the page stops adopting once the rebuild has rendered
|
|
29191
|
+
* (`stopAdoptingFrameworkRebuild`).
|
|
29192
|
+
* - **User edits stay unsaved** — they always happen after that window, so they are still reported.
|
|
29193
|
+
*
|
|
29194
|
+
* #### Notes
|
|
29195
|
+
* - Snapshots store only **raw values** (`getRawValue()`), never control instances — cheap to keep and compare.
|
|
29196
|
+
* - Restoring a `FormArray` adjusts its length and resets its contents; it never replaces the array instance.
|
|
29197
|
+
* - No structural information is stored in the snapshot, only data.
|
|
29111
29198
|
*/
|
|
29112
29199
|
class QdFormGroupManagerService {
|
|
29113
29200
|
_formGroups = new Map();
|
|
29114
29201
|
_formGroupsSnapshot = new Map();
|
|
29202
|
+
_knownControlInstances = new Map();
|
|
29203
|
+
_isAdoptingFrameworkRebuild = false;
|
|
29204
|
+
_frameworkRebuildSub;
|
|
29115
29205
|
_formGroupsChanged$ = new BehaviorSubject(undefined);
|
|
29206
|
+
_keysInInitialBuildup = new Set();
|
|
29207
|
+
/**
|
|
29208
|
+
* Registers a form under a key so the manager can track it.
|
|
29209
|
+
*
|
|
29210
|
+
* On first registration it also seeds the saved snapshot from the current values, so a freshly
|
|
29211
|
+
* registered form counts as unchanged until the user actually edits it.
|
|
29212
|
+
*/
|
|
29116
29213
|
setFormGroup(key, formGroup) {
|
|
29117
29214
|
this._formGroups.set(key, formGroup);
|
|
29215
|
+
if (!this._formGroupsSnapshot.has(key)) {
|
|
29216
|
+
this._formGroupsSnapshot.set(key, formGroup.getRawValue());
|
|
29217
|
+
this.rememberControlInstances(key, formGroup);
|
|
29218
|
+
this._keysInInitialBuildup.add(key);
|
|
29219
|
+
}
|
|
29118
29220
|
this._formGroupsChanged$.next();
|
|
29119
29221
|
}
|
|
29222
|
+
/**
|
|
29223
|
+
* Returns the form registered under the given key, or `undefined` if none is registered.
|
|
29224
|
+
*/
|
|
29120
29225
|
getFormGroup(key) {
|
|
29121
29226
|
return this._formGroups.get(key);
|
|
29122
29227
|
}
|
|
29228
|
+
/**
|
|
29229
|
+
* Returns all registered forms, keyed by their registration key.
|
|
29230
|
+
*/
|
|
29123
29231
|
getAllFormGroups() {
|
|
29124
29232
|
return this._formGroups;
|
|
29125
29233
|
}
|
|
29126
|
-
|
|
29127
|
-
|
|
29128
|
-
|
|
29129
|
-
}
|
|
29234
|
+
/**
|
|
29235
|
+
* Returns `true` if at least one form is registered.
|
|
29236
|
+
*/
|
|
29130
29237
|
hasFormGroups() {
|
|
29131
29238
|
return this._formGroups.size > 0;
|
|
29132
29239
|
}
|
|
29240
|
+
/**
|
|
29241
|
+
* Returns `true` if no form is registered under the given key yet.
|
|
29242
|
+
*/
|
|
29243
|
+
isFormGroupKeyUnique(key) {
|
|
29244
|
+
return !this._formGroups.has(key);
|
|
29245
|
+
}
|
|
29246
|
+
/**
|
|
29247
|
+
* Returns the current `.value` of every registered form, keyed by registration key.
|
|
29248
|
+
*
|
|
29249
|
+
* Uses Angular's `.value`, so disabled controls are omitted. Use the snapshot methods when you
|
|
29250
|
+
* need the raw values including disabled controls.
|
|
29251
|
+
*/
|
|
29133
29252
|
getAllValues() {
|
|
29134
29253
|
const allValues = {};
|
|
29135
29254
|
this._formGroups.forEach((formGroup, key) => (allValues[key] = formGroup.value));
|
|
29136
29255
|
return allValues;
|
|
29137
29256
|
}
|
|
29138
|
-
|
|
29139
|
-
|
|
29257
|
+
/**
|
|
29258
|
+
* Unregisters the form under the given key and drops its instance tracking.
|
|
29259
|
+
*
|
|
29260
|
+
* Does nothing if no form is registered for the key.
|
|
29261
|
+
*/
|
|
29262
|
+
tryRemoveFormGroup(key) {
|
|
29263
|
+
this._formGroups.delete(key);
|
|
29264
|
+
this._knownControlInstances.delete(key);
|
|
29265
|
+
this._keysInInitialBuildup.delete(key);
|
|
29266
|
+
this._formGroupsChanged$.next();
|
|
29140
29267
|
}
|
|
29141
|
-
|
|
29142
|
-
|
|
29143
|
-
|
|
29144
|
-
|
|
29145
|
-
|
|
29146
|
-
|
|
29147
|
-
|
|
29268
|
+
/**
|
|
29269
|
+
* Ends the initial-buildup phase for a key and fixes its baseline from the form as it is now.
|
|
29270
|
+
*
|
|
29271
|
+
* During that phase the key never counts as changed, so a form filled right after registration
|
|
29272
|
+
* (e.g. by an `effect`) is not flagged as unsaved. This stores the current values as the baseline,
|
|
29273
|
+
* so later edits are measured against the filled form.
|
|
29274
|
+
*
|
|
29275
|
+
* Does nothing if the key is not in its initial-buildup phase.
|
|
29276
|
+
*/
|
|
29277
|
+
endInitialBuildup(key) {
|
|
29278
|
+
if (!this._keysInInitialBuildup.delete(key))
|
|
29279
|
+
return;
|
|
29280
|
+
const formGroup = this._formGroups.get(key);
|
|
29281
|
+
if (formGroup) {
|
|
29282
|
+
this._formGroupsSnapshot.set(key, formGroup.getRawValue());
|
|
29283
|
+
this.rememberControlInstances(key, formGroup);
|
|
29284
|
+
}
|
|
29285
|
+
this._formGroupsChanged$.next();
|
|
29148
29286
|
}
|
|
29287
|
+
/**
|
|
29288
|
+
* Emits whether any registered form differs from its saved snapshot — i.e. whether the user has
|
|
29289
|
+
* unsaved changes.
|
|
29290
|
+
*
|
|
29291
|
+
* Framework-driven rebuilds are folded into the snapshot first while the framework is adopting
|
|
29292
|
+
* them (see the class docs), so they are not reported as changes.
|
|
29293
|
+
*/
|
|
29149
29294
|
$hasValuesChanged() {
|
|
29150
|
-
return this._formGroupsChanged$.pipe(observeOn(queueScheduler), switchMap$1(() =>
|
|
29151
|
-
if (!this.hasFormGroups())
|
|
29152
|
-
return of(false);
|
|
29153
|
-
const obs = Array.from(this._formGroups.entries()).map(([key, fg]) => fg.valueChanges.pipe(startWith(fg.getRawValue()), map(() => {
|
|
29154
|
-
const currentValues = fg.getRawValue();
|
|
29155
|
-
const snapshot = this._formGroupsSnapshot.get(key);
|
|
29156
|
-
return snapshot ? !isEqual$1(currentValues, snapshot) : true;
|
|
29157
|
-
})));
|
|
29158
|
-
return combineLatest(obs).pipe(map(changes => changes.some(Boolean)));
|
|
29159
|
-
}));
|
|
29295
|
+
return this._formGroupsChanged$.pipe(observeOn(queueScheduler), switchMap$1(() => this.anyRegisteredFormChanged$()));
|
|
29160
29296
|
}
|
|
29161
|
-
|
|
29162
|
-
|
|
29297
|
+
/**
|
|
29298
|
+
* Overwrites the saved snapshot with the **current** form values — for a **synchronous** save
|
|
29299
|
+
* that is already finished.
|
|
29300
|
+
*
|
|
29301
|
+
* - Stores each form's raw values (`getRawValue`) as the new saved snapshot.
|
|
29302
|
+
* - Takes them **directly, without a deep copy** — synchronous means no edits can slip in
|
|
29303
|
+
* between, so the cheap path is safe (only the async path needs the copy).
|
|
29304
|
+
* - Afterwards the current state counts as saved; later edits are measured against it.
|
|
29305
|
+
*
|
|
29306
|
+
* For an **asynchronous** save, use `createCurrentValuesSnapshot()` + `updateSavedSnapshotFromValues()`.
|
|
29307
|
+
*/
|
|
29308
|
+
updateSavedSnapshotFromCurrentValues() {
|
|
29309
|
+
this._formGroups.forEach((fg, key) => {
|
|
29310
|
+
this._formGroupsSnapshot.set(key, fg.getRawValue());
|
|
29311
|
+
this.rememberControlInstances(key, fg);
|
|
29312
|
+
});
|
|
29163
29313
|
}
|
|
29164
29314
|
/**
|
|
29165
|
-
*
|
|
29315
|
+
* Creates an independent snapshot of the **current** form values and returns it — for the
|
|
29316
|
+
* **click moment** of an **asynchronous** save.
|
|
29166
29317
|
*
|
|
29167
|
-
*
|
|
29168
|
-
*
|
|
29169
|
-
* while
|
|
29318
|
+
* - Deep-copies each form's raw values (`structuredClone`), detached from the live form.
|
|
29319
|
+
* - Writes **nothing** internally — the saved snapshot stays unchanged.
|
|
29320
|
+
* - Freezes the click-time state: later edits (while the backend runs) do not change the copy.
|
|
29321
|
+
* - Hand the result to `updateSavedSnapshotFromValues()` once the save succeeds.
|
|
29170
29322
|
*
|
|
29171
29323
|
* Throws if any form value is not structured-cloneable (functions, symbols, DOM nodes).
|
|
29172
29324
|
*/
|
|
29173
|
-
|
|
29325
|
+
createCurrentValuesSnapshot() {
|
|
29174
29326
|
const captured = new Map();
|
|
29175
29327
|
this._formGroups.forEach((fg, key) => {
|
|
29176
29328
|
try {
|
|
29177
29329
|
captured.set(key, structuredClone(fg.getRawValue()));
|
|
29178
29330
|
}
|
|
29179
29331
|
catch (err) {
|
|
29180
|
-
throw new Error(`Quadrel Framework | QdFormGroupManager -
|
|
29332
|
+
throw new Error(`Quadrel Framework | QdFormGroupManager - createCurrentValuesSnapshot() failed for group "${key}". ` +
|
|
29181
29333
|
`Form values must be structured-cloneable. Non-cloneable values like functions, ` +
|
|
29182
29334
|
`symbols, or DOM nodes are not supported. Original error: ${String(err)}`);
|
|
29183
29335
|
}
|
|
@@ -29185,15 +29337,27 @@ class QdFormGroupManagerService {
|
|
|
29185
29337
|
return captured;
|
|
29186
29338
|
}
|
|
29187
29339
|
/**
|
|
29188
|
-
*
|
|
29340
|
+
* Overwrites the saved snapshot with the **given** snapshot — on **success** of an
|
|
29341
|
+
* **asynchronous** save, using the snapshot taken earlier by `createCurrentValuesSnapshot()`.
|
|
29189
29342
|
*
|
|
29190
|
-
*
|
|
29191
|
-
*
|
|
29192
|
-
*
|
|
29193
|
-
*/
|
|
29194
|
-
|
|
29195
|
-
|
|
29343
|
+
* - Stores the passed snapshot as the new saved state.
|
|
29344
|
+
* - Uses the click-time snapshot, **not** the current form — so edits the user made while the
|
|
29345
|
+
* backend was running stay correctly marked as unsaved.
|
|
29346
|
+
*/
|
|
29347
|
+
updateSavedSnapshotFromValues(snapshot) {
|
|
29348
|
+
snapshot.forEach((values, key) => {
|
|
29349
|
+
this._formGroupsSnapshot.set(key, values);
|
|
29350
|
+
const formGroup = this._formGroups.get(key);
|
|
29351
|
+
if (formGroup)
|
|
29352
|
+
this.rememberControlInstances(key, formGroup);
|
|
29353
|
+
});
|
|
29196
29354
|
}
|
|
29355
|
+
/**
|
|
29356
|
+
* Resets every registered form back to its saved snapshot — used when the user discards changes.
|
|
29357
|
+
*
|
|
29358
|
+
* FormArrays are resized to match the snapshot length; pending async validators are cancelled
|
|
29359
|
+
* afterwards so no stale validation result survives the restore.
|
|
29360
|
+
*/
|
|
29197
29361
|
restoreFormGroupsFromSnapshot() {
|
|
29198
29362
|
this._formGroups.forEach((fg, key) => {
|
|
29199
29363
|
const snapshot = this._formGroupsSnapshot.get(key);
|
|
@@ -29204,68 +29368,171 @@ class QdFormGroupManagerService {
|
|
|
29204
29368
|
this.cancelPendingAsyncValidation();
|
|
29205
29369
|
}
|
|
29206
29370
|
/**
|
|
29207
|
-
*
|
|
29371
|
+
* Starts adopting framework rebuilds into the snapshot. While active, a framework-driven control
|
|
29372
|
+
* replacement (a `setControl` on a leaf or a whole `FormArray` rebuilt via clear/push) is folded
|
|
29373
|
+
* into the saved snapshot, so it is not reported as an unsaved change.
|
|
29374
|
+
*
|
|
29375
|
+
* User edits never fall in this short window, so they stay dirty. The framework starts it after a
|
|
29376
|
+
* successful commit and after every snapshot write; the page stops it deterministically once the
|
|
29377
|
+
* rebuild has rendered.
|
|
29378
|
+
*/
|
|
29379
|
+
startAdoptingFrameworkRebuild() {
|
|
29380
|
+
this._isAdoptingFrameworkRebuild = true;
|
|
29381
|
+
this._frameworkRebuildSub?.unsubscribe();
|
|
29382
|
+
const valueStreams = Array.from(this._formGroups.values()).map(fg => fg.valueChanges);
|
|
29383
|
+
if (valueStreams.length)
|
|
29384
|
+
this._frameworkRebuildSub = merge(...valueStreams).subscribe(() => this.adoptFrameworkRebuild());
|
|
29385
|
+
this.adoptFrameworkRebuild();
|
|
29386
|
+
}
|
|
29387
|
+
/**
|
|
29388
|
+
* Stops adopting framework rebuilds. After this, control replacements count as user edits again.
|
|
29208
29389
|
*
|
|
29209
|
-
*
|
|
29210
|
-
|
|
29211
|
-
|
|
29390
|
+
* The page calls this once the framework rebuild has rendered (via `afterNextRender`).
|
|
29391
|
+
*/
|
|
29392
|
+
stopAdoptingFrameworkRebuild() {
|
|
29393
|
+
this._isAdoptingFrameworkRebuild = false;
|
|
29394
|
+
this._frameworkRebuildSub?.unsubscribe();
|
|
29395
|
+
}
|
|
29396
|
+
/**
|
|
29397
|
+
* Emits whether every registered form is currently valid.
|
|
29398
|
+
*
|
|
29399
|
+
* A disabled control without validators is treated as valid.
|
|
29400
|
+
*/
|
|
29401
|
+
$areFormGroupsValid() {
|
|
29402
|
+
return this._formGroupsChanged$.pipe(observeOn(queueScheduler), switchMap$1(() => {
|
|
29403
|
+
if (!this.hasFormGroups())
|
|
29404
|
+
return of(false);
|
|
29405
|
+
const obs = Array.from(this._formGroups.values()).map(fg => fg.statusChanges.pipe(startWith(fg.status), map(() => this.areFormGroupsValid(fg))));
|
|
29406
|
+
return combineLatest(obs).pipe(map(valids => valids.every(Boolean)));
|
|
29407
|
+
}));
|
|
29408
|
+
}
|
|
29409
|
+
/**
|
|
29410
|
+
* Cancels every in-flight (PENDING) async validator across all registered forms, so no stale
|
|
29411
|
+
* validation result can land after a restore or a commit.
|
|
29412
|
+
*
|
|
29413
|
+
* The async validators themselves are kept — only the pending run is dropped. Each phase runs
|
|
29414
|
+
* over **all** pending controls before the next starts (detach → re-evaluate sync-only, no
|
|
29415
|
+
* cascade → re-attach), so validity is never recomputed while a run is still settling.
|
|
29212
29416
|
*/
|
|
29213
29417
|
cancelPendingAsyncValidation() {
|
|
29214
29418
|
this._formGroups.forEach(fg => {
|
|
29215
29419
|
const pendingControls = this.collectPendingControls(fg);
|
|
29216
29420
|
if (pendingControls.length === 0)
|
|
29217
29421
|
return;
|
|
29218
|
-
|
|
29219
|
-
|
|
29220
|
-
}
|
|
29221
|
-
for (const { control } of pendingControls) {
|
|
29222
|
-
control.updateValueAndValidity({ onlySelf: true });
|
|
29223
|
-
}
|
|
29224
|
-
for (const { control, asyncValidator } of pendingControls) {
|
|
29225
|
-
control.setAsyncValidators(asyncValidator);
|
|
29226
|
-
}
|
|
29422
|
+
pendingControls.forEach(({ control }) => control.clearAsyncValidators());
|
|
29423
|
+
pendingControls.forEach(({ control }) => control.updateValueAndValidity({ onlySelf: true }));
|
|
29424
|
+
pendingControls.forEach(({ control, asyncValidator }) => control.setAsyncValidators(asyncValidator));
|
|
29227
29425
|
});
|
|
29228
29426
|
}
|
|
29229
|
-
|
|
29230
|
-
|
|
29231
|
-
|
|
29232
|
-
|
|
29233
|
-
|
|
29234
|
-
|
|
29235
|
-
|
|
29236
|
-
|
|
29237
|
-
|
|
29238
|
-
|
|
29239
|
-
|
|
29240
|
-
|
|
29427
|
+
anyRegisteredFormChanged$() {
|
|
29428
|
+
if (!this.hasFormGroups())
|
|
29429
|
+
return of(false);
|
|
29430
|
+
const perForm = Array.from(this._formGroups.entries()).map(([key, fg]) => fg.valueChanges.pipe(startWith(null), map(() => this.hasFormChanged(key, fg))));
|
|
29431
|
+
return combineLatest(perForm).pipe(map(flags => flags.some(Boolean)));
|
|
29432
|
+
}
|
|
29433
|
+
hasFormChanged(key, fg) {
|
|
29434
|
+
if (this._keysInInitialBuildup.has(key))
|
|
29435
|
+
return false;
|
|
29436
|
+
const snapshot = this._formGroupsSnapshot.get(key);
|
|
29437
|
+
if (snapshot)
|
|
29438
|
+
this.adoptRebuildInGroup(key, fg, snapshot);
|
|
29439
|
+
return snapshot ? !isEqual$1(fg.getRawValue(), snapshot) : true;
|
|
29440
|
+
}
|
|
29441
|
+
adoptFrameworkRebuild() {
|
|
29442
|
+
this._formGroups.forEach((fg, key) => {
|
|
29443
|
+
const snapshot = this._formGroupsSnapshot.get(key);
|
|
29444
|
+
if (snapshot)
|
|
29445
|
+
this.adoptRebuildInGroup(key, fg, snapshot);
|
|
29241
29446
|
});
|
|
29242
29447
|
}
|
|
29243
|
-
|
|
29244
|
-
const
|
|
29245
|
-
|
|
29246
|
-
|
|
29247
|
-
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
|
|
29251
|
-
|
|
29252
|
-
|
|
29448
|
+
adoptRebuildInGroup(key, formGroup, snapshot) {
|
|
29449
|
+
const tracked = this._knownControlInstances.get(key) ?? new Map();
|
|
29450
|
+
this.adoptRebuiltControls(formGroup, snapshot, tracked, '');
|
|
29451
|
+
this._knownControlInstances.set(key, tracked);
|
|
29452
|
+
}
|
|
29453
|
+
adoptRebuiltControls(group, snapshot, tracked, prefix) {
|
|
29454
|
+
Object.entries(group.controls).forEach(([name, control]) => {
|
|
29455
|
+
const path = prefix ? `${prefix}.${name}` : name;
|
|
29456
|
+
const isNewKey = !(name in snapshot);
|
|
29457
|
+
if (control instanceof FormArray)
|
|
29458
|
+
this.adoptRebuiltList(control, snapshot, name, isNewKey);
|
|
29459
|
+
else if (control instanceof FormGroup)
|
|
29460
|
+
this.adoptRebuiltGroup(control, snapshot, name, isNewKey, tracked, path);
|
|
29461
|
+
else
|
|
29462
|
+
this.adoptRebuiltField(control, snapshot, name, isNewKey, tracked.get(path));
|
|
29463
|
+
tracked.set(path, control);
|
|
29464
|
+
});
|
|
29465
|
+
}
|
|
29466
|
+
adoptRebuiltList(control, snapshot, name, isNewKey) {
|
|
29467
|
+
if (isNewKey) {
|
|
29468
|
+
this.adoptNewPristineControl(control, snapshot, name);
|
|
29469
|
+
return;
|
|
29253
29470
|
}
|
|
29254
|
-
|
|
29471
|
+
if (!this.isAdoptableArrayRebuild(control, snapshot, name))
|
|
29472
|
+
return;
|
|
29473
|
+
const snapshotArray = snapshot[name];
|
|
29474
|
+
const currentArray = control.getRawValue();
|
|
29475
|
+
if (currentArray.length === snapshotArray.length && !isEqual$1(currentArray, snapshotArray))
|
|
29476
|
+
snapshot[name] = currentArray;
|
|
29477
|
+
}
|
|
29478
|
+
adoptRebuiltGroup(group, snapshot, name, isNewKey, tracked, path) {
|
|
29479
|
+
if (isNewKey)
|
|
29480
|
+
this.adoptNewPristineControl(group, snapshot, name);
|
|
29481
|
+
const nested = snapshot[name];
|
|
29482
|
+
if (this.isPlainObject(nested))
|
|
29483
|
+
this.adoptRebuiltControls(group, nested, tracked, path);
|
|
29484
|
+
}
|
|
29485
|
+
adoptRebuiltField(control, snapshot, name, isNewKey, known) {
|
|
29486
|
+
if (isNewKey)
|
|
29487
|
+
this.adoptNewPristineControl(control, snapshot, name);
|
|
29488
|
+
else if (this.isReplacedPristineControl(control, known))
|
|
29489
|
+
snapshot[name] = control.getRawValue();
|
|
29490
|
+
}
|
|
29491
|
+
adoptNewPristineControl(control, snapshot, name) {
|
|
29492
|
+
if (control.pristine)
|
|
29493
|
+
snapshot[name] = control.getRawValue();
|
|
29494
|
+
}
|
|
29495
|
+
isAdoptableArrayRebuild(control, snapshot, name) {
|
|
29496
|
+
return this._isAdoptingFrameworkRebuild && control.pristine && Array.isArray(snapshot[name]);
|
|
29497
|
+
}
|
|
29498
|
+
isReplacedPristineControl(control, known) {
|
|
29499
|
+
return this._isAdoptingFrameworkRebuild && !!known && known !== control && control.pristine;
|
|
29500
|
+
}
|
|
29501
|
+
rememberControlInstances(key, formGroup) {
|
|
29502
|
+
const tracked = new Map();
|
|
29503
|
+
this.collectControlInstances(formGroup, tracked, '');
|
|
29504
|
+
this._knownControlInstances.set(key, tracked);
|
|
29505
|
+
}
|
|
29506
|
+
collectControlInstances(group, tracked, prefix) {
|
|
29507
|
+
Object.entries(group.controls).forEach(([name, control]) => {
|
|
29508
|
+
const path = prefix ? `${prefix}.${name}` : name;
|
|
29509
|
+
tracked.set(path, control);
|
|
29510
|
+
if (control instanceof FormGroup)
|
|
29511
|
+
this.collectControlInstances(control, tracked, path);
|
|
29512
|
+
});
|
|
29513
|
+
}
|
|
29514
|
+
restoreFormGroup(fg, snapshot) {
|
|
29515
|
+
Object.entries(fg.controls).forEach(([ctrlKey, ctrl]) => this.restoreControl(ctrl, snapshot[ctrlKey]));
|
|
29516
|
+
}
|
|
29517
|
+
restoreControl(ctrl, newValue) {
|
|
29518
|
+
if (ctrl instanceof FormArray && Array.isArray(newValue))
|
|
29519
|
+
this.resetFormArrayToValues(ctrl, newValue);
|
|
29520
|
+
else if (ctrl instanceof FormGroup && this.isPlainObject(newValue))
|
|
29521
|
+
this.restoreFormGroup(ctrl, newValue);
|
|
29522
|
+
else
|
|
29523
|
+
ctrl.reset(newValue);
|
|
29524
|
+
}
|
|
29525
|
+
isPlainObject(value) {
|
|
29526
|
+
return !!value && typeof value === 'object';
|
|
29255
29527
|
}
|
|
29256
29528
|
resetFormArrayToValues(array, newValues) {
|
|
29257
29529
|
if (!Array.isArray(newValues))
|
|
29258
29530
|
return;
|
|
29259
|
-
|
|
29260
|
-
const newLen = newValues.length;
|
|
29261
|
-
if (oldLen === 0 && newLen === 0)
|
|
29531
|
+
if (array.length === 0 && newValues.length === 0)
|
|
29262
29532
|
return;
|
|
29263
|
-
if (
|
|
29533
|
+
if (array.length === 0)
|
|
29264
29534
|
return this.initFormArrayFromValues(array, newValues);
|
|
29265
|
-
|
|
29266
|
-
array.push(this.deepCloneControl(array.at(0)));
|
|
29267
|
-
while (array.length > newLen)
|
|
29268
|
-
array.removeAt(array.length - 1);
|
|
29535
|
+
this.resizeFormArray(array, newValues.length);
|
|
29269
29536
|
array.reset(newValues);
|
|
29270
29537
|
}
|
|
29271
29538
|
initFormArrayFromValues(array, values) {
|
|
@@ -29281,6 +29548,12 @@ class QdFormGroupManagerService {
|
|
|
29281
29548
|
}
|
|
29282
29549
|
array.reset(values);
|
|
29283
29550
|
}
|
|
29551
|
+
resizeFormArray(array, length) {
|
|
29552
|
+
while (array.length < length)
|
|
29553
|
+
array.push(this.deepCloneControl(array.at(0)));
|
|
29554
|
+
while (array.length > length)
|
|
29555
|
+
array.removeAt(array.length - 1);
|
|
29556
|
+
}
|
|
29284
29557
|
deepCloneControl(control) {
|
|
29285
29558
|
if (control instanceof FormControl)
|
|
29286
29559
|
return new FormControl(undefined, control.validator, control.asyncValidator);
|
|
@@ -29298,6 +29571,19 @@ class QdFormGroupManagerService {
|
|
|
29298
29571
|
areFormGroupsValid(fg) {
|
|
29299
29572
|
return Object.values(fg.controls).every(c => (c.disabled && !c.validator && !c.asyncValidator ? true : c.valid));
|
|
29300
29573
|
}
|
|
29574
|
+
collectPendingControls(control) {
|
|
29575
|
+
const result = this.childControlsOf(control).flatMap(child => this.collectPendingControls(child));
|
|
29576
|
+
if (control.status === 'PENDING')
|
|
29577
|
+
result.push({ control, asyncValidator: control.asyncValidator });
|
|
29578
|
+
return result;
|
|
29579
|
+
}
|
|
29580
|
+
childControlsOf(control) {
|
|
29581
|
+
if (control instanceof FormGroup)
|
|
29582
|
+
return Object.values(control.controls);
|
|
29583
|
+
if (control instanceof FormArray)
|
|
29584
|
+
return control.controls;
|
|
29585
|
+
return [];
|
|
29586
|
+
}
|
|
29301
29587
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFormGroupManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
29302
29588
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFormGroupManagerService });
|
|
29303
29589
|
}
|
|
@@ -29326,7 +29612,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29326
29612
|
* - The page switches to view mode via `deactivate()`.
|
|
29327
29613
|
*
|
|
29328
29614
|
* The service has no concept of "pending actions" or bypass windows. Framework actions update the
|
|
29329
|
-
* saved snapshot directly via `QdFormGroupManagerService.
|
|
29615
|
+
* saved snapshot directly via `QdFormGroupManagerService.updateSavedSnapshotFromValues(...)`; cancel-discard
|
|
29330
29616
|
* flows reset the form to the saved snapshot via `restoreFormGroupsFromSnapshot()`. Either way, the
|
|
29331
29617
|
* interceptor only observes the resulting form state — there is no time window where a bypass is active.
|
|
29332
29618
|
*/
|
|
@@ -29476,6 +29762,7 @@ class QdPageObjectHeaderComponent {
|
|
|
29476
29762
|
confirmationDialogService = inject(QdConfirmationDialogOpenerService);
|
|
29477
29763
|
contextService = inject(QdContextService);
|
|
29478
29764
|
resolverTriggerService = inject(QdResolverTriggerService);
|
|
29765
|
+
injector = inject(Injector);
|
|
29479
29766
|
pageStoreService = inject(QdPageStoreService);
|
|
29480
29767
|
config;
|
|
29481
29768
|
hasNavigation = false;
|
|
@@ -29596,7 +29883,7 @@ class QdPageObjectHeaderComponent {
|
|
|
29596
29883
|
this.setupResolverTrigger();
|
|
29597
29884
|
this.updateCustomActions();
|
|
29598
29885
|
this.subscribeToMetadataStream();
|
|
29599
|
-
this.formGroupManagerService.
|
|
29886
|
+
this.formGroupManagerService.updateSavedSnapshotFromCurrentValues();
|
|
29600
29887
|
this.initContexts();
|
|
29601
29888
|
}
|
|
29602
29889
|
ngOnChanges(changes) {
|
|
@@ -29621,7 +29908,7 @@ class QdPageObjectHeaderComponent {
|
|
|
29621
29908
|
return this.config.headerFacets != null;
|
|
29622
29909
|
}
|
|
29623
29910
|
edit() {
|
|
29624
|
-
this.formGroupManagerService.
|
|
29911
|
+
this.formGroupManagerService.updateSavedSnapshotFromCurrentValues();
|
|
29625
29912
|
this.pageStoreService.toggleViewonly(false);
|
|
29626
29913
|
if (this.editButton?.handler)
|
|
29627
29914
|
this.editButton.handler();
|
|
@@ -29651,7 +29938,8 @@ class QdPageObjectHeaderComponent {
|
|
|
29651
29938
|
onAfterSnapshot: () => {
|
|
29652
29939
|
this.formGroupManagerService.cancelPendingAsyncValidation();
|
|
29653
29940
|
this.pageStoreService.toggleViewonly(true);
|
|
29654
|
-
}
|
|
29941
|
+
},
|
|
29942
|
+
scheduleFrameworkRebuildStop: () => afterNextRender(() => this.formGroupManagerService.stopAdoptingFrameworkRebuild(), { injector: this.injector })
|
|
29655
29943
|
});
|
|
29656
29944
|
}
|
|
29657
29945
|
changeContext(context, selection, event) {
|
|
@@ -29679,7 +29967,7 @@ class QdPageObjectHeaderComponent {
|
|
|
29679
29967
|
.pipe(takeUntil(this._destroyed$), filter(shouldTrigger => shouldTrigger), tap(() => this._isLoadingSubject.next(true)), switchMap(() => this.pageObjectResolver.resolve()), tap(objectData => {
|
|
29680
29968
|
this._pageObjectDataSubject.next({ ...objectData, ...this._pendingMetadata });
|
|
29681
29969
|
this._pendingMetadata = {};
|
|
29682
|
-
}), tap(() => this._isLoadingSubject.next(false)), tap(() => this.formGroupManagerService.
|
|
29970
|
+
}), tap(() => this._isLoadingSubject.next(false)), tap(() => this.formGroupManagerService.updateSavedSnapshotFromCurrentValues()))
|
|
29683
29971
|
.subscribe();
|
|
29684
29972
|
}
|
|
29685
29973
|
initContexts() {
|
|
@@ -29870,7 +30158,7 @@ class QdPageStepperCancelDialogComponent {
|
|
|
29870
30158
|
</button>
|
|
29871
30159
|
<button qdButton (click)="close(true)">{{ 'i18n.qd.stepper.cancel.dialog.delete' | translate }}</button>
|
|
29872
30160
|
</qd-dialog-action>
|
|
29873
|
-
</qd-dialog> `, isInline: true, dependencies: [{ kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }, { kind: "component", type: QdTextSectionComponent, selector: "qd-text-section" }, { kind: "component", type: QdTextSectionParagraphComponent, selector: "qd-text-section-paragraph" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
30161
|
+
</qd-dialog> `, isInline: true, dependencies: [{ kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "component", type: QdTextSectionComponent, selector: "qd-text-section" }, { kind: "component", type: QdTextSectionParagraphComponent, selector: "qd-text-section-paragraph" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
29874
30162
|
}
|
|
29875
30163
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageStepperCancelDialogComponent, decorators: [{
|
|
29876
30164
|
type: Component,
|
|
@@ -31457,6 +31745,7 @@ class QdPageComponent {
|
|
|
31457
31745
|
bottomOffset$ = inject(QD_SAFE_BOTTOM_OFFSET, { optional: true });
|
|
31458
31746
|
dialogRef = inject(DialogRef, { optional: true });
|
|
31459
31747
|
navigationInterceptor = inject(QdPageNavigationInterceptorService);
|
|
31748
|
+
injector = inject(Injector);
|
|
31460
31749
|
confirmationDialogService = inject(QdConfirmationDialogOpenerService);
|
|
31461
31750
|
/**
|
|
31462
31751
|
* This property defines the configuration for the QdPage component, including the page type,
|
|
@@ -31653,7 +31942,10 @@ class QdPageComponent {
|
|
|
31653
31942
|
QdPageCommitActionExecutor.execute(action, values, {
|
|
31654
31943
|
formGroupManager: this.formGroupManagerService,
|
|
31655
31944
|
navigationInterceptor: this.navigationInterceptor,
|
|
31656
|
-
destroyed$: this._destroyed
|
|
31945
|
+
destroyed$: this._destroyed$,
|
|
31946
|
+
scheduleFrameworkRebuildStop: () => afterNextRender(() => this.formGroupManagerService.stopAdoptingFrameworkRebuild(), {
|
|
31947
|
+
injector: this.injector
|
|
31948
|
+
})
|
|
31657
31949
|
});
|
|
31658
31950
|
};
|
|
31659
31951
|
}
|
|
@@ -31765,6 +32057,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
31765
32057
|
* - Snapshots store only raw values and are taken by the page/header workflow (e.g. on init, edit, save). The confirmation dialog may restore all registered form groups from the snapshot.
|
|
31766
32058
|
* - FormArrays are restored with dynamic resizing (length is aligned to snapshot before values are reset).
|
|
31767
32059
|
*
|
|
32060
|
+
* ### **Filling a form right after connecting it**
|
|
32061
|
+
* - You can fill a connected form right after registration — e.g. an `effect` that adds controls or pushes `FormArray` rows. This first fill is the form's starting point, not a user change.
|
|
32062
|
+
* - So loading data into a fresh form never triggers a false "unsaved changes" prompt. Only later edits count.
|
|
32063
|
+
*
|
|
31768
32064
|
* ### **Page-Dialog (FullWidth) – Unsaved-Changes Close Contract**
|
|
31769
32065
|
* - When a QdPage is embedded in a full-width page dialog, the directive’s registered form groups are also used to guard dialog closing.
|
|
31770
32066
|
* - The page dialog can only close (e.g. via ESC or backdrop) if no unsaved changes are detected ($hasValuesChanged() is false).
|
|
@@ -31777,8 +32073,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
31777
32073
|
* ```html
|
|
31778
32074
|
* <qd-page [config]="pageConfig">
|
|
31779
32075
|
* <qd-section [config]="sectionConfig">
|
|
31780
|
-
* <qd-grid
|
|
31781
|
-
*
|
|
32076
|
+
* <qd-grid
|
|
32077
|
+
* qdConnectFormStateToPage="myFormGroup"
|
|
32078
|
+
* [formGroup]="myFormGroup">
|
|
32079
|
+
* My Section
|
|
31782
32080
|
* </qd-grid>
|
|
31783
32081
|
* </qd-section>
|
|
31784
32082
|
* </qd-page>
|
|
@@ -31798,36 +32096,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
31798
32096
|
class QdConnectFormStateToPageDirective {
|
|
31799
32097
|
formGroupManagerService = inject(QdFormGroupManagerService, { optional: true });
|
|
31800
32098
|
formGroupDirective = inject(FormGroupDirective, { host: true, optional: true });
|
|
32099
|
+
closeInitialBuildupHandle;
|
|
31801
32100
|
/**
|
|
31802
32101
|
* This key is used to manage and track the form state within the page. It has to be unique for each FormGroup Connector. Use the property name of the FormGroup in your component.
|
|
31803
32102
|
*/
|
|
31804
32103
|
qdConnectFormStateToPage;
|
|
31805
32104
|
ngOnInit() {
|
|
31806
|
-
if (!this.isValid())
|
|
31807
|
-
return;
|
|
31808
32105
|
const formGroup = this.formGroupDirective?.control;
|
|
31809
|
-
if (formGroup
|
|
31810
|
-
this.formGroupManagerService.setFormGroup(this.qdConnectFormStateToPage, formGroup);
|
|
31811
|
-
}
|
|
31812
|
-
}
|
|
31813
|
-
ngOnDestroy() {
|
|
31814
|
-
this.formGroupManagerService?.tryRemoveFormGroup(this.qdConnectFormStateToPage);
|
|
31815
|
-
}
|
|
31816
|
-
isValid() {
|
|
31817
|
-
const hasFormGroup = this.formGroupDirective?.control;
|
|
31818
|
-
if (!hasFormGroup) {
|
|
32106
|
+
if (!formGroup) {
|
|
31819
32107
|
console.error('Quadrel Framework | QdConnectFormStateToPage - Either a [formGroup] binding or <qd-quick-edit> with a FormGroup is required.');
|
|
31820
|
-
return
|
|
32108
|
+
return;
|
|
31821
32109
|
}
|
|
31822
32110
|
if (!this.formGroupManagerService) {
|
|
31823
32111
|
console.error('Quadrel Framework | QdConnectFormStateToPage - The connector must be used within <qd-page>.');
|
|
31824
|
-
return
|
|
32112
|
+
return;
|
|
31825
32113
|
}
|
|
31826
32114
|
if (!this.formGroupManagerService.isFormGroupKeyUnique(this.qdConnectFormStateToPage)) {
|
|
31827
32115
|
console.error(`Quadrel Framework | QdConnectFormStateToPage - The FormGroup key "${this.qdConnectFormStateToPage}" is not unique.`);
|
|
31828
|
-
return
|
|
32116
|
+
return;
|
|
31829
32117
|
}
|
|
31830
|
-
|
|
32118
|
+
this.formGroupManagerService.setFormGroup(this.qdConnectFormStateToPage, formGroup);
|
|
32119
|
+
this.scheduleInitialBaseline(this.formGroupManagerService);
|
|
32120
|
+
}
|
|
32121
|
+
ngOnDestroy() {
|
|
32122
|
+
if (this.closeInitialBuildupHandle)
|
|
32123
|
+
clearTimeout(this.closeInitialBuildupHandle);
|
|
32124
|
+
this.formGroupManagerService?.tryRemoveFormGroup(this.qdConnectFormStateToPage);
|
|
32125
|
+
}
|
|
32126
|
+
scheduleInitialBaseline(manager) {
|
|
32127
|
+
const key = this.qdConnectFormStateToPage;
|
|
32128
|
+
this.closeInitialBuildupHandle = setTimeout(() => manager.endInitialBuildup(key));
|
|
31831
32129
|
}
|
|
31832
32130
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdConnectFormStateToPageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
31833
32131
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: QdConnectFormStateToPageDirective, isStandalone: false, selector: "[qdConnectFormStateToPage]", inputs: { qdConnectFormStateToPage: "qdConnectFormStateToPage" }, ngImport: i0 });
|
|
@@ -33760,7 +34058,7 @@ class AddCommentDialogComponent {
|
|
|
33760
34058
|
};
|
|
33761
34059
|
}
|
|
33762
34060
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AddCommentDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
33763
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: AddCommentDialogComponent, isStandalone: false, selector: "qd-comment-dialog", ngImport: i0, template: "<qd-dialog>\n <form [formGroup]=\"form\">\n <qd-input [config]=\"authorInputConfig\"></qd-input>\n <qd-dropdown\n *ngIf=\"customInputConfig\"\n [config]=\"customInputConfig\"\n [formControlName]=\"'custom'\"\n [attr.data-test-id]=\"'comment-custom'\"\n ></qd-dropdown>\n\n <qd-richtext [config]=\"richtextConfig\" #commentText [formControlName]=\"'comment'\"> </qd-richtext>\n </form>\n <qd-dialog-action>\n <button qdButton qdButtonGhost color=\"secondary\" (click)=\"close()\" [data-test-id]=\"'button-cancel'\">\n {{ \"i18n.qd.comments.add.dialog.cancel\" | translate }}\n </button>\n <button\n [disabled]=\"!commentText.value || commentText.hasError\"\n qdButton\n (click)=\"close('Yes')\"\n [data-test-id]=\"'button-submit'\"\n >\n {{ \"i18n.qd.comments.add.dialog.submit\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [".validation-message{margin-top:4px;color:red;font-size:.875em}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDropdownComponent, selector: "qd-dropdown", inputs: ["value", "id", "formControlName", "config", "data-test-id", "qdPopoverMaxHeight", "dense"], outputs: ["valueChange", "enterClick", "clickHint", "clickReadonly", "clickViewonly", "optionsResolved"] }, { kind: "component", type: QdInputComponent, selector: "qd-input", inputs: ["formControlName", "value", "config", "isError", "data-test-id"], outputs: ["valueChange", "enterClick", "clickClear", "clickHint", "clickReadonly", "clickViewonly", "optionsResolved"] }, { kind: "component", type: QdRichtextComponent, selector: "qd-richtext", inputs: ["formControlName", "value", "config", "data-test-id"], outputs: ["valueChange", "clickHint", "clickReadonly", "clickViewonly"] }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog" }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34061
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: AddCommentDialogComponent, isStandalone: false, selector: "qd-comment-dialog", ngImport: i0, template: "<qd-dialog>\n <form [formGroup]=\"form\">\n <qd-input [config]=\"authorInputConfig\"></qd-input>\n <qd-dropdown\n *ngIf=\"customInputConfig\"\n [config]=\"customInputConfig\"\n [formControlName]=\"'custom'\"\n [attr.data-test-id]=\"'comment-custom'\"\n ></qd-dropdown>\n\n <qd-richtext [config]=\"richtextConfig\" #commentText [formControlName]=\"'comment'\"> </qd-richtext>\n </form>\n <qd-dialog-action>\n <button qdButton qdButtonGhost color=\"secondary\" (click)=\"close()\" [data-test-id]=\"'button-cancel'\">\n {{ \"i18n.qd.comments.add.dialog.cancel\" | translate }}\n </button>\n <button\n [disabled]=\"!commentText.value || commentText.hasError\"\n qdButton\n (click)=\"close('Yes')\"\n [data-test-id]=\"'button-submit'\"\n >\n {{ \"i18n.qd.comments.add.dialog.submit\" | translate }}\n </button>\n </qd-dialog-action>\n</qd-dialog>\n", styles: [".validation-message{margin-top:4px;color:red;font-size:.875em}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QdButtonComponent, selector: "button[qdButton], a[qdButton], button[qd-button]", inputs: ["disabled", "color", "icon", "data-test-id", "additionalInfo"] }, { kind: "directive", type: QdButtonGhostDirective, selector: "button[qdButtonGhost], a[qdButtonGhost]" }, { kind: "component", type: QdDropdownComponent, selector: "qd-dropdown", inputs: ["value", "id", "formControlName", "config", "data-test-id", "qdPopoverMaxHeight", "dense"], outputs: ["valueChange", "enterClick", "clickHint", "clickReadonly", "clickViewonly", "optionsResolved"] }, { kind: "component", type: QdInputComponent, selector: "qd-input", inputs: ["formControlName", "value", "config", "isError", "data-test-id"], outputs: ["valueChange", "enterClick", "clickClear", "clickHint", "clickReadonly", "clickViewonly", "optionsResolved"] }, { kind: "component", type: QdRichtextComponent, selector: "qd-richtext", inputs: ["formControlName", "value", "config", "data-test-id"], outputs: ["valueChange", "clickHint", "clickReadonly", "clickViewonly"] }, { kind: "component", type: QdDialogActionComponent, selector: "qd-dialog-action" }, { kind: "component", type: QdDialogComponent, selector: "qd-dialog", inputs: ["data-test-id"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33764
34062
|
}
|
|
33765
34063
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AddCommentDialogComponent, decorators: [{
|
|
33766
34064
|
type: Component,
|