@quadrel-enterprise-ui/framework 20.27.3-beta.234.1 → 20.27.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,5 +1,5 @@
|
|
|
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,
|
|
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
3
|
import { Dialog, DialogRef, 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';
|
|
@@ -1550,11 +1550,11 @@ class QdDialogComponent {
|
|
|
1550
1550
|
});
|
|
1551
1551
|
}
|
|
1552
1552
|
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
|
|
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 *ngIf=\"config.cancel\" qdButton qdButtonGhost color=\"secondary\" (click)=\"close()\">\n {{ config?.cancel?.label?.i18n ?? \"i18n.qd.dialog.action.cancel\" | translate }}\n </button>\n <button *ngIf=\"config.primary\" qdButton (click)=\"primaryActionClicked()\">\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
1554
|
}
|
|
1555
1555
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdDialogComponent, decorators: [{
|
|
1556
1556
|
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
|
|
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 *ngIf=\"config.cancel\" qdButton qdButtonGhost color=\"secondary\" (click)=\"close()\">\n {{ config?.cancel?.label?.i18n ?? \"i18n.qd.dialog.action.cancel\" | translate }}\n </button>\n <button *ngIf=\"config.primary\" qdButton (click)=\"primaryActionClicked()\">\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"] }]
|
|
1558
1558
|
}], propDecorators: { body: [{
|
|
1559
1559
|
type: ViewChild,
|
|
1560
1560
|
args: ['body']
|
|
@@ -28995,8 +28995,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
28995
28995
|
|
|
28996
28996
|
class QdPageCommitActionExecutor {
|
|
28997
28997
|
static execute(action, values, context) {
|
|
28998
|
-
const { formGroupManager, navigationInterceptor, destroyed$, onAfterSnapshot
|
|
28999
|
-
const captured = formGroupManager.
|
|
28998
|
+
const { formGroupManager, navigationInterceptor, destroyed$, onAfterSnapshot } = context;
|
|
28999
|
+
const captured = formGroupManager.captureFormValues();
|
|
29000
29000
|
let result$;
|
|
29001
29001
|
navigationInterceptor.executeWithBypass(() => {
|
|
29002
29002
|
const handlerResult = action.handler(values);
|
|
@@ -29004,9 +29004,7 @@ class QdPageCommitActionExecutor {
|
|
|
29004
29004
|
result$ = handlerResult;
|
|
29005
29005
|
});
|
|
29006
29006
|
const applySuccess = () => {
|
|
29007
|
-
formGroupManager.
|
|
29008
|
-
formGroupManager.startAdoptingFrameworkRebuild();
|
|
29009
|
-
scheduleFrameworkRebuildStop?.();
|
|
29007
|
+
formGroupManager.setFormGroupsSnapshot(captured);
|
|
29010
29008
|
navigationInterceptor.executeWithBypass(() => {
|
|
29011
29009
|
try {
|
|
29012
29010
|
onAfterSnapshot?.();
|
|
@@ -29050,170 +29048,136 @@ class QdPageCommitActionExecutor {
|
|
|
29050
29048
|
}
|
|
29051
29049
|
|
|
29052
29050
|
/**
|
|
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
|
-
*
|
|
29081
|
-
*
|
|
29051
|
+
* The QdFormGroupManagerService provides centralized registration, snapshotting, and value tracking
|
|
29052
|
+
* for multiple Angular `FormGroup` instances. It supports efficient state restoration, including dynamic
|
|
29053
|
+
* resizing of embedded `FormArray` controls, while avoiding any structural cloning in snapshots.
|
|
29054
|
+
*
|
|
29055
|
+
* #### Purpose:
|
|
29056
|
+
* This service is useful when managing multiple reactive forms that must be:
|
|
29057
|
+
* - Observed for changes
|
|
29058
|
+
* - Checked for validity
|
|
29059
|
+
* - Compared against a previous state (snapshot)
|
|
29060
|
+
* - Reset to a previous state, including dynamic form arrays
|
|
29061
|
+
*
|
|
29062
|
+
* #### Value Snapshot
|
|
29063
|
+
* Snapshots store only **raw values** (via `getRawValue()`), not any control instances.
|
|
29064
|
+
* This allows for minimal memory usage and easy comparison using deep equality checks.
|
|
29065
|
+
*
|
|
29066
|
+
* #### Smart FormArray Reset
|
|
29067
|
+
* When restoring a `FormArray`, the service ensures that the number and structure of controls
|
|
29068
|
+
* matches the snapshot values. Controls are cloned dynamically from an existing prototype.
|
|
29069
|
+
* If a `FormArray` was emptied (e.g. via `removeAt()`), a minimal control structure is inferred
|
|
29070
|
+
* from the snapshot data.
|
|
29071
|
+
*
|
|
29072
|
+
* #### Example Usage:
|
|
29073
|
+
*
|
|
29074
|
+
* **Register a FormGroup**
|
|
29075
|
+
* ```ts
|
|
29076
|
+
* service.setFormGroup('userForm', formGroup);
|
|
29077
|
+
* ```
|
|
29078
|
+
*
|
|
29079
|
+
* **Take a Snapshot**
|
|
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.
|
|
29082
29111
|
*/
|
|
29083
29112
|
class QdFormGroupManagerService {
|
|
29084
29113
|
_formGroups = new Map();
|
|
29085
29114
|
_formGroupsSnapshot = new Map();
|
|
29086
|
-
_knownControlInstances = new Map();
|
|
29087
|
-
_isAdoptingFrameworkRebuild = false;
|
|
29088
|
-
_frameworkRebuildSub;
|
|
29089
29115
|
_formGroupsChanged$ = new BehaviorSubject(undefined);
|
|
29090
|
-
_keysInInitialBuildup = new Set();
|
|
29091
|
-
/**
|
|
29092
|
-
* Registers a form under a key so the manager can track it.
|
|
29093
|
-
*
|
|
29094
|
-
* On first registration it also seeds the saved snapshot from the current values, so a freshly
|
|
29095
|
-
* registered form counts as unchanged until the user actually edits it.
|
|
29096
|
-
*/
|
|
29097
29116
|
setFormGroup(key, formGroup) {
|
|
29098
29117
|
this._formGroups.set(key, formGroup);
|
|
29099
|
-
if (!this._formGroupsSnapshot.has(key)) {
|
|
29100
|
-
this._formGroupsSnapshot.set(key, formGroup.getRawValue());
|
|
29101
|
-
this.rememberControlInstances(key, formGroup);
|
|
29102
|
-
this._keysInInitialBuildup.add(key);
|
|
29103
|
-
}
|
|
29104
29118
|
this._formGroupsChanged$.next();
|
|
29105
29119
|
}
|
|
29106
|
-
/**
|
|
29107
|
-
* Returns the form registered under the given key, or `undefined` if none is registered.
|
|
29108
|
-
*/
|
|
29109
29120
|
getFormGroup(key) {
|
|
29110
29121
|
return this._formGroups.get(key);
|
|
29111
29122
|
}
|
|
29112
|
-
/**
|
|
29113
|
-
* Returns all registered forms, keyed by their registration key.
|
|
29114
|
-
*/
|
|
29115
29123
|
getAllFormGroups() {
|
|
29116
29124
|
return this._formGroups;
|
|
29117
29125
|
}
|
|
29118
|
-
|
|
29119
|
-
|
|
29120
|
-
|
|
29126
|
+
tryRemoveFormGroup(key) {
|
|
29127
|
+
this._formGroups.delete(key);
|
|
29128
|
+
this._formGroupsChanged$.next();
|
|
29129
|
+
}
|
|
29121
29130
|
hasFormGroups() {
|
|
29122
29131
|
return this._formGroups.size > 0;
|
|
29123
29132
|
}
|
|
29124
|
-
/**
|
|
29125
|
-
* Returns `true` if no form is registered under the given key yet.
|
|
29126
|
-
*/
|
|
29127
|
-
isFormGroupKeyUnique(key) {
|
|
29128
|
-
return !this._formGroups.has(key);
|
|
29129
|
-
}
|
|
29130
|
-
/**
|
|
29131
|
-
* Returns the current `.value` of every registered form, keyed by registration key.
|
|
29132
|
-
*
|
|
29133
|
-
* Uses Angular's `.value`, so disabled controls are omitted. Use the snapshot methods when you
|
|
29134
|
-
* need the raw values including disabled controls.
|
|
29135
|
-
*/
|
|
29136
29133
|
getAllValues() {
|
|
29137
29134
|
const allValues = {};
|
|
29138
29135
|
this._formGroups.forEach((formGroup, key) => (allValues[key] = formGroup.value));
|
|
29139
29136
|
return allValues;
|
|
29140
29137
|
}
|
|
29141
|
-
|
|
29142
|
-
|
|
29143
|
-
*
|
|
29144
|
-
* Does nothing if no form is registered for the key.
|
|
29145
|
-
*/
|
|
29146
|
-
tryRemoveFormGroup(key) {
|
|
29147
|
-
this._formGroups.delete(key);
|
|
29148
|
-
this._knownControlInstances.delete(key);
|
|
29149
|
-
this._keysInInitialBuildup.delete(key);
|
|
29150
|
-
this._formGroupsChanged$.next();
|
|
29138
|
+
isFormGroupKeyUnique(key) {
|
|
29139
|
+
return !this._formGroups.has(key);
|
|
29151
29140
|
}
|
|
29152
|
-
|
|
29153
|
-
|
|
29154
|
-
|
|
29155
|
-
|
|
29156
|
-
|
|
29157
|
-
|
|
29158
|
-
|
|
29159
|
-
* Does nothing if the key is not in its initial-buildup phase.
|
|
29160
|
-
*/
|
|
29161
|
-
endInitialBuildup(key) {
|
|
29162
|
-
if (!this._keysInInitialBuildup.delete(key))
|
|
29163
|
-
return;
|
|
29164
|
-
const formGroup = this._formGroups.get(key);
|
|
29165
|
-
if (formGroup) {
|
|
29166
|
-
this._formGroupsSnapshot.set(key, formGroup.getRawValue());
|
|
29167
|
-
this.rememberControlInstances(key, formGroup);
|
|
29168
|
-
}
|
|
29169
|
-
this._formGroupsChanged$.next();
|
|
29141
|
+
$areFormGroupsValid() {
|
|
29142
|
+
return this._formGroupsChanged$.pipe(observeOn(queueScheduler), switchMap$1(() => {
|
|
29143
|
+
if (!this.hasFormGroups())
|
|
29144
|
+
return of(false);
|
|
29145
|
+
const obs = Array.from(this._formGroups.values()).map(fg => fg.statusChanges.pipe(startWith(fg.status), map(() => this.areFormGroupsValid(fg))));
|
|
29146
|
+
return combineLatest(obs).pipe(map(valids => valids.every(Boolean)));
|
|
29147
|
+
}));
|
|
29170
29148
|
}
|
|
29171
|
-
/**
|
|
29172
|
-
* Emits whether any registered form differs from its saved snapshot — i.e. whether the user has
|
|
29173
|
-
* unsaved changes.
|
|
29174
|
-
*
|
|
29175
|
-
* Framework-driven rebuilds are folded into the snapshot first while the framework is adopting
|
|
29176
|
-
* them (see the class docs), so they are not reported as changes.
|
|
29177
|
-
*/
|
|
29178
29149
|
$hasValuesChanged() {
|
|
29179
|
-
return this._formGroupsChanged$.pipe(observeOn(queueScheduler), switchMap$1(() =>
|
|
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
|
+
}));
|
|
29180
29160
|
}
|
|
29181
|
-
|
|
29182
|
-
|
|
29183
|
-
* that is already finished.
|
|
29184
|
-
*
|
|
29185
|
-
* - Stores each form's raw values (`getRawValue`) as the new saved snapshot.
|
|
29186
|
-
* - Takes them **directly, without a deep copy** — synchronous means no edits can slip in
|
|
29187
|
-
* between, so the cheap path is safe (only the async path needs the copy).
|
|
29188
|
-
* - Afterwards the current state counts as saved; later edits are measured against it.
|
|
29189
|
-
*
|
|
29190
|
-
* For an **asynchronous** save, use `createCurrentValuesSnapshot()` + `updateSavedSnapshotFromValues()`.
|
|
29191
|
-
*/
|
|
29192
|
-
updateSavedSnapshotFromCurrentValues() {
|
|
29193
|
-
this._formGroups.forEach((fg, key) => {
|
|
29194
|
-
this._formGroupsSnapshot.set(key, fg.getRawValue());
|
|
29195
|
-
this.rememberControlInstances(key, fg);
|
|
29196
|
-
});
|
|
29161
|
+
takeFormGroupsSnapshot() {
|
|
29162
|
+
this._formGroups.forEach((fg, key) => this._formGroupsSnapshot.set(key, fg.getRawValue()));
|
|
29197
29163
|
}
|
|
29198
29164
|
/**
|
|
29199
|
-
*
|
|
29200
|
-
* **click moment** of an **asynchronous** save.
|
|
29165
|
+
* Captures a deep-cloned snapshot of all current form values without mutating internal state.
|
|
29201
29166
|
*
|
|
29202
|
-
*
|
|
29203
|
-
*
|
|
29204
|
-
*
|
|
29205
|
-
* - Hand the result to `updateSavedSnapshotFromValues()` once the save succeeds.
|
|
29167
|
+
* Used by framework orchestration code that needs to remember the "at click time" state of a form
|
|
29168
|
+
* so the snapshot can later be set to exactly those values — even if the user edits the form
|
|
29169
|
+
* while an async commit action is pending.
|
|
29206
29170
|
*
|
|
29207
29171
|
* Throws if any form value is not structured-cloneable (functions, symbols, DOM nodes).
|
|
29208
29172
|
*/
|
|
29209
|
-
|
|
29173
|
+
captureFormValues() {
|
|
29210
29174
|
const captured = new Map();
|
|
29211
29175
|
this._formGroups.forEach((fg, key) => {
|
|
29212
29176
|
try {
|
|
29213
29177
|
captured.set(key, structuredClone(fg.getRawValue()));
|
|
29214
29178
|
}
|
|
29215
29179
|
catch (err) {
|
|
29216
|
-
throw new Error(`Quadrel Framework | QdFormGroupManager -
|
|
29180
|
+
throw new Error(`Quadrel Framework | QdFormGroupManager - captureFormValues() failed for group "${key}". ` +
|
|
29217
29181
|
`Form values must be structured-cloneable. Non-cloneable values like functions, ` +
|
|
29218
29182
|
`symbols, or DOM nodes are not supported. Original error: ${String(err)}`);
|
|
29219
29183
|
}
|
|
@@ -29221,27 +29185,15 @@ class QdFormGroupManagerService {
|
|
|
29221
29185
|
return captured;
|
|
29222
29186
|
}
|
|
29223
29187
|
/**
|
|
29224
|
-
*
|
|
29225
|
-
* **asynchronous** save, using the snapshot taken earlier by `createCurrentValuesSnapshot()`.
|
|
29226
|
-
*
|
|
29227
|
-
* - Stores the passed snapshot as the new saved state.
|
|
29228
|
-
* - Uses the click-time snapshot, **not** the current form — so edits the user made while the
|
|
29229
|
-
* backend was running stay correctly marked as unsaved.
|
|
29230
|
-
*/
|
|
29231
|
-
updateSavedSnapshotFromValues(snapshot) {
|
|
29232
|
-
snapshot.forEach((values, key) => {
|
|
29233
|
-
this._formGroupsSnapshot.set(key, values);
|
|
29234
|
-
const formGroup = this._formGroups.get(key);
|
|
29235
|
-
if (formGroup)
|
|
29236
|
-
this.rememberControlInstances(key, formGroup);
|
|
29237
|
-
});
|
|
29238
|
-
}
|
|
29239
|
-
/**
|
|
29240
|
-
* Resets every registered form back to its saved snapshot — used when the user discards changes.
|
|
29188
|
+
* Replaces the internal saved snapshot with the provided values.
|
|
29241
29189
|
*
|
|
29242
|
-
*
|
|
29243
|
-
*
|
|
29190
|
+
* Intended to be paired with `captureFormValues()` so that the snapshot can be set to the
|
|
29191
|
+
* values present when a commit action was initiated — independent of whether the user edited
|
|
29192
|
+
* the form in the meantime.
|
|
29244
29193
|
*/
|
|
29194
|
+
setFormGroupsSnapshot(values) {
|
|
29195
|
+
values.forEach((snapshot, key) => this._formGroupsSnapshot.set(key, snapshot));
|
|
29196
|
+
}
|
|
29245
29197
|
restoreFormGroupsFromSnapshot() {
|
|
29246
29198
|
this._formGroups.forEach((fg, key) => {
|
|
29247
29199
|
const snapshot = this._formGroupsSnapshot.get(key);
|
|
@@ -29252,171 +29204,68 @@ class QdFormGroupManagerService {
|
|
|
29252
29204
|
this.cancelPendingAsyncValidation();
|
|
29253
29205
|
}
|
|
29254
29206
|
/**
|
|
29255
|
-
*
|
|
29256
|
-
* replacement (a `setControl` on a leaf or a whole `FormArray` rebuilt via clear/push) is folded
|
|
29257
|
-
* into the saved snapshot, so it is not reported as an unsaved change.
|
|
29207
|
+
* Cancels any in-flight async validators on all registered form groups.
|
|
29258
29208
|
*
|
|
29259
|
-
*
|
|
29260
|
-
*
|
|
29261
|
-
*
|
|
29262
|
-
*/
|
|
29263
|
-
startAdoptingFrameworkRebuild() {
|
|
29264
|
-
this._isAdoptingFrameworkRebuild = true;
|
|
29265
|
-
this._frameworkRebuildSub?.unsubscribe();
|
|
29266
|
-
const valueStreams = Array.from(this._formGroups.values()).map(fg => fg.valueChanges);
|
|
29267
|
-
if (valueStreams.length)
|
|
29268
|
-
this._frameworkRebuildSub = merge(...valueStreams).subscribe(() => this.adoptFrameworkRebuild());
|
|
29269
|
-
this.adoptFrameworkRebuild();
|
|
29270
|
-
}
|
|
29271
|
-
/**
|
|
29272
|
-
* Stops adopting framework rebuilds. After this, control replacements count as user edits again.
|
|
29273
|
-
*
|
|
29274
|
-
* The page calls this once the framework rebuild has rendered (via `afterNextRender`).
|
|
29275
|
-
*/
|
|
29276
|
-
stopAdoptingFrameworkRebuild() {
|
|
29277
|
-
this._isAdoptingFrameworkRebuild = false;
|
|
29278
|
-
this._frameworkRebuildSub?.unsubscribe();
|
|
29279
|
-
}
|
|
29280
|
-
/**
|
|
29281
|
-
* Emits whether every registered form is currently valid.
|
|
29282
|
-
*
|
|
29283
|
-
* A disabled control without validators is treated as valid.
|
|
29284
|
-
*/
|
|
29285
|
-
$areFormGroupsValid() {
|
|
29286
|
-
return this._formGroupsChanged$.pipe(observeOn(queueScheduler), switchMap$1(() => {
|
|
29287
|
-
if (!this.hasFormGroups())
|
|
29288
|
-
return of(false);
|
|
29289
|
-
const obs = Array.from(this._formGroups.values()).map(fg => fg.statusChanges.pipe(startWith(fg.status), map(() => this.areFormGroupsValid(fg))));
|
|
29290
|
-
return combineLatest(obs).pipe(map(valids => valids.every(Boolean)));
|
|
29291
|
-
}));
|
|
29292
|
-
}
|
|
29293
|
-
/**
|
|
29294
|
-
* Cancels every in-flight (PENDING) async validator across all registered forms, so no stale
|
|
29295
|
-
* validation result can land after a restore or a commit.
|
|
29296
|
-
*
|
|
29297
|
-
* The async validators themselves are kept — only the pending run is dropped. Each phase runs
|
|
29298
|
-
* over **all** pending controls before the next starts (detach → re-evaluate sync-only, no
|
|
29299
|
-
* cascade → re-attach), so validity is never recomputed while a run is still settling.
|
|
29209
|
+
* 1. Collect all PENDING controls and their async validators
|
|
29210
|
+
* 2. Clear all async validators, then update validity (sync-only, no cascade)
|
|
29211
|
+
* 3. Re-attach async validators for future use
|
|
29300
29212
|
*/
|
|
29301
29213
|
cancelPendingAsyncValidation() {
|
|
29302
29214
|
this._formGroups.forEach(fg => {
|
|
29303
29215
|
const pendingControls = this.collectPendingControls(fg);
|
|
29304
29216
|
if (pendingControls.length === 0)
|
|
29305
29217
|
return;
|
|
29306
|
-
|
|
29307
|
-
|
|
29308
|
-
|
|
29309
|
-
|
|
29310
|
-
|
|
29311
|
-
|
|
29312
|
-
|
|
29313
|
-
|
|
29314
|
-
|
|
29315
|
-
return combineLatest(perForm).pipe(map(flags => flags.some(Boolean)));
|
|
29316
|
-
}
|
|
29317
|
-
hasFormChanged(key, fg) {
|
|
29318
|
-
if (this._keysInInitialBuildup.has(key))
|
|
29319
|
-
return false;
|
|
29320
|
-
const snapshot = this._formGroupsSnapshot.get(key);
|
|
29321
|
-
if (snapshot)
|
|
29322
|
-
this.adoptRebuildInGroup(key, fg, snapshot);
|
|
29323
|
-
return snapshot ? !isEqual$1(fg.getRawValue(), snapshot) : true;
|
|
29324
|
-
}
|
|
29325
|
-
adoptFrameworkRebuild() {
|
|
29326
|
-
this._formGroups.forEach((fg, key) => {
|
|
29327
|
-
const snapshot = this._formGroupsSnapshot.get(key);
|
|
29328
|
-
if (snapshot)
|
|
29329
|
-
this.adoptRebuildInGroup(key, fg, snapshot);
|
|
29218
|
+
for (const { control } of pendingControls) {
|
|
29219
|
+
control.clearAsyncValidators();
|
|
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
|
+
}
|
|
29330
29227
|
});
|
|
29331
29228
|
}
|
|
29332
|
-
|
|
29333
|
-
|
|
29334
|
-
|
|
29335
|
-
|
|
29336
|
-
|
|
29337
|
-
|
|
29338
|
-
|
|
29339
|
-
|
|
29340
|
-
|
|
29341
|
-
|
|
29342
|
-
|
|
29343
|
-
|
|
29344
|
-
this.adoptRebuiltGroup(control, snapshot, name, isNewKey, tracked, path);
|
|
29345
|
-
else
|
|
29346
|
-
this.adoptRebuiltField(control, snapshot, name, isNewKey, tracked.get(path));
|
|
29347
|
-
tracked.set(path, control);
|
|
29229
|
+
restoreFormGroup(fg, snapshot) {
|
|
29230
|
+
Object.entries(fg.controls).forEach(([ctrlKey, ctrl]) => {
|
|
29231
|
+
const newValue = snapshot[ctrlKey];
|
|
29232
|
+
if (ctrl instanceof FormArray && Array.isArray(newValue)) {
|
|
29233
|
+
this.resetFormArrayToValues(ctrl, newValue);
|
|
29234
|
+
}
|
|
29235
|
+
else if (ctrl instanceof FormGroup && newValue && typeof newValue === 'object') {
|
|
29236
|
+
this.restoreFormGroup(ctrl, newValue);
|
|
29237
|
+
}
|
|
29238
|
+
else {
|
|
29239
|
+
ctrl.reset(newValue);
|
|
29240
|
+
}
|
|
29348
29241
|
});
|
|
29349
29242
|
}
|
|
29350
|
-
|
|
29351
|
-
|
|
29352
|
-
|
|
29353
|
-
|
|
29243
|
+
collectPendingControls(control) {
|
|
29244
|
+
const result = [];
|
|
29245
|
+
if (control instanceof FormGroup) {
|
|
29246
|
+
Object.values(control.controls).forEach(c => result.push(...this.collectPendingControls(c)));
|
|
29354
29247
|
}
|
|
29355
|
-
if (
|
|
29356
|
-
|
|
29357
|
-
|
|
29358
|
-
|
|
29359
|
-
|
|
29360
|
-
|
|
29361
|
-
|
|
29362
|
-
adoptRebuiltGroup(group, snapshot, name, isNewKey, tracked, path) {
|
|
29363
|
-
if (isNewKey)
|
|
29364
|
-
this.adoptNewPristineControl(group, snapshot, name);
|
|
29365
|
-
const nested = snapshot[name];
|
|
29366
|
-
if (this.isPlainObject(nested))
|
|
29367
|
-
this.adoptRebuiltControls(group, nested, tracked, path);
|
|
29368
|
-
}
|
|
29369
|
-
adoptRebuiltField(control, snapshot, name, isNewKey, known) {
|
|
29370
|
-
if (isNewKey)
|
|
29371
|
-
this.adoptNewPristineControl(control, snapshot, name);
|
|
29372
|
-
else if (this.isReplacedPristineControl(control, known))
|
|
29373
|
-
snapshot[name] = control.getRawValue();
|
|
29374
|
-
}
|
|
29375
|
-
adoptNewPristineControl(control, snapshot, name) {
|
|
29376
|
-
if (control.pristine)
|
|
29377
|
-
snapshot[name] = control.getRawValue();
|
|
29378
|
-
}
|
|
29379
|
-
isAdoptableArrayRebuild(control, snapshot, name) {
|
|
29380
|
-
return this._isAdoptingFrameworkRebuild && control.pristine && Array.isArray(snapshot[name]);
|
|
29381
|
-
}
|
|
29382
|
-
isReplacedPristineControl(control, known) {
|
|
29383
|
-
return this._isAdoptingFrameworkRebuild && !!known && known !== control && control.pristine;
|
|
29384
|
-
}
|
|
29385
|
-
rememberControlInstances(key, formGroup) {
|
|
29386
|
-
const tracked = new Map();
|
|
29387
|
-
this.collectControlInstances(formGroup, tracked, '');
|
|
29388
|
-
this._knownControlInstances.set(key, tracked);
|
|
29389
|
-
}
|
|
29390
|
-
collectControlInstances(group, tracked, prefix) {
|
|
29391
|
-
Object.entries(group.controls).forEach(([name, control]) => {
|
|
29392
|
-
const path = prefix ? `${prefix}.${name}` : name;
|
|
29393
|
-
tracked.set(path, control);
|
|
29394
|
-
if (control instanceof FormGroup)
|
|
29395
|
-
this.collectControlInstances(control, tracked, path);
|
|
29396
|
-
});
|
|
29397
|
-
}
|
|
29398
|
-
restoreFormGroup(fg, snapshot) {
|
|
29399
|
-
Object.entries(fg.controls).forEach(([ctrlKey, ctrl]) => this.restoreControl(ctrl, snapshot[ctrlKey]));
|
|
29400
|
-
}
|
|
29401
|
-
restoreControl(ctrl, newValue) {
|
|
29402
|
-
if (ctrl instanceof FormArray && Array.isArray(newValue))
|
|
29403
|
-
this.resetFormArrayToValues(ctrl, newValue);
|
|
29404
|
-
else if (ctrl instanceof FormGroup && this.isPlainObject(newValue))
|
|
29405
|
-
this.restoreFormGroup(ctrl, newValue);
|
|
29406
|
-
else
|
|
29407
|
-
ctrl.reset(newValue);
|
|
29408
|
-
}
|
|
29409
|
-
isPlainObject(value) {
|
|
29410
|
-
return !!value && typeof value === 'object';
|
|
29248
|
+
else if (control instanceof FormArray) {
|
|
29249
|
+
control.controls.forEach(c => result.push(...this.collectPendingControls(c)));
|
|
29250
|
+
}
|
|
29251
|
+
if (control.status === 'PENDING') {
|
|
29252
|
+
result.push({ control, asyncValidator: control.asyncValidator });
|
|
29253
|
+
}
|
|
29254
|
+
return result;
|
|
29411
29255
|
}
|
|
29412
29256
|
resetFormArrayToValues(array, newValues) {
|
|
29413
29257
|
if (!Array.isArray(newValues))
|
|
29414
29258
|
return;
|
|
29415
|
-
|
|
29259
|
+
const oldLen = array.length;
|
|
29260
|
+
const newLen = newValues.length;
|
|
29261
|
+
if (oldLen === 0 && newLen === 0)
|
|
29416
29262
|
return;
|
|
29417
|
-
if (
|
|
29263
|
+
if (oldLen === 0 && newLen > 0)
|
|
29418
29264
|
return this.initFormArrayFromValues(array, newValues);
|
|
29419
|
-
|
|
29265
|
+
while (array.length < newLen)
|
|
29266
|
+
array.push(this.deepCloneControl(array.at(0)));
|
|
29267
|
+
while (array.length > newLen)
|
|
29268
|
+
array.removeAt(array.length - 1);
|
|
29420
29269
|
array.reset(newValues);
|
|
29421
29270
|
}
|
|
29422
29271
|
initFormArrayFromValues(array, values) {
|
|
@@ -29432,12 +29281,6 @@ class QdFormGroupManagerService {
|
|
|
29432
29281
|
}
|
|
29433
29282
|
array.reset(values);
|
|
29434
29283
|
}
|
|
29435
|
-
resizeFormArray(array, length) {
|
|
29436
|
-
while (array.length < length)
|
|
29437
|
-
array.push(this.deepCloneControl(array.at(0)));
|
|
29438
|
-
while (array.length > length)
|
|
29439
|
-
array.removeAt(array.length - 1);
|
|
29440
|
-
}
|
|
29441
29284
|
deepCloneControl(control) {
|
|
29442
29285
|
if (control instanceof FormControl)
|
|
29443
29286
|
return new FormControl(undefined, control.validator, control.asyncValidator);
|
|
@@ -29455,19 +29298,6 @@ class QdFormGroupManagerService {
|
|
|
29455
29298
|
areFormGroupsValid(fg) {
|
|
29456
29299
|
return Object.values(fg.controls).every(c => (c.disabled && !c.validator && !c.asyncValidator ? true : c.valid));
|
|
29457
29300
|
}
|
|
29458
|
-
collectPendingControls(control) {
|
|
29459
|
-
const result = this.childControlsOf(control).flatMap(child => this.collectPendingControls(child));
|
|
29460
|
-
if (control.status === 'PENDING')
|
|
29461
|
-
result.push({ control, asyncValidator: control.asyncValidator });
|
|
29462
|
-
return result;
|
|
29463
|
-
}
|
|
29464
|
-
childControlsOf(control) {
|
|
29465
|
-
if (control instanceof FormGroup)
|
|
29466
|
-
return Object.values(control.controls);
|
|
29467
|
-
if (control instanceof FormArray)
|
|
29468
|
-
return control.controls;
|
|
29469
|
-
return [];
|
|
29470
|
-
}
|
|
29471
29301
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFormGroupManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
29472
29302
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdFormGroupManagerService });
|
|
29473
29303
|
}
|
|
@@ -29496,7 +29326,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
29496
29326
|
* - The page switches to view mode via `deactivate()`.
|
|
29497
29327
|
*
|
|
29498
29328
|
* The service has no concept of "pending actions" or bypass windows. Framework actions update the
|
|
29499
|
-
* saved snapshot directly via `QdFormGroupManagerService.
|
|
29329
|
+
* saved snapshot directly via `QdFormGroupManagerService.setFormGroupsSnapshot(...)`; cancel-discard
|
|
29500
29330
|
* flows reset the form to the saved snapshot via `restoreFormGroupsFromSnapshot()`. Either way, the
|
|
29501
29331
|
* interceptor only observes the resulting form state — there is no time window where a bypass is active.
|
|
29502
29332
|
*/
|
|
@@ -29646,7 +29476,6 @@ class QdPageObjectHeaderComponent {
|
|
|
29646
29476
|
confirmationDialogService = inject(QdConfirmationDialogOpenerService);
|
|
29647
29477
|
contextService = inject(QdContextService);
|
|
29648
29478
|
resolverTriggerService = inject(QdResolverTriggerService);
|
|
29649
|
-
injector = inject(Injector);
|
|
29650
29479
|
pageStoreService = inject(QdPageStoreService);
|
|
29651
29480
|
config;
|
|
29652
29481
|
hasNavigation = false;
|
|
@@ -29767,7 +29596,7 @@ class QdPageObjectHeaderComponent {
|
|
|
29767
29596
|
this.setupResolverTrigger();
|
|
29768
29597
|
this.updateCustomActions();
|
|
29769
29598
|
this.subscribeToMetadataStream();
|
|
29770
|
-
this.formGroupManagerService.
|
|
29599
|
+
this.formGroupManagerService.takeFormGroupsSnapshot();
|
|
29771
29600
|
this.initContexts();
|
|
29772
29601
|
}
|
|
29773
29602
|
ngOnChanges(changes) {
|
|
@@ -29792,7 +29621,7 @@ class QdPageObjectHeaderComponent {
|
|
|
29792
29621
|
return this.config.headerFacets != null;
|
|
29793
29622
|
}
|
|
29794
29623
|
edit() {
|
|
29795
|
-
this.formGroupManagerService.
|
|
29624
|
+
this.formGroupManagerService.takeFormGroupsSnapshot();
|
|
29796
29625
|
this.pageStoreService.toggleViewonly(false);
|
|
29797
29626
|
if (this.editButton?.handler)
|
|
29798
29627
|
this.editButton.handler();
|
|
@@ -29822,8 +29651,7 @@ class QdPageObjectHeaderComponent {
|
|
|
29822
29651
|
onAfterSnapshot: () => {
|
|
29823
29652
|
this.formGroupManagerService.cancelPendingAsyncValidation();
|
|
29824
29653
|
this.pageStoreService.toggleViewonly(true);
|
|
29825
|
-
}
|
|
29826
|
-
scheduleFrameworkRebuildStop: () => afterNextRender(() => this.formGroupManagerService.stopAdoptingFrameworkRebuild(), { injector: this.injector })
|
|
29654
|
+
}
|
|
29827
29655
|
});
|
|
29828
29656
|
}
|
|
29829
29657
|
changeContext(context, selection, event) {
|
|
@@ -29851,11 +29679,13 @@ class QdPageObjectHeaderComponent {
|
|
|
29851
29679
|
.pipe(takeUntil(this._destroyed$), filter(shouldTrigger => shouldTrigger), tap(() => this._isLoadingSubject.next(true)), switchMap(() => this.pageObjectResolver.resolve()), tap(objectData => {
|
|
29852
29680
|
this._pageObjectDataSubject.next({ ...objectData, ...this._pendingMetadata });
|
|
29853
29681
|
this._pendingMetadata = {};
|
|
29854
|
-
}), tap(() => this._isLoadingSubject.next(false)), tap(() => this.formGroupManagerService.
|
|
29682
|
+
}), tap(() => this._isLoadingSubject.next(false)), tap(() => this.formGroupManagerService.takeFormGroupsSnapshot()))
|
|
29855
29683
|
.subscribe();
|
|
29856
29684
|
}
|
|
29857
29685
|
initContexts() {
|
|
29858
|
-
this.contexts$ = this.contextService.contexts$.pipe(map(contexts =>
|
|
29686
|
+
this.contexts$ = this.contextService.contexts$.pipe(map(contexts => this.config.hideContexts
|
|
29687
|
+
? []
|
|
29688
|
+
: contexts.filter(context => context.selection || this.config.pageType === 'overview')), map(contexts => contexts.map(({ selection, context }) => ({
|
|
29859
29689
|
label: context.label.i18n,
|
|
29860
29690
|
value: Array.isArray(selection?.value)
|
|
29861
29691
|
? selection?.value.filter(item => item !== null && item !== undefined)
|
|
@@ -29941,11 +29771,11 @@ class QdPageObjectHeaderComponent {
|
|
|
29941
29771
|
});
|
|
29942
29772
|
}
|
|
29943
29773
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageObjectHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
29944
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdPageObjectHeaderComponent, isStandalone: false, selector: "qd-page-object-header", inputs: { config: "config", hasNavigation: "hasNavigation", testId: ["data-test-id", "testId"] }, host: { listeners: { "click": "toggleFacets()" }, properties: { "style.borderBottom": "borderBottomStyle", "class.in-dialog": "this.isInDialog" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"loading-overlay\" *ngIf=\"isLoading$ | async\"><qd-spinner></qd-spinner></div>\n\n<div class=\"object-header-top\">\n <header class=\"object-header-title\">{{ config.title.i18n | translate }}</header>\n <div class=\"object-header-toolbar\">\n <button class=\"button-icon\" data-test-id=\"archive-button\">\n <qd-icon\n *ngIf=\"showArchive$ | async\"\n (click)=\"archiveButton?.handler()\"\n class=\"header-action-icon\"\n [icon]=\"'resultNew'\"\n ></qd-icon>\n </button>\n\n <button class=\"button-icon\" data-test-id=\"delete-button\">\n <qd-icon\n class=\"header-action-icon\"\n *ngIf=\"showDelete$ | async\"\n (click)=\"deleteButton?.handler()\"\n [icon]=\"'trash'\"\n ></qd-icon>\n </button>\n\n <button *ngIf=\"showEdit$ | async\" qdButton (click)=\"edit()\" data-test-id=\"edit-button\">\n {{ editButton?.label?.i18n || \"i18n.qd.page.header.edit\" | translate }}\n </button>\n\n <button *ngIf=\"showCancel$ | async\" qdButton qdButtonGhost (click)=\"cancel()\" data-test-id=\"cancel-button\">\n {{ cancelButton.label?.i18n || \"i18n.qd.page.header.cancel\" | translate }}\n </button>\n\n <button\n *ngIf=\"showSave$ | async\"\n qdButton\n (click)=\"save()\"\n data-test-id=\"save-button\"\n [disabled]=\"($canSave | async) === false\"\n >\n {{ saveButton.label?.i18n || \"i18n.qd.page.header.save\" | translate }}\n </button>\n\n <ng-container *ngIf=\"customActions$ | async as customActions\">\n <ng-container *ngIf=\"!adaptiveMode; else adaptive\">\n <button\n [qdMenuButton]=\"customActions\"\n [autoSize]=\"false\"\n [data-test-id]=\"'page-'\"\n *ngIf=\"customActions.actions.length > 1; else singleButton\"\n data-test-id=\"custom-button\"\n >\n {{ config?.customActionsLabel?.i18n || \"i18n.qd.page.header.actions\" | translate }}\n </button>\n\n <ng-template #singleButton>\n <ng-container *ngIf=\"(customActions$ | async).actions[0] as singleAction\">\n <button\n qdButton\n *ngIf=\"singleAction.handler && !singleAction.isHidden\"\n [disabled]=\"singleAction.isDisabled\"\n (click)=\"singleAction.handler()\"\n data-test-id=\"custom-button\"\n >\n {{ singleAction.label?.i18n | translate }}\n </button>\n </ng-container>\n </ng-template>\n </ng-container>\n\n <ng-template #adaptive>\n <button\n *ngIf=\"(customActions$ | async)?.actions.length >= 1\"\n type=\"button\"\n class=\"adaptive-menu-button\"\n [qdPopoverOnClick]=\"menu\"\n [qdPopoverCloseStrategy]=\"'onEveryClick'\"\n [qdPopoverStopPropagation]=\"true\"\n [qdPopoverMinWidth]=\"160\"\n >\n <qd-icon icon=\"overflowMenuVertical\"></qd-icon>\n </button>\n\n <ng-template #menu>\n <button\n *ngFor=\"let action of customActions.actions; let i = index\"\n class=\"adaptive-menu-entry\"\n type=\"button\"\n [disabled]=\"action.isDisabled\"\n (click)=\"action.handler()\"\n >\n {{ action.label.i18n | translate }}\n </button>\n </ng-template>\n </ng-template>\n </ng-container>\n </div>\n</div>\n\n<div class=\"object-header-facets\" [ngClass]=\"{ amongThemselves: adaptiveMode && !facetsCollapsed }\">\n <ng-container *ngIf=\"pageObjectData$ | async as data\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"let facet of headerFacets | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 : headerFacets?.length ?? 0)\"\n [attr.data-test-id]=\"testId + '-facet-' + facet.name\"\n >\n <div class=\"facet-label\">{{ facet.label.i18n | translate }}</div>\n <qd-dynamic-facet class=\"dynamic-facet\" [facet]=\"facet\" [data]=\"data\"></qd-dynamic-facet>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"!config.hideContexts && contexts$ | async as contexts\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"\n let context of contexts\n | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 - (headerFacets?.length ?? 0) : contexts?.length);\n let index = index;\n let first = first\n \"\n [ngClass]=\"{ hasDivider: headerFacets && first, adaptiveFirst: headerFacets && adaptiveMode && first }\"\n >\n <div class=\"facet-label\">{{ context.label | translate }}</div>\n\n <span *ngIf=\"context.value.length === 0\" class=\"no-selected-context\">{{\n \"i18n.qd.page.context.noSelectionPlaceholder\" | translate\n }}</span>\n\n <span *ngIf=\"context.value.length !== 0\">\n <qd-chip state=\"none\" [close]=\"isContextSelectable\" (closeClickEmitter)=\"clearContext(context.context)\">\n <ng-container *ngFor=\"let val of context.value; let last = last\">\n {{ val.label.i18n | translate }}<span *ngIf=\"!last\">, </span>\n </ng-container>\n </qd-chip>\n </span>\n\n <span *ngIf=\"isContextSelectable && (!facetsCollapsed || !adaptiveMode)\" class=\"select-button\">\n <button\n qdButton\n qdButtonLink\n type=\"button\"\n (click)=\"changeContext(context.context, context.selection, $event)\"\n [data-test-id]=\"'object-header-select-context-' + context.context.id\"\n >\n {{\n context.value.length !== 0\n ? (\"i18n.qd.page.context.button.change\" | translate)\n : (\"i18n.qd.page.context.button.select\" | translate)\n }}\n </button>\n </span>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"contexts$ | async as togglerContexts\">\n <span class=\"facets-toggler\" *ngIf=\"adaptiveMode && headerFacets?.length + togglerContexts.length > 2\">\n <span *ngIf=\"facetsCollapsed\">+{{ headerFacets.length + togglerContexts.length - 2 }}</span>\n <button qdIconButton [data-test-id]=\"'toggle-button'\">\n <qd-icon [icon]=\"facetsCollapsed ? 'ctrlDown' : 'ctrlTop'\"></qd-icon>\n </button>\n </span>\n </ng-container>\n</div>\n", styles: [":host{display:flex;flex-direction:column;justify-content:space-between;padding:1.25rem;border-bottom:.125rem solid rgb(213,213,213);background:#fff;gap:1.25rem}@media (max-width: 599.98px){:host{padding:1.25rem .9375rem}}:host .object-header-top{display:flex;align-items:flex-start;justify-content:space-between}:host .object-header-top .object-header-title{padding:2px;color:#171717;font-size:1.3125rem;font-weight:500}:host .object-header-top .object-header-toolbar{display:flex;align-items:center;column-gap:.5rem}:host .object-header-top .object-header-toolbar .button-icon{background:none}:host .object-header-top .object-header-toolbar .button-icon+.button-icon{margin-left:-.5rem}:host .object-header-top .object-header-toolbar .header-action-icon{color:#14516f;cursor:pointer;font-size:1.5rem}:host.in-dialog .object-header-toolbar{padding-right:2.5rem}:host .object-header-facets{position:relative;display:flex;flex-wrap:wrap;font-size:1.125rem;gap:1.25rem}:host .object-header-facets .object-header-facet{display:flex;min-width:3.75rem;flex-direction:column}:host .object-header-facets .object-header-facet.hasDivider{padding-left:1.25rem;border-left:.0625rem solid rgb(117,117,117)}:host .object-header-facets .object-header-facet.adaptiveFirst{padding-top:1.25rem}:host .object-header-facets .object-header-facet .facet-label{display:block;margin-bottom:0;color:#757575;font-size:.875rem;padding-bottom:.375rem;font-weight:500}:host .object-header-facets .facets-toggler{position:absolute;top:0;right:0;display:flex;align-items:center;padding-right:.3125rem;font-size:.625rem;line-height:.625rem}:host .object-header-facets.amongThemselves{display:grid}:host .object-header-facets.amongThemselves .object-header-facet.hasDivider{padding-left:0;border-top:.0625rem solid rgb(117,117,117);border-left:none}.loading-overlay{position:absolute;z-index:10000;inset:0;display:flex;align-items:center;justify-content:center;background-color:#0003}.no-selected-context{height:1.625rem;color:#979797;font-size:.875rem;line-height:1.625rem}.select-button{margin-top:.25rem}.adaptive-menu-button{display:flex;background:unset;color:#454545;font-size:2rem}.adaptive-menu-button:hover,.adaptive-menu-button:focus{color:#000}.adaptive-menu-entry{display:block;overflow:hidden;width:100%;min-height:2.25rem;padding:0 1rem;background:#fff0;text-align:left;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { 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: "directive", type: QdButtonLinkDirective, selector: "button[qdButtonLink], a[qdButtonLink], button[qd-button-link]" }, { kind: "component", type: QdIconButtonComponent, selector: "button[qdIconButton], a[qdIconButton], button[qd-icon-button]", inputs: ["color", "data-test-id"] }, { kind: "component", type: QdMenuButtonComponent, selector: "button[qdMenuButton], a[qdMenuButton]", inputs: ["qdMenuButton", "data-test-id", "autoSize"] }, { kind: "component", type: QdChipComponent, selector: "qd-chip", inputs: ["state", "close", "data", "data-test-id"], outputs: ["closeClickEmitter"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon", "status"] }, { kind: "component", type: QdSpinnerComponent, selector: "qd-spinner" }, { kind: "directive", type: QdPopoverOnClickDirective, selector: "[qdPopoverOnClick]", inputs: ["qdPopoverOnClick", "qdPopoverPlacement", "qdPopoverOffsetX", "qdPopoverOffsetY", "qdPopoverCloseStrategy", "qdPopoverDisabled", "qdPopoverStopPropagation", "qdPopoverBackgroundColor", "qdPopoverMaxHeight", "qdPopoverMinWidth", "qdPopoverMaxWidth", "qdPopoverAutoSize", "qdPopoverEnableKeyControl", "qdPopoverFlipIntoViewport"], outputs: ["opened", "closed"], exportAs: ["qdPopoverOnClick"] }, { kind: "component", type: QdDynamicFacetComponent, selector: "qd-dynamic-facet", inputs: ["facet", "data"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.SlicePipe, name: "slice" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
29774
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: QdPageObjectHeaderComponent, isStandalone: false, selector: "qd-page-object-header", inputs: { config: "config", hasNavigation: "hasNavigation", testId: ["data-test-id", "testId"] }, host: { listeners: { "click": "toggleFacets()" }, properties: { "style.borderBottom": "borderBottomStyle", "class.in-dialog": "this.isInDialog" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"loading-overlay\" *ngIf=\"isLoading$ | async\"><qd-spinner></qd-spinner></div>\n\n<div class=\"object-header-top\">\n <header class=\"object-header-title\">{{ config.title.i18n | translate }}</header>\n <div class=\"object-header-toolbar\">\n <button class=\"button-icon\" data-test-id=\"archive-button\">\n <qd-icon\n *ngIf=\"showArchive$ | async\"\n (click)=\"archiveButton?.handler()\"\n class=\"header-action-icon\"\n [icon]=\"'resultNew'\"\n ></qd-icon>\n </button>\n\n <button class=\"button-icon\" data-test-id=\"delete-button\">\n <qd-icon\n class=\"header-action-icon\"\n *ngIf=\"showDelete$ | async\"\n (click)=\"deleteButton?.handler()\"\n [icon]=\"'trash'\"\n ></qd-icon>\n </button>\n\n <button *ngIf=\"showEdit$ | async\" qdButton (click)=\"edit()\" data-test-id=\"edit-button\">\n {{ editButton?.label?.i18n || \"i18n.qd.page.header.edit\" | translate }}\n </button>\n\n <button *ngIf=\"showCancel$ | async\" qdButton qdButtonGhost (click)=\"cancel()\" data-test-id=\"cancel-button\">\n {{ cancelButton.label?.i18n || \"i18n.qd.page.header.cancel\" | translate }}\n </button>\n\n <button\n *ngIf=\"showSave$ | async\"\n qdButton\n (click)=\"save()\"\n data-test-id=\"save-button\"\n [disabled]=\"($canSave | async) === false\"\n >\n {{ saveButton.label?.i18n || \"i18n.qd.page.header.save\" | translate }}\n </button>\n\n <ng-container *ngIf=\"customActions$ | async as customActions\">\n <ng-container *ngIf=\"!adaptiveMode; else adaptive\">\n <button\n [qdMenuButton]=\"customActions\"\n [autoSize]=\"false\"\n [data-test-id]=\"'page-'\"\n *ngIf=\"customActions.actions.length > 1; else singleButton\"\n data-test-id=\"custom-button\"\n >\n {{ config?.customActionsLabel?.i18n || \"i18n.qd.page.header.actions\" | translate }}\n </button>\n\n <ng-template #singleButton>\n <ng-container *ngIf=\"(customActions$ | async).actions[0] as singleAction\">\n <button\n qdButton\n *ngIf=\"singleAction.handler && !singleAction.isHidden\"\n [disabled]=\"singleAction.isDisabled\"\n (click)=\"singleAction.handler()\"\n data-test-id=\"custom-button\"\n >\n {{ singleAction.label?.i18n | translate }}\n </button>\n </ng-container>\n </ng-template>\n </ng-container>\n\n <ng-template #adaptive>\n <button\n *ngIf=\"(customActions$ | async)?.actions.length >= 1\"\n type=\"button\"\n class=\"adaptive-menu-button\"\n [qdPopoverOnClick]=\"menu\"\n [qdPopoverCloseStrategy]=\"'onEveryClick'\"\n [qdPopoverStopPropagation]=\"true\"\n [qdPopoverMinWidth]=\"160\"\n >\n <qd-icon icon=\"overflowMenuVertical\"></qd-icon>\n </button>\n\n <ng-template #menu>\n <button\n *ngFor=\"let action of customActions.actions; let i = index\"\n class=\"adaptive-menu-entry\"\n type=\"button\"\n [disabled]=\"action.isDisabled\"\n (click)=\"action.handler()\"\n >\n {{ action.label.i18n | translate }}\n </button>\n </ng-template>\n </ng-template>\n </ng-container>\n </div>\n</div>\n\n<div class=\"object-header-facets\" [ngClass]=\"{ amongThemselves: adaptiveMode && !facetsCollapsed }\">\n <ng-container *ngIf=\"pageObjectData$ | async as data\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"let facet of headerFacets | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 : headerFacets?.length ?? 0)\"\n [attr.data-test-id]=\"testId + '-facet-' + facet.name\"\n >\n <div class=\"facet-label\">{{ facet.label.i18n | translate }}</div>\n <qd-dynamic-facet class=\"dynamic-facet\" [facet]=\"facet\" [data]=\"data\"></qd-dynamic-facet>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"!config.hideContexts && contexts$ | async as contexts\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"\n let context of contexts\n | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 - (headerFacets?.length ?? 0) : contexts?.length);\n let index = index;\n let first = first\n \"\n [ngClass]=\"{ hasDivider: headerFacets && first, adaptiveFirst: headerFacets && adaptiveMode && first }\"\n >\n <div class=\"facet-label\">{{ context.label | translate }}</div>\n\n <span *ngIf=\"context.value.length === 0\" class=\"no-selected-context\">{{\n \"i18n.qd.page.context.noSelectionPlaceholder\" | translate\n }}</span>\n\n <span *ngIf=\"context.value.length !== 0\">\n <qd-chip state=\"none\" [close]=\"isContextSelectable\" (closeClickEmitter)=\"clearContext(context.context)\">\n <ng-container *ngFor=\"let val of context.value; let last = last\">\n {{ val.label.i18n | translate }}<span *ngIf=\"!last\">, </span>\n </ng-container>\n </qd-chip>\n </span>\n\n <span *ngIf=\"isContextSelectable && (!facetsCollapsed || !adaptiveMode)\" class=\"select-button\">\n <button\n qdButton\n qdButtonLink\n type=\"button\"\n (click)=\"changeContext(context.context, context.selection, $event)\"\n [data-test-id]=\"'object-header-select-context-' + context.context.id\"\n >\n {{\n context.value.length !== 0\n ? (\"i18n.qd.page.context.button.change\" | translate)\n : (\"i18n.qd.page.context.button.select\" | translate)\n }}\n </button>\n </span>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"contexts$ | async as togglerContexts\">\n <span\n class=\"facets-toggler\"\n [attr.data-test-id]=\"testId + '-facets-toggler'\"\n *ngIf=\"adaptiveMode && headerFacets?.length + togglerContexts.length > 2\"\n >\n <span *ngIf=\"facetsCollapsed\">+{{ headerFacets.length + togglerContexts.length - 2 }}</span>\n <button qdIconButton [data-test-id]=\"'toggle-button'\">\n <qd-icon [icon]=\"facetsCollapsed ? 'ctrlDown' : 'ctrlTop'\"></qd-icon>\n </button>\n </span>\n </ng-container>\n</div>\n", styles: [":host{display:flex;flex-direction:column;justify-content:space-between;padding:1.25rem;border-bottom:.125rem solid rgb(213,213,213);background:#fff;gap:1.25rem}@media (max-width: 599.98px){:host{padding:1.25rem .9375rem}}:host .object-header-top{display:flex;align-items:flex-start;justify-content:space-between}:host .object-header-top .object-header-title{padding:2px;color:#171717;font-size:1.3125rem;font-weight:500}:host .object-header-top .object-header-toolbar{display:flex;align-items:center;column-gap:.5rem}:host .object-header-top .object-header-toolbar .button-icon{background:none}:host .object-header-top .object-header-toolbar .button-icon+.button-icon{margin-left:-.5rem}:host .object-header-top .object-header-toolbar .header-action-icon{color:#14516f;cursor:pointer;font-size:1.5rem}:host.in-dialog .object-header-toolbar{padding-right:2.5rem}:host .object-header-facets{position:relative;display:flex;flex-wrap:wrap;font-size:1.125rem;gap:1.25rem}:host .object-header-facets .object-header-facet{display:flex;min-width:3.75rem;flex-direction:column}:host .object-header-facets .object-header-facet.hasDivider{padding-left:1.25rem;border-left:.0625rem solid rgb(117,117,117)}:host .object-header-facets .object-header-facet.adaptiveFirst{padding-top:1.25rem}:host .object-header-facets .object-header-facet .facet-label{display:block;margin-bottom:0;color:#757575;font-size:.875rem;padding-bottom:.375rem;font-weight:500}:host .object-header-facets .facets-toggler{position:absolute;top:0;right:0;display:flex;align-items:center;padding-right:.3125rem;font-size:.625rem;line-height:.625rem}:host .object-header-facets.amongThemselves{display:grid}:host .object-header-facets.amongThemselves .object-header-facet.hasDivider{padding-left:0;border-top:.0625rem solid rgb(117,117,117);border-left:none}.loading-overlay{position:absolute;z-index:10000;inset:0;display:flex;align-items:center;justify-content:center;background-color:#0003}.no-selected-context{height:1.625rem;color:#979797;font-size:.875rem;line-height:1.625rem}.select-button{margin-top:.25rem}.adaptive-menu-button{display:flex;background:unset;color:#454545;font-size:2rem}.adaptive-menu-button:hover,.adaptive-menu-button:focus{color:#000}.adaptive-menu-entry{display:block;overflow:hidden;width:100%;min-height:2.25rem;padding:0 1rem;background:#fff0;text-align:left;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { 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: "directive", type: QdButtonLinkDirective, selector: "button[qdButtonLink], a[qdButtonLink], button[qd-button-link]" }, { kind: "component", type: QdIconButtonComponent, selector: "button[qdIconButton], a[qdIconButton], button[qd-icon-button]", inputs: ["color", "data-test-id"] }, { kind: "component", type: QdMenuButtonComponent, selector: "button[qdMenuButton], a[qdMenuButton]", inputs: ["qdMenuButton", "data-test-id", "autoSize"] }, { kind: "component", type: QdChipComponent, selector: "qd-chip", inputs: ["state", "close", "data", "data-test-id"], outputs: ["closeClickEmitter"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon", "status"] }, { kind: "component", type: QdSpinnerComponent, selector: "qd-spinner" }, { kind: "directive", type: QdPopoverOnClickDirective, selector: "[qdPopoverOnClick]", inputs: ["qdPopoverOnClick", "qdPopoverPlacement", "qdPopoverOffsetX", "qdPopoverOffsetY", "qdPopoverCloseStrategy", "qdPopoverDisabled", "qdPopoverStopPropagation", "qdPopoverBackgroundColor", "qdPopoverMaxHeight", "qdPopoverMinWidth", "qdPopoverMaxWidth", "qdPopoverAutoSize", "qdPopoverEnableKeyControl", "qdPopoverFlipIntoViewport"], outputs: ["opened", "closed"], exportAs: ["qdPopoverOnClick"] }, { kind: "component", type: QdDynamicFacetComponent, selector: "qd-dynamic-facet", inputs: ["facet", "data"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.SlicePipe, name: "slice" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
29945
29775
|
}
|
|
29946
29776
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageObjectHeaderComponent, decorators: [{
|
|
29947
29777
|
type: Component,
|
|
29948
|
-
args: [{ selector: 'qd-page-object-header', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[style.borderBottom]': 'borderBottomStyle' }, standalone: false, template: "<div class=\"loading-overlay\" *ngIf=\"isLoading$ | async\"><qd-spinner></qd-spinner></div>\n\n<div class=\"object-header-top\">\n <header class=\"object-header-title\">{{ config.title.i18n | translate }}</header>\n <div class=\"object-header-toolbar\">\n <button class=\"button-icon\" data-test-id=\"archive-button\">\n <qd-icon\n *ngIf=\"showArchive$ | async\"\n (click)=\"archiveButton?.handler()\"\n class=\"header-action-icon\"\n [icon]=\"'resultNew'\"\n ></qd-icon>\n </button>\n\n <button class=\"button-icon\" data-test-id=\"delete-button\">\n <qd-icon\n class=\"header-action-icon\"\n *ngIf=\"showDelete$ | async\"\n (click)=\"deleteButton?.handler()\"\n [icon]=\"'trash'\"\n ></qd-icon>\n </button>\n\n <button *ngIf=\"showEdit$ | async\" qdButton (click)=\"edit()\" data-test-id=\"edit-button\">\n {{ editButton?.label?.i18n || \"i18n.qd.page.header.edit\" | translate }}\n </button>\n\n <button *ngIf=\"showCancel$ | async\" qdButton qdButtonGhost (click)=\"cancel()\" data-test-id=\"cancel-button\">\n {{ cancelButton.label?.i18n || \"i18n.qd.page.header.cancel\" | translate }}\n </button>\n\n <button\n *ngIf=\"showSave$ | async\"\n qdButton\n (click)=\"save()\"\n data-test-id=\"save-button\"\n [disabled]=\"($canSave | async) === false\"\n >\n {{ saveButton.label?.i18n || \"i18n.qd.page.header.save\" | translate }}\n </button>\n\n <ng-container *ngIf=\"customActions$ | async as customActions\">\n <ng-container *ngIf=\"!adaptiveMode; else adaptive\">\n <button\n [qdMenuButton]=\"customActions\"\n [autoSize]=\"false\"\n [data-test-id]=\"'page-'\"\n *ngIf=\"customActions.actions.length > 1; else singleButton\"\n data-test-id=\"custom-button\"\n >\n {{ config?.customActionsLabel?.i18n || \"i18n.qd.page.header.actions\" | translate }}\n </button>\n\n <ng-template #singleButton>\n <ng-container *ngIf=\"(customActions$ | async).actions[0] as singleAction\">\n <button\n qdButton\n *ngIf=\"singleAction.handler && !singleAction.isHidden\"\n [disabled]=\"singleAction.isDisabled\"\n (click)=\"singleAction.handler()\"\n data-test-id=\"custom-button\"\n >\n {{ singleAction.label?.i18n | translate }}\n </button>\n </ng-container>\n </ng-template>\n </ng-container>\n\n <ng-template #adaptive>\n <button\n *ngIf=\"(customActions$ | async)?.actions.length >= 1\"\n type=\"button\"\n class=\"adaptive-menu-button\"\n [qdPopoverOnClick]=\"menu\"\n [qdPopoverCloseStrategy]=\"'onEveryClick'\"\n [qdPopoverStopPropagation]=\"true\"\n [qdPopoverMinWidth]=\"160\"\n >\n <qd-icon icon=\"overflowMenuVertical\"></qd-icon>\n </button>\n\n <ng-template #menu>\n <button\n *ngFor=\"let action of customActions.actions; let i = index\"\n class=\"adaptive-menu-entry\"\n type=\"button\"\n [disabled]=\"action.isDisabled\"\n (click)=\"action.handler()\"\n >\n {{ action.label.i18n | translate }}\n </button>\n </ng-template>\n </ng-template>\n </ng-container>\n </div>\n</div>\n\n<div class=\"object-header-facets\" [ngClass]=\"{ amongThemselves: adaptiveMode && !facetsCollapsed }\">\n <ng-container *ngIf=\"pageObjectData$ | async as data\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"let facet of headerFacets | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 : headerFacets?.length ?? 0)\"\n [attr.data-test-id]=\"testId + '-facet-' + facet.name\"\n >\n <div class=\"facet-label\">{{ facet.label.i18n | translate }}</div>\n <qd-dynamic-facet class=\"dynamic-facet\" [facet]=\"facet\" [data]=\"data\"></qd-dynamic-facet>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"!config.hideContexts && contexts$ | async as contexts\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"\n let context of contexts\n | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 - (headerFacets?.length ?? 0) : contexts?.length);\n let index = index;\n let first = first\n \"\n [ngClass]=\"{ hasDivider: headerFacets && first, adaptiveFirst: headerFacets && adaptiveMode && first }\"\n >\n <div class=\"facet-label\">{{ context.label | translate }}</div>\n\n <span *ngIf=\"context.value.length === 0\" class=\"no-selected-context\">{{\n \"i18n.qd.page.context.noSelectionPlaceholder\" | translate\n }}</span>\n\n <span *ngIf=\"context.value.length !== 0\">\n <qd-chip state=\"none\" [close]=\"isContextSelectable\" (closeClickEmitter)=\"clearContext(context.context)\">\n <ng-container *ngFor=\"let val of context.value; let last = last\">\n {{ val.label.i18n | translate }}<span *ngIf=\"!last\">, </span>\n </ng-container>\n </qd-chip>\n </span>\n\n <span *ngIf=\"isContextSelectable && (!facetsCollapsed || !adaptiveMode)\" class=\"select-button\">\n <button\n qdButton\n qdButtonLink\n type=\"button\"\n (click)=\"changeContext(context.context, context.selection, $event)\"\n [data-test-id]=\"'object-header-select-context-' + context.context.id\"\n >\n {{\n context.value.length !== 0\n ? (\"i18n.qd.page.context.button.change\" | translate)\n : (\"i18n.qd.page.context.button.select\" | translate)\n }}\n </button>\n </span>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"contexts$ | async as togglerContexts\">\n <span
|
|
29778
|
+
args: [{ selector: 'qd-page-object-header', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[style.borderBottom]': 'borderBottomStyle' }, standalone: false, template: "<div class=\"loading-overlay\" *ngIf=\"isLoading$ | async\"><qd-spinner></qd-spinner></div>\n\n<div class=\"object-header-top\">\n <header class=\"object-header-title\">{{ config.title.i18n | translate }}</header>\n <div class=\"object-header-toolbar\">\n <button class=\"button-icon\" data-test-id=\"archive-button\">\n <qd-icon\n *ngIf=\"showArchive$ | async\"\n (click)=\"archiveButton?.handler()\"\n class=\"header-action-icon\"\n [icon]=\"'resultNew'\"\n ></qd-icon>\n </button>\n\n <button class=\"button-icon\" data-test-id=\"delete-button\">\n <qd-icon\n class=\"header-action-icon\"\n *ngIf=\"showDelete$ | async\"\n (click)=\"deleteButton?.handler()\"\n [icon]=\"'trash'\"\n ></qd-icon>\n </button>\n\n <button *ngIf=\"showEdit$ | async\" qdButton (click)=\"edit()\" data-test-id=\"edit-button\">\n {{ editButton?.label?.i18n || \"i18n.qd.page.header.edit\" | translate }}\n </button>\n\n <button *ngIf=\"showCancel$ | async\" qdButton qdButtonGhost (click)=\"cancel()\" data-test-id=\"cancel-button\">\n {{ cancelButton.label?.i18n || \"i18n.qd.page.header.cancel\" | translate }}\n </button>\n\n <button\n *ngIf=\"showSave$ | async\"\n qdButton\n (click)=\"save()\"\n data-test-id=\"save-button\"\n [disabled]=\"($canSave | async) === false\"\n >\n {{ saveButton.label?.i18n || \"i18n.qd.page.header.save\" | translate }}\n </button>\n\n <ng-container *ngIf=\"customActions$ | async as customActions\">\n <ng-container *ngIf=\"!adaptiveMode; else adaptive\">\n <button\n [qdMenuButton]=\"customActions\"\n [autoSize]=\"false\"\n [data-test-id]=\"'page-'\"\n *ngIf=\"customActions.actions.length > 1; else singleButton\"\n data-test-id=\"custom-button\"\n >\n {{ config?.customActionsLabel?.i18n || \"i18n.qd.page.header.actions\" | translate }}\n </button>\n\n <ng-template #singleButton>\n <ng-container *ngIf=\"(customActions$ | async).actions[0] as singleAction\">\n <button\n qdButton\n *ngIf=\"singleAction.handler && !singleAction.isHidden\"\n [disabled]=\"singleAction.isDisabled\"\n (click)=\"singleAction.handler()\"\n data-test-id=\"custom-button\"\n >\n {{ singleAction.label?.i18n | translate }}\n </button>\n </ng-container>\n </ng-template>\n </ng-container>\n\n <ng-template #adaptive>\n <button\n *ngIf=\"(customActions$ | async)?.actions.length >= 1\"\n type=\"button\"\n class=\"adaptive-menu-button\"\n [qdPopoverOnClick]=\"menu\"\n [qdPopoverCloseStrategy]=\"'onEveryClick'\"\n [qdPopoverStopPropagation]=\"true\"\n [qdPopoverMinWidth]=\"160\"\n >\n <qd-icon icon=\"overflowMenuVertical\"></qd-icon>\n </button>\n\n <ng-template #menu>\n <button\n *ngFor=\"let action of customActions.actions; let i = index\"\n class=\"adaptive-menu-entry\"\n type=\"button\"\n [disabled]=\"action.isDisabled\"\n (click)=\"action.handler()\"\n >\n {{ action.label.i18n | translate }}\n </button>\n </ng-template>\n </ng-template>\n </ng-container>\n </div>\n</div>\n\n<div class=\"object-header-facets\" [ngClass]=\"{ amongThemselves: adaptiveMode && !facetsCollapsed }\">\n <ng-container *ngIf=\"pageObjectData$ | async as data\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"let facet of headerFacets | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 : headerFacets?.length ?? 0)\"\n [attr.data-test-id]=\"testId + '-facet-' + facet.name\"\n >\n <div class=\"facet-label\">{{ facet.label.i18n | translate }}</div>\n <qd-dynamic-facet class=\"dynamic-facet\" [facet]=\"facet\" [data]=\"data\"></qd-dynamic-facet>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"!config.hideContexts && contexts$ | async as contexts\">\n <div\n class=\"object-header-facet\"\n *ngFor=\"\n let context of contexts\n | slice : 0 : (adaptiveMode && facetsCollapsed ? 2 - (headerFacets?.length ?? 0) : contexts?.length);\n let index = index;\n let first = first\n \"\n [ngClass]=\"{ hasDivider: headerFacets && first, adaptiveFirst: headerFacets && adaptiveMode && first }\"\n >\n <div class=\"facet-label\">{{ context.label | translate }}</div>\n\n <span *ngIf=\"context.value.length === 0\" class=\"no-selected-context\">{{\n \"i18n.qd.page.context.noSelectionPlaceholder\" | translate\n }}</span>\n\n <span *ngIf=\"context.value.length !== 0\">\n <qd-chip state=\"none\" [close]=\"isContextSelectable\" (closeClickEmitter)=\"clearContext(context.context)\">\n <ng-container *ngFor=\"let val of context.value; let last = last\">\n {{ val.label.i18n | translate }}<span *ngIf=\"!last\">, </span>\n </ng-container>\n </qd-chip>\n </span>\n\n <span *ngIf=\"isContextSelectable && (!facetsCollapsed || !adaptiveMode)\" class=\"select-button\">\n <button\n qdButton\n qdButtonLink\n type=\"button\"\n (click)=\"changeContext(context.context, context.selection, $event)\"\n [data-test-id]=\"'object-header-select-context-' + context.context.id\"\n >\n {{\n context.value.length !== 0\n ? (\"i18n.qd.page.context.button.change\" | translate)\n : (\"i18n.qd.page.context.button.select\" | translate)\n }}\n </button>\n </span>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"contexts$ | async as togglerContexts\">\n <span\n class=\"facets-toggler\"\n [attr.data-test-id]=\"testId + '-facets-toggler'\"\n *ngIf=\"adaptiveMode && headerFacets?.length + togglerContexts.length > 2\"\n >\n <span *ngIf=\"facetsCollapsed\">+{{ headerFacets.length + togglerContexts.length - 2 }}</span>\n <button qdIconButton [data-test-id]=\"'toggle-button'\">\n <qd-icon [icon]=\"facetsCollapsed ? 'ctrlDown' : 'ctrlTop'\"></qd-icon>\n </button>\n </span>\n </ng-container>\n</div>\n", styles: [":host{display:flex;flex-direction:column;justify-content:space-between;padding:1.25rem;border-bottom:.125rem solid rgb(213,213,213);background:#fff;gap:1.25rem}@media (max-width: 599.98px){:host{padding:1.25rem .9375rem}}:host .object-header-top{display:flex;align-items:flex-start;justify-content:space-between}:host .object-header-top .object-header-title{padding:2px;color:#171717;font-size:1.3125rem;font-weight:500}:host .object-header-top .object-header-toolbar{display:flex;align-items:center;column-gap:.5rem}:host .object-header-top .object-header-toolbar .button-icon{background:none}:host .object-header-top .object-header-toolbar .button-icon+.button-icon{margin-left:-.5rem}:host .object-header-top .object-header-toolbar .header-action-icon{color:#14516f;cursor:pointer;font-size:1.5rem}:host.in-dialog .object-header-toolbar{padding-right:2.5rem}:host .object-header-facets{position:relative;display:flex;flex-wrap:wrap;font-size:1.125rem;gap:1.25rem}:host .object-header-facets .object-header-facet{display:flex;min-width:3.75rem;flex-direction:column}:host .object-header-facets .object-header-facet.hasDivider{padding-left:1.25rem;border-left:.0625rem solid rgb(117,117,117)}:host .object-header-facets .object-header-facet.adaptiveFirst{padding-top:1.25rem}:host .object-header-facets .object-header-facet .facet-label{display:block;margin-bottom:0;color:#757575;font-size:.875rem;padding-bottom:.375rem;font-weight:500}:host .object-header-facets .facets-toggler{position:absolute;top:0;right:0;display:flex;align-items:center;padding-right:.3125rem;font-size:.625rem;line-height:.625rem}:host .object-header-facets.amongThemselves{display:grid}:host .object-header-facets.amongThemselves .object-header-facet.hasDivider{padding-left:0;border-top:.0625rem solid rgb(117,117,117);border-left:none}.loading-overlay{position:absolute;z-index:10000;inset:0;display:flex;align-items:center;justify-content:center;background-color:#0003}.no-selected-context{height:1.625rem;color:#979797;font-size:.875rem;line-height:1.625rem}.select-button{margin-top:.25rem}.adaptive-menu-button{display:flex;background:unset;color:#454545;font-size:2rem}.adaptive-menu-button:hover,.adaptive-menu-button:focus{color:#000}.adaptive-menu-entry{display:block;overflow:hidden;width:100%;min-height:2.25rem;padding:0 1rem;background:#fff0;text-align:left;text-overflow:ellipsis;white-space:nowrap}\n"] }]
|
|
29949
29779
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
29950
29780
|
type: Input,
|
|
29951
29781
|
args: [{ required: true }]
|
|
@@ -31627,7 +31457,6 @@ class QdPageComponent {
|
|
|
31627
31457
|
bottomOffset$ = inject(QD_SAFE_BOTTOM_OFFSET, { optional: true });
|
|
31628
31458
|
dialogRef = inject(DialogRef, { optional: true });
|
|
31629
31459
|
navigationInterceptor = inject(QdPageNavigationInterceptorService);
|
|
31630
|
-
injector = inject(Injector);
|
|
31631
31460
|
confirmationDialogService = inject(QdConfirmationDialogOpenerService);
|
|
31632
31461
|
/**
|
|
31633
31462
|
* This property defines the configuration for the QdPage component, including the page type,
|
|
@@ -31824,10 +31653,7 @@ class QdPageComponent {
|
|
|
31824
31653
|
QdPageCommitActionExecutor.execute(action, values, {
|
|
31825
31654
|
formGroupManager: this.formGroupManagerService,
|
|
31826
31655
|
navigationInterceptor: this.navigationInterceptor,
|
|
31827
|
-
destroyed$: this._destroyed
|
|
31828
|
-
scheduleFrameworkRebuildStop: () => afterNextRender(() => this.formGroupManagerService.stopAdoptingFrameworkRebuild(), {
|
|
31829
|
-
injector: this.injector
|
|
31830
|
-
})
|
|
31656
|
+
destroyed$: this._destroyed$
|
|
31831
31657
|
});
|
|
31832
31658
|
};
|
|
31833
31659
|
}
|
|
@@ -31939,10 +31765,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
31939
31765
|
* - 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.
|
|
31940
31766
|
* - FormArrays are restored with dynamic resizing (length is aligned to snapshot before values are reset).
|
|
31941
31767
|
*
|
|
31942
|
-
* ### **Filling a form right after connecting it**
|
|
31943
|
-
* - 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.
|
|
31944
|
-
* - So loading data into a fresh form never triggers a false "unsaved changes" prompt. Only later edits count.
|
|
31945
|
-
*
|
|
31946
31768
|
* ### **Page-Dialog (FullWidth) – Unsaved-Changes Close Contract**
|
|
31947
31769
|
* - When a QdPage is embedded in a full-width page dialog, the directive’s registered form groups are also used to guard dialog closing.
|
|
31948
31770
|
* - The page dialog can only close (e.g. via ESC or backdrop) if no unsaved changes are detected ($hasValuesChanged() is false).
|
|
@@ -31955,10 +31777,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
31955
31777
|
* ```html
|
|
31956
31778
|
* <qd-page [config]="pageConfig">
|
|
31957
31779
|
* <qd-section [config]="sectionConfig">
|
|
31958
|
-
* <qd-grid
|
|
31959
|
-
*
|
|
31960
|
-
* [formGroup]="myFormGroup">
|
|
31961
|
-
* My Section
|
|
31780
|
+
* <qd-grid [formGroup]="myFormGroup" qdConnectFormStateToPage="myFormGroup">
|
|
31781
|
+
* My Section
|
|
31962
31782
|
* </qd-grid>
|
|
31963
31783
|
* </qd-section>
|
|
31964
31784
|
* </qd-page>
|
|
@@ -31978,36 +31798,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
31978
31798
|
class QdConnectFormStateToPageDirective {
|
|
31979
31799
|
formGroupManagerService = inject(QdFormGroupManagerService, { optional: true });
|
|
31980
31800
|
formGroupDirective = inject(FormGroupDirective, { host: true, optional: true });
|
|
31981
|
-
closeInitialBuildupHandle;
|
|
31982
31801
|
/**
|
|
31983
31802
|
* 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.
|
|
31984
31803
|
*/
|
|
31985
31804
|
qdConnectFormStateToPage;
|
|
31986
31805
|
ngOnInit() {
|
|
31806
|
+
if (!this.isValid())
|
|
31807
|
+
return;
|
|
31987
31808
|
const formGroup = this.formGroupDirective?.control;
|
|
31988
|
-
if (
|
|
31809
|
+
if (formGroup && this.qdConnectFormStateToPage) {
|
|
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) {
|
|
31989
31819
|
console.error('Quadrel Framework | QdConnectFormStateToPage - Either a [formGroup] binding or <qd-quick-edit> with a FormGroup is required.');
|
|
31990
|
-
return;
|
|
31820
|
+
return false;
|
|
31991
31821
|
}
|
|
31992
31822
|
if (!this.formGroupManagerService) {
|
|
31993
31823
|
console.error('Quadrel Framework | QdConnectFormStateToPage - The connector must be used within <qd-page>.');
|
|
31994
|
-
return;
|
|
31824
|
+
return false;
|
|
31995
31825
|
}
|
|
31996
31826
|
if (!this.formGroupManagerService.isFormGroupKeyUnique(this.qdConnectFormStateToPage)) {
|
|
31997
31827
|
console.error(`Quadrel Framework | QdConnectFormStateToPage - The FormGroup key "${this.qdConnectFormStateToPage}" is not unique.`);
|
|
31998
|
-
return;
|
|
31828
|
+
return false;
|
|
31999
31829
|
}
|
|
32000
|
-
|
|
32001
|
-
this.scheduleInitialBaseline(this.formGroupManagerService);
|
|
32002
|
-
}
|
|
32003
|
-
ngOnDestroy() {
|
|
32004
|
-
if (this.closeInitialBuildupHandle)
|
|
32005
|
-
clearTimeout(this.closeInitialBuildupHandle);
|
|
32006
|
-
this.formGroupManagerService?.tryRemoveFormGroup(this.qdConnectFormStateToPage);
|
|
32007
|
-
}
|
|
32008
|
-
scheduleInitialBaseline(manager) {
|
|
32009
|
-
const key = this.qdConnectFormStateToPage;
|
|
32010
|
-
this.closeInitialBuildupHandle = setTimeout(() => manager.endInitialBuildup(key));
|
|
31830
|
+
return true;
|
|
32011
31831
|
}
|
|
32012
31832
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdConnectFormStateToPageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
32013
31833
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: QdConnectFormStateToPageDirective, isStandalone: false, selector: "[qdConnectFormStateToPage]", inputs: { qdConnectFormStateToPage: "qdConnectFormStateToPage" }, ngImport: i0 });
|