@quadrel-enterprise-ui/framework 20.9.1 → 20.10.0-beta.140.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 +344 -190
- package/fesm2022/quadrel-enterprise-ui-framework.mjs.map +1 -1
- package/index.d.ts +206 -70
- package/package.json +1 -1
- package/src/assets/i18n/de.json +2 -1
- package/src/assets/i18n/en.json +1 -0
- package/src/assets/i18n/fr.json +1 -0
- package/src/assets/i18n/it.json +1 -0
|
@@ -9211,7 +9211,7 @@ class QdDateAdapter {
|
|
|
9211
9211
|
static parseToDate(formatted) {
|
|
9212
9212
|
if (!formatted)
|
|
9213
9213
|
return new Date();
|
|
9214
|
-
return moment(formatted, '
|
|
9214
|
+
return moment(formatted, 'DD.MM.YYYY').toDate();
|
|
9215
9215
|
}
|
|
9216
9216
|
/**
|
|
9217
9217
|
* Converts a localized Swiss date and time string into a JavaScript Date object.
|
|
@@ -9231,9 +9231,9 @@ class QdDateAdapter {
|
|
|
9231
9231
|
return new Date(new Date().setHours(hours, minutes, 0, 0));
|
|
9232
9232
|
}
|
|
9233
9233
|
else if (date && !time) {
|
|
9234
|
-
return moment(date, '
|
|
9234
|
+
return moment(date, 'DD.MM.YYYY').toDate();
|
|
9235
9235
|
}
|
|
9236
|
-
let format = '
|
|
9236
|
+
let format = 'DD.MM.YYYY ';
|
|
9237
9237
|
switch (time.length) {
|
|
9238
9238
|
case 8:
|
|
9239
9239
|
format = format + 'HH:mm:ss';
|
|
@@ -9242,14 +9242,14 @@ class QdDateAdapter {
|
|
|
9242
9242
|
default:
|
|
9243
9243
|
format = format + 'HH:mm';
|
|
9244
9244
|
}
|
|
9245
|
-
return moment(`${date} ${time}`, format
|
|
9245
|
+
return moment(`${date} ${time}`, format).toDate();
|
|
9246
9246
|
}
|
|
9247
9247
|
/**
|
|
9248
9248
|
* Converts a localized date string into a Moment.js object.
|
|
9249
9249
|
* If the input is null or undefined, it returns undefined.
|
|
9250
9250
|
*/
|
|
9251
9251
|
static parseToMoment(formatted) {
|
|
9252
|
-
return formatted ? moment(formatted, '
|
|
9252
|
+
return formatted ? moment(formatted, 'DD.MM.YYYY').locale('de') : undefined;
|
|
9253
9253
|
}
|
|
9254
9254
|
/**
|
|
9255
9255
|
* Formats a Moment.js object into a localized date string.
|
|
@@ -9258,7 +9258,7 @@ class QdDateAdapter {
|
|
|
9258
9258
|
static formatToLocalizedDateString(momentObj) {
|
|
9259
9259
|
if (!momentObj)
|
|
9260
9260
|
return '';
|
|
9261
|
-
return momentObj.
|
|
9261
|
+
return momentObj.format('DD.MM.YYYY');
|
|
9262
9262
|
}
|
|
9263
9263
|
/**
|
|
9264
9264
|
* Formats a JavaScript Date object into a localized date string.
|
|
@@ -9283,7 +9283,7 @@ class QdDateAdapter {
|
|
|
9283
9283
|
* Returns true if the date is valid, otherwise false.
|
|
9284
9284
|
*/
|
|
9285
9285
|
static isLocalizedDateStringValid(formatted) {
|
|
9286
|
-
return moment(formatted, '
|
|
9286
|
+
return moment(formatted, 'DD.MM.YYYY', true).isValid();
|
|
9287
9287
|
}
|
|
9288
9288
|
/**
|
|
9289
9289
|
* Validates if a date-time string matches the Swiss `DD.MM.YYYY HH:mm:ss` format.
|
|
@@ -9339,9 +9339,8 @@ class QdCalendarComponent {
|
|
|
9339
9339
|
this.setWeekdays();
|
|
9340
9340
|
this.todayDate = moment();
|
|
9341
9341
|
this.months = moment.months();
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
: this.todayDate).year());
|
|
9342
|
+
const parsedSelected = QdDateAdapter.parseToMoment(this.selectedDate);
|
|
9343
|
+
this.years = this.getYears((parsedSelected?.isValid() ? parsedSelected : this.todayDate).year());
|
|
9345
9344
|
this.setCurrentDate();
|
|
9346
9345
|
this.generateCalendar(this.currentDate);
|
|
9347
9346
|
}
|
|
@@ -9431,9 +9430,8 @@ class QdCalendarComponent {
|
|
|
9431
9430
|
return this.todayDate.isSame(momentObj, 'day');
|
|
9432
9431
|
}
|
|
9433
9432
|
setCurrentDate() {
|
|
9434
|
-
const
|
|
9435
|
-
|
|
9436
|
-
: this.todayDate);
|
|
9433
|
+
const parsedDate = QdDateAdapter.parseToMoment(this.selectedDate);
|
|
9434
|
+
const currentDate = (this.currentDate = parsedDate?.isValid() ? parsedDate : this.todayDate);
|
|
9437
9435
|
this.currentDate = currentDate;
|
|
9438
9436
|
this.selectedDate = QdDateAdapter.formatToLocalizedDateString(currentDate);
|
|
9439
9437
|
}
|
|
@@ -10995,6 +10993,7 @@ class QdInputComponent {
|
|
|
10995
10993
|
hasAutofocus;
|
|
10996
10994
|
hasOptions = false;
|
|
10997
10995
|
_value = { value: '' };
|
|
10996
|
+
_displayValue = '';
|
|
10998
10997
|
control;
|
|
10999
10998
|
_optionsResolver;
|
|
11000
10999
|
_subs = new Subscription();
|
|
@@ -11064,6 +11063,7 @@ class QdInputComponent {
|
|
|
11064
11063
|
}
|
|
11065
11064
|
writeValue(value) {
|
|
11066
11065
|
this._value = getValueWithUnit(value, this.config);
|
|
11066
|
+
this._displayValue = String(this._value.value ?? '');
|
|
11067
11067
|
}
|
|
11068
11068
|
setDisabledState(disabled) {
|
|
11069
11069
|
this.disabled = disabled;
|
|
@@ -11194,7 +11194,7 @@ class QdInputComponent {
|
|
|
11194
11194
|
provide: QD_FOCUSABLE_TOKEN,
|
|
11195
11195
|
useExisting: QdInputComponent
|
|
11196
11196
|
}
|
|
11197
|
-
], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["input"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<qd-form-label\n [label]=\"label\"\n [readonly]=\"readonly\"\n [viewonly]=\"viewonly\"\n [control]=\"control\"\n [tooltip]=\"config?.tooltip\"\n [data-test-id]=\"testId\"\n></qd-form-label>\n\n<ng-container *ngIf=\"!readonly && !viewonly && !hasOptions\">\n <div class=\"qd-input-input\" (keydown.enter)=\"emitEnterClick()\">\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</ng-container>\n\n<div *ngIf=\"!readonly && !viewonly && hasOptions\">\n <div\n class=\"qd-input-input\"\n qdInputOptions\n [qdPopoverMinWidth]=\"200\"\n [config]=\"config\"\n [value]=\"_value.value\"\n (optionSelected)=\"handleOptionSelected($event)\"\n (enterClick)=\"emitEnterClick()\"\n >\n <!-- handle (enterClick) by options directive here because event has to be fired after selection -->\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</div>\n\n<qd-form-readonly\n *ngIf=\"readonly\"\n [values]=\"valueAsList\"\n [readonlyAction]=\"readonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-readonly>\n\n<qd-form-viewonly\n *ngIf=\"viewonly\"\n [values]=\"valueAsList\"\n [viewonlyAction]=\"viewonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-viewonly>\n\n<ng-template #inputBox>\n <input\n #input\n [placeholder]=\"placeholder | translate\"\n [value]=\"hasOptions ? (_value.value.toString() | translate) :
|
|
11197
|
+
], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["input"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<qd-form-label\n [label]=\"label\"\n [readonly]=\"readonly\"\n [viewonly]=\"viewonly\"\n [control]=\"control\"\n [tooltip]=\"config?.tooltip\"\n [data-test-id]=\"testId\"\n></qd-form-label>\n\n<ng-container *ngIf=\"!readonly && !viewonly && !hasOptions\">\n <div class=\"qd-input-input\" (keydown.enter)=\"emitEnterClick()\">\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</ng-container>\n\n<div *ngIf=\"!readonly && !viewonly && hasOptions\">\n <div\n class=\"qd-input-input\"\n qdInputOptions\n [qdPopoverMinWidth]=\"200\"\n [config]=\"config\"\n [value]=\"_value.value\"\n (optionSelected)=\"handleOptionSelected($event)\"\n (enterClick)=\"emitEnterClick()\"\n >\n <!-- handle (enterClick) by options directive here because event has to be fired after selection -->\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</div>\n\n<qd-form-readonly\n *ngIf=\"readonly\"\n [values]=\"valueAsList\"\n [readonlyAction]=\"readonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-readonly>\n\n<qd-form-viewonly\n *ngIf=\"viewonly\"\n [values]=\"valueAsList\"\n [viewonlyAction]=\"viewonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-viewonly>\n\n<ng-template #inputBox>\n <input\n #input\n [placeholder]=\"placeholder | translate\"\n [value]=\"hasOptions ? (_value.value.toString() | translate) : _displayValue\"\n (input)=\"handleInput($event)\"\n (focus)=\"handleInputFocus()\"\n (blur)=\"handleInputBlur()\"\n [disabled]=\"disabled || readonly\"\n [type]=\"inputType\"\n [attr.inputmode]=\"inputMode\"\n [qdAutofocus]=\"hasAutofocus\"\n [attr.data-test-id]=\"testId + '-input'\"\n [step]=\"config?.step\"\n required\n />\n <div class=\"qd-input-suffix\">\n <qd-icon *ngIf=\"hasError && !hasOnlyUnitsError\" class=\"qd-input-error-icon\" icon=\"exclamationCircleSolid\"></qd-icon>\n <qd-icon *ngIf=\"clearable\" class=\"qd-input-clearable-icon\" icon=\"timesLarge\" (click)=\"clearInput()\"></qd-icon>\n <ng-content select=\"[qdIconButton]\"></ng-content>\n </div>\n <qd-input-units\n *ngIf=\"hasUnits\"\n [unit]=\"_value.unit\"\n [config]=\"config\"\n (unitChange)=\"handleUnitChange($event)\"\n (opened)=\"handleUnitsOpened()\"\n (closed)=\"handleUnitsClosed()\"\n ></qd-input-units>\n <div class=\"qd-input-suffix\" *ngIf=\"hasError && hasOnlyUnitsError\">\n <qd-icon class=\"qd-input-error-icon\" icon=\"exclamationCircleSolid\"></qd-icon>\n </div>\n</ng-template>\n", styles: [":host{position:relative;display:block;width:100%;flex-direction:column;margin-bottom:.75rem}:host .qd-input-input{display:flex;overflow:hidden;height:2.25rem;align-items:center;padding:0 .0625rem 0 .5rem;border:.0625rem solid rgb(180,180,180);border-radius:0;margin-bottom:.375rem;background-color:#fff}:host .qd-input-input:hover,:host .qd-input-input:active{padding:.0625rem 0 .0625rem .4375rem;border:.125rem solid rgb(23,23,23);border-radius:0}:host .qd-input-input input{overflow:hidden;width:100%;flex-grow:1;border:none;background-color:#fff;color:#171717;font-size:.875rem;line-height:1.875rem}:host .qd-input-input input:hover,:host .qd-input-input input:focus,:host .qd-input-input input:active{border:none;outline:none}:host .qd-input-input input::placeholder{color:#b4b4b4}:host .qd-input-input input:focus::placeholder{display:block;color:#b4b4b4}:host .qd-input-input .qd-input-suffix{display:flex;align-items:center;margin-left:.75rem}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-error-icon{padding-right:.5rem;color:#c70023}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-error-icon+.qd-input-clearable-icon{margin-left:-.25rem}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon{margin-right:.5625rem;color:#979797;cursor:pointer;font-size:1.25rem;line-height:2rem}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon:hover,:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon:focus,:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon:active{color:#171717}:host .qd-input-input qd-input-units+.qd-input-suffix{margin:0 0 0 -.1875rem}:host .qd-input-character-counter{padding-left:.125rem;color:#757575;float:right;font-size:.75rem;font-weight:300;line-height:.75rem}:host:after{display:block;height:0;clear:both;content:\".\";visibility:hidden}:host.qd-input-focus .qd-input-input{padding:.0625rem 0 .0625rem .4375rem;border:.125rem solid rgb(0,102,153);border-radius:0;outline:none}:host.qd-input-readonly .qd-input-readonly{color:#171717;font-size:.875rem;line-height:2.25rem}:host.qd-input-viewonly .qd-input-viewonly{color:#171717;font-size:.875rem;line-height:2.25rem}:host.qd-input-disabled .qd-input-input{border:.0625rem solid rgb(151,151,151);background-color:#f5f5f5}:host.qd-input-disabled .qd-input-input input{background-color:#f5f5f5;color:#979797}:host.qd-input-disabled .qd-input-input input::placeholder{opacity:0}:host.qd-input-disabled .qd-input-input:hover,:host.qd-input-disabled .qd-input-input:active{padding:0 .0625rem 0 .5rem;border:.0625rem solid rgb(151,151,151);background-color:#f5f5f5}:host.qd-input-disabled.qd-input-focus{border-color:#ff9b00}:host.qd-input--readonly-action .qd-input-readonly{color:#069;cursor:pointer}:host.qd-input--readonly-action .qd-input-readonly:hover,:host.qd-input--readonly-action .qd-input-readonly:active,:host.qd-input--readonly-action .qd-input-readonly:focus{text-decoration:underline}:host.qd-input--viewonly-action .qd-input-viewonly{color:#069;cursor:pointer}:host.qd-input--viewonly-action .qd-input-viewonly:hover,:host.qd-input--viewonly-action .qd-input-viewonly:active,:host.qd-input--viewonly-action .qd-input-viewonly:focus{text-decoration:underline}:host.qd-input-error .qd-input-input,:host.qd-input-error-from-outside .qd-input-input{padding:0 .0625rem 0 .5rem;border:.0625rem solid rgb(199,0,35)}:host.qd-input-error .qd-input-input:hover,:host.qd-input-error .qd-input-input:active,:host.qd-input-error-from-outside .qd-input-input:hover,:host.qd-input-error-from-outside .qd-input-input:active{padding:.0625rem 0 .0625rem .4375rem;border:.125rem solid rgb(199,0,35)}:host.qd-input-error.qd-input-focus,:host.qd-input-error-from-outside.qd-input-focus{border-color:#c70023}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: QdAutofocusDirective, selector: "[qdAutofocus]", inputs: ["qdAutofocus"] }, { kind: "component", type: QdIconComponent, selector: "qd-icon", inputs: ["icon"] }, { kind: "component", type: QdFormHintComponent, selector: "qd-form-hint", inputs: ["hint", "control", "hasError", "hintAction", "data-test-id"] }, { kind: "component", type: QdFormLabelComponent, selector: "qd-form-label", inputs: ["label", "isDisabled", "readonly", "viewonly", "control", "tooltip", "data-test-id"] }, { kind: "component", type: QdFormReadonlyComponent, selector: "qd-form-readonly", inputs: ["values", "readonlyAction", "data-test-id"] }, { kind: "component", type: QdFormViewonlyComponent, selector: "qd-form-viewonly", inputs: ["values", "viewonlyAction", "data-test-id"] }, { kind: "component", type: QdInputUnitsComponent, selector: "qd-input-units", inputs: ["config", "unit"], outputs: ["unitChange", "opened", "closed"] }, { kind: "directive", type: QdInputOptionsDirective, selector: "[qdInputOptions]", inputs: ["value", "config"], outputs: ["enterClick", "optionSelected"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] });
|
|
11198
11198
|
}
|
|
11199
11199
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdInputComponent, decorators: [{
|
|
11200
11200
|
type: Component,
|
|
@@ -11212,7 +11212,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
11212
11212
|
provide: QD_FOCUSABLE_TOKEN,
|
|
11213
11213
|
useExisting: QdInputComponent
|
|
11214
11214
|
}
|
|
11215
|
-
], standalone: false, template: "<qd-form-label\n [label]=\"label\"\n [readonly]=\"readonly\"\n [viewonly]=\"viewonly\"\n [control]=\"control\"\n [tooltip]=\"config?.tooltip\"\n [data-test-id]=\"testId\"\n></qd-form-label>\n\n<ng-container *ngIf=\"!readonly && !viewonly && !hasOptions\">\n <div class=\"qd-input-input\" (keydown.enter)=\"emitEnterClick()\">\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</ng-container>\n\n<div *ngIf=\"!readonly && !viewonly && hasOptions\">\n <div\n class=\"qd-input-input\"\n qdInputOptions\n [qdPopoverMinWidth]=\"200\"\n [config]=\"config\"\n [value]=\"_value.value\"\n (optionSelected)=\"handleOptionSelected($event)\"\n (enterClick)=\"emitEnterClick()\"\n >\n <!-- handle (enterClick) by options directive here because event has to be fired after selection -->\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</div>\n\n<qd-form-readonly\n *ngIf=\"readonly\"\n [values]=\"valueAsList\"\n [readonlyAction]=\"readonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-readonly>\n\n<qd-form-viewonly\n *ngIf=\"viewonly\"\n [values]=\"valueAsList\"\n [viewonlyAction]=\"viewonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-viewonly>\n\n<ng-template #inputBox>\n <input\n #input\n [placeholder]=\"placeholder | translate\"\n [value]=\"hasOptions ? (_value.value.toString() | translate) :
|
|
11215
|
+
], standalone: false, template: "<qd-form-label\n [label]=\"label\"\n [readonly]=\"readonly\"\n [viewonly]=\"viewonly\"\n [control]=\"control\"\n [tooltip]=\"config?.tooltip\"\n [data-test-id]=\"testId\"\n></qd-form-label>\n\n<ng-container *ngIf=\"!readonly && !viewonly && !hasOptions\">\n <div class=\"qd-input-input\" (keydown.enter)=\"emitEnterClick()\">\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</ng-container>\n\n<div *ngIf=\"!readonly && !viewonly && hasOptions\">\n <div\n class=\"qd-input-input\"\n qdInputOptions\n [qdPopoverMinWidth]=\"200\"\n [config]=\"config\"\n [value]=\"_value.value\"\n (optionSelected)=\"handleOptionSelected($event)\"\n (enterClick)=\"emitEnterClick()\"\n >\n <!-- handle (enterClick) by options directive here because event has to be fired after selection -->\n <ng-container *ngTemplateOutlet=\"inputBox\"></ng-container>\n </div>\n\n <span class=\"qd-input-character-counter\" *ngIf=\"hasMaxLength\">{{ numberOfCharacters }} / {{ maxLength }}</span>\n\n <qd-form-hint\n *ngIf=\"hint || hasError\"\n [hint]=\"hint\"\n [control]=\"control\"\n [hasError]=\"hasError\"\n [hintAction]=\"hintAction\"\n [data-test-id]=\"testId\"\n ></qd-form-hint>\n</div>\n\n<qd-form-readonly\n *ngIf=\"readonly\"\n [values]=\"valueAsList\"\n [readonlyAction]=\"readonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-readonly>\n\n<qd-form-viewonly\n *ngIf=\"viewonly\"\n [values]=\"valueAsList\"\n [viewonlyAction]=\"viewonlyAction\"\n [data-test-id]=\"testId\"\n></qd-form-viewonly>\n\n<ng-template #inputBox>\n <input\n #input\n [placeholder]=\"placeholder | translate\"\n [value]=\"hasOptions ? (_value.value.toString() | translate) : _displayValue\"\n (input)=\"handleInput($event)\"\n (focus)=\"handleInputFocus()\"\n (blur)=\"handleInputBlur()\"\n [disabled]=\"disabled || readonly\"\n [type]=\"inputType\"\n [attr.inputmode]=\"inputMode\"\n [qdAutofocus]=\"hasAutofocus\"\n [attr.data-test-id]=\"testId + '-input'\"\n [step]=\"config?.step\"\n required\n />\n <div class=\"qd-input-suffix\">\n <qd-icon *ngIf=\"hasError && !hasOnlyUnitsError\" class=\"qd-input-error-icon\" icon=\"exclamationCircleSolid\"></qd-icon>\n <qd-icon *ngIf=\"clearable\" class=\"qd-input-clearable-icon\" icon=\"timesLarge\" (click)=\"clearInput()\"></qd-icon>\n <ng-content select=\"[qdIconButton]\"></ng-content>\n </div>\n <qd-input-units\n *ngIf=\"hasUnits\"\n [unit]=\"_value.unit\"\n [config]=\"config\"\n (unitChange)=\"handleUnitChange($event)\"\n (opened)=\"handleUnitsOpened()\"\n (closed)=\"handleUnitsClosed()\"\n ></qd-input-units>\n <div class=\"qd-input-suffix\" *ngIf=\"hasError && hasOnlyUnitsError\">\n <qd-icon class=\"qd-input-error-icon\" icon=\"exclamationCircleSolid\"></qd-icon>\n </div>\n</ng-template>\n", styles: [":host{position:relative;display:block;width:100%;flex-direction:column;margin-bottom:.75rem}:host .qd-input-input{display:flex;overflow:hidden;height:2.25rem;align-items:center;padding:0 .0625rem 0 .5rem;border:.0625rem solid rgb(180,180,180);border-radius:0;margin-bottom:.375rem;background-color:#fff}:host .qd-input-input:hover,:host .qd-input-input:active{padding:.0625rem 0 .0625rem .4375rem;border:.125rem solid rgb(23,23,23);border-radius:0}:host .qd-input-input input{overflow:hidden;width:100%;flex-grow:1;border:none;background-color:#fff;color:#171717;font-size:.875rem;line-height:1.875rem}:host .qd-input-input input:hover,:host .qd-input-input input:focus,:host .qd-input-input input:active{border:none;outline:none}:host .qd-input-input input::placeholder{color:#b4b4b4}:host .qd-input-input input:focus::placeholder{display:block;color:#b4b4b4}:host .qd-input-input .qd-input-suffix{display:flex;align-items:center;margin-left:.75rem}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-error-icon{padding-right:.5rem;color:#c70023}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-error-icon+.qd-input-clearable-icon{margin-left:-.25rem}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon{margin-right:.5625rem;color:#979797;cursor:pointer;font-size:1.25rem;line-height:2rem}:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon:hover,:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon:focus,:host .qd-input-input .qd-input-suffix ::ng-deep .qd-input-clearable-icon:active{color:#171717}:host .qd-input-input qd-input-units+.qd-input-suffix{margin:0 0 0 -.1875rem}:host .qd-input-character-counter{padding-left:.125rem;color:#757575;float:right;font-size:.75rem;font-weight:300;line-height:.75rem}:host:after{display:block;height:0;clear:both;content:\".\";visibility:hidden}:host.qd-input-focus .qd-input-input{padding:.0625rem 0 .0625rem .4375rem;border:.125rem solid rgb(0,102,153);border-radius:0;outline:none}:host.qd-input-readonly .qd-input-readonly{color:#171717;font-size:.875rem;line-height:2.25rem}:host.qd-input-viewonly .qd-input-viewonly{color:#171717;font-size:.875rem;line-height:2.25rem}:host.qd-input-disabled .qd-input-input{border:.0625rem solid rgb(151,151,151);background-color:#f5f5f5}:host.qd-input-disabled .qd-input-input input{background-color:#f5f5f5;color:#979797}:host.qd-input-disabled .qd-input-input input::placeholder{opacity:0}:host.qd-input-disabled .qd-input-input:hover,:host.qd-input-disabled .qd-input-input:active{padding:0 .0625rem 0 .5rem;border:.0625rem solid rgb(151,151,151);background-color:#f5f5f5}:host.qd-input-disabled.qd-input-focus{border-color:#ff9b00}:host.qd-input--readonly-action .qd-input-readonly{color:#069;cursor:pointer}:host.qd-input--readonly-action .qd-input-readonly:hover,:host.qd-input--readonly-action .qd-input-readonly:active,:host.qd-input--readonly-action .qd-input-readonly:focus{text-decoration:underline}:host.qd-input--viewonly-action .qd-input-viewonly{color:#069;cursor:pointer}:host.qd-input--viewonly-action .qd-input-viewonly:hover,:host.qd-input--viewonly-action .qd-input-viewonly:active,:host.qd-input--viewonly-action .qd-input-viewonly:focus{text-decoration:underline}:host.qd-input-error .qd-input-input,:host.qd-input-error-from-outside .qd-input-input{padding:0 .0625rem 0 .5rem;border:.0625rem solid rgb(199,0,35)}:host.qd-input-error .qd-input-input:hover,:host.qd-input-error .qd-input-input:active,:host.qd-input-error-from-outside .qd-input-input:hover,:host.qd-input-error-from-outside .qd-input-input:active{padding:.0625rem 0 .0625rem .4375rem;border:.125rem solid rgb(199,0,35)}:host.qd-input-error.qd-input-focus,:host.qd-input-error-from-outside.qd-input-focus{border-color:#c70023}\n"] }]
|
|
11216
11216
|
}], propDecorators: { formControlName: [{
|
|
11217
11217
|
type: Input
|
|
11218
11218
|
}], value: [{
|
|
@@ -11434,8 +11434,6 @@ class QdDatepickerComponent {
|
|
|
11434
11434
|
this.displayedDateTime = date ? QdDateAdapter.formatToDateTimeLocaleString(date, this.config?.enableSeconds) : '';
|
|
11435
11435
|
this.displayedTime = date ? QdDateAdapter.formatToTimeLocaleString(date, this.config?.enableSeconds) : '';
|
|
11436
11436
|
this.changeDetectorRef.detectChanges();
|
|
11437
|
-
this.valueChange.emit(date);
|
|
11438
|
-
this._onChange(date);
|
|
11439
11437
|
}
|
|
11440
11438
|
setDisabledState(disabled) {
|
|
11441
11439
|
this.disabled = disabled;
|
|
@@ -12873,7 +12871,7 @@ class QdValidators {
|
|
|
12873
12871
|
return ({ value }) => {
|
|
12874
12872
|
if (!value)
|
|
12875
12873
|
return null;
|
|
12876
|
-
const localizedFormat = moment().
|
|
12874
|
+
const localizedFormat = moment().format('DD.MM.YYYY');
|
|
12877
12875
|
const formattedErrorKey = { date: { key: errorKey, params: { format: localizedFormat } } };
|
|
12878
12876
|
if (value === 'Invalid Date')
|
|
12879
12877
|
return formattedErrorKey;
|
|
@@ -27064,6 +27062,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
27064
27062
|
type: Input
|
|
27065
27063
|
}] } });
|
|
27066
27064
|
|
|
27065
|
+
class QdPageCommitActionExecutor {
|
|
27066
|
+
static execute(action, values, context) {
|
|
27067
|
+
const { formGroupManager, navigationInterceptor, destroyed$, onAfterSnapshot } = context;
|
|
27068
|
+
const captured = formGroupManager.captureFormValues();
|
|
27069
|
+
let result$;
|
|
27070
|
+
navigationInterceptor.executeWithBypass(() => {
|
|
27071
|
+
const handlerResult = action.handler(values);
|
|
27072
|
+
if (isObservable(handlerResult))
|
|
27073
|
+
result$ = handlerResult;
|
|
27074
|
+
});
|
|
27075
|
+
const applySuccess = () => {
|
|
27076
|
+
formGroupManager.setFormGroupsSnapshot(captured);
|
|
27077
|
+
navigationInterceptor.executeWithBypass(() => {
|
|
27078
|
+
onAfterSnapshot?.();
|
|
27079
|
+
try {
|
|
27080
|
+
action.onSuccess?.();
|
|
27081
|
+
}
|
|
27082
|
+
catch (err) {
|
|
27083
|
+
console.error('QD-UI | QdPage - onSuccess callback threw after form was marked as saved.', err);
|
|
27084
|
+
}
|
|
27085
|
+
});
|
|
27086
|
+
};
|
|
27087
|
+
if (result$) {
|
|
27088
|
+
result$.pipe(take(1), takeUntil(destroyed$)).subscribe({
|
|
27089
|
+
next: ok => {
|
|
27090
|
+
if (ok)
|
|
27091
|
+
applySuccess();
|
|
27092
|
+
},
|
|
27093
|
+
error: err => this.handleError(action, err)
|
|
27094
|
+
});
|
|
27095
|
+
}
|
|
27096
|
+
else {
|
|
27097
|
+
applySuccess();
|
|
27098
|
+
}
|
|
27099
|
+
}
|
|
27100
|
+
static handleError(action, err) {
|
|
27101
|
+
if (!action.onError) {
|
|
27102
|
+
console.error('QD-UI | QdPage - Commit action observable errored — form was not marked as saved. Provide an `onError` hook on the action, or handle errors in the handler.', err);
|
|
27103
|
+
return;
|
|
27104
|
+
}
|
|
27105
|
+
try {
|
|
27106
|
+
action.onError(err);
|
|
27107
|
+
}
|
|
27108
|
+
catch (callbackErr) {
|
|
27109
|
+
console.error('QD-UI | QdPage - onError callback threw.', callbackErr);
|
|
27110
|
+
}
|
|
27111
|
+
}
|
|
27112
|
+
}
|
|
27113
|
+
|
|
27067
27114
|
/**
|
|
27068
27115
|
* The QdFormGroupManagerService provides centralized registration, snapshotting, and value tracking
|
|
27069
27116
|
* for multiple Angular `FormGroup` instances. It supports efficient state restoration, including dynamic
|
|
@@ -27178,6 +27225,39 @@ class QdFormGroupManagerService {
|
|
|
27178
27225
|
takeFormGroupsSnapshot() {
|
|
27179
27226
|
this._formGroups.forEach((fg, key) => this._formGroupsSnapshot.set(key, fg.getRawValue()));
|
|
27180
27227
|
}
|
|
27228
|
+
/**
|
|
27229
|
+
* Captures a deep-cloned snapshot of all current form values without mutating internal state.
|
|
27230
|
+
*
|
|
27231
|
+
* Used by framework orchestration code that needs to remember the "at click time" state of a form
|
|
27232
|
+
* so the snapshot can later be set to exactly those values — even if the user edits the form
|
|
27233
|
+
* while an async commit action is pending.
|
|
27234
|
+
*
|
|
27235
|
+
* Throws if any form value is not structured-cloneable (functions, symbols, DOM nodes).
|
|
27236
|
+
*/
|
|
27237
|
+
captureFormValues() {
|
|
27238
|
+
const captured = new Map();
|
|
27239
|
+
this._formGroups.forEach((fg, key) => {
|
|
27240
|
+
try {
|
|
27241
|
+
captured.set(key, structuredClone(fg.getRawValue()));
|
|
27242
|
+
}
|
|
27243
|
+
catch (err) {
|
|
27244
|
+
throw new Error(`QD-UI | QdFormGroupManager - captureFormValues() failed for group "${key}". ` +
|
|
27245
|
+
`Form values must be structured-cloneable. Non-cloneable values like functions, ` +
|
|
27246
|
+
`symbols, or DOM nodes are not supported. Original error: ${String(err)}`);
|
|
27247
|
+
}
|
|
27248
|
+
});
|
|
27249
|
+
return captured;
|
|
27250
|
+
}
|
|
27251
|
+
/**
|
|
27252
|
+
* Replaces the internal saved snapshot with the provided values.
|
|
27253
|
+
*
|
|
27254
|
+
* Intended to be paired with `captureFormValues()` so that the snapshot can be set to the
|
|
27255
|
+
* values present when a commit action was initiated — independent of whether the user edited
|
|
27256
|
+
* the form in the meantime.
|
|
27257
|
+
*/
|
|
27258
|
+
setFormGroupsSnapshot(values) {
|
|
27259
|
+
values.forEach((snapshot, key) => this._formGroupsSnapshot.set(key, snapshot));
|
|
27260
|
+
}
|
|
27181
27261
|
restoreFormGroupsFromSnapshot() {
|
|
27182
27262
|
this._formGroups.forEach((fg, key) => {
|
|
27183
27263
|
const snapshot = this._formGroupsSnapshot.get(key);
|
|
@@ -27187,20 +27267,6 @@ class QdFormGroupManagerService {
|
|
|
27187
27267
|
});
|
|
27188
27268
|
this.cancelPendingAsyncValidation();
|
|
27189
27269
|
}
|
|
27190
|
-
restoreFormGroup(fg, snapshot) {
|
|
27191
|
-
Object.entries(fg.controls).forEach(([ctrlKey, ctrl]) => {
|
|
27192
|
-
const newValue = snapshot[ctrlKey];
|
|
27193
|
-
if (ctrl instanceof FormArray && Array.isArray(newValue)) {
|
|
27194
|
-
this.resetFormArrayToValues(ctrl, newValue);
|
|
27195
|
-
}
|
|
27196
|
-
else if (ctrl instanceof FormGroup && newValue && typeof newValue === 'object') {
|
|
27197
|
-
this.restoreFormGroup(ctrl, newValue);
|
|
27198
|
-
}
|
|
27199
|
-
else {
|
|
27200
|
-
ctrl.reset(newValue);
|
|
27201
|
-
}
|
|
27202
|
-
});
|
|
27203
|
-
}
|
|
27204
27270
|
/**
|
|
27205
27271
|
* Cancels any in-flight async validators on all registered form groups.
|
|
27206
27272
|
*
|
|
@@ -27224,6 +27290,20 @@ class QdFormGroupManagerService {
|
|
|
27224
27290
|
}
|
|
27225
27291
|
});
|
|
27226
27292
|
}
|
|
27293
|
+
restoreFormGroup(fg, snapshot) {
|
|
27294
|
+
Object.entries(fg.controls).forEach(([ctrlKey, ctrl]) => {
|
|
27295
|
+
const newValue = snapshot[ctrlKey];
|
|
27296
|
+
if (ctrl instanceof FormArray && Array.isArray(newValue)) {
|
|
27297
|
+
this.resetFormArrayToValues(ctrl, newValue);
|
|
27298
|
+
}
|
|
27299
|
+
else if (ctrl instanceof FormGroup && newValue && typeof newValue === 'object') {
|
|
27300
|
+
this.restoreFormGroup(ctrl, newValue);
|
|
27301
|
+
}
|
|
27302
|
+
else {
|
|
27303
|
+
ctrl.reset(newValue);
|
|
27304
|
+
}
|
|
27305
|
+
});
|
|
27306
|
+
}
|
|
27227
27307
|
collectPendingControls(control) {
|
|
27228
27308
|
const result = [];
|
|
27229
27309
|
if (control instanceof FormGroup) {
|
|
@@ -27289,6 +27369,122 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
27289
27369
|
type: Injectable
|
|
27290
27370
|
}] });
|
|
27291
27371
|
|
|
27372
|
+
/**
|
|
27373
|
+
* Intercepts router navigation when unsaved form changes exist on a QdPage.
|
|
27374
|
+
*
|
|
27375
|
+
* Provided per `QdPageComponent`. Activated automatically when the page enters an editable state
|
|
27376
|
+
* (create pages or inspect pages in edit mode). Deactivated when the page returns to view mode.
|
|
27377
|
+
*
|
|
27378
|
+
* #### When navigation is intercepted
|
|
27379
|
+
*
|
|
27380
|
+
* - A `NavigationStart` event occurs (browser back, shell back button, or programmatic navigation).
|
|
27381
|
+
* - The registered form groups report unsaved changes via `QdFormGroupManagerService.$hasValuesChanged()`.
|
|
27382
|
+
*
|
|
27383
|
+
* The navigation is cancelled and a confirmation dialog is shown. The user can either discard
|
|
27384
|
+
* changes and proceed or stay on the page.
|
|
27385
|
+
*
|
|
27386
|
+
* #### When navigation is allowed
|
|
27387
|
+
*
|
|
27388
|
+
* - No unsaved changes exist — the form is either pristine or the framework has updated the
|
|
27389
|
+
* saved snapshot after a successful commit action (Submit, Save, SaveDraft).
|
|
27390
|
+
* - The page switches to view mode via `deactivate()`.
|
|
27391
|
+
*
|
|
27392
|
+
* The service has no concept of "pending actions" or bypass windows. Framework actions update the
|
|
27393
|
+
* saved snapshot directly via `QdFormGroupManagerService.setFormGroupsSnapshot(...)`; cancel-discard
|
|
27394
|
+
* flows reset the form to the saved snapshot via `restoreFormGroupsFromSnapshot()`. Either way, the
|
|
27395
|
+
* interceptor only observes the resulting form state — there is no time window where a bypass is active.
|
|
27396
|
+
*/
|
|
27397
|
+
class QdPageNavigationInterceptorService {
|
|
27398
|
+
router = inject(Router);
|
|
27399
|
+
formGroupManager = inject(QdFormGroupManagerService);
|
|
27400
|
+
confirmationDialog = inject(QdConfirmationDialogOpenerService);
|
|
27401
|
+
_destroy$ = new Subject();
|
|
27402
|
+
_deactivate$ = new Subject();
|
|
27403
|
+
/**
|
|
27404
|
+
* Guards the re-dispatched navigation issued after the user confirms discard. While the dialog
|
|
27405
|
+
* is open and while the confirmed target is being re-dispatched, the router events must not
|
|
27406
|
+
* re-enter the interception pipeline — otherwise the dialog would open recursively.
|
|
27407
|
+
*/
|
|
27408
|
+
_bypassInterception = false;
|
|
27409
|
+
/**
|
|
27410
|
+
* URL of the navigation target accepted by the user in the most recent discard-confirm dialog.
|
|
27411
|
+
* Set by `navigateToConfirmedTarget()` and consumed once by `shouldIntercept()` when the
|
|
27412
|
+
* corresponding `NavigationStart` arrives.
|
|
27413
|
+
*/
|
|
27414
|
+
_confirmedTargetUrl;
|
|
27415
|
+
_confirmationMessage;
|
|
27416
|
+
ngOnDestroy() {
|
|
27417
|
+
this._confirmedTargetUrl = undefined;
|
|
27418
|
+
this._destroy$.next();
|
|
27419
|
+
this._destroy$.complete();
|
|
27420
|
+
this._deactivate$.complete();
|
|
27421
|
+
}
|
|
27422
|
+
/**
|
|
27423
|
+
* Starts intercepting navigation events. Replaces any previously active listener.
|
|
27424
|
+
*/
|
|
27425
|
+
activate(confirmationMessage) {
|
|
27426
|
+
this._confirmationMessage = confirmationMessage;
|
|
27427
|
+
this._deactivate$.next();
|
|
27428
|
+
this.listenForUnsavedNavigationAttempts();
|
|
27429
|
+
}
|
|
27430
|
+
/**
|
|
27431
|
+
* Stops intercepting and clears the pending confirmed-target bypass.
|
|
27432
|
+
*/
|
|
27433
|
+
deactivate() {
|
|
27434
|
+
this._deactivate$.next();
|
|
27435
|
+
this._confirmedTargetUrl = undefined;
|
|
27436
|
+
}
|
|
27437
|
+
/**
|
|
27438
|
+
* Runs the given action while temporarily suppressing navigation interception.
|
|
27439
|
+
*
|
|
27440
|
+
* Used by framework commit-action orchestration to wrap synchronous handler invocations and
|
|
27441
|
+
* post-commit `onSuccess` callbacks: any router navigation that occurs during `action()` is
|
|
27442
|
+
* passed through without raising the unsaved-changes dialog. Restoration happens in a `finally`
|
|
27443
|
+
* block, so the flag is released even if `action()` throws.
|
|
27444
|
+
*/
|
|
27445
|
+
executeWithBypass(action) {
|
|
27446
|
+
this._bypassInterception = true;
|
|
27447
|
+
try {
|
|
27448
|
+
action();
|
|
27449
|
+
}
|
|
27450
|
+
finally {
|
|
27451
|
+
this._bypassInterception = false;
|
|
27452
|
+
}
|
|
27453
|
+
}
|
|
27454
|
+
listenForUnsavedNavigationAttempts() {
|
|
27455
|
+
this.router.events
|
|
27456
|
+
.pipe(filter$1((event) => event instanceof NavigationStart), filter$1(() => !this._bypassInterception), filter$1(event => this.shouldIntercept(event)), concatMap$1(event => this.checkForPendingChanges(event)), filter$1(({ hasChanges }) => hasChanges), exhaustMap(({ event }) => this.cancelNavigationAndConfirm(event)), filter$1(({ confirmed }) => confirmed), takeUntil$1(this._deactivate$), takeUntil$1(this._destroy$))
|
|
27457
|
+
.subscribe(({ targetUrl }) => this.navigateToConfirmedTarget(targetUrl));
|
|
27458
|
+
}
|
|
27459
|
+
shouldIntercept(event) {
|
|
27460
|
+
if (this._confirmedTargetUrl !== undefined && this._confirmedTargetUrl === event.url) {
|
|
27461
|
+
this._confirmedTargetUrl = undefined;
|
|
27462
|
+
return false;
|
|
27463
|
+
}
|
|
27464
|
+
return true;
|
|
27465
|
+
}
|
|
27466
|
+
checkForPendingChanges(event) {
|
|
27467
|
+
return this.formGroupManager.$hasValuesChanged().pipe(take$1(1), map$1(hasChanges => ({ event, hasChanges })));
|
|
27468
|
+
}
|
|
27469
|
+
cancelNavigationAndConfirm(event) {
|
|
27470
|
+
this._bypassInterception = true;
|
|
27471
|
+
void this.router.navigateByUrl(this.router.url, { skipLocationChange: true });
|
|
27472
|
+
return this.confirmationDialog
|
|
27473
|
+
.showDiscardConfirmDialog(this._confirmationMessage, 'page-navigation-confirmation')
|
|
27474
|
+
.pipe(map$1(confirmed => ({ confirmed, targetUrl: event.url })), finalize(() => (this._bypassInterception = false)));
|
|
27475
|
+
}
|
|
27476
|
+
navigateToConfirmedTarget(targetUrl) {
|
|
27477
|
+
this._bypassInterception = false;
|
|
27478
|
+
this._confirmedTargetUrl = targetUrl;
|
|
27479
|
+
void this.router.navigateByUrl(targetUrl);
|
|
27480
|
+
}
|
|
27481
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageNavigationInterceptorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
27482
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageNavigationInterceptorService });
|
|
27483
|
+
}
|
|
27484
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageNavigationInterceptorService, decorators: [{
|
|
27485
|
+
type: Injectable
|
|
27486
|
+
}] });
|
|
27487
|
+
|
|
27292
27488
|
class QdResolverTriggerService {
|
|
27293
27489
|
route = inject(ActivatedRoute, { optional: true });
|
|
27294
27490
|
shouldTriggerResolver(triggerOn) {
|
|
@@ -27338,6 +27534,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
27338
27534
|
class QdPageObjectHeaderComponent {
|
|
27339
27535
|
pageObjectResolver = inject(QD_PAGE_OBJECT_RESOLVER_TOKEN, { optional: true });
|
|
27340
27536
|
formGroupManagerService = inject(QdFormGroupManagerService, { optional: true });
|
|
27537
|
+
navigationInterceptor = inject(QdPageNavigationInterceptorService);
|
|
27341
27538
|
dialogComponent = inject(QdDialogComponent, { optional: true });
|
|
27342
27539
|
dialog = inject(QdDialogService);
|
|
27343
27540
|
confirmationDialogService = inject(QdConfirmationDialogOpenerService);
|
|
@@ -27502,13 +27699,18 @@ class QdPageObjectHeaderComponent {
|
|
|
27502
27699
|
});
|
|
27503
27700
|
}
|
|
27504
27701
|
save() {
|
|
27505
|
-
const
|
|
27506
|
-
|
|
27507
|
-
|
|
27508
|
-
|
|
27509
|
-
|
|
27510
|
-
|
|
27511
|
-
|
|
27702
|
+
const saveAction = this.saveButton;
|
|
27703
|
+
if (!saveAction)
|
|
27704
|
+
return;
|
|
27705
|
+
QdPageCommitActionExecutor.execute(saveAction, this.formGroupManagerService.getAllValues(), {
|
|
27706
|
+
formGroupManager: this.formGroupManagerService,
|
|
27707
|
+
navigationInterceptor: this.navigationInterceptor,
|
|
27708
|
+
destroyed$: this._destroyed$,
|
|
27709
|
+
onAfterSnapshot: () => {
|
|
27710
|
+
this.formGroupManagerService.cancelPendingAsyncValidation();
|
|
27711
|
+
this.pageStoreService.toggleViewonly(true);
|
|
27712
|
+
}
|
|
27713
|
+
});
|
|
27512
27714
|
}
|
|
27513
27715
|
changeContext(context, selection, event) {
|
|
27514
27716
|
event.stopPropagation();
|
|
@@ -27536,7 +27738,7 @@ class QdPageObjectHeaderComponent {
|
|
|
27536
27738
|
.subscribe();
|
|
27537
27739
|
}
|
|
27538
27740
|
initContexts() {
|
|
27539
|
-
this.contexts$ = this.contextService.contexts$.pipe(map(contexts => contexts.filter(context => context.selection || this.config.pageType === 'overview')), map(contexts => contexts
|
|
27741
|
+
this.contexts$ = this.contextService.contexts$.pipe(map(contexts => contexts.filter(context => context.selection || this.config.pageType === 'overview')), map(contexts => contexts.map(({ selection, context }) => ({
|
|
27540
27742
|
label: context.label.i18n,
|
|
27541
27743
|
value: Array.isArray(selection?.value)
|
|
27542
27744
|
? selection?.value.filter(item => item !== null && item !== undefined)
|
|
@@ -27547,7 +27749,7 @@ class QdPageObjectHeaderComponent {
|
|
|
27547
27749
|
selection: selection?.value ?? []
|
|
27548
27750
|
}))));
|
|
27549
27751
|
this.contexts$
|
|
27550
|
-
.pipe(takeUntil(this._destroyed$), tap(contexts => (this._availableContexts = contexts
|
|
27752
|
+
.pipe(takeUntil(this._destroyed$), tap(contexts => (this._availableContexts = contexts.length)))
|
|
27551
27753
|
.subscribe();
|
|
27552
27754
|
}
|
|
27553
27755
|
updateCustomActions() {
|
|
@@ -28566,126 +28768,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
28566
28768
|
type: Output
|
|
28567
28769
|
}] } });
|
|
28568
28770
|
|
|
28569
|
-
/**
|
|
28570
|
-
* Intercepts router navigation when unsaved form changes exist on a QdPage.
|
|
28571
|
-
*
|
|
28572
|
-
* Provided per `QdPageComponent`. Activated automatically when the page enters an editable state
|
|
28573
|
-
* (create pages or inspect pages in edit mode). Deactivated when the page returns to view mode.
|
|
28574
|
-
*
|
|
28575
|
-
* #### When navigation is intercepted
|
|
28576
|
-
*
|
|
28577
|
-
* - The user has unsaved form changes (tracked via `QdFormGroupManagerService`).
|
|
28578
|
-
* - A `NavigationStart` event occurs (browser back, shell back button, or programmatic navigation).
|
|
28579
|
-
*
|
|
28580
|
-
* The current navigation is cancelled, and a confirmation dialog is shown. The user can either
|
|
28581
|
-
* discard changes and proceed or cancel and stay on the page.
|
|
28582
|
-
*
|
|
28583
|
-
* #### When navigation is allowed
|
|
28584
|
-
*
|
|
28585
|
-
* - No unsaved changes exist — navigation proceeds silently.
|
|
28586
|
-
* - A framework action (Submit, SaveDraft) wraps its handler via `executeWithBypass()`.
|
|
28587
|
-
* - A confirmed discard sets `allowNextNavigation()` before the cancel handler navigates.
|
|
28588
|
-
* - The page switches to view mode via `deactivate()`.
|
|
28589
|
-
*
|
|
28590
|
-
* Custom actions defined by the application are not bypassed. If a custom action navigates away
|
|
28591
|
-
* while unsaved changes exist, the confirmation dialog is shown.
|
|
28592
|
-
*/
|
|
28593
|
-
class QdPageNavigationInterceptorService {
|
|
28594
|
-
router = inject(Router);
|
|
28595
|
-
formGroupManager = inject(QdFormGroupManagerService);
|
|
28596
|
-
confirmationDialog = inject(QdConfirmationDialogOpenerService);
|
|
28597
|
-
_destroy$ = new Subject();
|
|
28598
|
-
_deactivate$ = new Subject();
|
|
28599
|
-
_bypassInterception = false;
|
|
28600
|
-
_allowedTargetUrls = new Set();
|
|
28601
|
-
_confirmationMessage;
|
|
28602
|
-
ngOnDestroy() {
|
|
28603
|
-
this._allowedTargetUrls.clear();
|
|
28604
|
-
this._destroy$.next();
|
|
28605
|
-
this._destroy$.complete();
|
|
28606
|
-
this._deactivate$.complete();
|
|
28607
|
-
}
|
|
28608
|
-
/**
|
|
28609
|
-
* Starts intercepting navigation events. Replaces any previously active listener.
|
|
28610
|
-
*/
|
|
28611
|
-
activate(confirmationMessage) {
|
|
28612
|
-
this._confirmationMessage = confirmationMessage;
|
|
28613
|
-
this._deactivate$.next();
|
|
28614
|
-
this.listenForUnsavedNavigationAttempts();
|
|
28615
|
-
}
|
|
28616
|
-
/**
|
|
28617
|
-
* Stops intercepting and clears all pending URL bypasses.
|
|
28618
|
-
*/
|
|
28619
|
-
deactivate() {
|
|
28620
|
-
this._deactivate$.next();
|
|
28621
|
-
this._allowedTargetUrls.clear();
|
|
28622
|
-
}
|
|
28623
|
-
/**
|
|
28624
|
-
* Whitelists the next navigation so it bypasses interception.
|
|
28625
|
-
* Without a URL, any next navigation is bypassed (wildcard). With a URL, only that
|
|
28626
|
-
* specific navigation is bypassed — non-matching navigations are still intercepted.
|
|
28627
|
-
*/
|
|
28628
|
-
allowNextNavigation(targetUrl) {
|
|
28629
|
-
this._allowedTargetUrls.add(targetUrl ?? '*');
|
|
28630
|
-
}
|
|
28631
|
-
/**
|
|
28632
|
-
* Executes the callback with interception temporarily disabled.
|
|
28633
|
-
* The callback must navigate synchronously — async navigation after the callback returns
|
|
28634
|
-
* will not be bypassed. This works because Angular's router emits NavigationStart
|
|
28635
|
-
* synchronously within the navigateByUrl() / navigate() call.
|
|
28636
|
-
*/
|
|
28637
|
-
executeWithBypass(fn) {
|
|
28638
|
-
this._bypassInterception = true;
|
|
28639
|
-
try {
|
|
28640
|
-
fn();
|
|
28641
|
-
}
|
|
28642
|
-
finally {
|
|
28643
|
-
this._bypassInterception = false;
|
|
28644
|
-
}
|
|
28645
|
-
}
|
|
28646
|
-
listenForUnsavedNavigationAttempts() {
|
|
28647
|
-
this.router.events
|
|
28648
|
-
.pipe(filter$1((event) => event instanceof NavigationStart), filter$1(() => !this._bypassInterception), filter$1(event => this.shouldIntercept(event)), concatMap$1(event => this.checkForPendingChanges(event)), filter$1(({ hasChanges }) => hasChanges), exhaustMap(({ event }) => this.cancelNavigationAndConfirm(event)), filter$1(({ confirmed }) => confirmed), takeUntil$1(this._deactivate$), takeUntil$1(this._destroy$))
|
|
28649
|
-
.subscribe(({ targetUrl }) => this.navigateToConfirmedTarget(targetUrl));
|
|
28650
|
-
}
|
|
28651
|
-
shouldIntercept(event) {
|
|
28652
|
-
if (this._allowedTargetUrls.has('*')) {
|
|
28653
|
-
this._allowedTargetUrls.clear();
|
|
28654
|
-
return false;
|
|
28655
|
-
}
|
|
28656
|
-
if (this._allowedTargetUrls.has(event.url)) {
|
|
28657
|
-
this._allowedTargetUrls.delete(event.url);
|
|
28658
|
-
return false;
|
|
28659
|
-
}
|
|
28660
|
-
return true;
|
|
28661
|
-
}
|
|
28662
|
-
checkForPendingChanges(event) {
|
|
28663
|
-
return this.formGroupManager.$hasValuesChanged().pipe(take$1(1), map$1(hasChanges => ({ event, hasChanges })));
|
|
28664
|
-
}
|
|
28665
|
-
cancelNavigationAndConfirm(event) {
|
|
28666
|
-
this._bypassInterception = true;
|
|
28667
|
-
void this.router.navigateByUrl(this.router.url, { skipLocationChange: true });
|
|
28668
|
-
return this.confirmationDialog
|
|
28669
|
-
.showDiscardConfirmDialog(this._confirmationMessage, 'page-navigation-confirmation')
|
|
28670
|
-
.pipe(map$1(confirmed => ({ confirmed, targetUrl: event.url })), finalize(() => (this._bypassInterception = false)));
|
|
28671
|
-
}
|
|
28672
|
-
navigateToConfirmedTarget(targetUrl) {
|
|
28673
|
-
this._bypassInterception = false;
|
|
28674
|
-
this._allowedTargetUrls.add(targetUrl);
|
|
28675
|
-
void this.router.navigateByUrl(targetUrl);
|
|
28676
|
-
}
|
|
28677
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageNavigationInterceptorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
28678
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageNavigationInterceptorService });
|
|
28679
|
-
}
|
|
28680
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: QdPageNavigationInterceptorService, decorators: [{
|
|
28681
|
-
type: Injectable
|
|
28682
|
-
}] });
|
|
28683
|
-
|
|
28684
28771
|
class QdPageSubmitActionService {
|
|
28685
28772
|
footerService = inject(QdPageFooterService);
|
|
28686
28773
|
formGroupManagerService = inject(QdFormGroupManagerService);
|
|
28774
|
+
navigationInterceptor = inject(QdPageNavigationInterceptorService);
|
|
28687
28775
|
_labelI18n = 'i18n.qd.page.footer.submit';
|
|
28688
|
-
|
|
28776
|
+
_submitAction;
|
|
28689
28777
|
_isVisible = true;
|
|
28690
28778
|
_destroyed$ = new Subject();
|
|
28691
28779
|
_cancelTrackFormValidity$ = new Subject();
|
|
@@ -28699,9 +28787,9 @@ class QdPageSubmitActionService {
|
|
|
28699
28787
|
this._isVisible = isVisible;
|
|
28700
28788
|
this.configureSubmitAction(pageTypeConfig.submit);
|
|
28701
28789
|
}
|
|
28702
|
-
configureSubmitAction(
|
|
28703
|
-
this._labelI18n =
|
|
28704
|
-
this.
|
|
28790
|
+
configureSubmitAction(action) {
|
|
28791
|
+
this._labelI18n = action.label?.i18n || 'i18n.qd.page.footer.submit';
|
|
28792
|
+
this._submitAction = action;
|
|
28705
28793
|
this.registerSubmitAction();
|
|
28706
28794
|
this.trackFormValidity();
|
|
28707
28795
|
}
|
|
@@ -28711,7 +28799,7 @@ class QdPageSubmitActionService {
|
|
|
28711
28799
|
key: 'submit',
|
|
28712
28800
|
action: {
|
|
28713
28801
|
titleI18n: this._labelI18n,
|
|
28714
|
-
handler: this.
|
|
28802
|
+
handler: (...args) => this.executeSubmit(...args),
|
|
28715
28803
|
isDisabled: true,
|
|
28716
28804
|
isVisible: this._isVisible,
|
|
28717
28805
|
type: QdFooterActionType.Primary
|
|
@@ -28719,12 +28807,16 @@ class QdPageSubmitActionService {
|
|
|
28719
28807
|
}
|
|
28720
28808
|
]);
|
|
28721
28809
|
}
|
|
28722
|
-
|
|
28723
|
-
|
|
28724
|
-
|
|
28725
|
-
|
|
28726
|
-
|
|
28727
|
-
|
|
28810
|
+
executeSubmit(...args) {
|
|
28811
|
+
const action = this._submitAction;
|
|
28812
|
+
if (!action)
|
|
28813
|
+
return;
|
|
28814
|
+
const values = this.formGroupManagerService.hasFormGroups() ? this.formGroupManagerService.getAllValues() : args;
|
|
28815
|
+
QdPageCommitActionExecutor.execute(action, values, {
|
|
28816
|
+
formGroupManager: this.formGroupManagerService,
|
|
28817
|
+
navigationInterceptor: this.navigationInterceptor,
|
|
28818
|
+
destroyed$: this._destroyed$
|
|
28819
|
+
});
|
|
28728
28820
|
}
|
|
28729
28821
|
trackFormValidity() {
|
|
28730
28822
|
this._cancelTrackFormValidity$.next();
|
|
@@ -28739,7 +28831,7 @@ class QdPageSubmitActionService {
|
|
|
28739
28831
|
key: 'submit',
|
|
28740
28832
|
action: {
|
|
28741
28833
|
titleI18n: this._labelI18n,
|
|
28742
|
-
handler: this.
|
|
28834
|
+
handler: (...args) => this.executeSubmit(...args),
|
|
28743
28835
|
isDisabled: !isValid,
|
|
28744
28836
|
isVisible: this._isVisible,
|
|
28745
28837
|
type: QdFooterActionType.Primary
|
|
@@ -28794,6 +28886,22 @@ const SAFE_BOTTOM_OFFSET_PX = 64;
|
|
|
28794
28886
|
*
|
|
28795
28887
|
* Please check the relevant interfaces for each page type: `QdPageConfigOverview`, `QdPageConfigCreate`, `QdPageConfigInspect`, and `QdPageConfigCustom`.
|
|
28796
28888
|
*
|
|
28889
|
+
* #### **Commit Actions**
|
|
28890
|
+
*
|
|
28891
|
+
* Commit actions (`submit`, `save`, `saveDraft`) can either run synchronously (handler returns `void`) or wait on an async result (handler returns `Observable<boolean>`). The framework waits for the first emission, advances the saved-state baseline on `true`, and exposes race-free hooks for side effects so navigation after a successful commit does not trigger the unsaved-changes dialog.
|
|
28892
|
+
*
|
|
28893
|
+
* `onSuccess` runs when the handler emits `true` (after the baseline has advanced), `onError` runs when the handler observable errors — e.g. a failed HTTP request that is not caught inside the pipeline.
|
|
28894
|
+
*
|
|
28895
|
+
* ```ts
|
|
28896
|
+
* submit: {
|
|
28897
|
+
* handler: (formValues) => this.api.create(formValues).pipe(map(() => true)),
|
|
28898
|
+
* onSuccess: () => this.router.navigateByUrl('/'),
|
|
28899
|
+
* onError: (err) => this.notifications.add('', { type: 'critical', i18n: 'i18n.myApp.create.failed' })
|
|
28900
|
+
* }
|
|
28901
|
+
* ```
|
|
28902
|
+
*
|
|
28903
|
+
* The same mechanism applies to `save` and `saveDraft`. For the full contract (success vs. planned `false` vs. error, anti-patterns, `discardUnsavedChanges()`), see `QdPageCommitAction` and its action-specific extensions `QdPageSaveAction`, `QdPageSaveDraftAction`, `QdPageCreateSubmitAction`, and `QdPageInspectSubmitAction`.
|
|
28904
|
+
*
|
|
28797
28905
|
* #### **Validation/Parameterization**
|
|
28798
28906
|
*
|
|
28799
28907
|
* Validation and parameterization are covered in a dedicated chapter in the Storybook. Please check the "Validation" section for more information.
|
|
@@ -28863,7 +28971,15 @@ const SAFE_BOTTOM_OFFSET_PX = 64;
|
|
|
28863
28971
|
* handler: () => handleCancel()
|
|
28864
28972
|
* },
|
|
28865
28973
|
* save: {
|
|
28866
|
-
* handler: (
|
|
28974
|
+
* handler: () => saveApi.save(form.value).pipe(
|
|
28975
|
+
* tap(result => notifications.success('Saved')),
|
|
28976
|
+
* map(() => true),
|
|
28977
|
+
* catchError(err => {
|
|
28978
|
+
* notifications.showError(err);
|
|
28979
|
+
* return of(false);
|
|
28980
|
+
* })
|
|
28981
|
+
* ),
|
|
28982
|
+
* onSuccess: () => router.navigate(['/overview'])
|
|
28867
28983
|
* }
|
|
28868
28984
|
* }
|
|
28869
28985
|
* };
|
|
@@ -28981,7 +29097,8 @@ const SAFE_BOTTOM_OFFSET_PX = 64;
|
|
|
28981
29097
|
* pageType: 'create',
|
|
28982
29098
|
* pageTypeConfig: {
|
|
28983
29099
|
* submit: {
|
|
28984
|
-
* handler: (formValues) =>
|
|
29100
|
+
* handler: (formValues) => createApi.create(formValues).pipe(map(() => true)),
|
|
29101
|
+
* onSuccess: () => router.navigate(['/items'])
|
|
28985
29102
|
* }
|
|
28986
29103
|
* }
|
|
28987
29104
|
* };
|
|
@@ -29074,7 +29191,14 @@ const SAFE_BOTTOM_OFFSET_PX = 64;
|
|
|
29074
29191
|
* handler: () => handleCancel()
|
|
29075
29192
|
* },
|
|
29076
29193
|
* save: {
|
|
29077
|
-
* handler: (formValues) =>
|
|
29194
|
+
* handler: (formValues) => saveApi.save(formValues).pipe(
|
|
29195
|
+
* map(() => true),
|
|
29196
|
+
* catchError(err => {
|
|
29197
|
+
* notifications.showError(err);
|
|
29198
|
+
* return of(false);
|
|
29199
|
+
* })
|
|
29200
|
+
* ),
|
|
29201
|
+
* onSuccess: () => router.navigate(['/overview'])
|
|
29078
29202
|
* }
|
|
29079
29203
|
* }
|
|
29080
29204
|
* };
|
|
@@ -29218,7 +29342,7 @@ class QdPageComponent {
|
|
|
29218
29342
|
if (this.config.pageType === 'create' && this.config?.pageTypeConfig?.cancel !== undefined)
|
|
29219
29343
|
this.handleCancelActionWithFormChanges();
|
|
29220
29344
|
if (this.config.pageType === 'create' && this.config?.pageTypeConfig?.saveDraft !== undefined)
|
|
29221
|
-
this.initSaveDraftFooterAction();
|
|
29345
|
+
this.initSaveDraftFooterAction(this.config.pageTypeConfig.saveDraft);
|
|
29222
29346
|
if (this.config.pageType === 'inspect')
|
|
29223
29347
|
this.pageStoreService.isViewonly$
|
|
29224
29348
|
.pipe(takeUntil(this._destroyed$))
|
|
@@ -29232,6 +29356,34 @@ class QdPageComponent {
|
|
|
29232
29356
|
this._destroyed$.next();
|
|
29233
29357
|
this._destroyed$.complete();
|
|
29234
29358
|
}
|
|
29359
|
+
/**
|
|
29360
|
+
* @description Resets all registered form groups to their last saved snapshot, marking the page
|
|
29361
|
+
* as no longer dirty.
|
|
29362
|
+
*
|
|
29363
|
+
* Intended for explicit consumer-driven discard scenarios where you want subsequent navigation
|
|
29364
|
+
* to bypass the unsaved-changes dialog — for example inside a commit action's `onError` hook
|
|
29365
|
+
* after an auth-expiry response, where the user must be redirected to the login page regardless
|
|
29366
|
+
* of unsaved edits.
|
|
29367
|
+
*
|
|
29368
|
+
* Prefer this over wiring custom bypass logic; it keeps the discard explicit and auditable in
|
|
29369
|
+
* application code.
|
|
29370
|
+
*
|
|
29371
|
+
* @example
|
|
29372
|
+
* ```ts
|
|
29373
|
+
* save: {
|
|
29374
|
+
* handler: (values) => api.save(values).pipe(map(() => true)),
|
|
29375
|
+
* onError: (err) => {
|
|
29376
|
+
* if (err.status === 401) {
|
|
29377
|
+
* this.pageComponent.discardUnsavedChanges();
|
|
29378
|
+
* this.router.navigateByUrl('/login');
|
|
29379
|
+
* }
|
|
29380
|
+
* }
|
|
29381
|
+
* }
|
|
29382
|
+
* ```
|
|
29383
|
+
*/
|
|
29384
|
+
discardUnsavedChanges() {
|
|
29385
|
+
this.formGroupManagerService.restoreFormGroupsFromSnapshot();
|
|
29386
|
+
}
|
|
29235
29387
|
checkConfigValidity() {
|
|
29236
29388
|
if (!this.config)
|
|
29237
29389
|
console.warn('QdUi | QdPageComponent - To configure the page you should provide a valid config.');
|
|
@@ -29244,11 +29396,14 @@ class QdPageComponent {
|
|
|
29244
29396
|
const action = pageTypeConfig[actionKey];
|
|
29245
29397
|
if (!action)
|
|
29246
29398
|
continue;
|
|
29399
|
+
const handler = actionKey === 'cancel'
|
|
29400
|
+
? () => action.handler()
|
|
29401
|
+
: this.generateCommitActionHandler(action);
|
|
29247
29402
|
actions.push({
|
|
29248
29403
|
actionKey,
|
|
29249
29404
|
partialAction: {
|
|
29250
29405
|
...(action?.label?.i18n ? { titleI18n: action.label.i18n } : {}),
|
|
29251
|
-
handler
|
|
29406
|
+
handler
|
|
29252
29407
|
}
|
|
29253
29408
|
});
|
|
29254
29409
|
}
|
|
@@ -29265,25 +29420,20 @@ class QdPageComponent {
|
|
|
29265
29420
|
partialAction: {
|
|
29266
29421
|
handler: hasChanged
|
|
29267
29422
|
? () => this.setupCancelConfirmation()
|
|
29268
|
-
: () =>
|
|
29269
|
-
this.navigationInterceptor.executeWithBypass(() => {
|
|
29270
|
-
this.config?.pageTypeConfig?.cancel?.handler();
|
|
29271
|
-
});
|
|
29272
|
-
}
|
|
29423
|
+
: () => this.config?.pageTypeConfig?.cancel?.handler()
|
|
29273
29424
|
}
|
|
29274
29425
|
}
|
|
29275
29426
|
]);
|
|
29276
29427
|
});
|
|
29277
29428
|
}
|
|
29278
|
-
initSaveDraftFooterAction() {
|
|
29279
|
-
const pageTypeConfig = this.config.pageTypeConfig;
|
|
29429
|
+
initSaveDraftFooterAction(saveDraft) {
|
|
29280
29430
|
this.footerService.setActions([
|
|
29281
29431
|
{
|
|
29282
29432
|
key: 'saveDraft',
|
|
29283
29433
|
action: {
|
|
29284
|
-
titleI18n:
|
|
29434
|
+
titleI18n: saveDraft.label?.i18n ?? 'i18n.qd.page.footer.saveDraft',
|
|
29285
29435
|
type: QdFooterActionType.Secondary,
|
|
29286
|
-
handler: this.
|
|
29436
|
+
handler: this.generateCommitActionHandler(saveDraft),
|
|
29287
29437
|
isVisible: true,
|
|
29288
29438
|
isDisabled: false
|
|
29289
29439
|
}
|
|
@@ -29310,12 +29460,14 @@ class QdPageComponent {
|
|
|
29310
29460
|
if (this._isInitialized)
|
|
29311
29461
|
this.operationModeChanged.emit(mode);
|
|
29312
29462
|
}
|
|
29313
|
-
|
|
29463
|
+
generateCommitActionHandler(action) {
|
|
29314
29464
|
return (...args) => {
|
|
29315
|
-
if (!handler)
|
|
29316
|
-
return;
|
|
29317
29465
|
const values = this.formGroupManagerService.hasFormGroups() ? this.formGroupManagerService.getAllValues() : args;
|
|
29318
|
-
|
|
29466
|
+
QdPageCommitActionExecutor.execute(action, values, {
|
|
29467
|
+
formGroupManager: this.formGroupManagerService,
|
|
29468
|
+
navigationInterceptor: this.navigationInterceptor,
|
|
29469
|
+
destroyed$: this._destroyed$
|
|
29470
|
+
});
|
|
29319
29471
|
};
|
|
29320
29472
|
}
|
|
29321
29473
|
setupCancelConfirmation() {
|
|
@@ -29324,8 +29476,8 @@ class QdPageComponent {
|
|
|
29324
29476
|
.showDiscardConfirmDialog(cancelConfig?.confirmationMessage, this.testId + '-cancel-confirmation')
|
|
29325
29477
|
.pipe(filter$1(result => result), takeUntil(this._destroyed$))
|
|
29326
29478
|
.subscribe(() => {
|
|
29479
|
+
this.formGroupManagerService.restoreFormGroupsFromSnapshot();
|
|
29327
29480
|
cancelConfig?.handler?.();
|
|
29328
|
-
this.navigationInterceptor.allowNextNavigation();
|
|
29329
29481
|
});
|
|
29330
29482
|
}
|
|
29331
29483
|
initSubmitValidation() {
|
|
@@ -29333,7 +29485,7 @@ class QdPageComponent {
|
|
|
29333
29485
|
this.formGroupManagerService
|
|
29334
29486
|
.$areFormGroupsValid()
|
|
29335
29487
|
.pipe(takeUntil(this._cancelSubmitValidation$), takeUntil(this._destroyed$), tap(isValid => {
|
|
29336
|
-
const submitDisabledInfoText = this.config.
|
|
29488
|
+
const submitDisabledInfoText = this.config.pageTypeConfig?.submit?.disabledInfo;
|
|
29337
29489
|
this.footerService.updateActions([
|
|
29338
29490
|
{
|
|
29339
29491
|
actionKey: 'submit',
|
|
@@ -32613,9 +32765,11 @@ class QdQuickEditComponent {
|
|
|
32613
32765
|
window.setTimeout(() => this.focusFirstControl());
|
|
32614
32766
|
}
|
|
32615
32767
|
removeRow(index) {
|
|
32616
|
-
|
|
32768
|
+
const { hasRemoveConfirmation, removeConfirmationMessage } = this.config;
|
|
32769
|
+
if (hasRemoveConfirmation || removeConfirmationMessage) {
|
|
32770
|
+
const message = removeConfirmationMessage ?? { i18n: 'i18n.qd.quick.edit.removeConfirmation.message' };
|
|
32617
32771
|
this.confirmationDialogService
|
|
32618
|
-
.showConfirmDialog({ message
|
|
32772
|
+
.showConfirmDialog({ message }, (this.testId ?? 'quick-edit') + '-remove-confirmation')
|
|
32619
32773
|
.pipe(filter$1(confirmed => confirmed))
|
|
32620
32774
|
.subscribe(() => this.executeRemoveRow(index));
|
|
32621
32775
|
return;
|