@valtimo/form-flow-management 13.43.0 → 13.45.0
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/valtimo-form-flow-management.mjs +1559 -186
- package/fesm2022/valtimo-form-flow-management.mjs.map +1 -1
- package/lib/components/editor/form-flow-editor.component.d.ts +34 -5
- package/lib/components/editor/form-flow-editor.component.d.ts.map +1 -1
- package/lib/components/editor/tabs/form-flow-json-editor-tab/form-flow-json-editor-tab.component.d.ts +17 -0
- package/lib/components/editor/tabs/form-flow-json-editor-tab/form-flow-json-editor-tab.component.d.ts.map +1 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-context-properties/form-flow-context-properties.component.d.ts +23 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-context-properties/form-flow-context-properties.component.d.ts.map +1 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-expression-help-modal/form-flow-expression-help-modal.component.d.ts +15 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-expression-help-modal/form-flow-expression-help-modal.component.d.ts.map +1 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-expression-list/form-flow-expression-list.component.d.ts +54 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-expression-list/form-flow-expression-list.component.d.ts.map +1 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-step-detail/form-flow-step-detail.component.d.ts +73 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-step-detail/form-flow-step-detail.component.d.ts.map +1 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-step-list/form-flow-step-list.component.d.ts +42 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-step-list/form-flow-step-list.component.d.ts.map +1 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-transition-list/form-flow-transition-list.component.d.ts +49 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-transition-list/form-flow-transition-list.component.d.ts.map +1 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-ui-editor-tab.component.d.ts +89 -0
- package/lib/components/editor/tabs/form-flow-ui-editor-tab/form-flow-ui-editor-tab.component.d.ts.map +1 -0
- package/lib/components/new-form-flow-modal/new-form-flow-modal.component.d.ts.map +1 -1
- package/lib/constants/form-flow-editor.test-ids.d.ts +19 -0
- package/lib/constants/form-flow-editor.test-ids.d.ts.map +1 -0
- package/lib/constants/index.d.ts +3 -0
- package/lib/constants/index.d.ts.map +1 -0
- package/lib/constants/injection-tokens.d.ts +5 -0
- package/lib/constants/injection-tokens.d.ts.map +1 -0
- package/lib/form-flow-management.module.d.ts +3 -1
- package/lib/form-flow-management.module.d.ts.map +1 -1
- package/lib/models/form-flow-custom-component.model.d.ts +18 -0
- package/lib/models/form-flow-custom-component.model.d.ts.map +1 -0
- package/lib/models/form-flow-editor.model.d.ts +6 -0
- package/lib/models/form-flow-editor.model.d.ts.map +1 -0
- package/lib/models/form-flow.model.d.ts +8 -6
- package/lib/models/form-flow.model.d.ts.map +1 -1
- package/lib/models/index.d.ts +2 -0
- package/lib/models/index.d.ts.map +1 -1
- package/lib/services/form-flow-component.service.d.ts +13 -0
- package/lib/services/form-flow-component.service.d.ts.map +1 -0
- package/lib/services/form-flow-download.service.d.ts.map +1 -1
- package/lib/services/form-flow-editor-form.service.d.ts +52 -0
- package/lib/services/form-flow-editor-form.service.d.ts.map +1 -0
- package/lib/services/form-flow.service.d.ts +2 -1
- package/lib/services/form-flow.service.d.ts.map +1 -1
- package/lib/services/index.d.ts +1 -0
- package/lib/services/index.d.ts.map +1 -1
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/translate.utils.d.ts +8 -0
- package/lib/utils/translate.utils.d.ts.map +1 -0
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
- package/public-api.d.ts.map +1 -1
|
@@ -1,21 +1,89 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, Component, ViewChild, NgModule } from '@angular/core';
|
|
2
|
+
import { InjectionToken, Injectable, Inject, EventEmitter, Output, Input, ChangeDetectionStrategy, Component, ViewChild, signal, ViewChildren, NgModule } from '@angular/core';
|
|
3
3
|
import * as i2 from '@valtimo/shared';
|
|
4
4
|
import { BaseApiService, getCaseManagementRouteParams, getContextObservable, getBuildingBlockManagementRouteParams } from '@valtimo/shared';
|
|
5
5
|
import * as i1 from '@angular/common/http';
|
|
6
|
+
import { filter, BehaviorSubject, combineLatest, tap, switchMap, of, map, Subscription, take, catchError, shareReplay, finalize } from 'rxjs';
|
|
6
7
|
import * as i1$2 from '@valtimo/components';
|
|
7
|
-
import { CARBON_CONSTANTS, ValtimoCdsModalDirective, ViewType, CarbonListComponent,
|
|
8
|
-
import
|
|
9
|
-
import * as i3$1 from '@angular/router';
|
|
8
|
+
import { CARBON_CONSTANTS, ValtimoCdsModalDirective, ViewType, CarbonListComponent, EditorModule, OverflowMenuComponent, OverflowMenuOptionComponent, TooltipIconModule, FitPageDirective, PendingChangesComponent, ConfirmationModalModule, RenderInPageHeaderDirective, OverflowMenuTriggerComponent, CarbonListModule } from '@valtimo/components';
|
|
9
|
+
import * as i3 from '@angular/router';
|
|
10
10
|
import * as i4 from '@ngx-translate/core';
|
|
11
11
|
import { TranslateModule } from '@ngx-translate/core';
|
|
12
12
|
import * as i5 from '@angular/common';
|
|
13
13
|
import { CommonModule } from '@angular/common';
|
|
14
|
-
import * as
|
|
15
|
-
import { ModalModule, InputModule, ButtonModule, IconModule,
|
|
14
|
+
import * as i2$1 from 'carbon-components-angular';
|
|
15
|
+
import { ModalModule, InputModule, ButtonModule, IconModule, DropdownModule, LayerModule, NotificationModule, TagModule, LoadingModule, Tab, TabsModule } from 'carbon-components-angular';
|
|
16
16
|
import * as i1$1 from '@angular/forms';
|
|
17
17
|
import { Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
18
|
-
import { ArrowLeft16 } from '@carbon/icons';
|
|
18
|
+
import { Add16, TrashCan16, ArrowDown16, ArrowUp16, Flag16, WarningFilled16, ArrowLeft16 } from '@carbon/icons';
|
|
19
|
+
import * as i4$1 from '@valtimo/form-management';
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
23
|
+
*
|
|
24
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
25
|
+
* you may not use this file except in compliance with the License.
|
|
26
|
+
* You may obtain a copy of the License at
|
|
27
|
+
*
|
|
28
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
29
|
+
*
|
|
30
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
31
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
32
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
33
|
+
* See the License for the specific language governing permissions and
|
|
34
|
+
* limitations under the License.
|
|
35
|
+
*/
|
|
36
|
+
const FORM_FLOW_EDITOR_TEST_IDS = {
|
|
37
|
+
saveButton: 'formFlowEditorSaveButton',
|
|
38
|
+
backButton: 'formFlowEditorBackButton',
|
|
39
|
+
stepList: 'formFlowEditorStepList',
|
|
40
|
+
stepListItem: 'formFlowEditorStepListItem',
|
|
41
|
+
addStepButton: 'formFlowEditorAddStepButton',
|
|
42
|
+
deleteStepButton: 'formFlowEditorDeleteStepButton',
|
|
43
|
+
makeStartStepButton: 'formFlowEditorMakeStartStepButton',
|
|
44
|
+
stepKeyInput: 'formFlowEditorStepKeyInput',
|
|
45
|
+
stepTitleInput: 'formFlowEditorStepTitleInput',
|
|
46
|
+
stepTypeDropdown: 'formFlowEditorStepTypeDropdown',
|
|
47
|
+
stepPropertyInput: 'formFlowEditorStepPropertyInput',
|
|
48
|
+
stepPropertyDropdown: 'formFlowEditorStepPropertyDropdown',
|
|
49
|
+
addTransitionButton: 'formFlowEditorAddTransitionButton',
|
|
50
|
+
transitionRow: 'formFlowEditorTransitionRow',
|
|
51
|
+
addExpressionButton: 'formFlowEditorAddExpressionButton',
|
|
52
|
+
expressionInput: 'formFlowEditorExpressionInput',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
57
|
+
*
|
|
58
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
59
|
+
* you may not use this file except in compliance with the License.
|
|
60
|
+
* You may obtain a copy of the License at
|
|
61
|
+
*
|
|
62
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
63
|
+
*
|
|
64
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
65
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
66
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
67
|
+
* See the License for the specific language governing permissions and
|
|
68
|
+
* limitations under the License.
|
|
69
|
+
*/
|
|
70
|
+
const FORM_FLOW_COMPONENT_TOKEN = new InjectionToken('Supported form-flow Angular components', { factory: () => [] });
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
74
|
+
*
|
|
75
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
76
|
+
* you may not use this file except in compliance with the License.
|
|
77
|
+
* You may obtain a copy of the License at
|
|
78
|
+
*
|
|
79
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
80
|
+
*
|
|
81
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
82
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
83
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
84
|
+
* See the License for the specific language governing permissions and
|
|
85
|
+
* limitations under the License.
|
|
86
|
+
*/
|
|
19
87
|
|
|
20
88
|
/*
|
|
21
89
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
@@ -33,6 +101,43 @@ import { ArrowLeft16 } from '@carbon/icons';
|
|
|
33
101
|
* limitations under the License.
|
|
34
102
|
*/
|
|
35
103
|
|
|
104
|
+
/*
|
|
105
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
106
|
+
*
|
|
107
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
108
|
+
* you may not use this file except in compliance with the License.
|
|
109
|
+
* You may obtain a copy of the License at
|
|
110
|
+
*
|
|
111
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
112
|
+
*
|
|
113
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
114
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
115
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
116
|
+
* See the License for the specific language governing permissions and
|
|
117
|
+
* limitations under the License.
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/*
|
|
121
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
122
|
+
*
|
|
123
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
124
|
+
* you may not use this file except in compliance with the License.
|
|
125
|
+
* You may obtain a copy of the License at
|
|
126
|
+
*
|
|
127
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
128
|
+
*
|
|
129
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
130
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
131
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
132
|
+
* See the License for the specific language governing permissions and
|
|
133
|
+
* limitations under the License.
|
|
134
|
+
*/
|
|
135
|
+
var FormFlowEditorTab;
|
|
136
|
+
(function (FormFlowEditorTab) {
|
|
137
|
+
FormFlowEditorTab["EDITOR"] = "editor";
|
|
138
|
+
FormFlowEditorTab["JSON_EDITOR"] = "jsonEditor";
|
|
139
|
+
})(FormFlowEditorTab || (FormFlowEditorTab = {}));
|
|
140
|
+
|
|
36
141
|
/*
|
|
37
142
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
38
143
|
*
|
|
@@ -58,6 +163,12 @@ class FormFlowService extends BaseApiService {
|
|
|
58
163
|
getFormFlowDefinitionSchema() {
|
|
59
164
|
return this.httpClient.get(this.getApiUrl('management/v1/form-flow-definition/schema'));
|
|
60
165
|
}
|
|
166
|
+
// The registry describes what can be used in a form flow definition (step types, expression
|
|
167
|
+
// beans and additional properties). The backend builds it once at startup, so this is a cheap
|
|
168
|
+
// call that is made fresh every time — no client-side caching.
|
|
169
|
+
getFormFlowRegistry() {
|
|
170
|
+
return this.httpClient.get(this.getApiUrl('management/v1/form-flow/registry'));
|
|
171
|
+
}
|
|
61
172
|
getFormFlowDefinitions(caseDefinitionKey, caseVersionTag) {
|
|
62
173
|
return this.httpClient.get(this.getApiUrl(`management/v1/case-definition/${caseDefinitionKey}/version/${caseVersionTag}/form-flow-definition`));
|
|
63
174
|
}
|
|
@@ -98,6 +209,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
98
209
|
}]
|
|
99
210
|
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ConfigService }] });
|
|
100
211
|
|
|
212
|
+
/*
|
|
213
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
214
|
+
*
|
|
215
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
216
|
+
* you may not use this file except in compliance with the License.
|
|
217
|
+
* You may obtain a copy of the License at
|
|
218
|
+
*
|
|
219
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
220
|
+
*
|
|
221
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
222
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
223
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
224
|
+
* See the License for the specific language governing permissions and
|
|
225
|
+
* limitations under the License.
|
|
226
|
+
*/
|
|
227
|
+
class FormFlowComponentService {
|
|
228
|
+
get supportedComponents$() {
|
|
229
|
+
return this._supportedComponents$.pipe(filter(components => !!components));
|
|
230
|
+
}
|
|
231
|
+
constructor(supportedCustomComponents) {
|
|
232
|
+
this.supportedCustomComponents = supportedCustomComponents;
|
|
233
|
+
this._supportedComponents$ = new BehaviorSubject(null);
|
|
234
|
+
this.setSupportedComponents(supportedCustomComponents);
|
|
235
|
+
}
|
|
236
|
+
setSupportedComponents(supportedComponents) {
|
|
237
|
+
this._supportedComponents$.next(supportedComponents);
|
|
238
|
+
}
|
|
239
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowComponentService, deps: [{ token: FORM_FLOW_COMPONENT_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
240
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowComponentService, providedIn: 'root' }); }
|
|
241
|
+
}
|
|
242
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowComponentService, decorators: [{
|
|
243
|
+
type: Injectable,
|
|
244
|
+
args: [{
|
|
245
|
+
providedIn: 'root',
|
|
246
|
+
}]
|
|
247
|
+
}], ctorParameters: () => [{ type: Array, decorators: [{
|
|
248
|
+
type: Inject,
|
|
249
|
+
args: [FORM_FLOW_COMPONENT_TOKEN]
|
|
250
|
+
}] }] });
|
|
251
|
+
|
|
101
252
|
/*
|
|
102
253
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
103
254
|
*
|
|
@@ -152,7 +303,16 @@ class NewFormFlowModalComponent {
|
|
|
152
303
|
this.closeEvent.emit({
|
|
153
304
|
key: this.key.value ?? '',
|
|
154
305
|
startStep: 'start-step',
|
|
155
|
-
|
|
306
|
+
// Every form flow needs a start step, so create one up front: without it the editor opens on a
|
|
307
|
+
// "start step does not exist" error for a flow the user has only just named. The form
|
|
308
|
+
// definition is intentionally left blank — the editor flags it as the field still to fill in.
|
|
309
|
+
steps: [
|
|
310
|
+
{
|
|
311
|
+
key: 'start-step',
|
|
312
|
+
type: { name: 'form', properties: { definition: '' } },
|
|
313
|
+
nextSteps: [],
|
|
314
|
+
},
|
|
315
|
+
],
|
|
156
316
|
});
|
|
157
317
|
this.resetForm();
|
|
158
318
|
}
|
|
@@ -162,7 +322,7 @@ class NewFormFlowModalComponent {
|
|
|
162
322
|
}, CARBON_CONSTANTS.modalAnimationMs);
|
|
163
323
|
}
|
|
164
324
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: NewFormFlowModalComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
165
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: NewFormFlowModalComponent, isStandalone: true, selector: "valtimo-new-form-flow-modal", inputs: { open: "open" }, outputs: { closeEvent: "closeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n [open]=\"open\"\n showFooter=\"true\"\n [title]=\"'formFlow.add' | translate\"\n valtimoCdsModal\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{ 'formFlow.add' | translate }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <form [formGroup]=\"form\">\n <cds-label [invalid]=\"key.dirty && key.invalid\">\n {{ 'formFlow.key' | translate }}\n\n <input\n formControlName=\"key\"\n cdsText\n placeholder=\"{{ 'formFlow.key' | translate }}\"\n [attr.modal-primary-focus]=\"true\"\n [invalid]=\"key.dirty && key.invalid\"\n />\n </cds-label>\n </form>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button [disabled]=\"this.form.invalid\" (click)=\"onConfirm()\" cdsButton=\"primary\">\n {{ 'interface.create' | translate }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ModalModule }, { kind: "component", type:
|
|
325
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: NewFormFlowModalComponent, isStandalone: true, selector: "valtimo-new-form-flow-modal", inputs: { open: "open" }, outputs: { closeEvent: "closeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n [open]=\"open\"\n showFooter=\"true\"\n [title]=\"'formFlow.add' | translate\"\n valtimoCdsModal\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{ 'formFlow.add' | translate }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <form [formGroup]=\"form\">\n <cds-label [invalid]=\"key.dirty && key.invalid\">\n {{ 'formFlow.key' | translate }}\n\n <input\n formControlName=\"key\"\n cdsText\n placeholder=\"{{ 'formFlow.key' | translate }}\"\n [attr.modal-primary-focus]=\"true\"\n [invalid]=\"key.dirty && key.invalid\"\n />\n </cds-label>\n </form>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button [disabled]=\"this.form.invalid\" (click)=\"onConfirm()\" cdsButton=\"primary\">\n {{ 'interface.create' | translate }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ModalModule }, { kind: "component", type: i2$1.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$1.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$1.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$1.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$1.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i2$1.Label, selector: "cds-label, ibm-label", inputs: ["labelInputID", "disabled", "skeleton", "helperText", "invalidText", "invalid", "warn", "warnText", "ariaLabel"] }, { kind: "directive", type: i2$1.TextInput, selector: "[cdsText], [ibmText]", inputs: ["theme", "size", "invalid", "warn", "skeleton"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: ValtimoCdsModalDirective, selector: "[valtimoCdsModal]", inputs: ["minContentHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
166
326
|
}
|
|
167
327
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: NewFormFlowModalComponent, decorators: [{
|
|
168
328
|
type: Component,
|
|
@@ -289,13 +449,13 @@ class FormFlowOverviewComponent {
|
|
|
289
449
|
this._refresh$.next(null);
|
|
290
450
|
});
|
|
291
451
|
}
|
|
292
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowOverviewComponent, deps: [{ token: FormFlowService }, { token: i2.GlobalNotificationService }, { token: i3
|
|
293
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: FormFlowOverviewComponent, isStandalone: false, selector: "ng-component", viewQueries: [{ propertyName: "carbonList", first: true, predicate: CarbonListComponent, descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [actionItems]=\"ACTION_ITEMS\"\n [header]=\"false\"\n [items]=\"formFlowDefinitions$ | async\"\n [fields]=\"FIELDS\"\n [loading]=\"loading$ | async\"\n (rowClicked)=\"editFormFlowDetails($event)\"\n>\n <ng-container header> {{ 'formFlow.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addFormFlowButton\"></ng-container>\n </div>\n\n <valtimo-no-results\n [action]=\"addFormFlowButton\"\n description=\"{{ 'formFlow.noResults.description' | translate }}\"\n title=\"{{ 'formFlow.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-new-form-flow-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-new-form-flow-modal>\n\n<ng-template #addFormFlowButton>\n <button *ngIf=\"hasEditPermissions$ | async\" cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'formFlow.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"formFlow.deleteModalContent\"\n [outputOnConfirm]=\"deleteDefinitionKey$ | async\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete($event)\"\n></valtimo-confirmation-modal>\n", dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
|
|
452
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowOverviewComponent, deps: [{ token: FormFlowService }, { token: i2.GlobalNotificationService }, { token: i3.ActivatedRoute }, { token: i3.Router }, { token: i4.TranslateService }, { token: i2.EditPermissionsService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
453
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: FormFlowOverviewComponent, isStandalone: false, selector: "ng-component", viewQueries: [{ propertyName: "carbonList", first: true, predicate: CarbonListComponent, descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [actionItems]=\"ACTION_ITEMS\"\n [header]=\"false\"\n [items]=\"formFlowDefinitions$ | async\"\n [fields]=\"FIELDS\"\n [loading]=\"loading$ | async\"\n (rowClicked)=\"editFormFlowDetails($event)\"\n>\n <ng-container header> {{ 'formFlow.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addFormFlowButton\"></ng-container>\n </div>\n\n <valtimo-no-results\n [action]=\"addFormFlowButton\"\n description=\"{{ 'formFlow.noResults.description' | translate }}\"\n title=\"{{ 'formFlow.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-new-form-flow-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-new-form-flow-modal>\n\n<ng-template #addFormFlowButton>\n <button *ngIf=\"hasEditPermissions$ | async\" cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'formFlow.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"formFlow.deleteModalContent\"\n [outputOnConfirm]=\"deleteDefinitionKey$ | async\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete($event)\"\n></valtimo-confirmation-modal>\n", dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i1$2.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }, { kind: "component", type: i1$2.CarbonListComponent, selector: "valtimo-carbon-list", inputs: ["items", "fields", "tableTranslations", "paginatorConfig", "pagination", "loading", "skeletonRowCount", "actions", "actionItems", "showActionItems", "header", "hideColumnHeader", "initialSortState", "sortState", "isSearchable", "initialSearchValue", "searchDebounceMs", "invalidSearchFields", "searchFields", "enableSingleSelection", "lastColumnTemplate", "paginationIdentifier", "showSelectionColumn", "striped", "hideToolbar", "lockedTooltipTranslationKey", "movingRowsEnabled", "dragAndDrop", "dragAndDropDisabled", "expandedRowTemplate", "expandedRowKey", "trackByKey"], outputs: ["rowClicked", "paginationClicked", "paginationSet", "search", "sortChanged", "moveRow", "itemsReordered"] }, { kind: "component", type: i1$2.CarbonNoResultsComponent, selector: "valtimo-no-results", inputs: ["action", "description", "illustration", "title", "smallPadding", "collapseVertically", "alwaysRenderVertically"] }, { kind: "component", type: NewFormFlowModalComponent, selector: "valtimo-new-form-flow-modal", inputs: ["open"], outputs: ["closeEvent"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
294
454
|
}
|
|
295
455
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowOverviewComponent, decorators: [{
|
|
296
456
|
type: Component,
|
|
297
457
|
args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [actionItems]=\"ACTION_ITEMS\"\n [header]=\"false\"\n [items]=\"formFlowDefinitions$ | async\"\n [fields]=\"FIELDS\"\n [loading]=\"loading$ | async\"\n (rowClicked)=\"editFormFlowDetails($event)\"\n>\n <ng-container header> {{ 'formFlow.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addFormFlowButton\"></ng-container>\n </div>\n\n <valtimo-no-results\n [action]=\"addFormFlowButton\"\n description=\"{{ 'formFlow.noResults.description' | translate }}\"\n title=\"{{ 'formFlow.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-new-form-flow-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-new-form-flow-modal>\n\n<ng-template #addFormFlowButton>\n <button *ngIf=\"hasEditPermissions$ | async\" cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'formFlow.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"formFlow.deleteModalContent\"\n [outputOnConfirm]=\"deleteDefinitionKey$ | async\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete($event)\"\n></valtimo-confirmation-modal>\n" }]
|
|
298
|
-
}], ctorParameters: () => [{ type: FormFlowService }, { type: i2.GlobalNotificationService }, { type: i3
|
|
458
|
+
}], ctorParameters: () => [{ type: FormFlowService }, { type: i2.GlobalNotificationService }, { type: i3.ActivatedRoute }, { type: i3.Router }, { type: i4.TranslateService }, { type: i2.EditPermissionsService }], propDecorators: { carbonList: [{
|
|
299
459
|
type: ViewChild,
|
|
300
460
|
args: [CarbonListComponent]
|
|
301
461
|
}] } });
|
|
@@ -335,7 +495,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
335
495
|
}] });
|
|
336
496
|
|
|
337
497
|
/*
|
|
338
|
-
* Copyright 2015-
|
|
498
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
339
499
|
*
|
|
340
500
|
* Licensed under EUPL, Version 1.2 (the "License");
|
|
341
501
|
* you may not use this file except in compliance with the License.
|
|
@@ -349,177 +509,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
349
509
|
* See the License for the specific language governing permissions and
|
|
350
510
|
* limitations under the License.
|
|
351
511
|
*/
|
|
352
|
-
class
|
|
353
|
-
constructor(
|
|
354
|
-
this.
|
|
355
|
-
this.
|
|
356
|
-
this.
|
|
357
|
-
this.
|
|
358
|
-
this.notificationService = notificationService;
|
|
359
|
-
this.pageHeaderService = pageHeaderService;
|
|
360
|
-
this.pageTitleService = pageTitleService;
|
|
361
|
-
this.route = route;
|
|
362
|
-
this.router = router;
|
|
363
|
-
this.translateService = translateService;
|
|
364
|
-
this.readOnly$ = new BehaviorSubject(false);
|
|
365
|
-
this.valid$ = new BehaviorSubject(false);
|
|
366
|
-
this.loading$ = new BehaviorSubject(true);
|
|
367
|
-
this.showDeleteModal$ = new BehaviorSubject(false);
|
|
368
|
-
this.CARBON_THEME = 'g10';
|
|
369
|
-
this._context$ = getContextObservable(this.route);
|
|
370
|
-
this._params$ = this._context$.pipe(switchMap(context => {
|
|
371
|
-
if (context === 'buildingBlock') {
|
|
372
|
-
return combineLatest([
|
|
373
|
-
getBuildingBlockManagementRouteParams(this.route),
|
|
374
|
-
this.route.params,
|
|
375
|
-
]).pipe(map(([bbParams, params]) => ({
|
|
376
|
-
caseDefinitionKey: bbParams?.buildingBlockDefinitionKey ?? '',
|
|
377
|
-
caseDefinitionVersionTag: bbParams?.buildingBlockDefinitionVersionTag ?? '',
|
|
378
|
-
formFlowDefinitionKey: params.formFlowDefinitionKey,
|
|
379
|
-
})));
|
|
380
|
-
}
|
|
381
|
-
return combineLatest([
|
|
382
|
-
getCaseManagementRouteParams(this.route),
|
|
383
|
-
this.route.params,
|
|
384
|
-
]).pipe(map(([caseManagementParams, params]) => ({
|
|
385
|
-
...(caseManagementParams ?? { caseDefinitionKey: '', caseDefinitionVersionTag: '' }),
|
|
386
|
-
...params,
|
|
387
|
-
})));
|
|
388
|
-
}));
|
|
389
|
-
this.formFlowSchemaJson$ = this.formFlowService
|
|
390
|
-
.getFormFlowDefinitionSchema()
|
|
391
|
-
.pipe(shareReplay(1));
|
|
512
|
+
class FormFlowJsonEditorTabComponent {
|
|
513
|
+
constructor() {
|
|
514
|
+
this.disabled = false;
|
|
515
|
+
this.schema = null;
|
|
516
|
+
this.validEvent = new EventEmitter();
|
|
517
|
+
this.valueChangeEvent = new EventEmitter();
|
|
392
518
|
this.editorOptions = {
|
|
393
519
|
quickSuggestions: { other: true, comments: false, strings: true },
|
|
394
520
|
};
|
|
395
|
-
this._formFlowDefinition2$ = combineLatest([this._params$, this._context$]).pipe(tap(() => this.loading$.next(true)), switchMap(([params, context]) => {
|
|
396
|
-
this.initBreadcrumbs(params, context);
|
|
397
|
-
if (context === 'buildingBlock') {
|
|
398
|
-
return this.formFlowService.getBuildingBlockFormFlowDefinitionByKey(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey);
|
|
399
|
-
}
|
|
400
|
-
return this.formFlowService.getFormFlowDefinitionByKey(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey);
|
|
401
|
-
}), tap((formFlowDefinition) => {
|
|
402
|
-
this.pageTitleService.setCustomPageTitle(formFlowDefinition.key);
|
|
403
|
-
this.readOnly$.next(formFlowDefinition.readOnly === true);
|
|
404
|
-
this.loading$.next(false);
|
|
405
|
-
}));
|
|
406
|
-
this.model$ = this._formFlowDefinition2$.pipe(map((formFlowDefinition) => this.getEditorModel(formFlowDefinition)));
|
|
407
|
-
this._updatedModelValue$ = new BehaviorSubject('');
|
|
408
|
-
this.compactMode$ = this.pageHeaderService.compactMode$;
|
|
409
|
-
this.iconService.registerAll([ArrowLeft16]);
|
|
410
|
-
this.pageTitleService.disableReset();
|
|
411
|
-
}
|
|
412
|
-
ngOnDestroy() {
|
|
413
|
-
this.pageTitleService.enableReset();
|
|
414
|
-
this.breadcrumbService.clearThirdBreadcrumb();
|
|
415
|
-
this.breadcrumbService.clearFourthBreadcrumb();
|
|
416
521
|
}
|
|
417
522
|
onValid(valid) {
|
|
418
|
-
this.
|
|
523
|
+
this.validEvent.emit(valid);
|
|
419
524
|
}
|
|
420
525
|
onValueChange(value) {
|
|
421
|
-
this.
|
|
422
|
-
}
|
|
423
|
-
updateFormFlowDefinition() {
|
|
424
|
-
this.loading$.next(true);
|
|
425
|
-
combineLatest([this._params$, this._updatedModelValue$, this._context$])
|
|
426
|
-
.pipe(take(1), switchMap(([params, updatedModelValue, context]) => {
|
|
427
|
-
const updatedDefinition = {
|
|
428
|
-
...JSON.parse(updatedModelValue),
|
|
429
|
-
key: params.formFlowDefinitionKey,
|
|
430
|
-
};
|
|
431
|
-
if (context === 'buildingBlock') {
|
|
432
|
-
return this.formFlowService.updateBuildingBlockFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey, updatedDefinition);
|
|
433
|
-
}
|
|
434
|
-
return this.formFlowService.updateFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey, updatedDefinition);
|
|
435
|
-
}), finalize(() => this.loading$.next(false)))
|
|
436
|
-
.subscribe(result => {
|
|
437
|
-
this.showSuccessMessage(result.key);
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
onDelete() {
|
|
441
|
-
this.loading$.next(true);
|
|
442
|
-
combineLatest([this._params$, this._context$])
|
|
443
|
-
.pipe(take(1), switchMap(([params, context]) => {
|
|
444
|
-
if (context === 'buildingBlock') {
|
|
445
|
-
return this.formFlowService.deleteBuildingBlockFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey);
|
|
446
|
-
}
|
|
447
|
-
return this.formFlowService.deleteFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey);
|
|
448
|
-
}))
|
|
449
|
-
.subscribe(() => {
|
|
450
|
-
this.router.navigate(['../'], { relativeTo: this.route });
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
showDeleteModal() {
|
|
454
|
-
this.showDeleteModal$.next(true);
|
|
526
|
+
this.valueChangeEvent.emit(value);
|
|
455
527
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
.pipe(take(1))
|
|
459
|
-
.subscribe((params) => this.formFlowDownloadService.downloadJson(JSON.parse(model.value), params));
|
|
460
|
-
}
|
|
461
|
-
navigateBack() {
|
|
462
|
-
this.router.navigate(['../'], { relativeTo: this.route });
|
|
463
|
-
}
|
|
464
|
-
getEditorModel(formFlowDefinition) {
|
|
465
|
-
const clone = { ...formFlowDefinition };
|
|
466
|
-
delete clone.readOnly;
|
|
467
|
-
return {
|
|
468
|
-
value: JSON.stringify(clone),
|
|
469
|
-
language: 'json',
|
|
470
|
-
uri: `inmemory://form-flow/${formFlowDefinition.key}.formflow.json`,
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
showSuccessMessage(formFlowDefinitionKey) {
|
|
474
|
-
this.notificationService.showToast({
|
|
475
|
-
caption: this.translateService.instant('formFlow.savedSuccessTitleMessage', {
|
|
476
|
-
key: formFlowDefinitionKey,
|
|
477
|
-
}),
|
|
478
|
-
type: 'success',
|
|
479
|
-
title: this.translateService.instant('formFlow.savedSuccessTitle'),
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
initBreadcrumbs(params, context) {
|
|
483
|
-
if (context === 'buildingBlock') {
|
|
484
|
-
const route = `/building-block-management/building-block/${params.caseDefinitionKey}/version/${params.caseDefinitionVersionTag}`;
|
|
485
|
-
const generalRoute = `${route}/general`;
|
|
486
|
-
this.breadcrumbService.setThirdBreadcrumb({
|
|
487
|
-
route: [generalRoute],
|
|
488
|
-
content: `${params.caseDefinitionKey} (${params.caseDefinitionVersionTag})`,
|
|
489
|
-
href: generalRoute,
|
|
490
|
-
});
|
|
491
|
-
const routeWithFormFlows = `${route}/form-flows`;
|
|
492
|
-
this.breadcrumbService.setFourthBreadcrumb({
|
|
493
|
-
route: [routeWithFormFlows],
|
|
494
|
-
content: this.translateService.instant('buildingBlockManagement.tabs.formFlows'),
|
|
495
|
-
href: routeWithFormFlows,
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
else {
|
|
499
|
-
const route = `/case-management/case/${params.caseDefinitionKey}/version/${params.caseDefinitionVersionTag}`;
|
|
500
|
-
this.breadcrumbService.setThirdBreadcrumb({
|
|
501
|
-
route: [route],
|
|
502
|
-
content: `${params.caseDefinitionKey} (${params.caseDefinitionVersionTag})`,
|
|
503
|
-
href: route,
|
|
504
|
-
});
|
|
505
|
-
const routeWithFormFlows = `${route}/form-flows`;
|
|
506
|
-
this.breadcrumbService.setFourthBreadcrumb({
|
|
507
|
-
route: [routeWithFormFlows],
|
|
508
|
-
content: this.translateService.instant('caseManagement.tabs.formFlows'),
|
|
509
|
-
href: routeWithFormFlows,
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowEditorComponent, deps: [{ token: i1$2.BreadcrumbService }, { token: FormFlowDownloadService }, { token: FormFlowService }, { token: i3.IconService }, { token: i2.GlobalNotificationService }, { token: i1$2.PageHeaderService }, { token: i1$2.PageTitleService }, { token: i3$1.ActivatedRoute }, { token: i3$1.Router }, { token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
514
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowEditorComponent, isStandalone: false, selector: "ng-component", ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n@if ({model: model$ | async, schema: formFlowSchemaJson$ | async}; as obs) {\n @if (obs.model && obs.schema) {\n <valtimo-editor\n [disabled]=\"(loading$ | async) === false && readOnly$ | async\"\n [editorOptions]=\"editorOptions\"\n [model]=\"obs.model\"\n [jsonSchema]=\"obs.schema\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n [fitPage]=\"true\"\n ></valtimo-editor>\n } @else {\n <cds-loading></cds-loading>\n }\n}\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n compactMode: compactMode$ | async,\n valid: valid$ | async,\n readOnly: readOnly$ | async,\n model: model$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\n <v-overflow-menu placement=\"bottom-end\">\n <v-overflow-menu-trigger overflowTrigger></v-overflow-menu-trigger>\n <v-overflow-menu-option\n [disabled]=\"!obs.valid && !obs.readOnly\"\n (selected)=\"downloadFormFlowDefinition(obs.model)\"\n >{{ 'interface.export' | translate }}\n </v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.readOnly\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}\n </v-overflow-menu-option>\n </v-overflow-menu>\n\n <button cdsButton=\"secondary\" [size]=\"obs.compactMode ? 'sm' : 'md'\" (click)=\"navigateBack()\">\n {{ 'interface.back' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"arrow--left\" size=\"16\"></svg>\n </button>\n\n <button\n [disabled]=\"!obs.valid || obs.readOnly\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n (click)=\"updateFormFlowDefinition()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"formFlow.deleteModalContent\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete()\"\n></valtimo-confirmation-modal>\n", styles: ["cds-loading{display:flex;justify-content:center}.buttons-container{display:flex;align-items:center;justify-self:flex-end;gap:16px}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i3.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i1$2.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }, { kind: "component", type: i1$2.EditorComponent, selector: "valtimo-editor", inputs: ["editorOptions", "model", "disabled", "formatOnLoad", "widthPx", "heightPx", "heightStyle", "jsonSchema", "fitPage"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "directive", type: i1$2.RenderInPageHeaderDirective, selector: "[renderInPageHeader]", inputs: ["fullWidth"] }, { kind: "component", type: i3.Loading, selector: "cds-loading, ibm-loading", inputs: ["title", "isActive", "size", "overlay"] }, { kind: "component", type: i1$2.OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: i1$2.OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }, { kind: "component", type: i1$2.OverflowMenuTriggerComponent, selector: "v-overflow-menu-trigger", inputs: ["compact", "label"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
528
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowJsonEditorTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
529
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowJsonEditorTabComponent, isStandalone: true, selector: "valtimo-form-flow-json-editor-tab", inputs: { disabled: "disabled", model: "model", schema: "schema" }, outputs: { validEvent: "validEvent", valueChangeEvent: "valueChangeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n@if (schema) {\n <valtimo-editor\n [disabled]=\"disabled ?? false\"\n [editorOptions]=\"editorOptions\"\n [fitPage]=\"true\"\n [jsonSchema]=\"schema\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-editor>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i1$2.EditorComponent, selector: "valtimo-editor", inputs: ["editorOptions", "model", "disabled", "formatOnLoad", "widthPx", "heightPx", "heightStyle", "jsonSchema", "fitPage"], outputs: ["validEvent", "valueChangeEvent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
515
530
|
}
|
|
516
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type:
|
|
531
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowJsonEditorTabComponent, decorators: [{
|
|
517
532
|
type: Component,
|
|
518
|
-
args: [{ standalone:
|
|
519
|
-
}],
|
|
533
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-json-editor-tab', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, EditorModule], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n@if (schema) {\n <valtimo-editor\n [disabled]=\"disabled ?? false\"\n [editorOptions]=\"editorOptions\"\n [fitPage]=\"true\"\n [jsonSchema]=\"schema\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-editor>\n}\n" }]
|
|
534
|
+
}], propDecorators: { disabled: [{
|
|
535
|
+
type: Input
|
|
536
|
+
}], model: [{
|
|
537
|
+
type: Input
|
|
538
|
+
}], schema: [{
|
|
539
|
+
type: Input
|
|
540
|
+
}], validEvent: [{
|
|
541
|
+
type: Output
|
|
542
|
+
}], valueChangeEvent: [{
|
|
543
|
+
type: Output
|
|
544
|
+
}] } });
|
|
520
545
|
|
|
521
546
|
/*
|
|
522
|
-
* Copyright 2015-
|
|
547
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
523
548
|
*
|
|
524
549
|
* Licensed under EUPL, Version 1.2 (the "License");
|
|
525
550
|
* you may not use this file except in compliance with the License.
|
|
@@ -533,9 +558,1354 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
533
558
|
* See the License for the specific language governing permissions and
|
|
534
559
|
* limitations under the License.
|
|
535
560
|
*/
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
561
|
+
/**
|
|
562
|
+
* Builds and reads the reactive form tree that backs the visual form flow editor.
|
|
563
|
+
*
|
|
564
|
+
* The form mirrors the definition JSON: a `startStep` control and a `steps` array where every step
|
|
565
|
+
* has its key, title, type (name plus a dynamic properties group driven by the registry),
|
|
566
|
+
* transitions and the three expression lists. Serialization produces the same JSON contract the
|
|
567
|
+
* JSON editor tab emits, so both tabs are interchangeable to the surrounding editor page.
|
|
568
|
+
*/
|
|
569
|
+
class FormFlowEditorFormService {
|
|
570
|
+
constructor(fb) {
|
|
571
|
+
this.fb = fb;
|
|
572
|
+
}
|
|
573
|
+
buildDefinitionForm(definition, registry) {
|
|
574
|
+
return this.fb.group({
|
|
575
|
+
startStep: this.fb.control(definition.startStep ?? '', Validators.required),
|
|
576
|
+
steps: this.fb.array((definition.steps ?? []).map(step => this.buildStepGroup(step, registry)), [uniqueStepKeysValidator]),
|
|
577
|
+
}, { validators: [startStepExistsValidator, transitionTargetsExistValidator] });
|
|
578
|
+
}
|
|
579
|
+
buildStepGroup(step, registry) {
|
|
580
|
+
const typeName = step.type?.name ?? '';
|
|
581
|
+
// The legacy single `nextStep` field is normalized into a `nextSteps` transition, mirroring
|
|
582
|
+
// how the backend interprets it.
|
|
583
|
+
const transitions = step.nextSteps ?? (step.nextStep ? [{ step: step.nextStep }] : []);
|
|
584
|
+
return this.fb.group({
|
|
585
|
+
key: this.fb.control(step.key ?? '', Validators.required),
|
|
586
|
+
title: this.fb.control(step.title ?? ''),
|
|
587
|
+
typeName: this.fb.control(typeName, Validators.required),
|
|
588
|
+
properties: this.buildPropertiesGroup(typeName, (step.type?.properties ?? {}), registry),
|
|
589
|
+
nextSteps: this.fb.array(transitions.map(transition => this.buildTransitionGroup(transition)), [singleDefaultTransitionValidator]),
|
|
590
|
+
onOpen: this.buildExpressionArray(step.onOpen),
|
|
591
|
+
onComplete: this.buildExpressionArray(step.onComplete),
|
|
592
|
+
onBack: this.buildExpressionArray(step.onBack),
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
buildNewStepGroup(existingKeys, registry) {
|
|
596
|
+
const defaultType = registry.stepTypes.find(stepType => stepType.name === 'form')?.name ??
|
|
597
|
+
registry.stepTypes[0]?.name ??
|
|
598
|
+
'form';
|
|
599
|
+
return this.buildStepGroup({
|
|
600
|
+
key: this.generateStepKey(existingKeys),
|
|
601
|
+
type: { name: defaultType, properties: {} },
|
|
602
|
+
nextSteps: [],
|
|
603
|
+
}, registry);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Builds the dynamic properties group for a step type. Known types get one required control per
|
|
607
|
+
* registry property; unknown (custom) types keep controls for whatever properties the loaded
|
|
608
|
+
* definition already contained. Values for properties that exist in both the old and the new
|
|
609
|
+
* type are preserved.
|
|
610
|
+
*/
|
|
611
|
+
buildPropertiesGroup(typeName, currentProperties, registry) {
|
|
612
|
+
const registryStepType = registry.stepTypes.find(stepType => stepType.name === typeName);
|
|
613
|
+
const propertyNames = registryStepType
|
|
614
|
+
? registryStepType.properties.map(property => property.name)
|
|
615
|
+
: Object.keys(currentProperties);
|
|
616
|
+
return this.fb.group(Object.fromEntries(propertyNames.map(name => [
|
|
617
|
+
name,
|
|
618
|
+
this.fb.control(currentProperties[name] ?? '', Validators.required),
|
|
619
|
+
])));
|
|
620
|
+
}
|
|
621
|
+
buildTransitionGroup(transition) {
|
|
622
|
+
return this.fb.group({
|
|
623
|
+
step: this.fb.control(transition?.step ?? '', Validators.required),
|
|
624
|
+
condition: this.fb.control(transition?.condition ?? ''),
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
buildExpressionControl(expression = '') {
|
|
628
|
+
return this.fb.control(expression, Validators.required);
|
|
629
|
+
}
|
|
630
|
+
serialize(form, definitionKey) {
|
|
631
|
+
const value = form.getRawValue();
|
|
632
|
+
return {
|
|
633
|
+
key: definitionKey,
|
|
634
|
+
startStep: value.startStep,
|
|
635
|
+
steps: value.steps.map(step => ({
|
|
636
|
+
key: step.key,
|
|
637
|
+
...(step.title?.trim() ? { title: step.title.trim() } : {}),
|
|
638
|
+
type: {
|
|
639
|
+
name: step.typeName,
|
|
640
|
+
properties: step.properties,
|
|
641
|
+
},
|
|
642
|
+
nextSteps: step.nextSteps.map(transition => ({
|
|
643
|
+
step: transition.step,
|
|
644
|
+
...(transition.condition?.trim() ? { condition: transition.condition.trim() } : {}),
|
|
645
|
+
})),
|
|
646
|
+
onBack: step.onBack,
|
|
647
|
+
onOpen: step.onOpen,
|
|
648
|
+
onComplete: step.onComplete,
|
|
649
|
+
})),
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Rewrites every reference to a renamed step key: the start step and all transition targets.
|
|
654
|
+
* Used while the user types a new key, so the definition stays internally consistent.
|
|
655
|
+
*/
|
|
656
|
+
renameStepReferences(form, oldKey, newKey) {
|
|
657
|
+
if (!oldKey || oldKey === newKey)
|
|
658
|
+
return;
|
|
659
|
+
const startStepControl = form.get('startStep');
|
|
660
|
+
if (startStepControl?.value === oldKey) {
|
|
661
|
+
startStepControl.setValue(newKey, { emitEvent: false });
|
|
662
|
+
}
|
|
663
|
+
this.getStepsArray(form).controls.forEach(stepGroup => {
|
|
664
|
+
stepGroup.get('nextSteps').controls.forEach(transitionGroup => {
|
|
665
|
+
const stepControl = transitionGroup.get('step');
|
|
666
|
+
if (stepControl?.value === oldKey) {
|
|
667
|
+
stepControl.setValue(newKey, { emitEvent: false });
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
getStepsArray(form) {
|
|
673
|
+
return form.get('steps');
|
|
674
|
+
}
|
|
675
|
+
buildExpressionArray(expressions) {
|
|
676
|
+
return this.fb.array((expressions ?? []).map(expression => this.buildExpressionControl(expression)));
|
|
677
|
+
}
|
|
678
|
+
generateStepKey(existingKeys) {
|
|
679
|
+
let index = existingKeys.length + 1;
|
|
680
|
+
while (existingKeys.includes(`step-${index}`)) {
|
|
681
|
+
index++;
|
|
682
|
+
}
|
|
683
|
+
return `step-${index}`;
|
|
684
|
+
}
|
|
685
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowEditorFormService, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
686
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowEditorFormService }); }
|
|
687
|
+
}
|
|
688
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowEditorFormService, decorators: [{
|
|
689
|
+
type: Injectable
|
|
690
|
+
}], ctorParameters: () => [{ type: i1$1.FormBuilder }] });
|
|
691
|
+
/** Marks duplicated step keys invalid on the `steps` array. */
|
|
692
|
+
const uniqueStepKeysValidator = (control) => {
|
|
693
|
+
const keys = control.controls
|
|
694
|
+
.map(stepGroup => (stepGroup.get('key')?.value ?? '').trim())
|
|
695
|
+
.filter(key => key !== '');
|
|
696
|
+
const duplicates = keys.filter((key, index) => keys.indexOf(key) !== index);
|
|
697
|
+
return duplicates.length ? { duplicateStepKeys: { keys: [...new Set(duplicates)] } } : null;
|
|
698
|
+
};
|
|
699
|
+
/** The configured start step must reference an existing step. */
|
|
700
|
+
const startStepExistsValidator = (control) => {
|
|
701
|
+
const startStep = control.get('startStep')?.value;
|
|
702
|
+
if (!startStep)
|
|
703
|
+
return null;
|
|
704
|
+
const keys = control.get('steps').controls.map(stepGroup => stepGroup.get('key')?.value);
|
|
705
|
+
return keys.includes(startStep) ? null : { startStepMissing: { startStep } };
|
|
706
|
+
};
|
|
707
|
+
/** Every transition must point to an existing step. */
|
|
708
|
+
const transitionTargetsExistValidator = (control) => {
|
|
709
|
+
const steps = control.get('steps').controls;
|
|
710
|
+
const keys = steps.map(stepGroup => stepGroup.get('key')?.value);
|
|
711
|
+
const unknownTargets = steps.flatMap(stepGroup => stepGroup.get('nextSteps').controls
|
|
712
|
+
.map(transitionGroup => transitionGroup.get('step')?.value)
|
|
713
|
+
.filter(target => !!target && !keys.includes(target)));
|
|
714
|
+
return unknownTargets.length
|
|
715
|
+
? { unknownTransitionTargets: { targets: [...new Set(unknownTargets)] } }
|
|
716
|
+
: null;
|
|
717
|
+
};
|
|
718
|
+
/**
|
|
719
|
+
* Transitions are evaluated in order and the first condition-less entry acts as the default, so at
|
|
720
|
+
* most one transition per step may omit its condition.
|
|
721
|
+
*/
|
|
722
|
+
const singleDefaultTransitionValidator = (control) => {
|
|
723
|
+
const defaultCount = control.controls.filter(transitionGroup => !(transitionGroup.get('condition')?.value ?? '').trim()).length;
|
|
724
|
+
return defaultCount > 1 ? { multipleDefaultTransitions: true } : null;
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
/*
|
|
728
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
729
|
+
*
|
|
730
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
731
|
+
* you may not use this file except in compliance with the License.
|
|
732
|
+
* You may obtain a copy of the License at
|
|
733
|
+
*
|
|
734
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
735
|
+
*
|
|
736
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
737
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
738
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
739
|
+
* See the License for the specific language governing permissions and
|
|
740
|
+
* limitations under the License.
|
|
741
|
+
*/
|
|
742
|
+
/**
|
|
743
|
+
* Translates a key and falls back to the given value when no translation exists (ngx-translate
|
|
744
|
+
* returns the key itself in that case). Used for registry-driven labels where only the well-known
|
|
745
|
+
* entries have translations.
|
|
746
|
+
*/
|
|
747
|
+
function translateWithFallback(translateService, key, fallback) {
|
|
748
|
+
const translation = translateService.instant(key);
|
|
749
|
+
return translation === key ? fallback : translation;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/*
|
|
753
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
754
|
+
*
|
|
755
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
756
|
+
* you may not use this file except in compliance with the License.
|
|
757
|
+
* You may obtain a copy of the License at
|
|
758
|
+
*
|
|
759
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
760
|
+
*
|
|
761
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
762
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
763
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
764
|
+
* See the License for the specific language governing permissions and
|
|
765
|
+
* limitations under the License.
|
|
766
|
+
*/
|
|
767
|
+
|
|
768
|
+
/*
|
|
769
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
770
|
+
*
|
|
771
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
772
|
+
* you may not use this file except in compliance with the License.
|
|
773
|
+
* You may obtain a copy of the License at
|
|
774
|
+
*
|
|
775
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
776
|
+
*
|
|
777
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
778
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
779
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
780
|
+
* See the License for the specific language governing permissions and
|
|
781
|
+
* limitations under the License.
|
|
782
|
+
*/
|
|
783
|
+
/**
|
|
784
|
+
* Lists the `additionalProperties` entries that expressions can rely on, grouped by how the form
|
|
785
|
+
* flow is linked (user task or start event). The property names come from the form flow registry,
|
|
786
|
+
* so this always matches what the backend actually provides.
|
|
787
|
+
*/
|
|
788
|
+
class FormFlowContextPropertiesComponent {
|
|
789
|
+
set registry(registry) {
|
|
790
|
+
const properties = registry?.additionalProperties ?? [];
|
|
791
|
+
this.groups = [
|
|
792
|
+
{ labelKey: 'formFlow.uiEditor.additionalPropertiesUserTask', context: 'userTask' },
|
|
793
|
+
{ labelKey: 'formFlow.uiEditor.additionalPropertiesStartEvent', context: 'startEvent' },
|
|
794
|
+
]
|
|
795
|
+
.map(group => ({
|
|
796
|
+
labelKey: group.labelKey,
|
|
797
|
+
properties: properties.filter(property => property.context === group.context),
|
|
798
|
+
}))
|
|
799
|
+
.filter(group => group.properties.length > 0);
|
|
800
|
+
}
|
|
801
|
+
constructor(translateService) {
|
|
802
|
+
this.translateService = translateService;
|
|
803
|
+
this.groups = [];
|
|
804
|
+
}
|
|
805
|
+
getDescription(property) {
|
|
806
|
+
return translateWithFallback(this.translateService, `formFlow.uiEditor.additionalProperties.${property.name}`, '');
|
|
807
|
+
}
|
|
808
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowContextPropertiesComponent, deps: [{ token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
809
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowContextPropertiesComponent, isStandalone: true, selector: "valtimo-form-flow-context-properties", inputs: { registry: "registry" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n@if (groups.length) {\n <div class=\"context-properties\">\n <p class=\"context-properties__title\">\n {{ 'formFlow.uiEditor.additionalPropertiesTitle' | translate }}\n </p>\n\n @for (group of groups; track group.labelKey) {\n <p class=\"context-properties__group\">{{ group.labelKey | translate }}</p>\n\n <ul>\n @for (property of group.properties; track property.name) {\n <li>\n <code>additionalProperties.{{ property.name }}</code>\n\n @if (!property.alwaysPresent) {\n <span class=\"context-properties__optional\">{{\n 'formFlow.uiEditor.additionalPropertiesOptional' | translate\n }}</span>\n }\n\n @if (getDescription(property)) {\n \u2014 {{ getDescription(property) }}\n }\n </li>\n }\n </ul>\n }\n </div>\n}\n", styles: [".context-properties{display:flex;flex-direction:column;gap:var(--cds-spacing-02);color:var(--cds-text-secondary);font-size:var(--cds-label-01-font-size, .75rem)}.context-properties__title{font-weight:600}.context-properties__group{margin-top:var(--cds-spacing-02)}.context-properties__optional{color:var(--cds-text-helper);font-style:italic;margin-left:var(--cds-spacing-02)}.context-properties ul{display:flex;flex-direction:column;gap:var(--cds-spacing-02);list-style:disc;padding-left:var(--cds-spacing-05)}.context-properties code{font-family:IBM Plex Mono,monospace;color:var(--cds-text-primary)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
810
|
+
}
|
|
811
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowContextPropertiesComponent, decorators: [{
|
|
812
|
+
type: Component,
|
|
813
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-context-properties', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, TranslateModule], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n@if (groups.length) {\n <div class=\"context-properties\">\n <p class=\"context-properties__title\">\n {{ 'formFlow.uiEditor.additionalPropertiesTitle' | translate }}\n </p>\n\n @for (group of groups; track group.labelKey) {\n <p class=\"context-properties__group\">{{ group.labelKey | translate }}</p>\n\n <ul>\n @for (property of group.properties; track property.name) {\n <li>\n <code>additionalProperties.{{ property.name }}</code>\n\n @if (!property.alwaysPresent) {\n <span class=\"context-properties__optional\">{{\n 'formFlow.uiEditor.additionalPropertiesOptional' | translate\n }}</span>\n }\n\n @if (getDescription(property)) {\n \u2014 {{ getDescription(property) }}\n }\n </li>\n }\n </ul>\n }\n </div>\n}\n", styles: [".context-properties{display:flex;flex-direction:column;gap:var(--cds-spacing-02);color:var(--cds-text-secondary);font-size:var(--cds-label-01-font-size, .75rem)}.context-properties__title{font-weight:600}.context-properties__group{margin-top:var(--cds-spacing-02)}.context-properties__optional{color:var(--cds-text-helper);font-style:italic;margin-left:var(--cds-spacing-02)}.context-properties ul{display:flex;flex-direction:column;gap:var(--cds-spacing-02);list-style:disc;padding-left:var(--cds-spacing-05)}.context-properties code{font-family:IBM Plex Mono,monospace;color:var(--cds-text-primary)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
814
|
+
}], ctorParameters: () => [{ type: i4.TranslateService }], propDecorators: { registry: [{
|
|
815
|
+
type: Input
|
|
816
|
+
}] } });
|
|
817
|
+
|
|
818
|
+
/*
|
|
819
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
820
|
+
*
|
|
821
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
822
|
+
* you may not use this file except in compliance with the License.
|
|
823
|
+
* You may obtain a copy of the License at
|
|
824
|
+
*
|
|
825
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
826
|
+
*
|
|
827
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
828
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
829
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
830
|
+
* See the License for the specific language governing permissions and
|
|
831
|
+
* limitations under the License.
|
|
832
|
+
*/
|
|
833
|
+
/**
|
|
834
|
+
* Explains how SpEL expressions work in a form flow — conditions, action hooks and the data they
|
|
835
|
+
* can access — in a modal, so the editor itself only needs a compact pointer to this help.
|
|
836
|
+
*/
|
|
837
|
+
class FormFlowExpressionHelpModalComponent {
|
|
838
|
+
constructor() {
|
|
839
|
+
this.open = false;
|
|
840
|
+
this.registry = null;
|
|
841
|
+
this.closeEvent = new EventEmitter();
|
|
842
|
+
}
|
|
843
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowExpressionHelpModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
844
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: FormFlowExpressionHelpModalComponent, isStandalone: true, selector: "valtimo-form-flow-expression-help-modal", inputs: { open: "open", registry: "registry" }, outputs: { closeEvent: "closeEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n size=\"md\"\n [open]=\"open\"\n [title]=\"'formFlow.uiEditor.help.title' | translate\"\n valtimoCdsModal\n (close)=\"closeEvent.emit()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"closeEvent.emit()\">\n <h3 cdsModalHeaderHeading>{{ 'formFlow.uiEditor.help.title' | translate }}</h3>\n </cds-modal-header>\n\n <section cdsModalContent class=\"expression-help\">\n <p>{{ 'formFlow.uiEditor.help.intro' | translate }}</p>\n\n <h5>{{ 'formFlow.uiEditor.help.conditionsHeading' | translate }}</h5>\n\n <p>{{ 'formFlow.uiEditor.conditionExamplesTitle' | translate }}</p>\n\n <ul>\n <li>\n <code>{{ '${step.submissionData.age >= 21}' }}</code>\n \u2014 {{ 'formFlow.uiEditor.conditionExampleSubmission' | translate }}\n </li>\n\n <li>\n <code>{{ '${additionalProperties.documentId != null}' }}</code>\n \u2014 {{ 'formFlow.uiEditor.conditionExampleContext' | translate }}\n </li>\n </ul>\n\n <p>{{ 'formFlow.uiEditor.conditionExampleDefault' | translate }}</p>\n\n <h5>{{ 'formFlow.uiEditor.help.actionsHeading' | translate }}</h5>\n\n <p>{{ 'formFlow.uiEditor.help.actionsBody' | translate }}</p>\n\n <h5>{{ 'formFlow.uiEditor.help.dataHeading' | translate }}</h5>\n\n <valtimo-form-flow-context-properties\n [registry]=\"registry\"\n ></valtimo-form-flow-context-properties>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"primary\" (click)=\"closeEvent.emit()\">\n {{ 'interface.close' | translate }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", styles: [".expression-help{display:flex;flex-direction:column;gap:var(--cds-spacing-04);color:var(--cds-text-primary)}.expression-help h5{margin-top:var(--cds-spacing-03)}.expression-help p{color:var(--cds-text-secondary)}.expression-help ul{display:flex;flex-direction:column;gap:var(--cds-spacing-02);list-style:disc;padding-left:var(--cds-spacing-05);color:var(--cds-text-secondary)}.expression-help code{font-family:IBM Plex Mono,monospace;color:var(--cds-text-primary)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: ModalModule }, { kind: "component", type: i2$1.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$1.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$1.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$1.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$1.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: ValtimoCdsModalDirective, selector: "[valtimoCdsModal]", inputs: ["minContentHeight"] }, { kind: "component", type: FormFlowContextPropertiesComponent, selector: "valtimo-form-flow-context-properties", inputs: ["registry"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
845
|
+
}
|
|
846
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowExpressionHelpModalComponent, decorators: [{
|
|
847
|
+
type: Component,
|
|
848
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-expression-help-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
849
|
+
CommonModule,
|
|
850
|
+
TranslateModule,
|
|
851
|
+
ButtonModule,
|
|
852
|
+
ModalModule,
|
|
853
|
+
ValtimoCdsModalDirective,
|
|
854
|
+
FormFlowContextPropertiesComponent,
|
|
855
|
+
], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal\n size=\"md\"\n [open]=\"open\"\n [title]=\"'formFlow.uiEditor.help.title' | translate\"\n valtimoCdsModal\n (close)=\"closeEvent.emit()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"closeEvent.emit()\">\n <h3 cdsModalHeaderHeading>{{ 'formFlow.uiEditor.help.title' | translate }}</h3>\n </cds-modal-header>\n\n <section cdsModalContent class=\"expression-help\">\n <p>{{ 'formFlow.uiEditor.help.intro' | translate }}</p>\n\n <h5>{{ 'formFlow.uiEditor.help.conditionsHeading' | translate }}</h5>\n\n <p>{{ 'formFlow.uiEditor.conditionExamplesTitle' | translate }}</p>\n\n <ul>\n <li>\n <code>{{ '${step.submissionData.age >= 21}' }}</code>\n \u2014 {{ 'formFlow.uiEditor.conditionExampleSubmission' | translate }}\n </li>\n\n <li>\n <code>{{ '${additionalProperties.documentId != null}' }}</code>\n \u2014 {{ 'formFlow.uiEditor.conditionExampleContext' | translate }}\n </li>\n </ul>\n\n <p>{{ 'formFlow.uiEditor.conditionExampleDefault' | translate }}</p>\n\n <h5>{{ 'formFlow.uiEditor.help.actionsHeading' | translate }}</h5>\n\n <p>{{ 'formFlow.uiEditor.help.actionsBody' | translate }}</p>\n\n <h5>{{ 'formFlow.uiEditor.help.dataHeading' | translate }}</h5>\n\n <valtimo-form-flow-context-properties\n [registry]=\"registry\"\n ></valtimo-form-flow-context-properties>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"primary\" (click)=\"closeEvent.emit()\">\n {{ 'interface.close' | translate }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", styles: [".expression-help{display:flex;flex-direction:column;gap:var(--cds-spacing-04);color:var(--cds-text-primary)}.expression-help h5{margin-top:var(--cds-spacing-03)}.expression-help p{color:var(--cds-text-secondary)}.expression-help ul{display:flex;flex-direction:column;gap:var(--cds-spacing-02);list-style:disc;padding-left:var(--cds-spacing-05);color:var(--cds-text-secondary)}.expression-help code{font-family:IBM Plex Mono,monospace;color:var(--cds-text-primary)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
856
|
+
}], propDecorators: { open: [{
|
|
857
|
+
type: Input
|
|
858
|
+
}], registry: [{
|
|
859
|
+
type: Input
|
|
860
|
+
}], closeEvent: [{
|
|
861
|
+
type: Output
|
|
862
|
+
}] } });
|
|
863
|
+
|
|
864
|
+
/*
|
|
865
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
866
|
+
*
|
|
867
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
868
|
+
* you may not use this file except in compliance with the License.
|
|
869
|
+
* You may obtain a copy of the License at
|
|
870
|
+
*
|
|
871
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
872
|
+
*
|
|
873
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
874
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
875
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
876
|
+
* See the License for the specific language governing permissions and
|
|
877
|
+
* limitations under the License.
|
|
878
|
+
*/
|
|
879
|
+
/**
|
|
880
|
+
* Editable list of SpEL expressions for one of a step's lifecycle hooks (on open, on complete,
|
|
881
|
+
* on back). The add-menu offers the expression beans from the form flow registry as ready-made
|
|
882
|
+
* templates next to a blank expression.
|
|
883
|
+
*/
|
|
884
|
+
class FormFlowExpressionListComponent {
|
|
885
|
+
set registry(registry) {
|
|
886
|
+
this.suggestions = (registry?.expressionBeans ?? []).flatMap(bean => bean.methods.map(method => {
|
|
887
|
+
const call = `${bean.name}.${this.formatMethod(method)}`;
|
|
888
|
+
return { label: call, expression: `\${${call}}` };
|
|
889
|
+
}));
|
|
890
|
+
}
|
|
891
|
+
constructor(formService, iconService) {
|
|
892
|
+
this.formService = formService;
|
|
893
|
+
this.iconService = iconService;
|
|
894
|
+
this.label = '';
|
|
895
|
+
this.description = '';
|
|
896
|
+
this.readOnly = false;
|
|
897
|
+
/** While false the admin is still modelling and empty-expression errors stay hidden; a save
|
|
898
|
+
* attempt flips it to reveal them. */
|
|
899
|
+
this.revealErrors = false;
|
|
900
|
+
this.suggestions = [];
|
|
901
|
+
this.testIds = FORM_FLOW_EDITOR_TEST_IDS;
|
|
902
|
+
this.iconService.registerAll([Add16, TrashCan16]);
|
|
903
|
+
}
|
|
904
|
+
get expressionControls() {
|
|
905
|
+
return this.expressions.controls;
|
|
906
|
+
}
|
|
907
|
+
addExpression(expression = '') {
|
|
908
|
+
this.expressions.push(this.formService.buildExpressionControl(expression));
|
|
909
|
+
this.expressions.markAsDirty();
|
|
910
|
+
}
|
|
911
|
+
removeExpression(index) {
|
|
912
|
+
this.expressions.removeAt(index);
|
|
913
|
+
this.expressions.markAsDirty();
|
|
914
|
+
}
|
|
915
|
+
formatMethod(method) {
|
|
916
|
+
return `${method.name}(${method.parameters.map(parameter => parameter.name).join(', ')})`;
|
|
917
|
+
}
|
|
918
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowExpressionListComponent, deps: [{ token: FormFlowEditorFormService }, { token: i2$1.IconService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
919
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowExpressionListComponent, isStandalone: true, selector: "valtimo-form-flow-expression-list", inputs: { expressions: "expressions", label: "label", description: "description", readOnly: "readOnly", revealErrors: "revealErrors", registry: "registry" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"expression-list\">\n <div class=\"expression-list__header\">\n <h6>{{ label }}</h6>\n\n <p class=\"expression-list__description\">{{ description }}</p>\n </div>\n\n @for (control of expressionControls; track control; let index = $index) {\n <div class=\"expression-list__row\">\n <input\n cdsText\n [formControl]=\"control\"\n [invalid]=\"control.invalid && revealErrors\"\n [attr.data-test-id]=\"testIds.expressionInput\"\n placeholder=\"${valtimoFormFlow.completeTask(additionalProperties)}\"\n />\n\n @if (!readOnly) {\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'interface.delete' | translate\"\n (click)=\"removeExpression(index)\"\n >\n <svg cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n }\n </div>\n } @empty {\n <p class=\"expression-list__empty\">{{ 'formFlow.uiEditor.noExpressions' | translate }}</p>\n }\n\n @if (!readOnly) {\n <div class=\"expression-list__add\">\n <v-overflow-menu placement=\"bottom-start\">\n <button\n cdsButton=\"tertiary\"\n size=\"sm\"\n overflowTrigger\n [attr.data-test-id]=\"testIds.addExpressionButton\"\n >\n {{ 'formFlow.uiEditor.addExpression' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n\n <v-overflow-menu-option (selected)=\"addExpression()\">{{\n 'formFlow.uiEditor.blankExpression' | translate\n }}</v-overflow-menu-option>\n\n @for (suggestion of suggestions; track suggestion.label) {\n <v-overflow-menu-option (selected)=\"addExpression(suggestion.expression)\">{{\n suggestion.label\n }}</v-overflow-menu-option>\n }\n </v-overflow-menu>\n </div>\n }\n</div>\n", styles: [".expression-list{display:flex;flex-direction:column;gap:var(--cds-spacing-03)}.expression-list__header h6{margin:0}.expression-list__description{color:var(--cds-text-secondary);font-size:var(--cds-label-01-font-size, .75rem);margin:var(--cds-spacing-02) 0 0 0}.expression-list__row{display:flex;align-items:center;gap:var(--cds-spacing-03)}.expression-list__row input{flex:1;font-family:IBM Plex Mono,monospace}.expression-list__empty{color:var(--cds-text-helper);font-size:var(--cds-body-compact-01-font-size, .875rem);margin:0}.expression-list__add{margin-top:var(--cds-spacing-03)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "ngmodule", type: InputModule }, { kind: "directive", type: i2$1.TextInput, selector: "[cdsText], [ibmText]", inputs: ["theme", "size", "invalid", "warn", "skeleton"] }, { kind: "component", type: OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
920
|
+
}
|
|
921
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowExpressionListComponent, decorators: [{
|
|
922
|
+
type: Component,
|
|
923
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-expression-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
924
|
+
CommonModule,
|
|
925
|
+
ReactiveFormsModule,
|
|
926
|
+
TranslateModule,
|
|
927
|
+
ButtonModule,
|
|
928
|
+
IconModule,
|
|
929
|
+
InputModule,
|
|
930
|
+
OverflowMenuComponent,
|
|
931
|
+
OverflowMenuOptionComponent,
|
|
932
|
+
], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"expression-list\">\n <div class=\"expression-list__header\">\n <h6>{{ label }}</h6>\n\n <p class=\"expression-list__description\">{{ description }}</p>\n </div>\n\n @for (control of expressionControls; track control; let index = $index) {\n <div class=\"expression-list__row\">\n <input\n cdsText\n [formControl]=\"control\"\n [invalid]=\"control.invalid && revealErrors\"\n [attr.data-test-id]=\"testIds.expressionInput\"\n placeholder=\"${valtimoFormFlow.completeTask(additionalProperties)}\"\n />\n\n @if (!readOnly) {\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'interface.delete' | translate\"\n (click)=\"removeExpression(index)\"\n >\n <svg cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n }\n </div>\n } @empty {\n <p class=\"expression-list__empty\">{{ 'formFlow.uiEditor.noExpressions' | translate }}</p>\n }\n\n @if (!readOnly) {\n <div class=\"expression-list__add\">\n <v-overflow-menu placement=\"bottom-start\">\n <button\n cdsButton=\"tertiary\"\n size=\"sm\"\n overflowTrigger\n [attr.data-test-id]=\"testIds.addExpressionButton\"\n >\n {{ 'formFlow.uiEditor.addExpression' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n\n <v-overflow-menu-option (selected)=\"addExpression()\">{{\n 'formFlow.uiEditor.blankExpression' | translate\n }}</v-overflow-menu-option>\n\n @for (suggestion of suggestions; track suggestion.label) {\n <v-overflow-menu-option (selected)=\"addExpression(suggestion.expression)\">{{\n suggestion.label\n }}</v-overflow-menu-option>\n }\n </v-overflow-menu>\n </div>\n }\n</div>\n", styles: [".expression-list{display:flex;flex-direction:column;gap:var(--cds-spacing-03)}.expression-list__header h6{margin:0}.expression-list__description{color:var(--cds-text-secondary);font-size:var(--cds-label-01-font-size, .75rem);margin:var(--cds-spacing-02) 0 0 0}.expression-list__row{display:flex;align-items:center;gap:var(--cds-spacing-03)}.expression-list__row input{flex:1;font-family:IBM Plex Mono,monospace}.expression-list__empty{color:var(--cds-text-helper);font-size:var(--cds-body-compact-01-font-size, .875rem);margin:0}.expression-list__add{margin-top:var(--cds-spacing-03)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
933
|
+
}], ctorParameters: () => [{ type: FormFlowEditorFormService }, { type: i2$1.IconService }], propDecorators: { expressions: [{
|
|
934
|
+
type: Input
|
|
935
|
+
}], label: [{
|
|
936
|
+
type: Input
|
|
937
|
+
}], description: [{
|
|
938
|
+
type: Input
|
|
939
|
+
}], readOnly: [{
|
|
940
|
+
type: Input
|
|
941
|
+
}], revealErrors: [{
|
|
942
|
+
type: Input
|
|
943
|
+
}], registry: [{
|
|
944
|
+
type: Input
|
|
945
|
+
}] } });
|
|
946
|
+
|
|
947
|
+
/*
|
|
948
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
949
|
+
*
|
|
950
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
951
|
+
* you may not use this file except in compliance with the License.
|
|
952
|
+
* You may obtain a copy of the License at
|
|
953
|
+
*
|
|
954
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
955
|
+
*
|
|
956
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
957
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
958
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
959
|
+
* See the License for the specific language governing permissions and
|
|
960
|
+
* limitations under the License.
|
|
961
|
+
*/
|
|
962
|
+
/**
|
|
963
|
+
* Editable list of a step's outgoing transitions. Transitions are evaluated top to bottom — the
|
|
964
|
+
* first entry whose condition holds is taken and a condition-less entry acts as the default — so
|
|
965
|
+
* the rows can be reordered.
|
|
966
|
+
*/
|
|
967
|
+
class FormFlowTransitionListComponent {
|
|
968
|
+
constructor(formService, iconService) {
|
|
969
|
+
this.formService = formService;
|
|
970
|
+
this.iconService = iconService;
|
|
971
|
+
this.stepKeys = [];
|
|
972
|
+
this.readOnly = false;
|
|
973
|
+
/** While false the admin is still modelling and empty-target errors stay hidden; a save attempt
|
|
974
|
+
* flips it to reveal them. */
|
|
975
|
+
this.revealErrors = false;
|
|
976
|
+
this.testIds = FORM_FLOW_EDITOR_TEST_IDS;
|
|
977
|
+
// Per-row dropdown items, cached per transition group. The Carbon dropdown resets its visual
|
|
978
|
+
// selection whenever its items array is swapped, so the array is only rebuilt when the step keys
|
|
979
|
+
// or the row's own selection actually change.
|
|
980
|
+
this._rowItems = new WeakMap();
|
|
981
|
+
this.iconService.registerAll([Add16, ArrowDown16, ArrowUp16, TrashCan16]);
|
|
982
|
+
}
|
|
983
|
+
ngOnChanges(changes) {
|
|
984
|
+
if (changes['stepKeys']) {
|
|
985
|
+
// A new keys reference invalidates every cached row; rows lazily rebuild on the next read.
|
|
986
|
+
this.transitionGroups.forEach(group => this._rowItems.delete(group));
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
get transitionGroups() {
|
|
990
|
+
return this.transitions.controls;
|
|
991
|
+
}
|
|
992
|
+
getRowItems(transitionGroup) {
|
|
993
|
+
const value = transitionGroup.get('step')?.value ?? '';
|
|
994
|
+
const cached = this._rowItems.get(transitionGroup);
|
|
995
|
+
if (cached && cached.keys === this.stepKeys && cached.value === value) {
|
|
996
|
+
return cached.items;
|
|
997
|
+
}
|
|
998
|
+
const items = this.stepKeys.map(key => ({ content: key, id: key, selected: key === value }));
|
|
999
|
+
this._rowItems.set(transitionGroup, { keys: this.stepKeys, value, items });
|
|
1000
|
+
return items;
|
|
1001
|
+
}
|
|
1002
|
+
addTransition() {
|
|
1003
|
+
this.transitions.push(this.formService.buildTransitionGroup());
|
|
1004
|
+
this.transitions.markAsDirty();
|
|
1005
|
+
}
|
|
1006
|
+
removeTransition(index) {
|
|
1007
|
+
this.transitions.removeAt(index);
|
|
1008
|
+
this.transitions.markAsDirty();
|
|
1009
|
+
}
|
|
1010
|
+
moveTransition(index, offset) {
|
|
1011
|
+
const target = index + offset;
|
|
1012
|
+
if (target < 0 || target >= this.transitions.length)
|
|
1013
|
+
return;
|
|
1014
|
+
const control = this.transitions.at(index);
|
|
1015
|
+
this.transitions.removeAt(index);
|
|
1016
|
+
this.transitions.insert(target, control);
|
|
1017
|
+
this.transitions.markAsDirty();
|
|
1018
|
+
}
|
|
1019
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowTransitionListComponent, deps: [{ token: FormFlowEditorFormService }, { token: i2$1.IconService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1020
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowTransitionListComponent, isStandalone: true, selector: "valtimo-form-flow-transition-list", inputs: { transitions: "transitions", stepKeys: "stepKeys", readOnly: "readOnly", revealErrors: "revealErrors" }, usesOnChanges: true, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"transition-list\">\n @for (transitionGroup of transitionGroups; track transitionGroup; let index = $index) {\n <div\n class=\"transition-list__row\"\n [formGroup]=\"transitionGroup\"\n [attr.data-test-id]=\"testIds.transitionRow\"\n >\n <div class=\"transition-list__target\">\n <cds-dropdown\n formControlName=\"step\"\n itemValueKey=\"id\"\n [invalid]=\"transitionGroup.get('step')?.invalid && revealErrors\"\n [label]=\"targetStepLabelTemplate\"\n [placeholder]=\"'formFlow.uiEditor.selectStep' | translate\"\n >\n <cds-dropdown-list [items]=\"getRowItems(transitionGroup)\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #targetStepLabelTemplate>\n <span class=\"transition-list__label\">\n {{ 'formFlow.uiEditor.targetStep' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.targetStep' | translate\"\n ></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n\n <cds-label class=\"transition-list__condition\">\n <span class=\"transition-list__label\">\n {{ 'formFlow.uiEditor.condition' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.condition' | translate\"\n ></v-tooltip-icon>\n </span>\n\n <input\n cdsText\n formControlName=\"condition\"\n placeholder=\"{{ 'formFlow.uiEditor.conditionPlaceholder' | translate }}\"\n />\n </cds-label>\n\n @if (!readOnly) {\n <div class=\"transition-list__actions\">\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [disabled]=\"index === 0\"\n [attr.aria-label]=\"'formFlow.uiEditor.moveUp' | translate\"\n (click)=\"moveTransition(index, -1)\"\n >\n <svg cdsIcon=\"arrow--up\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [disabled]=\"index === transitionGroups.length - 1\"\n [attr.aria-label]=\"'formFlow.uiEditor.moveDown' | translate\"\n (click)=\"moveTransition(index, 1)\"\n >\n <svg cdsIcon=\"arrow--down\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'interface.delete' | translate\"\n (click)=\"removeTransition(index)\"\n >\n <svg cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </div>\n }\n </div>\n } @empty {\n <p class=\"transition-list__empty\">{{ 'formFlow.uiEditor.noTransitions' | translate }}</p>\n }\n\n @if (revealErrors && transitions.hasError('multipleDefaultTransitions')) {\n <p class=\"transition-list__error\">\n {{ 'formFlow.uiEditor.multipleDefaultTransitions' | translate }}\n </p>\n }\n\n @if (!readOnly) {\n <div>\n <button\n cdsButton=\"tertiary\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.addTransitionButton\"\n (click)=\"addTransition()\"\n >\n {{ 'formFlow.uiEditor.addTransition' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n </div>\n }\n</div>\n", styles: [".transition-list{display:flex;flex-direction:column;gap:var(--cds-spacing-04)}.transition-list__row{display:flex;align-items:flex-end;gap:var(--cds-spacing-04);padding:var(--cds-spacing-04);border:1px solid var(--cds-border-subtle)}.transition-list__target{flex:0 0 220px}.transition-list__condition{flex:1;margin-bottom:0}.transition-list__condition input{font-family:IBM Plex Mono,monospace}.transition-list__actions{display:flex;align-items:center}.transition-list__label{display:inline-flex;align-items:center;gap:var(--cds-spacing-02)}.transition-list__empty{color:var(--cds-text-helper);font-size:var(--cds-body-compact-01-font-size, .875rem);margin:0}.transition-list__error{color:var(--cds-text-error);font-size:var(--cds-label-01-font-size, .75rem);margin:0}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i2$1.Dropdown, selector: "cds-dropdown, ibm-dropdown", inputs: ["id", "label", "hideLabel", "helperText", "placeholder", "displayValue", "clearText", "size", "type", "theme", "disabled", "readonly", "skeleton", "inline", "disableArrowKeys", "invalid", "invalidText", "warn", "warnText", "appendInline", "scrollableContainer", "itemValueKey", "selectionFeedback", "menuButtonLabel", "selectedLabel", "dropUp", "fluid"], outputs: ["selected", "onClose", "close"] }, { kind: "component", type: i2$1.DropdownList, selector: "cds-dropdown-list, ibm-dropdown-list", inputs: ["ariaLabel", "items", "listTpl", "type", "showTitles"], outputs: ["select", "scroll", "blurIntent"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i2$1.Label, selector: "cds-label, ibm-label", inputs: ["labelInputID", "disabled", "skeleton", "helperText", "invalidText", "invalid", "warn", "warnText", "ariaLabel"] }, { kind: "directive", type: i2$1.TextInput, selector: "[cdsText], [ibmText]", inputs: ["theme", "size", "invalid", "warn", "skeleton"] }, { kind: "ngmodule", type: TooltipIconModule }, { kind: "component", type: i1$2.TooltipIconComponent, selector: "v-tooltip-icon", inputs: ["tooltip", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1021
|
+
}
|
|
1022
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowTransitionListComponent, decorators: [{
|
|
1023
|
+
type: Component,
|
|
1024
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-transition-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1025
|
+
CommonModule,
|
|
1026
|
+
ReactiveFormsModule,
|
|
1027
|
+
TranslateModule,
|
|
1028
|
+
ButtonModule,
|
|
1029
|
+
DropdownModule,
|
|
1030
|
+
IconModule,
|
|
1031
|
+
InputModule,
|
|
1032
|
+
TooltipIconModule,
|
|
1033
|
+
], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"transition-list\">\n @for (transitionGroup of transitionGroups; track transitionGroup; let index = $index) {\n <div\n class=\"transition-list__row\"\n [formGroup]=\"transitionGroup\"\n [attr.data-test-id]=\"testIds.transitionRow\"\n >\n <div class=\"transition-list__target\">\n <cds-dropdown\n formControlName=\"step\"\n itemValueKey=\"id\"\n [invalid]=\"transitionGroup.get('step')?.invalid && revealErrors\"\n [label]=\"targetStepLabelTemplate\"\n [placeholder]=\"'formFlow.uiEditor.selectStep' | translate\"\n >\n <cds-dropdown-list [items]=\"getRowItems(transitionGroup)\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #targetStepLabelTemplate>\n <span class=\"transition-list__label\">\n {{ 'formFlow.uiEditor.targetStep' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.targetStep' | translate\"\n ></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n\n <cds-label class=\"transition-list__condition\">\n <span class=\"transition-list__label\">\n {{ 'formFlow.uiEditor.condition' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.condition' | translate\"\n ></v-tooltip-icon>\n </span>\n\n <input\n cdsText\n formControlName=\"condition\"\n placeholder=\"{{ 'formFlow.uiEditor.conditionPlaceholder' | translate }}\"\n />\n </cds-label>\n\n @if (!readOnly) {\n <div class=\"transition-list__actions\">\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [disabled]=\"index === 0\"\n [attr.aria-label]=\"'formFlow.uiEditor.moveUp' | translate\"\n (click)=\"moveTransition(index, -1)\"\n >\n <svg cdsIcon=\"arrow--up\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [disabled]=\"index === transitionGroups.length - 1\"\n [attr.aria-label]=\"'formFlow.uiEditor.moveDown' | translate\"\n (click)=\"moveTransition(index, 1)\"\n >\n <svg cdsIcon=\"arrow--down\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"ghost\"\n size=\"md\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"'interface.delete' | translate\"\n (click)=\"removeTransition(index)\"\n >\n <svg cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </div>\n }\n </div>\n } @empty {\n <p class=\"transition-list__empty\">{{ 'formFlow.uiEditor.noTransitions' | translate }}</p>\n }\n\n @if (revealErrors && transitions.hasError('multipleDefaultTransitions')) {\n <p class=\"transition-list__error\">\n {{ 'formFlow.uiEditor.multipleDefaultTransitions' | translate }}\n </p>\n }\n\n @if (!readOnly) {\n <div>\n <button\n cdsButton=\"tertiary\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.addTransitionButton\"\n (click)=\"addTransition()\"\n >\n {{ 'formFlow.uiEditor.addTransition' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n </div>\n }\n</div>\n", styles: [".transition-list{display:flex;flex-direction:column;gap:var(--cds-spacing-04)}.transition-list__row{display:flex;align-items:flex-end;gap:var(--cds-spacing-04);padding:var(--cds-spacing-04);border:1px solid var(--cds-border-subtle)}.transition-list__target{flex:0 0 220px}.transition-list__condition{flex:1;margin-bottom:0}.transition-list__condition input{font-family:IBM Plex Mono,monospace}.transition-list__actions{display:flex;align-items:center}.transition-list__label{display:inline-flex;align-items:center;gap:var(--cds-spacing-02)}.transition-list__empty{color:var(--cds-text-helper);font-size:var(--cds-body-compact-01-font-size, .875rem);margin:0}.transition-list__error{color:var(--cds-text-error);font-size:var(--cds-label-01-font-size, .75rem);margin:0}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
1034
|
+
}], ctorParameters: () => [{ type: FormFlowEditorFormService }, { type: i2$1.IconService }], propDecorators: { transitions: [{
|
|
1035
|
+
type: Input
|
|
1036
|
+
}], stepKeys: [{
|
|
1037
|
+
type: Input
|
|
1038
|
+
}], readOnly: [{
|
|
1039
|
+
type: Input
|
|
1040
|
+
}], revealErrors: [{
|
|
1041
|
+
type: Input
|
|
1042
|
+
}] } });
|
|
1043
|
+
|
|
1044
|
+
/*
|
|
1045
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
1046
|
+
*
|
|
1047
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
1048
|
+
* you may not use this file except in compliance with the License.
|
|
1049
|
+
* You may obtain a copy of the License at
|
|
1050
|
+
*
|
|
1051
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
1052
|
+
*
|
|
1053
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1054
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
1055
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1056
|
+
* See the License for the specific language governing permissions and
|
|
1057
|
+
* limitations under the License.
|
|
1058
|
+
*/
|
|
1059
|
+
/**
|
|
1060
|
+
* Detail editor for a single form flow step: its key, title, type with type-specific properties,
|
|
1061
|
+
* outgoing transitions and the three lifecycle expression lists.
|
|
1062
|
+
*/
|
|
1063
|
+
class FormFlowStepDetailComponent {
|
|
1064
|
+
constructor(formService, iconService, translateService) {
|
|
1065
|
+
this.formService = formService;
|
|
1066
|
+
this.iconService = iconService;
|
|
1067
|
+
this.translateService = translateService;
|
|
1068
|
+
this.registry = null;
|
|
1069
|
+
this.stepKeys = [];
|
|
1070
|
+
this.formOptions = [];
|
|
1071
|
+
this.componentOptions = [];
|
|
1072
|
+
this.isStartStep = false;
|
|
1073
|
+
this.readOnly = false;
|
|
1074
|
+
this.duplicateKey = false;
|
|
1075
|
+
/** When false the admin is still modelling and required-field errors stay hidden; a save attempt
|
|
1076
|
+
* flips it to reveal them. */
|
|
1077
|
+
this.revealErrors = false;
|
|
1078
|
+
this.deleteStepEvent = new EventEmitter();
|
|
1079
|
+
this.makeStartEvent = new EventEmitter();
|
|
1080
|
+
this.stepTypeItems = [];
|
|
1081
|
+
this.$showHelpModal = signal(false);
|
|
1082
|
+
// The "how do expressions work" action on the inline help notifications, opening the modal that
|
|
1083
|
+
// holds the full explanation.
|
|
1084
|
+
this.helpActions = [];
|
|
1085
|
+
this.testIds = FORM_FLOW_EDITOR_TEST_IDS;
|
|
1086
|
+
// Cached per property: the Carbon dropdown resets its visual selection whenever its items array
|
|
1087
|
+
// is swapped, so an array is only rebuilt when its options or its selection change.
|
|
1088
|
+
this._selectItemsCache = new Map();
|
|
1089
|
+
this._typeSubscription = new Subscription();
|
|
1090
|
+
this.iconService.registerAll([Flag16, TrashCan16]);
|
|
1091
|
+
}
|
|
1092
|
+
ngOnInit() {
|
|
1093
|
+
this.helpActions = [
|
|
1094
|
+
{
|
|
1095
|
+
text: this.translateService.instant('formFlow.uiEditor.help.button'),
|
|
1096
|
+
click: () => this.$showHelpModal.set(true),
|
|
1097
|
+
},
|
|
1098
|
+
];
|
|
1099
|
+
}
|
|
1100
|
+
ngOnChanges(changes) {
|
|
1101
|
+
if (changes['stepGroup'] || changes['registry'] || changes['componentOptions']) {
|
|
1102
|
+
this.buildStepTypeItems();
|
|
1103
|
+
}
|
|
1104
|
+
if (changes['stepGroup'] || changes['registry']) {
|
|
1105
|
+
this.openTypeSubscription();
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
ngOnDestroy() {
|
|
1109
|
+
this._typeSubscription.unsubscribe();
|
|
1110
|
+
}
|
|
1111
|
+
get propertiesGroup() {
|
|
1112
|
+
return this.stepGroup.get('properties');
|
|
1113
|
+
}
|
|
1114
|
+
get propertyNames() {
|
|
1115
|
+
return Object.keys(this.propertiesGroup.controls);
|
|
1116
|
+
}
|
|
1117
|
+
get transitions() {
|
|
1118
|
+
return this.stepGroup.get('nextSteps');
|
|
1119
|
+
}
|
|
1120
|
+
getExpressions(hook) {
|
|
1121
|
+
return this.stepGroup.get(hook);
|
|
1122
|
+
}
|
|
1123
|
+
isControlInvalid(name) {
|
|
1124
|
+
const control = this.stepGroup.get(name);
|
|
1125
|
+
return !!control && control.invalid && this.revealErrors;
|
|
1126
|
+
}
|
|
1127
|
+
getPropertyLabel(name) {
|
|
1128
|
+
return translateWithFallback(this.translateService, `formFlow.uiEditor.properties.${name}`, name);
|
|
1129
|
+
}
|
|
1130
|
+
// The `definition` property of the built-in `form` step type references a form of the
|
|
1131
|
+
// surrounding case definition or building block, so it is offered as a choice list.
|
|
1132
|
+
isFormDefinitionProperty(name) {
|
|
1133
|
+
return name === 'definition' && this.formOptions.length > 0;
|
|
1134
|
+
}
|
|
1135
|
+
getFormDefinitionItems() {
|
|
1136
|
+
return this.getSelectItems('definition', this.formOptions);
|
|
1137
|
+
}
|
|
1138
|
+
// The `componentId` property of the built-in `custom-component` step type references an Angular
|
|
1139
|
+
// component registered by the implementation, so the registered ids are offered as a choice list.
|
|
1140
|
+
isCustomComponentProperty(name) {
|
|
1141
|
+
return name === 'componentId' && this.componentOptions.length > 0;
|
|
1142
|
+
}
|
|
1143
|
+
getCustomComponentItems() {
|
|
1144
|
+
return this.getSelectItems('componentId', this.componentOptions);
|
|
1145
|
+
}
|
|
1146
|
+
getSelectItems(propertyName, options) {
|
|
1147
|
+
const value = this.propertiesGroup.get(propertyName)?.value ?? '';
|
|
1148
|
+
const cached = this._selectItemsCache.get(propertyName);
|
|
1149
|
+
if (cached && cached.options === options && cached.value === value) {
|
|
1150
|
+
return cached.items;
|
|
1151
|
+
}
|
|
1152
|
+
// A value that is no longer available stays selectable, so opening an older definition never
|
|
1153
|
+
// silently changes it.
|
|
1154
|
+
const names = options.includes(value) || !value ? options : [...options, value];
|
|
1155
|
+
const items = names.map(name => ({ content: name, id: name, selected: name === value }));
|
|
1156
|
+
this._selectItemsCache.set(propertyName, { options, value, items });
|
|
1157
|
+
return items;
|
|
1158
|
+
}
|
|
1159
|
+
buildStepTypeItems() {
|
|
1160
|
+
const registryTypeNames = (this.registry?.stepTypes ?? []).map(stepType => stepType.name);
|
|
1161
|
+
const currentTypeName = this.stepGroup?.get('typeName')?.value;
|
|
1162
|
+
const typeNames = currentTypeName && !registryTypeNames.includes(currentTypeName)
|
|
1163
|
+
? [...registryTypeNames, currentTypeName]
|
|
1164
|
+
: registryTypeNames;
|
|
1165
|
+
this.stepTypeItems = typeNames.map(name => ({
|
|
1166
|
+
content: name,
|
|
1167
|
+
id: name,
|
|
1168
|
+
selected: name === currentTypeName,
|
|
1169
|
+
// `custom-component` steps need a component registered by the implementation; without any,
|
|
1170
|
+
// the type cannot be configured (unless the step already uses it).
|
|
1171
|
+
disabled: name === 'custom-component' &&
|
|
1172
|
+
this.componentOptions.length === 0 &&
|
|
1173
|
+
currentTypeName !== 'custom-component',
|
|
1174
|
+
}));
|
|
1175
|
+
}
|
|
1176
|
+
getTypeTooltip() {
|
|
1177
|
+
const tooltip = this.translateService.instant('formFlow.uiEditor.fieldTooltips.type');
|
|
1178
|
+
return this.componentOptions.length === 0
|
|
1179
|
+
? `${tooltip} ${this.translateService.instant('formFlow.uiEditor.fieldTooltips.typeNoComponents')}`
|
|
1180
|
+
: tooltip;
|
|
1181
|
+
}
|
|
1182
|
+
// Selecting another step type swaps the properties group for the controls that type needs,
|
|
1183
|
+
// keeping the values of properties both types share.
|
|
1184
|
+
openTypeSubscription() {
|
|
1185
|
+
this._typeSubscription.unsubscribe();
|
|
1186
|
+
this._typeSubscription = new Subscription();
|
|
1187
|
+
if (!this.stepGroup || !this.registry)
|
|
1188
|
+
return;
|
|
1189
|
+
this._typeSubscription.add(this.stepGroup.get('typeName')?.valueChanges.subscribe(typeName => {
|
|
1190
|
+
const currentValues = this.propertiesGroup.getRawValue();
|
|
1191
|
+
this.stepGroup.setControl('properties', this.formService.buildPropertiesGroup(typeName, currentValues, this.registry));
|
|
1192
|
+
}));
|
|
1193
|
+
}
|
|
1194
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowStepDetailComponent, deps: [{ token: FormFlowEditorFormService }, { token: i2$1.IconService }, { token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1195
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowStepDetailComponent, isStandalone: true, selector: "valtimo-form-flow-step-detail", inputs: { stepGroup: "stepGroup", registry: "registry", stepKeys: "stepKeys", formOptions: "formOptions", componentOptions: "componentOptions", isStartStep: "isStartStep", readOnly: "readOnly", duplicateKey: "duplicateKey", revealErrors: "revealErrors" }, outputs: { deleteStepEvent: "deleteStepEvent", makeStartEvent: "makeStartEvent" }, usesOnChanges: true, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"step-detail\" [cdsLayer]=\"1\" [formGroup]=\"stepGroup\">\n <div class=\"step-detail__header\">\n <div class=\"step-detail__heading\">\n <h5>{{ 'formFlow.uiEditor.stepDetails' | translate }}</h5>\n\n @if (isStartStep) {\n <cds-tag type=\"green\" size=\"sm\">{{ 'formFlow.uiEditor.startStep' | translate }}</cds-tag>\n }\n </div>\n\n @if (!readOnly) {\n <div class=\"step-detail__header-actions\">\n @if (!isStartStep) {\n <button\n cdsButton=\"tertiary\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.makeStartStepButton\"\n (click)=\"makeStartEvent.emit()\"\n >\n {{ 'formFlow.uiEditor.makeStartStep' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"flag\" size=\"16\"></svg>\n </button>\n }\n\n <button\n cdsButton=\"danger--ghost\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.deleteStepButton\"\n (click)=\"deleteStepEvent.emit()\"\n >\n {{ 'formFlow.uiEditor.deleteStep' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </div>\n }\n </div>\n\n <div class=\"step-detail__grid\">\n <cds-label\n [invalid]=\"isControlInvalid('key') || (duplicateKey && revealErrors)\"\n [invalidText]=\"\n duplicateKey\n ? ('formFlow.uiEditor.duplicateKey' | translate)\n : ('formFlow.uiEditor.required' | translate)\n \"\n >\n <span class=\"step-detail__label\">\n {{ 'formFlow.key' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.key' | translate\"\n ></v-tooltip-icon>\n </span>\n\n <input\n cdsText\n formControlName=\"key\"\n [invalid]=\"isControlInvalid('key') || (duplicateKey && revealErrors)\"\n [attr.data-test-id]=\"testIds.stepKeyInput\"\n placeholder=\"{{ 'formFlow.uiEditor.keyPlaceholder' | translate }}\"\n />\n </cds-label>\n\n <cds-label>\n <span class=\"step-detail__label\">\n {{ 'formFlow.uiEditor.title' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.title' | translate\"\n ></v-tooltip-icon>\n </span>\n\n <input\n cdsText\n formControlName=\"title\"\n [attr.data-test-id]=\"testIds.stepTitleInput\"\n placeholder=\"{{ 'formFlow.uiEditor.titlePlaceholder' | translate }}\"\n />\n </cds-label>\n\n <div [attr.data-test-id]=\"testIds.stepTypeDropdown\">\n <cds-dropdown formControlName=\"typeName\" itemValueKey=\"id\" [label]=\"stepTypeLabelTemplate\">\n <cds-dropdown-list [items]=\"stepTypeItems\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #stepTypeLabelTemplate>\n <span class=\"step-detail__label\">\n {{ 'formFlow.uiEditor.stepType' | translate }}\n\n <v-tooltip-icon [tooltip]=\"getTypeTooltip()\"></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n\n <ng-container formGroupName=\"properties\">\n @for (propertyName of propertyNames; track propertyName) {\n @if (isFormDefinitionProperty(propertyName)) {\n <div [attr.data-test-id]=\"testIds.stepPropertyDropdown\">\n <cds-dropdown\n formControlName=\"definition\"\n itemValueKey=\"id\"\n [invalid]=\"propertiesGroup.get('definition')?.invalid && revealErrors\"\n [label]=\"formDefinitionLabelTemplate\"\n [placeholder]=\"'formFlow.uiEditor.selectForm' | translate\"\n >\n <cds-dropdown-list [items]=\"getFormDefinitionItems()\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #formDefinitionLabelTemplate>\n <span class=\"step-detail__label\">\n {{ getPropertyLabel(propertyName) }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.definition' | translate\"\n ></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n } @else if (isCustomComponentProperty(propertyName)) {\n <div [attr.data-test-id]=\"testIds.stepPropertyDropdown\">\n <cds-dropdown\n formControlName=\"componentId\"\n itemValueKey=\"id\"\n [invalid]=\"propertiesGroup.get('componentId')?.invalid && revealErrors\"\n [label]=\"componentIdLabelTemplate\"\n [placeholder]=\"'formFlow.uiEditor.selectComponent' | translate\"\n >\n <cds-dropdown-list [items]=\"getCustomComponentItems()\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #componentIdLabelTemplate>\n <span class=\"step-detail__label\">\n {{ getPropertyLabel(propertyName) }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.componentId' | translate\"\n ></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n } @else {\n <cds-label\n [invalid]=\"propertiesGroup.get(propertyName)?.invalid && revealErrors\"\n [invalidText]=\"'formFlow.uiEditor.required' | translate\"\n >\n {{ getPropertyLabel(propertyName) }}\n\n <input\n cdsText\n [formControlName]=\"propertyName\"\n [invalid]=\"propertiesGroup.get(propertyName)?.invalid && revealErrors\"\n [attr.data-test-id]=\"testIds.stepPropertyInput\"\n />\n </cds-label>\n }\n }\n </ng-container>\n </div>\n\n <hr class=\"step-detail__divider\" />\n\n <section class=\"step-detail__section\">\n <div class=\"step-detail__section-header\">\n <h5>{{ 'formFlow.uiEditor.navigation' | translate }}</h5>\n\n <p class=\"step-detail__section-description\">\n {{ 'formFlow.uiEditor.navigationDescription' | translate }}\n </p>\n </div>\n\n <valtimo-form-flow-transition-list\n [readOnly]=\"readOnly\"\n [revealErrors]=\"revealErrors\"\n [stepKeys]=\"stepKeys\"\n [transitions]=\"transitions\"\n ></valtimo-form-flow-transition-list>\n\n <cds-actionable-notification\n class=\"step-detail__help-notification\"\n [notificationObj]=\"{\n type: 'info',\n lowContrast: true,\n showClose: false,\n title: 'formFlow.uiEditor.help.conditionsInlineTitle' | translate,\n message: 'formFlow.uiEditor.help.conditionsInlineMessage' | translate,\n actions: helpActions,\n }\"\n ></cds-actionable-notification>\n </section>\n\n <hr class=\"step-detail__divider\" />\n\n <section class=\"step-detail__section\">\n <div class=\"step-detail__section-header\">\n <h5>{{ 'formFlow.uiEditor.actions' | translate }}</h5>\n\n <p class=\"step-detail__section-description\">\n {{ 'formFlow.uiEditor.actionsDescription' | translate }}\n </p>\n </div>\n\n <cds-actionable-notification\n class=\"step-detail__help-notification\"\n [notificationObj]=\"{\n type: 'info',\n lowContrast: true,\n showClose: false,\n title: 'formFlow.uiEditor.help.actionsInlineTitle' | translate,\n message: 'formFlow.uiEditor.help.actionsInlineMessage' | translate,\n actions: helpActions,\n }\"\n ></cds-actionable-notification>\n\n <valtimo-form-flow-expression-list\n [description]=\"'formFlow.uiEditor.onOpenDescription' | translate\"\n [expressions]=\"getExpressions('onOpen')\"\n [label]=\"'formFlow.uiEditor.onOpen' | translate\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n ></valtimo-form-flow-expression-list>\n\n <valtimo-form-flow-expression-list\n [description]=\"'formFlow.uiEditor.onCompleteDescription' | translate\"\n [expressions]=\"getExpressions('onComplete')\"\n [label]=\"'formFlow.uiEditor.onComplete' | translate\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n ></valtimo-form-flow-expression-list>\n\n <valtimo-form-flow-expression-list\n [description]=\"'formFlow.uiEditor.onBackDescription' | translate\"\n [expressions]=\"getExpressions('onBack')\"\n [label]=\"'formFlow.uiEditor.onBack' | translate\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n ></valtimo-form-flow-expression-list>\n </section>\n\n <valtimo-form-flow-expression-help-modal\n [open]=\"$showHelpModal()\"\n [registry]=\"registry\"\n (closeEvent)=\"$showHelpModal.set(false)\"\n ></valtimo-form-flow-expression-help-modal>\n</div>\n", styles: [".step-detail{display:flex;flex-direction:column;gap:var(--cds-spacing-06)}.step-detail__header{display:flex;align-items:center;justify-content:space-between;gap:var(--cds-spacing-05)}.step-detail__heading{display:flex;align-items:center;gap:var(--cds-spacing-03)}.step-detail__heading cds-tag{margin:0}.step-detail__header-actions{display:flex;align-items:center;gap:var(--cds-spacing-03)}.step-detail__grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--cds-spacing-05)}.step-detail__grid cds-label{margin-bottom:0}.step-detail__divider{border:none;border-top:1px solid var(--cds-border-subtle);margin:0;width:100%}.step-detail__section{display:flex;flex-direction:column;gap:var(--cds-spacing-05)}.step-detail__section-header h5{margin:0 0 var(--cds-spacing-02) 0}.step-detail__section-description{color:var(--cds-text-secondary);font-size:var(--cds-body-compact-01-font-size, .875rem);margin:0}.step-detail__help-notification{display:block;margin:0;max-inline-size:none;max-width:none}.step-detail__label{display:inline-flex;align-items:center;gap:var(--cds-spacing-02)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i2$1.Dropdown, selector: "cds-dropdown, ibm-dropdown", inputs: ["id", "label", "hideLabel", "helperText", "placeholder", "displayValue", "clearText", "size", "type", "theme", "disabled", "readonly", "skeleton", "inline", "disableArrowKeys", "invalid", "invalidText", "warn", "warnText", "appendInline", "scrollableContainer", "itemValueKey", "selectionFeedback", "menuButtonLabel", "selectedLabel", "dropUp", "fluid"], outputs: ["selected", "onClose", "close"] }, { kind: "component", type: i2$1.DropdownList, selector: "cds-dropdown-list, ibm-dropdown-list", inputs: ["ariaLabel", "items", "listTpl", "type", "showTitles"], outputs: ["select", "scroll", "blurIntent"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i2$1.Label, selector: "cds-label, ibm-label", inputs: ["labelInputID", "disabled", "skeleton", "helperText", "invalidText", "invalid", "warn", "warnText", "ariaLabel"] }, { kind: "directive", type: i2$1.TextInput, selector: "[cdsText], [ibmText]", inputs: ["theme", "size", "invalid", "warn", "skeleton"] }, { kind: "ngmodule", type: LayerModule }, { kind: "directive", type: i2$1.LayerDirective, selector: "[cdsLayer], [ibmLayer]", inputs: ["ibmLayer", "cdsLayer"], exportAs: ["layer"] }, { kind: "ngmodule", type: NotificationModule }, { kind: "component", type: i2$1.ActionableNotification, selector: "cds-actionable-notification, ibm-actionable-notification", inputs: ["notificationObj"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i2$1.Tag, selector: "cds-tag, ibm-tag", inputs: ["type", "size", "class", "skeleton"] }, { kind: "ngmodule", type: TooltipIconModule }, { kind: "component", type: i1$2.TooltipIconComponent, selector: "v-tooltip-icon", inputs: ["tooltip", "disabled"] }, { kind: "component", type: FormFlowExpressionHelpModalComponent, selector: "valtimo-form-flow-expression-help-modal", inputs: ["open", "registry"], outputs: ["closeEvent"] }, { kind: "component", type: FormFlowExpressionListComponent, selector: "valtimo-form-flow-expression-list", inputs: ["expressions", "label", "description", "readOnly", "revealErrors", "registry"] }, { kind: "component", type: FormFlowTransitionListComponent, selector: "valtimo-form-flow-transition-list", inputs: ["transitions", "stepKeys", "readOnly", "revealErrors"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1196
|
+
}
|
|
1197
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowStepDetailComponent, decorators: [{
|
|
1198
|
+
type: Component,
|
|
1199
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-step-detail', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1200
|
+
CommonModule,
|
|
1201
|
+
ReactiveFormsModule,
|
|
1202
|
+
TranslateModule,
|
|
1203
|
+
ButtonModule,
|
|
1204
|
+
DropdownModule,
|
|
1205
|
+
IconModule,
|
|
1206
|
+
InputModule,
|
|
1207
|
+
LayerModule,
|
|
1208
|
+
NotificationModule,
|
|
1209
|
+
TagModule,
|
|
1210
|
+
TooltipIconModule,
|
|
1211
|
+
FormFlowExpressionHelpModalComponent,
|
|
1212
|
+
FormFlowExpressionListComponent,
|
|
1213
|
+
FormFlowTransitionListComponent,
|
|
1214
|
+
], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"step-detail\" [cdsLayer]=\"1\" [formGroup]=\"stepGroup\">\n <div class=\"step-detail__header\">\n <div class=\"step-detail__heading\">\n <h5>{{ 'formFlow.uiEditor.stepDetails' | translate }}</h5>\n\n @if (isStartStep) {\n <cds-tag type=\"green\" size=\"sm\">{{ 'formFlow.uiEditor.startStep' | translate }}</cds-tag>\n }\n </div>\n\n @if (!readOnly) {\n <div class=\"step-detail__header-actions\">\n @if (!isStartStep) {\n <button\n cdsButton=\"tertiary\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.makeStartStepButton\"\n (click)=\"makeStartEvent.emit()\"\n >\n {{ 'formFlow.uiEditor.makeStartStep' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"flag\" size=\"16\"></svg>\n </button>\n }\n\n <button\n cdsButton=\"danger--ghost\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.deleteStepButton\"\n (click)=\"deleteStepEvent.emit()\"\n >\n {{ 'formFlow.uiEditor.deleteStep' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </div>\n }\n </div>\n\n <div class=\"step-detail__grid\">\n <cds-label\n [invalid]=\"isControlInvalid('key') || (duplicateKey && revealErrors)\"\n [invalidText]=\"\n duplicateKey\n ? ('formFlow.uiEditor.duplicateKey' | translate)\n : ('formFlow.uiEditor.required' | translate)\n \"\n >\n <span class=\"step-detail__label\">\n {{ 'formFlow.key' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.key' | translate\"\n ></v-tooltip-icon>\n </span>\n\n <input\n cdsText\n formControlName=\"key\"\n [invalid]=\"isControlInvalid('key') || (duplicateKey && revealErrors)\"\n [attr.data-test-id]=\"testIds.stepKeyInput\"\n placeholder=\"{{ 'formFlow.uiEditor.keyPlaceholder' | translate }}\"\n />\n </cds-label>\n\n <cds-label>\n <span class=\"step-detail__label\">\n {{ 'formFlow.uiEditor.title' | translate }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.title' | translate\"\n ></v-tooltip-icon>\n </span>\n\n <input\n cdsText\n formControlName=\"title\"\n [attr.data-test-id]=\"testIds.stepTitleInput\"\n placeholder=\"{{ 'formFlow.uiEditor.titlePlaceholder' | translate }}\"\n />\n </cds-label>\n\n <div [attr.data-test-id]=\"testIds.stepTypeDropdown\">\n <cds-dropdown formControlName=\"typeName\" itemValueKey=\"id\" [label]=\"stepTypeLabelTemplate\">\n <cds-dropdown-list [items]=\"stepTypeItems\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #stepTypeLabelTemplate>\n <span class=\"step-detail__label\">\n {{ 'formFlow.uiEditor.stepType' | translate }}\n\n <v-tooltip-icon [tooltip]=\"getTypeTooltip()\"></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n\n <ng-container formGroupName=\"properties\">\n @for (propertyName of propertyNames; track propertyName) {\n @if (isFormDefinitionProperty(propertyName)) {\n <div [attr.data-test-id]=\"testIds.stepPropertyDropdown\">\n <cds-dropdown\n formControlName=\"definition\"\n itemValueKey=\"id\"\n [invalid]=\"propertiesGroup.get('definition')?.invalid && revealErrors\"\n [label]=\"formDefinitionLabelTemplate\"\n [placeholder]=\"'formFlow.uiEditor.selectForm' | translate\"\n >\n <cds-dropdown-list [items]=\"getFormDefinitionItems()\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #formDefinitionLabelTemplate>\n <span class=\"step-detail__label\">\n {{ getPropertyLabel(propertyName) }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.definition' | translate\"\n ></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n } @else if (isCustomComponentProperty(propertyName)) {\n <div [attr.data-test-id]=\"testIds.stepPropertyDropdown\">\n <cds-dropdown\n formControlName=\"componentId\"\n itemValueKey=\"id\"\n [invalid]=\"propertiesGroup.get('componentId')?.invalid && revealErrors\"\n [label]=\"componentIdLabelTemplate\"\n [placeholder]=\"'formFlow.uiEditor.selectComponent' | translate\"\n >\n <cds-dropdown-list [items]=\"getCustomComponentItems()\"></cds-dropdown-list>\n </cds-dropdown>\n\n <ng-template #componentIdLabelTemplate>\n <span class=\"step-detail__label\">\n {{ getPropertyLabel(propertyName) }}\n\n <v-tooltip-icon\n [tooltip]=\"'formFlow.uiEditor.fieldTooltips.componentId' | translate\"\n ></v-tooltip-icon>\n </span>\n </ng-template>\n </div>\n } @else {\n <cds-label\n [invalid]=\"propertiesGroup.get(propertyName)?.invalid && revealErrors\"\n [invalidText]=\"'formFlow.uiEditor.required' | translate\"\n >\n {{ getPropertyLabel(propertyName) }}\n\n <input\n cdsText\n [formControlName]=\"propertyName\"\n [invalid]=\"propertiesGroup.get(propertyName)?.invalid && revealErrors\"\n [attr.data-test-id]=\"testIds.stepPropertyInput\"\n />\n </cds-label>\n }\n }\n </ng-container>\n </div>\n\n <hr class=\"step-detail__divider\" />\n\n <section class=\"step-detail__section\">\n <div class=\"step-detail__section-header\">\n <h5>{{ 'formFlow.uiEditor.navigation' | translate }}</h5>\n\n <p class=\"step-detail__section-description\">\n {{ 'formFlow.uiEditor.navigationDescription' | translate }}\n </p>\n </div>\n\n <valtimo-form-flow-transition-list\n [readOnly]=\"readOnly\"\n [revealErrors]=\"revealErrors\"\n [stepKeys]=\"stepKeys\"\n [transitions]=\"transitions\"\n ></valtimo-form-flow-transition-list>\n\n <cds-actionable-notification\n class=\"step-detail__help-notification\"\n [notificationObj]=\"{\n type: 'info',\n lowContrast: true,\n showClose: false,\n title: 'formFlow.uiEditor.help.conditionsInlineTitle' | translate,\n message: 'formFlow.uiEditor.help.conditionsInlineMessage' | translate,\n actions: helpActions,\n }\"\n ></cds-actionable-notification>\n </section>\n\n <hr class=\"step-detail__divider\" />\n\n <section class=\"step-detail__section\">\n <div class=\"step-detail__section-header\">\n <h5>{{ 'formFlow.uiEditor.actions' | translate }}</h5>\n\n <p class=\"step-detail__section-description\">\n {{ 'formFlow.uiEditor.actionsDescription' | translate }}\n </p>\n </div>\n\n <cds-actionable-notification\n class=\"step-detail__help-notification\"\n [notificationObj]=\"{\n type: 'info',\n lowContrast: true,\n showClose: false,\n title: 'formFlow.uiEditor.help.actionsInlineTitle' | translate,\n message: 'formFlow.uiEditor.help.actionsInlineMessage' | translate,\n actions: helpActions,\n }\"\n ></cds-actionable-notification>\n\n <valtimo-form-flow-expression-list\n [description]=\"'formFlow.uiEditor.onOpenDescription' | translate\"\n [expressions]=\"getExpressions('onOpen')\"\n [label]=\"'formFlow.uiEditor.onOpen' | translate\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n ></valtimo-form-flow-expression-list>\n\n <valtimo-form-flow-expression-list\n [description]=\"'formFlow.uiEditor.onCompleteDescription' | translate\"\n [expressions]=\"getExpressions('onComplete')\"\n [label]=\"'formFlow.uiEditor.onComplete' | translate\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n ></valtimo-form-flow-expression-list>\n\n <valtimo-form-flow-expression-list\n [description]=\"'formFlow.uiEditor.onBackDescription' | translate\"\n [expressions]=\"getExpressions('onBack')\"\n [label]=\"'formFlow.uiEditor.onBack' | translate\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n ></valtimo-form-flow-expression-list>\n </section>\n\n <valtimo-form-flow-expression-help-modal\n [open]=\"$showHelpModal()\"\n [registry]=\"registry\"\n (closeEvent)=\"$showHelpModal.set(false)\"\n ></valtimo-form-flow-expression-help-modal>\n</div>\n", styles: [".step-detail{display:flex;flex-direction:column;gap:var(--cds-spacing-06)}.step-detail__header{display:flex;align-items:center;justify-content:space-between;gap:var(--cds-spacing-05)}.step-detail__heading{display:flex;align-items:center;gap:var(--cds-spacing-03)}.step-detail__heading cds-tag{margin:0}.step-detail__header-actions{display:flex;align-items:center;gap:var(--cds-spacing-03)}.step-detail__grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--cds-spacing-05)}.step-detail__grid cds-label{margin-bottom:0}.step-detail__divider{border:none;border-top:1px solid var(--cds-border-subtle);margin:0;width:100%}.step-detail__section{display:flex;flex-direction:column;gap:var(--cds-spacing-05)}.step-detail__section-header h5{margin:0 0 var(--cds-spacing-02) 0}.step-detail__section-description{color:var(--cds-text-secondary);font-size:var(--cds-body-compact-01-font-size, .875rem);margin:0}.step-detail__help-notification{display:block;margin:0;max-inline-size:none;max-width:none}.step-detail__label{display:inline-flex;align-items:center;gap:var(--cds-spacing-02)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
1215
|
+
}], ctorParameters: () => [{ type: FormFlowEditorFormService }, { type: i2$1.IconService }, { type: i4.TranslateService }], propDecorators: { stepGroup: [{
|
|
1216
|
+
type: Input
|
|
1217
|
+
}], registry: [{
|
|
1218
|
+
type: Input
|
|
1219
|
+
}], stepKeys: [{
|
|
1220
|
+
type: Input
|
|
1221
|
+
}], formOptions: [{
|
|
1222
|
+
type: Input
|
|
1223
|
+
}], componentOptions: [{
|
|
1224
|
+
type: Input
|
|
1225
|
+
}], isStartStep: [{
|
|
1226
|
+
type: Input
|
|
1227
|
+
}], readOnly: [{
|
|
1228
|
+
type: Input
|
|
1229
|
+
}], duplicateKey: [{
|
|
1230
|
+
type: Input
|
|
1231
|
+
}], revealErrors: [{
|
|
1232
|
+
type: Input
|
|
1233
|
+
}], deleteStepEvent: [{
|
|
1234
|
+
type: Output
|
|
1235
|
+
}], makeStartEvent: [{
|
|
1236
|
+
type: Output
|
|
1237
|
+
}] } });
|
|
1238
|
+
|
|
1239
|
+
/*
|
|
1240
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
1241
|
+
*
|
|
1242
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
1243
|
+
* you may not use this file except in compliance with the License.
|
|
1244
|
+
* You may obtain a copy of the License at
|
|
1245
|
+
*
|
|
1246
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
1247
|
+
*
|
|
1248
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1249
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
1250
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1251
|
+
* See the License for the specific language governing permissions and
|
|
1252
|
+
* limitations under the License.
|
|
1253
|
+
*/
|
|
1254
|
+
/** Clickable overview of all steps in the flow, ordered as defined, with the start step first. */
|
|
1255
|
+
class FormFlowStepListComponent {
|
|
1256
|
+
constructor(iconService) {
|
|
1257
|
+
this.iconService = iconService;
|
|
1258
|
+
this.steps = [];
|
|
1259
|
+
this.selectedIndex = null;
|
|
1260
|
+
this.readOnly = false;
|
|
1261
|
+
this.selectEvent = new EventEmitter();
|
|
1262
|
+
this.addEvent = new EventEmitter();
|
|
1263
|
+
this.testIds = FORM_FLOW_EDITOR_TEST_IDS;
|
|
1264
|
+
this.iconService.registerAll([Add16, WarningFilled16]);
|
|
1265
|
+
}
|
|
1266
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowStepListComponent, deps: [{ token: i2$1.IconService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1267
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowStepListComponent, isStandalone: true, selector: "valtimo-form-flow-step-list", inputs: { steps: "steps", selectedIndex: "selectedIndex", readOnly: "readOnly" }, outputs: { selectEvent: "selectEvent", addEvent: "addEvent" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"step-list__header\">\n <span class=\"step-list__title\">{{ 'formFlow.uiEditor.stepsTitle' | translate }}</span>\n\n @if (!readOnly) {\n <button\n cdsButton=\"primary\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.addStepButton\"\n (click)=\"addEvent.emit()\"\n >\n {{ 'formFlow.uiEditor.addStep' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n }\n</div>\n\n<ul class=\"step-list__list\" [attr.data-test-id]=\"testIds.stepList\">\n @for (step of steps; track $index) {\n <li class=\"step-list__list-item\">\n <button\n class=\"step-list__item\"\n [class.step-list__item--selected]=\"selectedIndex === $index\"\n [attr.data-test-id]=\"testIds.stepListItem\"\n type=\"button\"\n (click)=\"selectEvent.emit($index)\"\n >\n <span class=\"step-list__item-heading\">\n <span class=\"step-list__item-key\" [title]=\"step.key\">{{ step.key }}</span>\n\n @if (step.invalid) {\n <svg\n class=\"step-list__item-warning\"\n cdsIcon=\"warning--filled\"\n size=\"16\"\n [attr.aria-label]=\"'formFlow.uiEditor.stepInvalid' | translate\"\n ></svg>\n }\n </span>\n\n @if (step.title) {\n <span class=\"step-list__item-title\">{{ step.title }}</span>\n }\n\n <span class=\"step-list__item-tags\">\n @if (step.isStart) {\n <cds-tag type=\"green\" size=\"sm\">{{\n 'formFlow.uiEditor.startStep' | translate\n }}</cds-tag>\n }\n\n <cds-tag type=\"blue\" size=\"sm\">{{ step.typeName }}</cds-tag>\n </span>\n </button>\n </li>\n }\n</ul>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column;min-height:0}.step-list__header{align-items:center;border-bottom:1px solid var(--cds-border-subtle, #e0e0e0);display:flex;flex:0 0 auto;justify-content:space-between;padding:12px}.step-list__title{color:var(--cds-text-secondary, #525252);font-size:14px;line-height:24px;font-weight:600}.step-list__list{flex:1 1 auto;list-style:none;margin:0;min-height:0;overflow-y:auto;padding:0}.step-list__list-item{margin:0}.step-list__item{background:transparent;border:0;border-left:3px solid transparent;cursor:pointer;display:flex;flex-direction:column;gap:.375rem;padding:.75rem 1rem;text-align:left;width:100%}.step-list__item:hover{background:var(--cds-layer-hover-01, #e8e8e8)}.step-list__item:focus-visible{outline:2px solid var(--cds-focus, #0f62fe);outline-offset:-2px}.step-list__item--selected{background:var(--cds-layer-selected-01, #e0e0e0);border-left-color:var(--cds-border-interactive, #0f62fe)}.step-list__item--selected:hover{background:var(--cds-layer-selected-hover-01, #d1d1d1)}.step-list__item-heading{align-items:center;display:flex;gap:.5rem;justify-content:space-between;max-width:100%}.step-list__item-key{color:var(--cds-text-primary, #161616);font-size:.875rem;font-weight:600;line-height:1.29;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.step-list__item-warning{fill:var(--cds-support-error, #da1e28);flex-shrink:0;height:1rem;width:1rem}.step-list__item-title{color:var(--cds-text-secondary, #525252);font-size:.75rem;line-height:1.34}.step-list__item-tags{align-items:center;display:flex;flex-wrap:wrap;gap:.25rem}.step-list__item-tags ::ng-deep .cds--tag{margin:0;max-width:none;box-shadow:0 0 0 1px var(--cds-layer-01)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i2$1.Tag, selector: "cds-tag, ibm-tag", inputs: ["type", "size", "class", "skeleton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1268
|
+
}
|
|
1269
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowStepListComponent, decorators: [{
|
|
1270
|
+
type: Component,
|
|
1271
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-step-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, TranslateModule, ButtonModule, IconModule, TagModule], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div class=\"step-list__header\">\n <span class=\"step-list__title\">{{ 'formFlow.uiEditor.stepsTitle' | translate }}</span>\n\n @if (!readOnly) {\n <button\n cdsButton=\"primary\"\n size=\"sm\"\n [attr.data-test-id]=\"testIds.addStepButton\"\n (click)=\"addEvent.emit()\"\n >\n {{ 'formFlow.uiEditor.addStep' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n }\n</div>\n\n<ul class=\"step-list__list\" [attr.data-test-id]=\"testIds.stepList\">\n @for (step of steps; track $index) {\n <li class=\"step-list__list-item\">\n <button\n class=\"step-list__item\"\n [class.step-list__item--selected]=\"selectedIndex === $index\"\n [attr.data-test-id]=\"testIds.stepListItem\"\n type=\"button\"\n (click)=\"selectEvent.emit($index)\"\n >\n <span class=\"step-list__item-heading\">\n <span class=\"step-list__item-key\" [title]=\"step.key\">{{ step.key }}</span>\n\n @if (step.invalid) {\n <svg\n class=\"step-list__item-warning\"\n cdsIcon=\"warning--filled\"\n size=\"16\"\n [attr.aria-label]=\"'formFlow.uiEditor.stepInvalid' | translate\"\n ></svg>\n }\n </span>\n\n @if (step.title) {\n <span class=\"step-list__item-title\">{{ step.title }}</span>\n }\n\n <span class=\"step-list__item-tags\">\n @if (step.isStart) {\n <cds-tag type=\"green\" size=\"sm\">{{\n 'formFlow.uiEditor.startStep' | translate\n }}</cds-tag>\n }\n\n <cds-tag type=\"blue\" size=\"sm\">{{ step.typeName }}</cds-tag>\n </span>\n </button>\n </li>\n }\n</ul>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column;min-height:0}.step-list__header{align-items:center;border-bottom:1px solid var(--cds-border-subtle, #e0e0e0);display:flex;flex:0 0 auto;justify-content:space-between;padding:12px}.step-list__title{color:var(--cds-text-secondary, #525252);font-size:14px;line-height:24px;font-weight:600}.step-list__list{flex:1 1 auto;list-style:none;margin:0;min-height:0;overflow-y:auto;padding:0}.step-list__list-item{margin:0}.step-list__item{background:transparent;border:0;border-left:3px solid transparent;cursor:pointer;display:flex;flex-direction:column;gap:.375rem;padding:.75rem 1rem;text-align:left;width:100%}.step-list__item:hover{background:var(--cds-layer-hover-01, #e8e8e8)}.step-list__item:focus-visible{outline:2px solid var(--cds-focus, #0f62fe);outline-offset:-2px}.step-list__item--selected{background:var(--cds-layer-selected-01, #e0e0e0);border-left-color:var(--cds-border-interactive, #0f62fe)}.step-list__item--selected:hover{background:var(--cds-layer-selected-hover-01, #d1d1d1)}.step-list__item-heading{align-items:center;display:flex;gap:.5rem;justify-content:space-between;max-width:100%}.step-list__item-key{color:var(--cds-text-primary, #161616);font-size:.875rem;font-weight:600;line-height:1.29;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.step-list__item-warning{fill:var(--cds-support-error, #da1e28);flex-shrink:0;height:1rem;width:1rem}.step-list__item-title{color:var(--cds-text-secondary, #525252);font-size:.75rem;line-height:1.34}.step-list__item-tags{align-items:center;display:flex;flex-wrap:wrap;gap:.25rem}.step-list__item-tags ::ng-deep .cds--tag{margin:0;max-width:none;box-shadow:0 0 0 1px var(--cds-layer-01)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
1272
|
+
}], ctorParameters: () => [{ type: i2$1.IconService }], propDecorators: { steps: [{
|
|
1273
|
+
type: Input
|
|
1274
|
+
}], selectedIndex: [{
|
|
1275
|
+
type: Input
|
|
1276
|
+
}], readOnly: [{
|
|
1277
|
+
type: Input
|
|
1278
|
+
}], selectEvent: [{
|
|
1279
|
+
type: Output
|
|
1280
|
+
}], addEvent: [{
|
|
1281
|
+
type: Output
|
|
1282
|
+
}] } });
|
|
1283
|
+
|
|
1284
|
+
/*
|
|
1285
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
1286
|
+
*
|
|
1287
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
1288
|
+
* you may not use this file except in compliance with the License.
|
|
1289
|
+
* You may obtain a copy of the License at
|
|
1290
|
+
*
|
|
1291
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
1292
|
+
*
|
|
1293
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1294
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
1295
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1296
|
+
* See the License for the specific language governing permissions and
|
|
1297
|
+
* limitations under the License.
|
|
1298
|
+
*/
|
|
1299
|
+
/**
|
|
1300
|
+
* The visual form flow editor. Renders the definition as a step list with a detail panel and emits
|
|
1301
|
+
* the modified definition as JSON through the same contract as the JSON editor tab, so the
|
|
1302
|
+
* surrounding editor page treats both tabs identically.
|
|
1303
|
+
*/
|
|
1304
|
+
class FormFlowUiEditorTabComponent {
|
|
1305
|
+
constructor(formService, formFlowService, formFlowComponentService, formManagementService, route, translateService) {
|
|
1306
|
+
this.formService = formService;
|
|
1307
|
+
this.formFlowService = formFlowService;
|
|
1308
|
+
this.formFlowComponentService = formFlowComponentService;
|
|
1309
|
+
this.formManagementService = formManagementService;
|
|
1310
|
+
this.route = route;
|
|
1311
|
+
this.translateService = translateService;
|
|
1312
|
+
this.model = null;
|
|
1313
|
+
this.readOnly = false;
|
|
1314
|
+
/** The parent's save-attempt state: while false the admin is still modelling and no validation
|
|
1315
|
+
* errors are shown; a save attempt flips it and reveals them. */
|
|
1316
|
+
this.revealErrors = false;
|
|
1317
|
+
this.validEvent = new EventEmitter();
|
|
1318
|
+
this.valueChangeEvent = new EventEmitter();
|
|
1319
|
+
this.form = null;
|
|
1320
|
+
this.registry = null;
|
|
1321
|
+
this.$selectedIndex = signal(null);
|
|
1322
|
+
this.$stepListItems = signal([]);
|
|
1323
|
+
// Only replaced when the keys themselves change: the Carbon dropdown resets its visual
|
|
1324
|
+
// selection whenever its items array is swapped, so a stable reference prevents flicker while
|
|
1325
|
+
// typing in unrelated fields.
|
|
1326
|
+
this.$stepKeys = signal([]);
|
|
1327
|
+
this.$formOptions = signal([]);
|
|
1328
|
+
this.$componentOptions = signal([]);
|
|
1329
|
+
this.$definitionErrors = signal([]);
|
|
1330
|
+
this.$parseFailed = signal(false);
|
|
1331
|
+
this.testIds = FORM_FLOW_EDITOR_TEST_IDS;
|
|
1332
|
+
this._definition = null;
|
|
1333
|
+
this._definitionKey = '';
|
|
1334
|
+
this._previousStepKeys = [];
|
|
1335
|
+
this._formSubscriptions = new Subscription();
|
|
1336
|
+
this._subscriptions = new Subscription();
|
|
1337
|
+
this._subscriptions.add(this.formFlowService.getFormFlowRegistry().subscribe(registry => {
|
|
1338
|
+
this.registry = registry;
|
|
1339
|
+
this.buildForm();
|
|
1340
|
+
}));
|
|
1341
|
+
// The custom Angular components an implementation has registered for `custom-component`
|
|
1342
|
+
// steps. The editor offers their ids as choices.
|
|
1343
|
+
this._subscriptions.add(this.formFlowComponentService.supportedComponents$
|
|
1344
|
+
.pipe(take(1))
|
|
1345
|
+
.subscribe(components => this.$componentOptions.set(components.map(component => component.id).sort())));
|
|
1346
|
+
this.loadFormOptions();
|
|
1347
|
+
}
|
|
1348
|
+
ngOnChanges(changes) {
|
|
1349
|
+
if (changes['model']) {
|
|
1350
|
+
this.parseModel();
|
|
1351
|
+
this.buildForm();
|
|
1352
|
+
}
|
|
1353
|
+
if (changes['readOnly'] && !changes['readOnly'].firstChange) {
|
|
1354
|
+
this.applyReadOnly();
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
ngOnDestroy() {
|
|
1358
|
+
this._subscriptions.unsubscribe();
|
|
1359
|
+
this._formSubscriptions.unsubscribe();
|
|
1360
|
+
}
|
|
1361
|
+
get selectedStepGroup() {
|
|
1362
|
+
const index = this.$selectedIndex();
|
|
1363
|
+
if (this.form === null || index === null)
|
|
1364
|
+
return null;
|
|
1365
|
+
return this.stepsArray.controls[index] ?? null;
|
|
1366
|
+
}
|
|
1367
|
+
isSelectedKeyDuplicate() {
|
|
1368
|
+
const index = this.$selectedIndex();
|
|
1369
|
+
if (index === null)
|
|
1370
|
+
return false;
|
|
1371
|
+
const keys = this.stepsArray.controls.map(stepGroup => stepGroup.get('key')?.value);
|
|
1372
|
+
return keys.filter(key => key === keys[index]).length > 1;
|
|
1373
|
+
}
|
|
1374
|
+
isSelectedStepStart() {
|
|
1375
|
+
return (!!this.selectedStepGroup &&
|
|
1376
|
+
this.selectedStepGroup.get('key')?.value === this.form?.get('startStep')?.value);
|
|
1377
|
+
}
|
|
1378
|
+
onMakeStartStep() {
|
|
1379
|
+
const key = this.selectedStepGroup?.get('key')?.value;
|
|
1380
|
+
if (key) {
|
|
1381
|
+
this.form?.get('startStep')?.setValue(key);
|
|
1382
|
+
this.form?.get('startStep')?.markAsDirty();
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
onSelectStep(index) {
|
|
1386
|
+
this.$selectedIndex.set(index);
|
|
1387
|
+
}
|
|
1388
|
+
onAddStep() {
|
|
1389
|
+
if (!this.form || !this.registry)
|
|
1390
|
+
return;
|
|
1391
|
+
const existingKeys = this.stepsArray.controls.map(stepGroup => stepGroup.get('key')?.value);
|
|
1392
|
+
this.stepsArray.push(this.formService.buildNewStepGroup(existingKeys, this.registry));
|
|
1393
|
+
this.stepsArray.markAsDirty();
|
|
1394
|
+
this.$selectedIndex.set(this.stepsArray.length - 1);
|
|
1395
|
+
}
|
|
1396
|
+
onDeleteStep() {
|
|
1397
|
+
const index = this.$selectedIndex();
|
|
1398
|
+
if (index === null || !this.form)
|
|
1399
|
+
return;
|
|
1400
|
+
const deletedKey = this.stepsArray.at(index).get('key')?.value;
|
|
1401
|
+
this.stepsArray.removeAt(index);
|
|
1402
|
+
this.stepsArray.markAsDirty();
|
|
1403
|
+
// Deleting the start step promotes the first remaining step, so the definition stays valid.
|
|
1404
|
+
const startStepControl = this.form.get('startStep');
|
|
1405
|
+
if (startStepControl?.value === deletedKey) {
|
|
1406
|
+
startStepControl.setValue(this.stepsArray.at(0)?.get('key')?.value ?? '');
|
|
1407
|
+
}
|
|
1408
|
+
if (this.stepsArray.length === 0) {
|
|
1409
|
+
this.$selectedIndex.set(null);
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
this.$selectedIndex.set(Math.min(index, this.stepsArray.length - 1));
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
get stepsArray() {
|
|
1416
|
+
return this.formService.getStepsArray(this.form);
|
|
1417
|
+
}
|
|
1418
|
+
parseModel() {
|
|
1419
|
+
this._definition = null;
|
|
1420
|
+
this.$parseFailed.set(false);
|
|
1421
|
+
if (!this.model?.value)
|
|
1422
|
+
return;
|
|
1423
|
+
try {
|
|
1424
|
+
this._definition = JSON.parse(this.model.value);
|
|
1425
|
+
this._definitionKey = this._definition.key;
|
|
1426
|
+
}
|
|
1427
|
+
catch {
|
|
1428
|
+
this.$parseFailed.set(true);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
buildForm() {
|
|
1432
|
+
if (!this._definition || !this.registry)
|
|
1433
|
+
return;
|
|
1434
|
+
this._formSubscriptions.unsubscribe();
|
|
1435
|
+
this._formSubscriptions = new Subscription();
|
|
1436
|
+
this.form = this.formService.buildDefinitionForm(this._definition, this.registry);
|
|
1437
|
+
this._previousStepKeys = this.currentStepKeys();
|
|
1438
|
+
this.applyReadOnly();
|
|
1439
|
+
this.selectInitialStep();
|
|
1440
|
+
this.refreshDerivedState();
|
|
1441
|
+
// The initial emission is the container's clean baseline; every change after that re-emits the
|
|
1442
|
+
// serialized definition and its validity, mirroring the JSON editor tab.
|
|
1443
|
+
this.emitState();
|
|
1444
|
+
// Validity only changes together with values (validators run on value changes and the
|
|
1445
|
+
// readOnly enable/disable path suppresses events), so valueChanges alone keeps every derived
|
|
1446
|
+
// signal fresh.
|
|
1447
|
+
this._formSubscriptions.add(this.form.valueChanges.subscribe(() => {
|
|
1448
|
+
this.trackStepKeyRenames();
|
|
1449
|
+
this.refreshDerivedState();
|
|
1450
|
+
this.emitState();
|
|
1451
|
+
}));
|
|
1452
|
+
}
|
|
1453
|
+
selectInitialStep() {
|
|
1454
|
+
const startStep = this._definition?.startStep;
|
|
1455
|
+
const startIndex = (this._definition?.steps ?? []).findIndex(step => step.key === startStep);
|
|
1456
|
+
if ((this._definition?.steps ?? []).length === 0) {
|
|
1457
|
+
this.$selectedIndex.set(null);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
this.$selectedIndex.set(startIndex >= 0 ? startIndex : 0);
|
|
1461
|
+
}
|
|
1462
|
+
applyReadOnly() {
|
|
1463
|
+
if (!this.form)
|
|
1464
|
+
return;
|
|
1465
|
+
if (this.readOnly) {
|
|
1466
|
+
this.form.disable({ emitEvent: false });
|
|
1467
|
+
}
|
|
1468
|
+
else {
|
|
1469
|
+
this.form.enable({ emitEvent: false });
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
// Renaming a step key rewrites the start step and every transition that targeted the old key, so
|
|
1473
|
+
// the definition never breaks while typing a new key.
|
|
1474
|
+
trackStepKeyRenames() {
|
|
1475
|
+
const currentKeys = this.currentStepKeys();
|
|
1476
|
+
if (currentKeys.length === this._previousStepKeys.length) {
|
|
1477
|
+
const changedIndices = currentKeys
|
|
1478
|
+
.map((key, index) => (key !== this._previousStepKeys[index] ? index : -1))
|
|
1479
|
+
.filter(index => index !== -1);
|
|
1480
|
+
if (changedIndices.length === 1) {
|
|
1481
|
+
const index = changedIndices[0];
|
|
1482
|
+
this.formService.renameStepReferences(this.form, this._previousStepKeys[index], currentKeys[index]);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
this._previousStepKeys = currentKeys;
|
|
1486
|
+
}
|
|
1487
|
+
currentStepKeys() {
|
|
1488
|
+
return this.stepsArray.controls.map(stepGroup => stepGroup.get('key')?.value ?? '');
|
|
1489
|
+
}
|
|
1490
|
+
refreshDerivedState() {
|
|
1491
|
+
if (!this.form)
|
|
1492
|
+
return;
|
|
1493
|
+
const startStep = this.form.get('startStep')?.value;
|
|
1494
|
+
const keys = this.currentStepKeys();
|
|
1495
|
+
this.$stepListItems.set(this.stepsArray.controls.map(stepGroup => ({
|
|
1496
|
+
key: stepGroup.get('key')?.value ?? '',
|
|
1497
|
+
title: stepGroup.get('title')?.value ?? '',
|
|
1498
|
+
typeName: stepGroup.get('typeName')?.value ?? '',
|
|
1499
|
+
isStart: stepGroup.get('key')?.value === startStep,
|
|
1500
|
+
invalid: stepGroup.invalid,
|
|
1501
|
+
})));
|
|
1502
|
+
if (!this.arraysEqual(keys, this.$stepKeys())) {
|
|
1503
|
+
this.$stepKeys.set(keys);
|
|
1504
|
+
}
|
|
1505
|
+
this.$definitionErrors.set(this.collectDefinitionErrors());
|
|
1506
|
+
}
|
|
1507
|
+
// Loads the form definitions of the surrounding case definition or building block, so the
|
|
1508
|
+
// `form` step type can offer them as choices instead of a free-text form key.
|
|
1509
|
+
loadFormOptions() {
|
|
1510
|
+
this._subscriptions.add(getContextObservable(this.route)
|
|
1511
|
+
.pipe(take(1), switchMap(context => {
|
|
1512
|
+
if (context === 'buildingBlock') {
|
|
1513
|
+
return getBuildingBlockManagementRouteParams(this.route).pipe(take(1), switchMap(params => this.formManagementService.queryFormDefinitionsBuildingBlock(params?.buildingBlockDefinitionKey ?? '', params?.buildingBlockDefinitionVersionTag ?? '', { size: 1000 })), map(page => page.content));
|
|
1514
|
+
}
|
|
1515
|
+
return getCaseManagementRouteParams(this.route).pipe(take(1), switchMap(params => this.formManagementService.queryFormDefinitionsCase(params?.caseDefinitionKey ?? '', params?.caseDefinitionVersionTag ?? '', { size: 1000 })), map(response => response.content));
|
|
1516
|
+
}), map(formDefinitions => [...new Set(formDefinitions.map(definition => definition.name).filter(Boolean))].sort()), catchError(() => of([])))
|
|
1517
|
+
.subscribe(names => this.$formOptions.set(names)));
|
|
1518
|
+
}
|
|
1519
|
+
arraysEqual(left, right) {
|
|
1520
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1521
|
+
}
|
|
1522
|
+
collectDefinitionErrors() {
|
|
1523
|
+
const errors = [];
|
|
1524
|
+
const formErrors = this.form?.errors ?? {};
|
|
1525
|
+
const stepErrors = this.form?.get('steps')?.errors ?? {};
|
|
1526
|
+
if (stepErrors['duplicateStepKeys']) {
|
|
1527
|
+
errors.push(this.translateService.instant('formFlow.uiEditor.errors.duplicateKeys', {
|
|
1528
|
+
keys: stepErrors['duplicateStepKeys'].keys.join(', '),
|
|
1529
|
+
}));
|
|
1530
|
+
}
|
|
1531
|
+
if (formErrors['startStepMissing']) {
|
|
1532
|
+
errors.push(this.translateService.instant('formFlow.uiEditor.errors.startStepMissing', {
|
|
1533
|
+
startStep: formErrors['startStepMissing'].startStep,
|
|
1534
|
+
}));
|
|
1535
|
+
}
|
|
1536
|
+
if (formErrors['unknownTransitionTargets']) {
|
|
1537
|
+
errors.push(this.translateService.instant('formFlow.uiEditor.errors.unknownTargets', {
|
|
1538
|
+
targets: formErrors['unknownTransitionTargets'].targets.join(', '),
|
|
1539
|
+
}));
|
|
1540
|
+
}
|
|
1541
|
+
// A step can be invalid on its own — a missing type, an unselected form, an empty transition
|
|
1542
|
+
// target or expression — which the definition-level checks above never name. `invalid` (not
|
|
1543
|
+
// `!valid`) skips disabled controls, so a read-only flow is never reported as incomplete; this
|
|
1544
|
+
// mirrors the warning icon shown per step. The notification is only *shown* after a save attempt
|
|
1545
|
+
// (see `revealErrors` in the template), so collecting these stays quiet while modelling.
|
|
1546
|
+
this.form?.get('steps')?.controls.forEach((stepGroup, index) => {
|
|
1547
|
+
if (!stepGroup.invalid)
|
|
1548
|
+
return;
|
|
1549
|
+
const key = (stepGroup.get('key')?.value ?? '').trim();
|
|
1550
|
+
errors.push(this.translateService.instant('formFlow.uiEditor.errors.stepIncomplete', {
|
|
1551
|
+
step: key || `#${index + 1}`,
|
|
1552
|
+
}));
|
|
1553
|
+
});
|
|
1554
|
+
return errors;
|
|
1555
|
+
}
|
|
1556
|
+
emitState() {
|
|
1557
|
+
if (!this.form)
|
|
1558
|
+
return;
|
|
1559
|
+
const definition = this.formService.serialize(this.form, this._definitionKey);
|
|
1560
|
+
this.valueChangeEvent.emit(JSON.stringify(definition));
|
|
1561
|
+
this.validEvent.emit(this.form.valid);
|
|
1562
|
+
}
|
|
1563
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowUiEditorTabComponent, deps: [{ token: FormFlowEditorFormService }, { token: FormFlowService }, { token: FormFlowComponentService }, { token: i4$1.FormManagementService }, { token: i3.ActivatedRoute }, { token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1564
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowUiEditorTabComponent, isStandalone: true, selector: "valtimo-form-flow-ui-editor-tab", inputs: { model: "model", readOnly: "readOnly", revealErrors: "revealErrors" }, outputs: { validEvent: "validEvent", valueChangeEvent: "valueChangeEvent" }, providers: [FormFlowEditorFormService], usesOnChanges: true, ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n@if ($parseFailed()) {\n <div class=\"ui-editor__notification\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'warning',\n title: 'formFlow.uiEditor.parseFailedTitle' | translate,\n message: 'formFlow.uiEditor.parseFailedMessage' | translate,\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </div>\n} @else if (form && registry) {\n <div [formGroup]=\"form\">\n <!-- fitPage sizes this container to the viewport; the notification, sidebar and panel all live\n inside it, so toggling the notification reflows the body instead of shifting the container's\n top offset (which fitPage only re-measures on a header change or resize). -->\n <div class=\"ui-editor\" fitPage>\n @if (revealErrors && $definitionErrors().length) {\n <div class=\"ui-editor__notification\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'error',\n title: 'formFlow.uiEditor.errors.title' | translate,\n message: $definitionErrors().join(' '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </div>\n }\n\n <div class=\"ui-editor__body\">\n <aside class=\"ui-editor__sidebar\">\n <valtimo-form-flow-step-list\n [readOnly]=\"readOnly\"\n [selectedIndex]=\"$selectedIndex()\"\n [steps]=\"$stepListItems()\"\n (addEvent)=\"onAddStep()\"\n (selectEvent)=\"onSelectStep($event)\"\n ></valtimo-form-flow-step-list>\n </aside>\n\n <div class=\"ui-editor__panel\">\n @if (selectedStepGroup; as stepGroup) {\n <valtimo-form-flow-step-detail\n [componentOptions]=\"$componentOptions()\"\n [duplicateKey]=\"isSelectedKeyDuplicate()\"\n [formOptions]=\"$formOptions()\"\n [isStartStep]=\"isSelectedStepStart()\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n [stepGroup]=\"stepGroup\"\n [stepKeys]=\"$stepKeys()\"\n (deleteStepEvent)=\"onDeleteStep()\"\n (makeStartEvent)=\"onMakeStartStep()\"\n ></valtimo-form-flow-step-detail>\n } @else {\n <div class=\"ui-editor__empty\">\n <h5 class=\"ui-editor__empty-title\">\n {{ 'formFlow.uiEditor.noStepsTitle' | translate }}\n </h5>\n\n <p class=\"ui-editor__empty-message\">\n {{ 'formFlow.uiEditor.noStepsDescription' | translate }}\n </p>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n} @else {\n <div class=\"ui-editor__loading\">\n <cds-loading></cds-loading>\n </div>\n}\n", styles: [".ui-editor{display:flex;flex-direction:column;border:1px solid var(--cds-border-subtle, #e0e0e0);overflow:hidden}.ui-editor__body{display:flex;flex:1 1 auto;min-height:0}.ui-editor__sidebar{background:var(--cds-layer-01, #f4f4f4);border-right:1px solid var(--cds-border-subtle, #e0e0e0);display:flex;flex:0 0 22rem;flex-direction:column;min-height:0}.ui-editor__panel{flex:1 1 auto;min-width:0;overflow-y:auto;padding:1rem 1.5rem 1.5rem}.ui-editor__notification{flex:0 0 auto}.ui-editor__notification cds-inline-notification{display:block;max-width:none;margin:0}.ui-editor__notification ::ng-deep .cds--inline-notification--low-contrast:before{border-width:0 0 1px 0}.ui-editor__empty{align-items:center;border:1px dashed var(--cds-border-strong, #8d8d8d);display:flex;flex-direction:column;gap:.5rem;margin:2rem auto;max-width:30rem;padding:2rem;text-align:center}.ui-editor__empty-title{margin:0}.ui-editor__empty-message{color:var(--cds-text-secondary, #525252);margin:0 0 .5rem}.ui-editor__loading{display:flex;width:100%;justify-content:center;padding:var(--cds-spacing-07)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "directive", type: FitPageDirective, selector: "[fitPage]", inputs: ["spaceAdjustment", "fitPageDisabled", "disableOverflow", "bottomMargin"] }, { kind: "ngmodule", type: LoadingModule }, { kind: "component", type: i2$1.Loading, selector: "cds-loading, ibm-loading", inputs: ["title", "isActive", "size", "overlay"] }, { kind: "ngmodule", type: NotificationModule }, { kind: "component", type: i2$1.Notification, selector: "cds-notification, cds-inline-notification, ibm-notification, ibm-inline-notification", inputs: ["notificationObj"] }, { kind: "component", type: FormFlowStepDetailComponent, selector: "valtimo-form-flow-step-detail", inputs: ["stepGroup", "registry", "stepKeys", "formOptions", "componentOptions", "isStartStep", "readOnly", "duplicateKey", "revealErrors"], outputs: ["deleteStepEvent", "makeStartEvent"] }, { kind: "component", type: FormFlowStepListComponent, selector: "valtimo-form-flow-step-list", inputs: ["steps", "selectedIndex", "readOnly"], outputs: ["selectEvent", "addEvent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1565
|
+
}
|
|
1566
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowUiEditorTabComponent, decorators: [{
|
|
1567
|
+
type: Component,
|
|
1568
|
+
args: [{ standalone: true, selector: 'valtimo-form-flow-ui-editor-tab', changeDetection: ChangeDetectionStrategy.OnPush, providers: [FormFlowEditorFormService], imports: [
|
|
1569
|
+
CommonModule,
|
|
1570
|
+
ReactiveFormsModule,
|
|
1571
|
+
TranslateModule,
|
|
1572
|
+
FitPageDirective,
|
|
1573
|
+
LoadingModule,
|
|
1574
|
+
NotificationModule,
|
|
1575
|
+
FormFlowStepDetailComponent,
|
|
1576
|
+
FormFlowStepListComponent,
|
|
1577
|
+
], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n@if ($parseFailed()) {\n <div class=\"ui-editor__notification\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'warning',\n title: 'formFlow.uiEditor.parseFailedTitle' | translate,\n message: 'formFlow.uiEditor.parseFailedMessage' | translate,\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </div>\n} @else if (form && registry) {\n <div [formGroup]=\"form\">\n <!-- fitPage sizes this container to the viewport; the notification, sidebar and panel all live\n inside it, so toggling the notification reflows the body instead of shifting the container's\n top offset (which fitPage only re-measures on a header change or resize). -->\n <div class=\"ui-editor\" fitPage>\n @if (revealErrors && $definitionErrors().length) {\n <div class=\"ui-editor__notification\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'error',\n title: 'formFlow.uiEditor.errors.title' | translate,\n message: $definitionErrors().join(' '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </div>\n }\n\n <div class=\"ui-editor__body\">\n <aside class=\"ui-editor__sidebar\">\n <valtimo-form-flow-step-list\n [readOnly]=\"readOnly\"\n [selectedIndex]=\"$selectedIndex()\"\n [steps]=\"$stepListItems()\"\n (addEvent)=\"onAddStep()\"\n (selectEvent)=\"onSelectStep($event)\"\n ></valtimo-form-flow-step-list>\n </aside>\n\n <div class=\"ui-editor__panel\">\n @if (selectedStepGroup; as stepGroup) {\n <valtimo-form-flow-step-detail\n [componentOptions]=\"$componentOptions()\"\n [duplicateKey]=\"isSelectedKeyDuplicate()\"\n [formOptions]=\"$formOptions()\"\n [isStartStep]=\"isSelectedStepStart()\"\n [readOnly]=\"readOnly\"\n [registry]=\"registry\"\n [revealErrors]=\"revealErrors\"\n [stepGroup]=\"stepGroup\"\n [stepKeys]=\"$stepKeys()\"\n (deleteStepEvent)=\"onDeleteStep()\"\n (makeStartEvent)=\"onMakeStartStep()\"\n ></valtimo-form-flow-step-detail>\n } @else {\n <div class=\"ui-editor__empty\">\n <h5 class=\"ui-editor__empty-title\">\n {{ 'formFlow.uiEditor.noStepsTitle' | translate }}\n </h5>\n\n <p class=\"ui-editor__empty-message\">\n {{ 'formFlow.uiEditor.noStepsDescription' | translate }}\n </p>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n} @else {\n <div class=\"ui-editor__loading\">\n <cds-loading></cds-loading>\n </div>\n}\n", styles: [".ui-editor{display:flex;flex-direction:column;border:1px solid var(--cds-border-subtle, #e0e0e0);overflow:hidden}.ui-editor__body{display:flex;flex:1 1 auto;min-height:0}.ui-editor__sidebar{background:var(--cds-layer-01, #f4f4f4);border-right:1px solid var(--cds-border-subtle, #e0e0e0);display:flex;flex:0 0 22rem;flex-direction:column;min-height:0}.ui-editor__panel{flex:1 1 auto;min-width:0;overflow-y:auto;padding:1rem 1.5rem 1.5rem}.ui-editor__notification{flex:0 0 auto}.ui-editor__notification cds-inline-notification{display:block;max-width:none;margin:0}.ui-editor__notification ::ng-deep .cds--inline-notification--low-contrast:before{border-width:0 0 1px 0}.ui-editor__empty{align-items:center;border:1px dashed var(--cds-border-strong, #8d8d8d);display:flex;flex-direction:column;gap:.5rem;margin:2rem auto;max-width:30rem;padding:2rem;text-align:center}.ui-editor__empty-title{margin:0}.ui-editor__empty-message{color:var(--cds-text-secondary, #525252);margin:0 0 .5rem}.ui-editor__loading{display:flex;width:100%;justify-content:center;padding:var(--cds-spacing-07)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
1578
|
+
}], ctorParameters: () => [{ type: FormFlowEditorFormService }, { type: FormFlowService }, { type: FormFlowComponentService }, { type: i4$1.FormManagementService }, { type: i3.ActivatedRoute }, { type: i4.TranslateService }], propDecorators: { model: [{
|
|
1579
|
+
type: Input
|
|
1580
|
+
}], readOnly: [{
|
|
1581
|
+
type: Input
|
|
1582
|
+
}], revealErrors: [{
|
|
1583
|
+
type: Input
|
|
1584
|
+
}], validEvent: [{
|
|
1585
|
+
type: Output
|
|
1586
|
+
}], valueChangeEvent: [{
|
|
1587
|
+
type: Output
|
|
1588
|
+
}] } });
|
|
1589
|
+
|
|
1590
|
+
/*
|
|
1591
|
+
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
1592
|
+
*
|
|
1593
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
1594
|
+
* you may not use this file except in compliance with the License.
|
|
1595
|
+
* You may obtain a copy of the License at
|
|
1596
|
+
*
|
|
1597
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
1598
|
+
*
|
|
1599
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1600
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
1601
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1602
|
+
* See the License for the specific language governing permissions and
|
|
1603
|
+
* limitations under the License.
|
|
1604
|
+
*/
|
|
1605
|
+
class FormFlowEditorComponent extends PendingChangesComponent {
|
|
1606
|
+
constructor(breadcrumbService, formFlowDownloadService, formFlowService, iconService, notificationService, pageHeaderService, pageTitleService, route, router, translateService) {
|
|
1607
|
+
super();
|
|
1608
|
+
this.breadcrumbService = breadcrumbService;
|
|
1609
|
+
this.formFlowDownloadService = formFlowDownloadService;
|
|
1610
|
+
this.formFlowService = formFlowService;
|
|
1611
|
+
this.iconService = iconService;
|
|
1612
|
+
this.notificationService = notificationService;
|
|
1613
|
+
this.pageHeaderService = pageHeaderService;
|
|
1614
|
+
this.pageTitleService = pageTitleService;
|
|
1615
|
+
this.route = route;
|
|
1616
|
+
this.router = router;
|
|
1617
|
+
this.translateService = translateService;
|
|
1618
|
+
this.readOnly$ = new BehaviorSubject(false);
|
|
1619
|
+
this.valid$ = new BehaviorSubject(false);
|
|
1620
|
+
this.loading$ = new BehaviorSubject(true);
|
|
1621
|
+
this.showDeleteModal$ = new BehaviorSubject(false);
|
|
1622
|
+
this.$activeTab = signal(FormFlowEditorTab.JSON_EDITOR);
|
|
1623
|
+
// Validate-on-save: while the admin is modelling nothing is flagged. Clicking Save on an invalid
|
|
1624
|
+
// definition flips this instead of calling the backend, which reveals the errors in the active
|
|
1625
|
+
// tab and — from then on — gates the Save button on validity until the definition is fixed.
|
|
1626
|
+
this.$saveAttempted = signal(false);
|
|
1627
|
+
this.FormFlowEditorTab = FormFlowEditorTab;
|
|
1628
|
+
this.testIds = FORM_FLOW_EDITOR_TEST_IDS;
|
|
1629
|
+
this._context$ = getContextObservable(this.route);
|
|
1630
|
+
this._params$ = this._context$.pipe(switchMap(context => {
|
|
1631
|
+
if (context === 'buildingBlock') {
|
|
1632
|
+
return combineLatest([
|
|
1633
|
+
getBuildingBlockManagementRouteParams(this.route),
|
|
1634
|
+
this.route.params,
|
|
1635
|
+
]).pipe(map(([bbParams, params]) => ({
|
|
1636
|
+
caseDefinitionKey: bbParams?.buildingBlockDefinitionKey ?? '',
|
|
1637
|
+
caseDefinitionVersionTag: bbParams?.buildingBlockDefinitionVersionTag ?? '',
|
|
1638
|
+
formFlowDefinitionKey: params.formFlowDefinitionKey,
|
|
1639
|
+
})));
|
|
1640
|
+
}
|
|
1641
|
+
return combineLatest([
|
|
1642
|
+
getCaseManagementRouteParams(this.route),
|
|
1643
|
+
this.route.params,
|
|
1644
|
+
]).pipe(map(([caseManagementParams, params]) => ({
|
|
1645
|
+
...(caseManagementParams ?? { caseDefinitionKey: '', caseDefinitionVersionTag: '' }),
|
|
1646
|
+
...params,
|
|
1647
|
+
})));
|
|
1648
|
+
}));
|
|
1649
|
+
this.formFlowSchemaJson$ = this.formFlowService
|
|
1650
|
+
.getFormFlowDefinitionSchema()
|
|
1651
|
+
.pipe(shareReplay(1));
|
|
1652
|
+
this._formFlowDefinition2$ = combineLatest([this._params$, this._context$]).pipe(tap(() => this.loading$.next(true)), switchMap(([params, context]) => {
|
|
1653
|
+
this.initBreadcrumbs(params, context);
|
|
1654
|
+
if (context === 'buildingBlock') {
|
|
1655
|
+
return this.formFlowService.getBuildingBlockFormFlowDefinitionByKey(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey);
|
|
1656
|
+
}
|
|
1657
|
+
return this.formFlowService.getFormFlowDefinitionByKey(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey);
|
|
1658
|
+
}), tap((formFlowDefinition) => {
|
|
1659
|
+
this.pageTitleService.setCustomPageTitle(formFlowDefinition.key);
|
|
1660
|
+
this.readOnly$.next(formFlowDefinition.readOnly === true);
|
|
1661
|
+
this.loading$.next(false);
|
|
1662
|
+
// A freshly loaded definition is the clean baseline: the next editor emission recaptures it,
|
|
1663
|
+
// saving without edits is possible from either tab, and the unsaved-changes flag is cleared.
|
|
1664
|
+
this._updatedModelValue$.next(this.serializeDefinition(formFlowDefinition));
|
|
1665
|
+
this._pendingBaseline = null;
|
|
1666
|
+
this.pendingChanges = false;
|
|
1667
|
+
this.valid$.next(true);
|
|
1668
|
+
// A reloaded definition starts a fresh modelling session; drop any prior save-attempt state.
|
|
1669
|
+
this.$saveAttempted.set(false);
|
|
1670
|
+
}));
|
|
1671
|
+
this.model$ = this._formFlowDefinition2$.pipe(map((formFlowDefinition) => this.getEditorModel(formFlowDefinition)));
|
|
1672
|
+
this._updatedModelValue$ = new BehaviorSubject('');
|
|
1673
|
+
// The editor's serialized value captured on (re)load. Edits that differ from it mark the editor
|
|
1674
|
+
// dirty so the leave-page guard can warn about unsaved changes.
|
|
1675
|
+
this._pendingBaseline = null;
|
|
1676
|
+
this.compactMode$ = this.pageHeaderService.compactMode$;
|
|
1677
|
+
this.iconService.registerAll([ArrowLeft16]);
|
|
1678
|
+
this.pageTitleService.disableReset();
|
|
1679
|
+
this.restoreActiveTabFromUrl();
|
|
1680
|
+
}
|
|
1681
|
+
ngOnDestroy() {
|
|
1682
|
+
this.pendingChanges = false;
|
|
1683
|
+
this.pageTitleService.enableReset();
|
|
1684
|
+
this.breadcrumbService.clearThirdBreadcrumb();
|
|
1685
|
+
this.breadcrumbService.clearFourthBreadcrumb();
|
|
1686
|
+
}
|
|
1687
|
+
onValid(valid) {
|
|
1688
|
+
this.valid$.next(valid);
|
|
1689
|
+
}
|
|
1690
|
+
onValueChange(value) {
|
|
1691
|
+
// The first emission after a (re)load is the editor's baseline. Comparison is done on the
|
|
1692
|
+
// normalized (whitespace-insensitive) JSON so the JSON editor's format-on-load reflow is not
|
|
1693
|
+
// mistaken for an edit; only a genuine content change marks the editor dirty.
|
|
1694
|
+
const normalized = this.normalizeJson(value);
|
|
1695
|
+
if (this._pendingBaseline === null) {
|
|
1696
|
+
this._pendingBaseline = normalized;
|
|
1697
|
+
this.pendingChanges = false;
|
|
1698
|
+
}
|
|
1699
|
+
else {
|
|
1700
|
+
this.pendingChanges = normalized !== this._pendingBaseline;
|
|
1701
|
+
}
|
|
1702
|
+
this._updatedModelValue$.next(value);
|
|
1703
|
+
}
|
|
1704
|
+
setActiveTab(tab) {
|
|
1705
|
+
if (this.$activeTab() === tab)
|
|
1706
|
+
return;
|
|
1707
|
+
combineLatest([this._params$, this._context$])
|
|
1708
|
+
.pipe(take(1))
|
|
1709
|
+
.subscribe(([params, context]) => {
|
|
1710
|
+
this.router.navigate(this.editorRouteSegments(params, context, tab));
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
onCancelRedirect() {
|
|
1714
|
+
this.syncTabHeaders();
|
|
1715
|
+
}
|
|
1716
|
+
updateFormFlowDefinition() {
|
|
1717
|
+
// Validate on the click: while modelling nothing is flagged, so clicking Save on an invalid
|
|
1718
|
+
// definition reveals its errors (via $saveAttempted → the tabs' revealErrors) instead of
|
|
1719
|
+
// sending it to the backend. From here on the Save button gates on validity until it is fixed.
|
|
1720
|
+
if (!this.valid$.value) {
|
|
1721
|
+
this.$saveAttempted.set(true);
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
this.loading$.next(true);
|
|
1725
|
+
combineLatest([this._params$, this._updatedModelValue$, this._context$])
|
|
1726
|
+
.pipe(take(1), switchMap(([params, updatedModelValue, context]) => {
|
|
1727
|
+
const updatedDefinition = {
|
|
1728
|
+
...JSON.parse(updatedModelValue),
|
|
1729
|
+
key: params.formFlowDefinitionKey,
|
|
1730
|
+
};
|
|
1731
|
+
if (context === 'buildingBlock') {
|
|
1732
|
+
return this.formFlowService.updateBuildingBlockFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey, updatedDefinition);
|
|
1733
|
+
}
|
|
1734
|
+
return this.formFlowService.updateFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey, updatedDefinition);
|
|
1735
|
+
}), finalize(() => this.loading$.next(false)))
|
|
1736
|
+
.subscribe(result => {
|
|
1737
|
+
// The saved value is the new clean baseline for the leave-page guard.
|
|
1738
|
+
this._pendingBaseline = this.normalizeJson(this._updatedModelValue$.getValue());
|
|
1739
|
+
this.pendingChanges = false;
|
|
1740
|
+
this.$saveAttempted.set(false);
|
|
1741
|
+
this.showSuccessMessage(result.key);
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
onDelete() {
|
|
1745
|
+
this.loading$.next(true);
|
|
1746
|
+
this.pendingChanges = false;
|
|
1747
|
+
combineLatest([this._params$, this._context$])
|
|
1748
|
+
.pipe(take(1), switchMap(([params, context]) => {
|
|
1749
|
+
if (context === 'buildingBlock') {
|
|
1750
|
+
return this.formFlowService
|
|
1751
|
+
.deleteBuildingBlockFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey)
|
|
1752
|
+
.pipe(map(() => ({ params, context })));
|
|
1753
|
+
}
|
|
1754
|
+
return this.formFlowService
|
|
1755
|
+
.deleteFormFlowDefinition(params.caseDefinitionKey, params.caseDefinitionVersionTag, params.formFlowDefinitionKey)
|
|
1756
|
+
.pipe(map(() => ({ params, context })));
|
|
1757
|
+
}))
|
|
1758
|
+
.subscribe(({ params, context }) => {
|
|
1759
|
+
this.router.navigate(this.overviewRouteSegments(params, context));
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1762
|
+
showDeleteModal() {
|
|
1763
|
+
this.showDeleteModal$.next(true);
|
|
1764
|
+
}
|
|
1765
|
+
downloadFormFlowDefinition(model) {
|
|
1766
|
+
this._params$
|
|
1767
|
+
.pipe(take(1))
|
|
1768
|
+
.subscribe((params) => this.formFlowDownloadService.downloadJson(JSON.parse(model.value), params));
|
|
1769
|
+
}
|
|
1770
|
+
navigateBack() {
|
|
1771
|
+
combineLatest([this._params$, this._context$])
|
|
1772
|
+
.pipe(take(1))
|
|
1773
|
+
.subscribe(([params, context]) => {
|
|
1774
|
+
this.router.navigate(this.overviewRouteSegments(params, context));
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
restoreActiveTabFromUrl() {
|
|
1778
|
+
const url = this.route.snapshot.url;
|
|
1779
|
+
const lastSegment = url[url.length - 1]?.path;
|
|
1780
|
+
if (lastSegment === 'editor') {
|
|
1781
|
+
this.$activeTab.set(FormFlowEditorTab.EDITOR);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
// The Carbon tab header highlights the clicked tab immediately; if the leave-page prompt is
|
|
1785
|
+
// cancelled, restore the highlight to the tab that is actually still active.
|
|
1786
|
+
syncTabHeaders() {
|
|
1787
|
+
const order = [FormFlowEditorTab.JSON_EDITOR, FormFlowEditorTab.EDITOR];
|
|
1788
|
+
this._tabs?.forEach((tab, index) => {
|
|
1789
|
+
tab.active = order[index] === this.$activeTab();
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
editorRouteSegments(params, context, tab) {
|
|
1793
|
+
const segments = [...this.overviewRouteSegments(params, context), params.formFlowDefinitionKey];
|
|
1794
|
+
return tab === FormFlowEditorTab.EDITOR ? [...segments, 'editor'] : segments;
|
|
1795
|
+
}
|
|
1796
|
+
overviewRouteSegments(params, context) {
|
|
1797
|
+
if (context === 'buildingBlock') {
|
|
1798
|
+
return [
|
|
1799
|
+
'/building-block-management',
|
|
1800
|
+
'building-block',
|
|
1801
|
+
params.caseDefinitionKey,
|
|
1802
|
+
'version',
|
|
1803
|
+
params.caseDefinitionVersionTag,
|
|
1804
|
+
'form-flows',
|
|
1805
|
+
];
|
|
1806
|
+
}
|
|
1807
|
+
return [
|
|
1808
|
+
'/case-management',
|
|
1809
|
+
'case',
|
|
1810
|
+
params.caseDefinitionKey,
|
|
1811
|
+
'version',
|
|
1812
|
+
params.caseDefinitionVersionTag,
|
|
1813
|
+
'form-flows',
|
|
1814
|
+
];
|
|
1815
|
+
}
|
|
1816
|
+
serializeDefinition(formFlowDefinition) {
|
|
1817
|
+
const clone = { ...formFlowDefinition };
|
|
1818
|
+
delete clone.readOnly;
|
|
1819
|
+
return JSON.stringify(clone);
|
|
1820
|
+
}
|
|
1821
|
+
getEditorModel(formFlowDefinition) {
|
|
1822
|
+
return {
|
|
1823
|
+
value: this.serializeDefinition(formFlowDefinition),
|
|
1824
|
+
language: 'json',
|
|
1825
|
+
uri: `inmemory://form-flow/${formFlowDefinition.key}.formflow.json`,
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
// Canonicalizes a JSON string by dropping insignificant whitespace, so reformatting (such as the
|
|
1829
|
+
// JSON editor's format-on-load) does not register as a change. Invalid JSON is compared as-is.
|
|
1830
|
+
normalizeJson(value) {
|
|
1831
|
+
try {
|
|
1832
|
+
return JSON.stringify(JSON.parse(value));
|
|
1833
|
+
}
|
|
1834
|
+
catch {
|
|
1835
|
+
return value;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
showSuccessMessage(formFlowDefinitionKey) {
|
|
1839
|
+
this.notificationService.showToast({
|
|
1840
|
+
caption: this.translateService.instant('formFlow.savedSuccessTitleMessage', {
|
|
1841
|
+
key: formFlowDefinitionKey,
|
|
1842
|
+
}),
|
|
1843
|
+
type: 'success',
|
|
1844
|
+
title: this.translateService.instant('formFlow.savedSuccessTitle'),
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
initBreadcrumbs(params, context) {
|
|
1848
|
+
if (context === 'buildingBlock') {
|
|
1849
|
+
const route = `/building-block-management/building-block/${params.caseDefinitionKey}/version/${params.caseDefinitionVersionTag}`;
|
|
1850
|
+
const generalRoute = `${route}/general`;
|
|
1851
|
+
this.breadcrumbService.setThirdBreadcrumb({
|
|
1852
|
+
route: [generalRoute],
|
|
1853
|
+
content: `${params.caseDefinitionKey} (${params.caseDefinitionVersionTag})`,
|
|
1854
|
+
href: generalRoute,
|
|
1855
|
+
});
|
|
1856
|
+
const routeWithFormFlows = `${route}/form-flows`;
|
|
1857
|
+
// The breadcrumb service translates this key on render (and re-translates on load / language
|
|
1858
|
+
// switch), so a direct page load shows the label, not the raw key.
|
|
1859
|
+
this.breadcrumbService.setFourthBreadcrumb({
|
|
1860
|
+
route: [routeWithFormFlows],
|
|
1861
|
+
content: 'buildingBlockManagement.tabs.formFlows',
|
|
1862
|
+
href: routeWithFormFlows,
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
else {
|
|
1866
|
+
const route = `/case-management/case/${params.caseDefinitionKey}/version/${params.caseDefinitionVersionTag}`;
|
|
1867
|
+
this.breadcrumbService.setThirdBreadcrumb({
|
|
1868
|
+
route: [route],
|
|
1869
|
+
content: `${params.caseDefinitionKey} (${params.caseDefinitionVersionTag})`,
|
|
1870
|
+
href: route,
|
|
1871
|
+
});
|
|
1872
|
+
const routeWithFormFlows = `${route}/form-flows`;
|
|
1873
|
+
this.breadcrumbService.setFourthBreadcrumb({
|
|
1874
|
+
route: [routeWithFormFlows],
|
|
1875
|
+
content: 'caseManagement.tabs.formFlows',
|
|
1876
|
+
href: routeWithFormFlows,
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowEditorComponent, deps: [{ token: i1$2.BreadcrumbService }, { token: FormFlowDownloadService }, { token: FormFlowService }, { token: i2$1.IconService }, { token: i2.GlobalNotificationService }, { token: i1$2.PageHeaderService }, { token: i1$2.PageTitleService }, { token: i3.ActivatedRoute }, { token: i3.Router }, { token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1881
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: FormFlowEditorComponent, isStandalone: false, selector: "ng-component", viewQueries: [{ propertyName: "_tabs", predicate: Tab, descendants: true }], usesInheritance: true, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<ng-container *ngIf=\"model$ | async as model; else loading\">\n <!-- The headers only switch the active tab; the tab content is rendered below (gated by the\n active-tab signal) instead of inside the Carbon tab panes. Carbon removes a pane's content\n as soon as another header is clicked, which would destroy the editor's unsaved form before\n the leave-page confirmation is resolved. -->\n <cds-tabs type=\"contained\">\n <cds-tab\n [active]=\"$activeTab() === FormFlowEditorTab.JSON_EDITOR\"\n [heading]=\"'formFlow.tabs.jsonEditor' | translate\"\n (selected)=\"setActiveTab(FormFlowEditorTab.JSON_EDITOR)\"\n ></cds-tab>\n\n <cds-tab\n [active]=\"$activeTab() === FormFlowEditorTab.EDITOR\"\n [heading]=\"'formFlow.tabs.editor' | translate\"\n (selected)=\"setActiveTab(FormFlowEditorTab.EDITOR)\"\n ></cds-tab>\n </cds-tabs>\n\n <!-- White surface for the tab content. The Carbon tab pane used to provide this; since the\n content is rendered outside the panes, the wrapper supplies the layer background. -->\n <div class=\"form-flow-editor__content\">\n @if ($activeTab() === FormFlowEditorTab.EDITOR) {\n <valtimo-form-flow-ui-editor-tab\n [model]=\"model\"\n [readOnly]=\"readOnly$ | async\"\n [revealErrors]=\"$saveAttempted()\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-form-flow-ui-editor-tab>\n }\n\n @if ($activeTab() === FormFlowEditorTab.JSON_EDITOR) {\n <valtimo-form-flow-json-editor-tab\n [disabled]=\"(loading$ | async) === false && (readOnly$ | async)\"\n [model]=\"model\"\n [schema]=\"formFlowSchemaJson$ | async\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-form-flow-json-editor-tab>\n }\n </div>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n compactMode: compactMode$ | async,\n valid: valid$ | async,\n readOnly: readOnly$ | async,\n model: model$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\n <v-overflow-menu placement=\"bottom-end\">\n <v-overflow-menu-trigger overflowTrigger></v-overflow-menu-trigger>\n <v-overflow-menu-option\n [disabled]=\"!obs.valid && !obs.readOnly\"\n (selected)=\"downloadFormFlowDefinition(obs.model)\"\n >{{ 'interface.export' | translate }}\n </v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.readOnly\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}\n </v-overflow-menu-option>\n </v-overflow-menu>\n\n <button\n cdsButton=\"secondary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.backButton\"\n (click)=\"navigateBack()\"\n >\n {{ 'interface.back' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"arrow--left\" size=\"16\"></svg>\n </button>\n\n <button\n [disabled]=\"obs.readOnly || ($saveAttempted() && !obs.valid)\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.saveButton\"\n (click)=\"updateFormFlowDefinition()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"formFlow.deleteModalContent\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete()\"\n></valtimo-confirmation-modal>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: ["cds-loading{display:flex;justify-content:center}.loading-container{display:flex;width:100%;justify-content:center}.buttons-container{display:flex;align-items:center;justify-self:flex-end;gap:16px}.buttons-container button{height:min-content}:host ::ng-deep .cds--tab-content{background-color:var(--cds-layer);outline:none!important;padding:0}.form-flow-editor__content{background-color:var(--cds-layer)}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i1$2.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }, { kind: "directive", type: i1$2.RenderInPageHeaderDirective, selector: "[renderInPageHeader]", inputs: ["fullWidth"] }, { kind: "component", type: i2$1.Loading, selector: "cds-loading, ibm-loading", inputs: ["title", "isActive", "size", "overlay"] }, { kind: "component", type: i1$2.OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: i1$2.OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }, { kind: "component", type: i1$2.OverflowMenuTriggerComponent, selector: "v-overflow-menu-trigger", inputs: ["compact", "label"] }, { kind: "component", type: i2$1.Tabs, selector: "cds-tabs, ibm-tabs", inputs: ["position", "cacheActive", "followFocus", "isNavigation", "ariaLabel", "ariaLabelledby", "type", "theme", "skeleton"] }, { kind: "component", type: i2$1.Tab, selector: "cds-tab, ibm-tab", inputs: ["heading", "title", "context", "active", "disabled", "tabIndex", "id", "cacheActive", "tabContent", "templateContext"], outputs: ["selected"] }, { kind: "component", type: FormFlowJsonEditorTabComponent, selector: "valtimo-form-flow-json-editor-tab", inputs: ["disabled", "model", "schema"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "component", type: FormFlowUiEditorTabComponent, selector: "valtimo-form-flow-ui-editor-tab", inputs: ["model", "readOnly", "revealErrors"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1882
|
+
}
|
|
1883
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowEditorComponent, decorators: [{
|
|
1884
|
+
type: Component,
|
|
1885
|
+
args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<ng-container *ngIf=\"model$ | async as model; else loading\">\n <!-- The headers only switch the active tab; the tab content is rendered below (gated by the\n active-tab signal) instead of inside the Carbon tab panes. Carbon removes a pane's content\n as soon as another header is clicked, which would destroy the editor's unsaved form before\n the leave-page confirmation is resolved. -->\n <cds-tabs type=\"contained\">\n <cds-tab\n [active]=\"$activeTab() === FormFlowEditorTab.JSON_EDITOR\"\n [heading]=\"'formFlow.tabs.jsonEditor' | translate\"\n (selected)=\"setActiveTab(FormFlowEditorTab.JSON_EDITOR)\"\n ></cds-tab>\n\n <cds-tab\n [active]=\"$activeTab() === FormFlowEditorTab.EDITOR\"\n [heading]=\"'formFlow.tabs.editor' | translate\"\n (selected)=\"setActiveTab(FormFlowEditorTab.EDITOR)\"\n ></cds-tab>\n </cds-tabs>\n\n <!-- White surface for the tab content. The Carbon tab pane used to provide this; since the\n content is rendered outside the panes, the wrapper supplies the layer background. -->\n <div class=\"form-flow-editor__content\">\n @if ($activeTab() === FormFlowEditorTab.EDITOR) {\n <valtimo-form-flow-ui-editor-tab\n [model]=\"model\"\n [readOnly]=\"readOnly$ | async\"\n [revealErrors]=\"$saveAttempted()\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-form-flow-ui-editor-tab>\n }\n\n @if ($activeTab() === FormFlowEditorTab.JSON_EDITOR) {\n <valtimo-form-flow-json-editor-tab\n [disabled]=\"(loading$ | async) === false && (readOnly$ | async)\"\n [model]=\"model\"\n [schema]=\"formFlowSchemaJson$ | async\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-form-flow-json-editor-tab>\n }\n </div>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n compactMode: compactMode$ | async,\n valid: valid$ | async,\n readOnly: readOnly$ | async,\n model: model$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\n <v-overflow-menu placement=\"bottom-end\">\n <v-overflow-menu-trigger overflowTrigger></v-overflow-menu-trigger>\n <v-overflow-menu-option\n [disabled]=\"!obs.valid && !obs.readOnly\"\n (selected)=\"downloadFormFlowDefinition(obs.model)\"\n >{{ 'interface.export' | translate }}\n </v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.readOnly\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}\n </v-overflow-menu-option>\n </v-overflow-menu>\n\n <button\n cdsButton=\"secondary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.backButton\"\n (click)=\"navigateBack()\"\n >\n {{ 'interface.back' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"arrow--left\" size=\"16\"></svg>\n </button>\n\n <button\n [disabled]=\"obs.readOnly || ($saveAttempted() && !obs.valid)\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.saveButton\"\n (click)=\"updateFormFlowDefinition()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"formFlow.deleteModalContent\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete()\"\n></valtimo-confirmation-modal>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: ["cds-loading{display:flex;justify-content:center}.loading-container{display:flex;width:100%;justify-content:center}.buttons-container{display:flex;align-items:center;justify-self:flex-end;gap:16px}.buttons-container button{height:min-content}:host ::ng-deep .cds--tab-content{background-color:var(--cds-layer);outline:none!important;padding:0}.form-flow-editor__content{background-color:var(--cds-layer)}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
|
|
1886
|
+
}], ctorParameters: () => [{ type: i1$2.BreadcrumbService }, { type: FormFlowDownloadService }, { type: FormFlowService }, { type: i2$1.IconService }, { type: i2.GlobalNotificationService }, { type: i1$2.PageHeaderService }, { type: i1$2.PageTitleService }, { type: i3.ActivatedRoute }, { type: i3.Router }, { type: i4.TranslateService }], propDecorators: { _tabs: [{
|
|
1887
|
+
type: ViewChildren,
|
|
1888
|
+
args: [Tab]
|
|
1889
|
+
}] } });
|
|
1890
|
+
|
|
1891
|
+
/*
|
|
1892
|
+
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
1893
|
+
*
|
|
1894
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
1895
|
+
* you may not use this file except in compliance with the License.
|
|
1896
|
+
* You may obtain a copy of the License at
|
|
1897
|
+
*
|
|
1898
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
1899
|
+
*
|
|
1900
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1901
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
1902
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1903
|
+
* See the License for the specific language governing permissions and
|
|
1904
|
+
* limitations under the License.
|
|
1905
|
+
*/
|
|
1906
|
+
class DeleteFormFlowModalComponent {
|
|
1907
|
+
constructor() {
|
|
1908
|
+
this.deleteEvent = new EventEmitter();
|
|
539
1909
|
}
|
|
540
1910
|
onDelete(formFlowDefinitionKey) {
|
|
541
1911
|
this.deleteEvent.emit(formFlowDefinitionKey);
|
|
@@ -571,9 +1941,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
571
1941
|
*/
|
|
572
1942
|
class FormFlowManagementModule {
|
|
573
1943
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
574
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.25", ngImport: i0, type: FormFlowManagementModule, declarations: [FormFlowOverviewComponent,
|
|
575
|
-
FormFlowEditorComponent,
|
|
576
|
-
DeleteFormFlowModalComponent], imports: [CommonModule,
|
|
1944
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.25", ngImport: i0, type: FormFlowManagementModule, declarations: [FormFlowOverviewComponent, FormFlowEditorComponent, DeleteFormFlowModalComponent], imports: [CommonModule,
|
|
577
1945
|
ButtonModule,
|
|
578
1946
|
FormsModule,
|
|
579
1947
|
ModalModule,
|
|
@@ -593,7 +1961,10 @@ class FormFlowManagementModule {
|
|
|
593
1961
|
CarbonListModule,
|
|
594
1962
|
DropdownModule,
|
|
595
1963
|
ConfirmationModalModule,
|
|
596
|
-
NewFormFlowModalComponent
|
|
1964
|
+
NewFormFlowModalComponent,
|
|
1965
|
+
TabsModule,
|
|
1966
|
+
FormFlowJsonEditorTabComponent,
|
|
1967
|
+
FormFlowUiEditorTabComponent] }); }
|
|
597
1968
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowManagementModule, imports: [CommonModule,
|
|
598
1969
|
ButtonModule,
|
|
599
1970
|
FormsModule,
|
|
@@ -613,16 +1984,15 @@ class FormFlowManagementModule {
|
|
|
613
1984
|
CarbonListModule,
|
|
614
1985
|
DropdownModule,
|
|
615
1986
|
ConfirmationModalModule,
|
|
616
|
-
NewFormFlowModalComponent
|
|
1987
|
+
NewFormFlowModalComponent,
|
|
1988
|
+
TabsModule,
|
|
1989
|
+
FormFlowJsonEditorTabComponent,
|
|
1990
|
+
FormFlowUiEditorTabComponent] }); }
|
|
617
1991
|
}
|
|
618
1992
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormFlowManagementModule, decorators: [{
|
|
619
1993
|
type: NgModule,
|
|
620
1994
|
args: [{
|
|
621
|
-
declarations: [
|
|
622
|
-
FormFlowOverviewComponent,
|
|
623
|
-
FormFlowEditorComponent,
|
|
624
|
-
DeleteFormFlowModalComponent,
|
|
625
|
-
],
|
|
1995
|
+
declarations: [FormFlowOverviewComponent, FormFlowEditorComponent, DeleteFormFlowModalComponent],
|
|
626
1996
|
imports: [
|
|
627
1997
|
CommonModule,
|
|
628
1998
|
ButtonModule,
|
|
@@ -645,6 +2015,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
645
2015
|
DropdownModule,
|
|
646
2016
|
ConfirmationModalModule,
|
|
647
2017
|
NewFormFlowModalComponent,
|
|
2018
|
+
TabsModule,
|
|
2019
|
+
FormFlowJsonEditorTabComponent,
|
|
2020
|
+
FormFlowUiEditorTabComponent,
|
|
648
2021
|
],
|
|
649
2022
|
}]
|
|
650
2023
|
}] });
|
|
@@ -672,5 +2045,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
672
2045
|
* Generated bundle index. Do not edit.
|
|
673
2046
|
*/
|
|
674
2047
|
|
|
675
|
-
export { FormFlowEditorComponent, FormFlowManagementModule, FormFlowOverviewComponent, FormFlowService, NewFormFlowModalComponent };
|
|
2048
|
+
export { FORM_FLOW_COMPONENT_TOKEN, FORM_FLOW_EDITOR_TEST_IDS, FormFlowComponentService, FormFlowEditorComponent, FormFlowEditorTab, FormFlowManagementModule, FormFlowOverviewComponent, FormFlowService, NewFormFlowModalComponent };
|
|
676
2049
|
//# sourceMappingURL=valtimo-form-flow-management.mjs.map
|