ngx-histaff-alpha 5.9.0 → 5.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{ngx-histaff-alpha-ai-hint-for-table.component-BEJPXipC.mjs → ngx-histaff-alpha-ai-hint-for-table.component-BO_Ci77l.mjs} +3 -3
- package/fesm2022/{ngx-histaff-alpha-ai-hint-for-table.component-BEJPXipC.mjs.map → ngx-histaff-alpha-ai-hint-for-table.component-BO_Ci77l.mjs.map} +1 -1
- package/fesm2022/ngx-histaff-alpha-core-form-design.component-CGf-jOqR.mjs +3279 -0
- package/fesm2022/ngx-histaff-alpha-core-form-design.component-CGf-jOqR.mjs.map +1 -0
- package/fesm2022/{ngx-histaff-alpha-core-sticker-collection.component-0ugr1j-a.mjs → ngx-histaff-alpha-core-sticker-collection.component-zkrmzfmx.mjs} +3 -3
- package/fesm2022/{ngx-histaff-alpha-core-sticker-collection.component-0ugr1j-a.mjs.map → ngx-histaff-alpha-core-sticker-collection.component-zkrmzfmx.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-core-toast-loading.component-BWihKjC_.mjs → ngx-histaff-alpha-core-toast-loading.component-BLGPX2ov.mjs} +2 -2
- package/fesm2022/{ngx-histaff-alpha-core-toast-loading.component-BWihKjC_.mjs.map → ngx-histaff-alpha-core-toast-loading.component-BLGPX2ov.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-core-workflow-consume.component-DrQHJfwa.mjs → ngx-histaff-alpha-core-workflow-consume.component-DKYtFwki.mjs} +2 -2
- package/fesm2022/{ngx-histaff-alpha-core-workflow-consume.component-DrQHJfwa.mjs.map → ngx-histaff-alpha-core-workflow-consume.component-DKYtFwki.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-db-settings.component-HZpqrGn1.mjs → ngx-histaff-alpha-db-settings.component-CBB_Ahf3.mjs} +5 -5
- package/fesm2022/{ngx-histaff-alpha-db-settings.component-HZpqrGn1.mjs.map → ngx-histaff-alpha-db-settings.component-CBB_Ahf3.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-design-wrapper.component-DOWaBKbT.mjs → ngx-histaff-alpha-design-wrapper.component-DaSrXARH.mjs} +5 -5
- package/fesm2022/{ngx-histaff-alpha-design-wrapper.component-DOWaBKbT.mjs.map → ngx-histaff-alpha-design-wrapper.component-DaSrXARH.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-design-wrapper.route-pj-38Cdb.mjs → ngx-histaff-alpha-design-wrapper.route-RgR94c12.mjs} +5 -5
- package/fesm2022/{ngx-histaff-alpha-design-wrapper.route-pj-38Cdb.mjs.map → ngx-histaff-alpha-design-wrapper.route-RgR94c12.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-form-array-props.component-Df_gc4y9.mjs → ngx-histaff-alpha-form-array-props.component-CCccglL5.mjs} +4 -3
- package/fesm2022/ngx-histaff-alpha-form-array-props.component-CCccglL5.mjs.map +1 -0
- package/fesm2022/{ngx-histaff-alpha-live-form.component-awOXOZCF.mjs → ngx-histaff-alpha-live-form.component-Df56ufkx.mjs} +2 -2
- package/fesm2022/{ngx-histaff-alpha-live-form.component-awOXOZCF.mjs.map → ngx-histaff-alpha-live-form.component-Df56ufkx.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-ngx-histaff-alpha-Cbe09AO4.mjs → ngx-histaff-alpha-ngx-histaff-alpha-CYQ3j6IP.mjs} +41 -15
- package/fesm2022/ngx-histaff-alpha-ngx-histaff-alpha-CYQ3j6IP.mjs.map +1 -0
- package/fesm2022/{ngx-histaff-alpha-tracker-studio.component-BmhaBTLn.mjs → ngx-histaff-alpha-tracker-studio.component-h4r8nXvw.mjs} +2 -2
- package/fesm2022/{ngx-histaff-alpha-tracker-studio.component-BmhaBTLn.mjs.map → ngx-histaff-alpha-tracker-studio.component-h4r8nXvw.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-wf-form-assign.component-BR4s070F.mjs → ngx-histaff-alpha-wf-form-assign.component-Dcwd8mdT.mjs} +2 -2
- package/fesm2022/{ngx-histaff-alpha-wf-form-assign.component-BR4s070F.mjs.map → ngx-histaff-alpha-wf-form-assign.component-Dcwd8mdT.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-wf-instance-status.component-BsIAEqxC.mjs → ngx-histaff-alpha-wf-instance-status.component-BEucf9x7.mjs} +4 -4
- package/fesm2022/{ngx-histaff-alpha-wf-instance-status.component-BsIAEqxC.mjs.map → ngx-histaff-alpha-wf-instance-status.component-BEucf9x7.mjs.map} +1 -1
- package/fesm2022/{ngx-histaff-alpha-wf-instance-step-react.component-BUDlrylJ.mjs → ngx-histaff-alpha-wf-instance-step-react.component-CR-UUVFi.mjs} +3 -3
- package/fesm2022/{ngx-histaff-alpha-wf-instance-step-react.component-BUDlrylJ.mjs.map → ngx-histaff-alpha-wf-instance-step-react.component-CR-UUVFi.mjs.map} +1 -1
- package/fesm2022/ngx-histaff-alpha.mjs +1 -1
- package/lib/app/enum/EnumFormBaseContolType.d.ts +1 -0
- package/lib/app/libraries/core-form-design/core-form-array-control-palette/core-form-array-control-palette.component.d.ts +6 -0
- package/lib/app/libraries/core-form-design/core-form-array-layout-editor/core-form-array-layout-editor.component.d.ts +1 -1
- package/lib/app/libraries/core-form-design/core-form-design.component.d.ts +1 -2
- package/lib/app/libraries/core-form-design/core-form-design.service.d.ts +13 -2
- package/lib/app/libraries/core-form-design/core-form-group-editor.component.d.ts +104 -0
- package/lib/app/libraries/core-form-design/field-setting/field-setting.component.d.ts +2 -1
- package/package.json +1 -1
- package/fesm2022/ngx-histaff-alpha-core-form-design.component-PXmIWw_y.mjs +0 -2214
- package/fesm2022/ngx-histaff-alpha-core-form-design.component-PXmIWw_y.mjs.map +0 -1
- package/fesm2022/ngx-histaff-alpha-form-array-props.component-Df_gc4y9.mjs.map +0 -1
- package/fesm2022/ngx-histaff-alpha-ngx-histaff-alpha-Cbe09AO4.mjs.map +0 -1
|
@@ -0,0 +1,3279 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, ChangeDetectorRef, Component, signal, effect, HostListener, ChangeDetectionStrategy, Injectable, computed, viewChild, ViewContainerRef, Input, EventEmitter, Output, input, Renderer2, isDevMode } from '@angular/core';
|
|
3
|
+
import { trigger, state, transition, style, animate } from '@angular/animations';
|
|
4
|
+
import { n as CoreFormDesignService, b as CoreWorkflowService, B as BaseEditComponent, i as AlertService, L as AppConfigService, O as EnumCorePageEditMode, P as EnumCoreButtonVNSCode, R as READONLY_WORKFLOW_FIELDS, Q as noneAutoClosedAlertOptions, p as DialogService, M as MultiLanguageService, C as CorePageHeaderComponent, U as CoreFormComponent, V as CoreButtonGroupVnsComponent, T as TranslatePipe, E as EnumFormBaseContolType, e as BaseComponent, W as EnumCoreTablePipeType, J as CoreCheckboxComponent, r as CoreDropdownComponent, g as TooltipDirective, G as GptService, l as alertOptions, y as CoreRadioGroupComponent, X as HtmlTooltipDirective, c as EnumCoreFormControlSeekerSourceType, Y as CoreControlNoFormArrayComponent, Z as EnumFormDesignMode, q as DomService, K as JsonService, z as ApplicationHelpService, _ as CoreControlComponent, j as TableCellPipe, H as HotKeysDirective, s as CoreChecklistComponent, u as CoreFormControlSeekerComponent, $ as CoreAttachmentComponent, t as CoreDatePickerComponent, a0 as CoreMonthSelectorComponent, v as CoreCurrencyInputComponent } from './ngx-histaff-alpha-ngx-histaff-alpha-CYQ3j6IP.mjs';
|
|
5
|
+
import * as i1 from '@angular/forms';
|
|
6
|
+
import { FormsModule, FormGroup, FormControl, FormArray, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
7
|
+
import { NgStyle, JsonPipe, AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
|
8
|
+
import { BehaviorSubject, catchError, of, filter, distinctUntilChanged, tap, switchMap } from 'rxjs';
|
|
9
|
+
import { Router, ActivatedRoute } from '@angular/router';
|
|
10
|
+
import { CoreStickerCollectionComponent } from './ngx-histaff-alpha-core-sticker-collection.component-zkrmzfmx.mjs';
|
|
11
|
+
import { D as DbService, C as CoreToastLoadingComponent } from './ngx-histaff-alpha-core-toast-loading.component-BLGPX2ov.mjs';
|
|
12
|
+
import { EnumTranslateKey } from 'alpha-global-constants';
|
|
13
|
+
import { L as LiveFormComponent } from './ngx-histaff-alpha-live-form.component-Df56ufkx.mjs';
|
|
14
|
+
|
|
15
|
+
const slideFromTopFadeIn = trigger('slideFromTopFadeIn', [
|
|
16
|
+
state('in', style({ opacity: 1, transform: 'translateY(0)' })),
|
|
17
|
+
state('out', style({ opacity: 0, transform: 'translateY(-50vh)' })),
|
|
18
|
+
transition('out => in', [
|
|
19
|
+
animate('200ms ease-out')
|
|
20
|
+
]),
|
|
21
|
+
transition('in => out', [
|
|
22
|
+
animate('150ms ease-in')
|
|
23
|
+
])
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
class SaveLiveJsonComponent {
|
|
27
|
+
constructor() {
|
|
28
|
+
this.fds = inject(CoreFormDesignService);
|
|
29
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
30
|
+
this.wfs = inject(CoreWorkflowService);
|
|
31
|
+
}
|
|
32
|
+
copyToClipboard() {
|
|
33
|
+
navigator.clipboard.writeText(this.fds.$saveLiveJson());
|
|
34
|
+
}
|
|
35
|
+
download() {
|
|
36
|
+
const blob = new Blob([this.fds.$saveLiveJson()], { type: 'application/json' });
|
|
37
|
+
const url = URL.createObjectURL(blob);
|
|
38
|
+
const link = document.createElement('a');
|
|
39
|
+
link.href = url;
|
|
40
|
+
link.download = `${this.fds.$afInstance().name || 'form'}.json`;
|
|
41
|
+
link.click();
|
|
42
|
+
}
|
|
43
|
+
ngAfterViewInit() {
|
|
44
|
+
setTimeout(() => this.cdr.markForCheck());
|
|
45
|
+
}
|
|
46
|
+
ngOnDestroy() {
|
|
47
|
+
this.wfs.$initialFormDesignJson.set('{}');
|
|
48
|
+
}
|
|
49
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SaveLiveJsonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
50
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: SaveLiveJsonComponent, isStandalone: true, selector: "save-live-json", ngImport: i0, template: "<pre class=\"json-preview\">{{ fds.$saveLiveJson() }}</pre>", styles: [".json-preview{background-color:#fff;color:#333;padding:1rem 1.5rem;border-radius:10px;box-shadow:0 2px 6px #00000014;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;overflow-x:auto;line-height:1.5;width:390px;height:calc(100vh - 125px);margin:0}\n"] }); }
|
|
51
|
+
}
|
|
52
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SaveLiveJsonComponent, decorators: [{
|
|
53
|
+
type: Component,
|
|
54
|
+
args: [{ selector: 'save-live-json', imports: [], template: "<pre class=\"json-preview\">{{ fds.$saveLiveJson() }}</pre>", styles: [".json-preview{background-color:#fff;color:#333;padding:1rem 1.5rem;border-radius:10px;box-shadow:0 2px 6px #00000014;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;overflow-x:auto;line-height:1.5;width:390px;height:calc(100vh - 125px);margin:0}\n"] }]
|
|
55
|
+
}] });
|
|
56
|
+
|
|
57
|
+
class FormMetadataComponent extends BaseEditComponent {
|
|
58
|
+
onEscape() {
|
|
59
|
+
this.coreFormDesignService.$showFormMetadata.set(false);
|
|
60
|
+
}
|
|
61
|
+
onWindowClick(_) {
|
|
62
|
+
/*
|
|
63
|
+
Trigger What Happens
|
|
64
|
+
🖱️ First Click: Opens checklist overlay, but Angular hasn’t re-checked the DOM yet → markForCheck() is called, but view not updated yet
|
|
65
|
+
🖱️ Second Click: Now DOM is updated, click is registered inside the actual rendered checklist, interaction works
|
|
66
|
+
*/
|
|
67
|
+
//This lets Angular finish updating the DOM before running change detection.
|
|
68
|
+
setTimeout(() => this.cdr.markForCheck());
|
|
69
|
+
}
|
|
70
|
+
constructor(dialogService, mls) {
|
|
71
|
+
super(dialogService);
|
|
72
|
+
this.dialogService = dialogService;
|
|
73
|
+
this.mls = mls;
|
|
74
|
+
this.coreFormDesignService = inject(CoreFormDesignService);
|
|
75
|
+
this.wfs = inject(CoreWorkflowService);
|
|
76
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
77
|
+
this.alertService = inject(AlertService);
|
|
78
|
+
this.router = inject(Router);
|
|
79
|
+
this.appConfigService = inject(AppConfigService);
|
|
80
|
+
this.form = this.coreFormDesignService.formMetadata;
|
|
81
|
+
this.$loading = signal(false);
|
|
82
|
+
this.mode$ = new BehaviorSubject(EnumCorePageEditMode.UPDATE);
|
|
83
|
+
this.customButtonChecklistOptions$ = this.coreFormDesignService.formMetadataCustomButtonChecklistOptions$;
|
|
84
|
+
this.$sections = this.coreFormDesignService.$formMetadataSections;
|
|
85
|
+
this.$isInWorkflowDesign = this.coreFormDesignService.$isInWorkflowDesign;
|
|
86
|
+
this.customButtons = [
|
|
87
|
+
EnumCoreButtonVNSCode.NONE_HEADER_SAVE,
|
|
88
|
+
];
|
|
89
|
+
effect(() => {
|
|
90
|
+
if (this.coreFormDesignService.$shouldPatchMetadataForm()) {
|
|
91
|
+
this.patchFormFromInstance();
|
|
92
|
+
this.coreFormDesignService.$shouldPatchMetadataForm.set(false); // ✅ reset
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
effect(() => {
|
|
96
|
+
const isInWorkflowDesign = this.$isInWorkflowDesign();
|
|
97
|
+
if (isInWorkflowDesign) {
|
|
98
|
+
READONLY_WORKFLOW_FIELDS.forEach(x => {
|
|
99
|
+
const control = this.form.get(x);
|
|
100
|
+
control?.disable();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
patchFormFromInstance() {
|
|
108
|
+
const instance = this.coreFormDesignService.$afInstance();
|
|
109
|
+
this.form.patchValue({
|
|
110
|
+
name: instance.name,
|
|
111
|
+
captionCode: instance.captionCode,
|
|
112
|
+
entityTable: instance.entityTable ?? '',
|
|
113
|
+
submitApi: instance.submitApi,
|
|
114
|
+
postSubmitRoute: instance.postSubmitRoute ?? '',
|
|
115
|
+
postCancelRoute: instance.postCancelRoute ?? '',
|
|
116
|
+
normalMode: instance.normalMode ?? false,
|
|
117
|
+
autoGetByIdOff: instance.autoGetByIdOff ?? false,
|
|
118
|
+
hasIdOfStringType: instance.hasIdOfStringType ?? false,
|
|
119
|
+
customFormButtonItems: instance.customFormButtonItems ?? [],
|
|
120
|
+
crudCreate: instance.crud?.c ?? '',
|
|
121
|
+
crudRead: instance.crud?.r ?? '',
|
|
122
|
+
crudUpdate: instance.crud?.u ?? '',
|
|
123
|
+
crudDelete: instance.crud?.d ?? '',
|
|
124
|
+
crudReadChildren: instance.crud?.rChildren ?? '',
|
|
125
|
+
entityUniqueIndexesJson: JSON.stringify(instance.entityUniqueIndexes ?? [])
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
onButtonClick(e) {
|
|
129
|
+
if (e.code === EnumCoreButtonVNSCode.NONE_HEADER_SAVE) {
|
|
130
|
+
if (this.form.valid) {
|
|
131
|
+
this.$loading.set(true);
|
|
132
|
+
const apiObject = this.coreFormDesignService.editMode$.value === EnumCorePageEditMode.CREATE ?
|
|
133
|
+
this.coreFormDesignService.createFormInstance(this.coreFormDesignService.$afInstance()) :
|
|
134
|
+
this.coreFormDesignService.updateFormInstance(this.coreFormDesignService.$afInstance());
|
|
135
|
+
const apiObservable = apiObject.pipe(catchError(err => {
|
|
136
|
+
this.$loading.set(false);
|
|
137
|
+
const msg = err?.error?.message || // API-defined message
|
|
138
|
+
err?.message || // Native JS or HttpErrorResponse
|
|
139
|
+
err?.statusText || // Fallback
|
|
140
|
+
'An unknown error occurred.';
|
|
141
|
+
this.alertService.error(msg, noneAutoClosedAlertOptions);
|
|
142
|
+
return of(msg);
|
|
143
|
+
}));
|
|
144
|
+
apiObservable.subscribe(x => {
|
|
145
|
+
this.$loading.set(false);
|
|
146
|
+
if (x.ok && x.status === 200 && x.body?.statusCode === 200) {
|
|
147
|
+
this.coreFormDesignService.$showFormMetadata.set(false);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.coreFormDesignService.formMetadataCheckError$.next(true);
|
|
153
|
+
setTimeout(() => this.cdr.markForCheck());
|
|
154
|
+
setTimeout(() => this.coreFormDesignService.formMetadataCheckError$.next(false), 5000);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
onClose() {
|
|
159
|
+
this.coreFormDesignService.$showFormMetadata.set(false);
|
|
160
|
+
}
|
|
161
|
+
onSave(e) {
|
|
162
|
+
}
|
|
163
|
+
onFormCreated(event) {
|
|
164
|
+
const form = event.formGroup;
|
|
165
|
+
form.get('normalMode')?.valueChanges.subscribe(x => {
|
|
166
|
+
this.coreFormDesignService.$liveFormNormalMode.set(!!x);
|
|
167
|
+
});
|
|
168
|
+
form.get('customFormButtonItems')?.valueChanges.subscribe(x => {
|
|
169
|
+
if (Array.isArray(x)) {
|
|
170
|
+
this.coreFormDesignService.$customFormButtonItems.set(x);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
this.coreFormDesignService.$customFormButtonItems.set(undefined);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
const widthField = form.get('width');
|
|
177
|
+
if (!!widthField) {
|
|
178
|
+
widthField.valueChanges.subscribe(width => {
|
|
179
|
+
console.warn(`WAR [width$] changed explicitly:`, width);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.warn(`WAR [widthField] not found`);
|
|
184
|
+
}
|
|
185
|
+
form.valueChanges.subscribe(value => {
|
|
186
|
+
console.warn('[width] from form.valueChanges', value?.width);
|
|
187
|
+
const current = this.coreFormDesignService.$afInstance();
|
|
188
|
+
const { crudCreate, crudRead, crudUpdate, crudDelete, crudReadChildren,
|
|
189
|
+
// ES6 destructuring: Pull id out of the object and throw it away under the name _ignored — don’t include it in rest
|
|
190
|
+
id: _ignored, ...rest } = value;
|
|
191
|
+
const updatedCrud = {
|
|
192
|
+
...current.crud,
|
|
193
|
+
...(crudCreate !== undefined ? { c: crudCreate } : {}),
|
|
194
|
+
...(crudRead !== undefined ? { r: crudRead } : {}),
|
|
195
|
+
...(crudUpdate !== undefined ? { u: crudUpdate } : {}),
|
|
196
|
+
...(crudDelete !== undefined ? { d: crudDelete } : {}),
|
|
197
|
+
...(crudReadChildren !== undefined ? { rChildren: crudReadChildren } : {})
|
|
198
|
+
};
|
|
199
|
+
const next = {
|
|
200
|
+
...current,
|
|
201
|
+
...rest,
|
|
202
|
+
width: rest.width ?? current.width, // That way, rogue undefined values can’t silently override valid ones.
|
|
203
|
+
crud: updatedCrud
|
|
204
|
+
};
|
|
205
|
+
console.warn('[SET $afInstance width] from FormMetadataComponent', next.width);
|
|
206
|
+
this.coreFormDesignService.$afInstance.set(next);
|
|
207
|
+
});
|
|
208
|
+
form.statusChanges.subscribe(status => {
|
|
209
|
+
// Optional: Save status if you want to show error summary, disable save button, etc.
|
|
210
|
+
this.formStatus = status; // string: 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED'
|
|
211
|
+
});
|
|
212
|
+
this.form = form;
|
|
213
|
+
}
|
|
214
|
+
ngAfterViewInit() {
|
|
215
|
+
// Defer to next tick to avoid ExpressionChangedAfterItHasBeenCheckedError
|
|
216
|
+
this.$isInWorkflowDesign.set(this.router.url.includes('/workflow/design'));
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
const allButtons = Object.values(EnumCoreButtonVNSCode);
|
|
219
|
+
const noneHeaderOptions = allButtons
|
|
220
|
+
.filter(value => value.startsWith('NONE_HEADER_') && value !== 'NONE_HEADER_IMPORT_OPTION')
|
|
221
|
+
.map(value => ({
|
|
222
|
+
value,
|
|
223
|
+
text: this.mls.trans(value),
|
|
224
|
+
checked: false
|
|
225
|
+
}));
|
|
226
|
+
this.customButtonChecklistOptions$.next(noneHeaderOptions);
|
|
227
|
+
const sections = this.coreFormDesignService.$placeholderSections();
|
|
228
|
+
// 🔥 DO NOT READ this.$afInstance()
|
|
229
|
+
this.coreFormDesignService.$afInstance.update(instance => ({
|
|
230
|
+
...instance,
|
|
231
|
+
formDesignSections: sections
|
|
232
|
+
}));
|
|
233
|
+
// Inform Angular to re-check since values arrived late
|
|
234
|
+
this.cdr.markForCheck();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: FormMetadataComponent, deps: [{ token: DialogService }, { token: MultiLanguageService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
238
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: FormMetadataComponent, isStandalone: true, selector: "form-metadata", host: { listeners: { "document:keydown.escape": "onEscape($event)", "window:click": "onWindowClick($event)" } }, usesInheritance: true, ngImport: i0, template: "<div class=\"form-metadata-container\">\r\n\r\n <div class=\"metadata-body\">\r\n\r\n <div class=\"header-n-form\">\r\n\r\n @if (!$isInWorkflowDesign()) {\r\n\r\n <div class=\"h15\"></div>\r\n\r\n <div class=\"d-flex d-flex-start w-100 pr1rem\">\r\n <core-button-group-vns [showCaption]=\"true\" [forHeader]=\"false\" (buttonClick)=\"onButtonClick($event)\"\r\n [shownItems]=\"customButtons\" class=\"buttonGroupCustom\"></core-button-group-vns>\r\n </div>\r\n <div class=\"h15\"></div>\r\n\r\n <div class=\"header-wrapper\">\r\n <core-page-header [title]=\"'FORM OVERVIEW'\" [hideButtonGroup]=\"true\"></core-page-header>\r\n <div class=\"close-wrapper\" (click)=\"onClose()\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </div>\r\n\r\n } \r\n\r\n\r\n\r\n <div class=\"af-instance\">\r\n\r\n <div class=\"af-instance-left\">\r\n <core-form \r\n [inputSections]=\"$sections()\" \r\n [width]=\"800\" \r\n [mode$]=\"mode$\" \r\n [checkError$]=\"coreFormDesignService.formMetadataCheckError$\"\r\n [hideButtons]=\"true\" \r\n [forceCreateNewForm]=\"true\"\r\n [hideDevSector]=\"!!$isInWorkflowDesign()\"\r\n (onFormCreated)=\"onFormCreated($event)\" />\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n @if (!$isInWorkflowDesign()) {\r\n <div class=\"af-instance-right\">\r\n <save-live-json></save-live-json>\r\n </div>\r\n }\r\n\r\n\r\n </div>\r\n\r\n</div>", styles: [".form-metadata-container .header-wrapper{padding:0 15px}.form-metadata-container .header-wrapper .close-wrapper{display:flex;position:absolute;right:7px;top:8px;width:24px;height:24px;color:#848484;cursor:pointer}.form-metadata-container .header-wrapper .close-wrapper i{font-size:24px}.form-metadata-container .af-instance{display:flex}.form-metadata-container .h15{height:15px!important}.form-metadata-container .pr1rem{padding-right:1rem}.form-metadata-container .metadata-body{display:flex;align-items:flex-start;justify-content:space-between}.form-metadata-container .af-instance-right{margin-top:45px;margin-right:15px}\n"], dependencies: [{ kind: "component", type: CorePageHeaderComponent, selector: "core-page-header", inputs: ["instanceNumber", "shownItems", "title", "hideButtonGroup", "htmlTooltipRef"], outputs: ["buttonClick"] }, { kind: "component", type: CoreFormComponent, selector: "core-form", inputs: ["formName", "width", "submitText", "leftInputSections", "leftInputSectionsFlexSize", "inputSections", "mode$", "bottomTemplateRef", "customFormButtonItems", "showCaptionButton", "disableSaveButton", "disableCancelButton", "checkError$", "hideButtons", "forceCreateNewForm", "secondBottomTemplateRef", "hideDevSector", "hideCalculatedSection", "buttonGroupTemplateRef"], outputs: ["onFormCreated", "onFormRefCreated", "onSubmit", "onSave", "onCancal", "buttonClick"] }, { kind: "component", type: SaveLiveJsonComponent, selector: "save-live-json" }, { kind: "component", type: CoreButtonGroupVnsComponent, selector: "core-button-group-vns", inputs: ["height", "instanceNumber", "forHeader", "fixedShow", "shownItems", "showCaption", "lastChildEffectOff"], outputs: ["buttonClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
239
|
+
}
|
|
240
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: FormMetadataComponent, decorators: [{
|
|
241
|
+
type: Component,
|
|
242
|
+
args: [{ selector: 'form-metadata', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
243
|
+
TranslatePipe,
|
|
244
|
+
CorePageHeaderComponent,
|
|
245
|
+
CoreFormComponent,
|
|
246
|
+
SaveLiveJsonComponent,
|
|
247
|
+
CoreButtonGroupVnsComponent
|
|
248
|
+
], template: "<div class=\"form-metadata-container\">\r\n\r\n <div class=\"metadata-body\">\r\n\r\n <div class=\"header-n-form\">\r\n\r\n @if (!$isInWorkflowDesign()) {\r\n\r\n <div class=\"h15\"></div>\r\n\r\n <div class=\"d-flex d-flex-start w-100 pr1rem\">\r\n <core-button-group-vns [showCaption]=\"true\" [forHeader]=\"false\" (buttonClick)=\"onButtonClick($event)\"\r\n [shownItems]=\"customButtons\" class=\"buttonGroupCustom\"></core-button-group-vns>\r\n </div>\r\n <div class=\"h15\"></div>\r\n\r\n <div class=\"header-wrapper\">\r\n <core-page-header [title]=\"'FORM OVERVIEW'\" [hideButtonGroup]=\"true\"></core-page-header>\r\n <div class=\"close-wrapper\" (click)=\"onClose()\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </div>\r\n\r\n } \r\n\r\n\r\n\r\n <div class=\"af-instance\">\r\n\r\n <div class=\"af-instance-left\">\r\n <core-form \r\n [inputSections]=\"$sections()\" \r\n [width]=\"800\" \r\n [mode$]=\"mode$\" \r\n [checkError$]=\"coreFormDesignService.formMetadataCheckError$\"\r\n [hideButtons]=\"true\" \r\n [forceCreateNewForm]=\"true\"\r\n [hideDevSector]=\"!!$isInWorkflowDesign()\"\r\n (onFormCreated)=\"onFormCreated($event)\" />\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n @if (!$isInWorkflowDesign()) {\r\n <div class=\"af-instance-right\">\r\n <save-live-json></save-live-json>\r\n </div>\r\n }\r\n\r\n\r\n </div>\r\n\r\n</div>", styles: [".form-metadata-container .header-wrapper{padding:0 15px}.form-metadata-container .header-wrapper .close-wrapper{display:flex;position:absolute;right:7px;top:8px;width:24px;height:24px;color:#848484;cursor:pointer}.form-metadata-container .header-wrapper .close-wrapper i{font-size:24px}.form-metadata-container .af-instance{display:flex}.form-metadata-container .h15{height:15px!important}.form-metadata-container .pr1rem{padding-right:1rem}.form-metadata-container .metadata-body{display:flex;align-items:flex-start;justify-content:space-between}.form-metadata-container .af-instance-right{margin-top:45px;margin-right:15px}\n"] }]
|
|
249
|
+
}], ctorParameters: () => [{ type: DialogService }, { type: MultiLanguageService }], propDecorators: { onEscape: [{
|
|
250
|
+
type: HostListener,
|
|
251
|
+
args: ['document:keydown.escape', ['$event']]
|
|
252
|
+
}], onWindowClick: [{
|
|
253
|
+
type: HostListener,
|
|
254
|
+
args: ['window:click', ['$event']]
|
|
255
|
+
}] } });
|
|
256
|
+
|
|
257
|
+
class AttachmentFieldHelperService {
|
|
258
|
+
static isAttachment(control) {
|
|
259
|
+
return control.controlType === 'ATTACHMENT';
|
|
260
|
+
}
|
|
261
|
+
static autoGenerateAttachmentNames(baseName) {
|
|
262
|
+
return {
|
|
263
|
+
field: baseName + 'Buffer',
|
|
264
|
+
assignTo: baseName
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
static buildHiddenAssignToField(baseFieldName) {
|
|
268
|
+
return {
|
|
269
|
+
field: baseFieldName,
|
|
270
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
271
|
+
type: 'string',
|
|
272
|
+
label: '',
|
|
273
|
+
value: '',
|
|
274
|
+
hidden: true,
|
|
275
|
+
flexSize: 0
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
static injectAssignToFieldIntoRow(row, assignToField) {
|
|
279
|
+
row.cells.push({ flexSize: assignToField.flexSize ?? 12, control: assignToField });
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Called when user edits the "field" of an ATTACHMENT control
|
|
283
|
+
*/
|
|
284
|
+
static handleFieldNameChangeForAttachment(control, form, typedFieldName) {
|
|
285
|
+
const { field, assignTo } = this.autoGenerateAttachmentNames(typedFieldName);
|
|
286
|
+
control.field = field;
|
|
287
|
+
control.assignTo = assignTo;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Called in drop handler when user drops a new ATTACHMENT
|
|
291
|
+
*/
|
|
292
|
+
static setupAttachmentOnDrop(newControl, form) {
|
|
293
|
+
if (this.isAttachment(newControl)) {
|
|
294
|
+
const baseName = newControl.field.replace(/Buffer$/, '');
|
|
295
|
+
const { field, assignTo } = this.autoGenerateAttachmentNames(baseName);
|
|
296
|
+
newControl.field = field;
|
|
297
|
+
newControl.assignTo = assignTo;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AttachmentFieldHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
301
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AttachmentFieldHelperService, providedIn: 'root' }); }
|
|
302
|
+
}
|
|
303
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AttachmentFieldHelperService, decorators: [{
|
|
304
|
+
type: Injectable,
|
|
305
|
+
args: [{
|
|
306
|
+
providedIn: 'root'
|
|
307
|
+
}]
|
|
308
|
+
}] });
|
|
309
|
+
|
|
310
|
+
class BasePropsComponent extends BaseComponent {
|
|
311
|
+
constructor() {
|
|
312
|
+
super(...arguments);
|
|
313
|
+
this.coreFormDesignService = inject(CoreFormDesignService);
|
|
314
|
+
this.$selectedCell = this.coreFormDesignService.$selectedCell;
|
|
315
|
+
this.$formContext = this.coreFormDesignService.$formContext;
|
|
316
|
+
this.form = this.coreFormDesignService.formDesign;
|
|
317
|
+
this.patchControl = this.coreFormDesignService.patchControl.bind(this.coreFormDesignService);
|
|
318
|
+
this.normalize = this.coreFormDesignService.normalize.bind(this.coreFormDesignService);
|
|
319
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
320
|
+
this.setControlProp = this.coreFormDesignService.setControlProp.bind(this.coreFormDesignService);
|
|
321
|
+
this.$control = computed(() => {
|
|
322
|
+
const cell = this.$selectedCell();
|
|
323
|
+
if (!cell?.control)
|
|
324
|
+
throw new Error('No control selected!');
|
|
325
|
+
return this.normalize(cell.control);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BasePropsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
329
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: BasePropsComponent, isStandalone: true, selector: "base-props", usesInheritance: true, ngImport: i0, template: './base-props.component.html', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
330
|
+
}
|
|
331
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BasePropsComponent, decorators: [{
|
|
332
|
+
type: Component,
|
|
333
|
+
args: [{
|
|
334
|
+
selector: 'base-props',
|
|
335
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
336
|
+
template: './base-props.component.html'
|
|
337
|
+
}]
|
|
338
|
+
}] });
|
|
339
|
+
|
|
340
|
+
class MandatoryPropsComponent extends BasePropsComponent {
|
|
341
|
+
constructor() {
|
|
342
|
+
super(...arguments);
|
|
343
|
+
this.$fieldInputRef = viewChild('fieldInput');
|
|
344
|
+
this.afs = inject(CoreFormDesignService);
|
|
345
|
+
this.$isNormalField = computed(() => this.afs.$currentControl()?.type !== 'calculated');
|
|
346
|
+
this.$typedField = computed(() => {
|
|
347
|
+
const control = this.$control();
|
|
348
|
+
const field = AttachmentFieldHelperService.isAttachment(control)
|
|
349
|
+
? control.assignTo || ''
|
|
350
|
+
: control.field;
|
|
351
|
+
return field;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
onFieldChange(val) {
|
|
355
|
+
this.setControlProp('field', val);
|
|
356
|
+
const expected = this.$typedField();
|
|
357
|
+
const actual = this.$fieldInputRef()?.nativeElement.value;
|
|
358
|
+
if (!!actual && actual !== expected) {
|
|
359
|
+
console.warn('Manual fix: input mismatch detected. Forcing value reset.');
|
|
360
|
+
this.$fieldInputRef().nativeElement.value = expected;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MandatoryPropsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
364
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: MandatoryPropsComponent, isStandalone: true, selector: "mandatory-props", viewQueries: [{ propertyName: "$fieldInputRef", first: true, predicate: ["fieldInput"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"setting-group mandatory-group\" [ngStyle]=\"{ padding: '15px', maxWidth: '352px' }\">\r\n\r\n <div class=\"setting-row\">\r\n <label>Field</label>\r\n <!-- <input class=\"form-control\" type=\"text\" \r\n [ngModel]=\"$typedField()\" \r\n (focus)=\"capturePreviousField()\"\r\n (ngModelChange)=\"setControlProp('field', $event, previousFieldName)\" /> -->\r\n <input #fieldInput class=\"form-control\" type=\"text\" \r\n [ngModel]=\"$typedField()\" \r\n (ngModelChange)=\"onFieldChange($event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Label</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control()?.label\" (ngModelChange)=\"setControlProp('label', $event)\" />\r\n </div>\r\n\r\n @if ($isNormalField()) {\r\n\r\n <div class=\"setting-row\">\r\n <label>Flex Size</label>\r\n <input class=\"form-control\" type=\"number\" [ngModel]=\"$control()?.flexSize\" (ngModelChange)=\"setControlProp('flexSize', $event)\" />\r\n </div>\r\n\r\n @if ($control()?.controlType !== 'FORM_ARRAY') {\r\n <div class=\"setting-row\">\r\n <label>Default</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control()?.value\" (ngModelChange)=\"setControlProp('value', $event)\" />\r\n </div>\r\n }\r\n }\r\n\r\n</div>", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
365
|
+
}
|
|
366
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MandatoryPropsComponent, decorators: [{
|
|
367
|
+
type: Component,
|
|
368
|
+
args: [{ selector: 'mandatory-props', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgStyle, FormsModule], template: "<div class=\"setting-group mandatory-group\" [ngStyle]=\"{ padding: '15px', maxWidth: '352px' }\">\r\n\r\n <div class=\"setting-row\">\r\n <label>Field</label>\r\n <!-- <input class=\"form-control\" type=\"text\" \r\n [ngModel]=\"$typedField()\" \r\n (focus)=\"capturePreviousField()\"\r\n (ngModelChange)=\"setControlProp('field', $event, previousFieldName)\" /> -->\r\n <input #fieldInput class=\"form-control\" type=\"text\" \r\n [ngModel]=\"$typedField()\" \r\n (ngModelChange)=\"onFieldChange($event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Label</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control()?.label\" (ngModelChange)=\"setControlProp('label', $event)\" />\r\n </div>\r\n\r\n @if ($isNormalField()) {\r\n\r\n <div class=\"setting-row\">\r\n <label>Flex Size</label>\r\n <input class=\"form-control\" type=\"number\" [ngModel]=\"$control()?.flexSize\" (ngModelChange)=\"setControlProp('flexSize', $event)\" />\r\n </div>\r\n\r\n @if ($control()?.controlType !== 'FORM_ARRAY') {\r\n <div class=\"setting-row\">\r\n <label>Default</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control()?.value\" (ngModelChange)=\"setControlProp('value', $event)\" />\r\n </div>\r\n }\r\n }\r\n\r\n</div>", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"] }]
|
|
369
|
+
}] });
|
|
370
|
+
|
|
371
|
+
class OptionalPropsComponent extends BasePropsComponent {
|
|
372
|
+
constructor() {
|
|
373
|
+
super(...arguments);
|
|
374
|
+
this.pipeOptions = Object.values(EnumCoreTablePipeType);
|
|
375
|
+
this.pipeOptions$ = new BehaviorSubject(Object.values(EnumCoreTablePipeType).map(x => ({ value: x, text: x })));
|
|
376
|
+
}
|
|
377
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: OptionalPropsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
378
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: OptionalPropsComponent, isStandalone: true, selector: "optional-props", usesInheritance: true, ngImport: i0, template: "<div class=\"setting-group optional-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Readonly</label>\r\n <core-checkbox ngModel ngDefaultControl [ngModel]=\"$control().readonly\" (ngModelChange)=\"setControlProp('readonly', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Hidden</label>\r\n <core-checkbox [ngModel]=\"$control().hidden\" (ngModelChange)=\"setControlProp('hidden', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Disabled</label>\r\n <core-checkbox [ngModel]=\"$control().disabled\" (ngModelChange)=\"setControlProp('disabled', $event)\" /> \r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Hint</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().hint\" (ngModelChange)=\"setControlProp('hint', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Pipe</label>\r\n <div class=\"control-wrapper\">\r\n <core-dropdown [ngModel]=\"$control().pipe\" (ngModelChange)=\"setControlProp('pipe', $event)\"\r\n [paramMode]=\"true\"\r\n [options$]=\"pipeOptions$\"\r\n >\r\n </core-dropdown>\r\n </div>\r\n </div>\r\n\r\n</div>", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CoreCheckboxComponent, selector: "core-checkbox", inputs: ["text", "tooltipPosition", "tooltip", "inputValue", "disabled"], outputs: ["onClick"] }, { kind: "component", type: CoreDropdownComponent, selector: "core-dropdown", inputs: ["getByIdObject$", "paramMode", "shownFrom", "options$", "height", "placeholder", "loading", "warningDisable", "clearDisable", "fitHeightWithItemCount", "itemHeight", "optionApiDriven", "optionApi", "optionHttpVerb", "optionHttpPayload", "optionValueFrom", "optionTextFrom"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
379
|
+
}
|
|
380
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: OptionalPropsComponent, decorators: [{
|
|
381
|
+
type: Component,
|
|
382
|
+
args: [{ selector: 'optional-props', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
383
|
+
NgStyle,
|
|
384
|
+
FormsModule,
|
|
385
|
+
CoreCheckboxComponent,
|
|
386
|
+
CoreDropdownComponent
|
|
387
|
+
], template: "<div class=\"setting-group optional-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Readonly</label>\r\n <core-checkbox ngModel ngDefaultControl [ngModel]=\"$control().readonly\" (ngModelChange)=\"setControlProp('readonly', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Hidden</label>\r\n <core-checkbox [ngModel]=\"$control().hidden\" (ngModelChange)=\"setControlProp('hidden', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Disabled</label>\r\n <core-checkbox [ngModel]=\"$control().disabled\" (ngModelChange)=\"setControlProp('disabled', $event)\" /> \r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Hint</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().hint\" (ngModelChange)=\"setControlProp('hint', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Pipe</label>\r\n <div class=\"control-wrapper\">\r\n <core-dropdown [ngModel]=\"$control().pipe\" (ngModelChange)=\"setControlProp('pipe', $event)\"\r\n [paramMode]=\"true\"\r\n [options$]=\"pipeOptions$\"\r\n >\r\n </core-dropdown>\r\n </div>\r\n </div>\r\n\r\n</div>", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"] }]
|
|
388
|
+
}] });
|
|
389
|
+
|
|
390
|
+
class PropsHostComponent {
|
|
391
|
+
constructor() {
|
|
392
|
+
this.vcRef = inject(ViewContainerRef);
|
|
393
|
+
}
|
|
394
|
+
set props(value) {
|
|
395
|
+
this._props = value;
|
|
396
|
+
this.assignInputs();
|
|
397
|
+
}
|
|
398
|
+
async ngOnInit() {
|
|
399
|
+
if (this.loadComponent) {
|
|
400
|
+
const componentType = await this.loadComponent();
|
|
401
|
+
this.componentRef = this.vcRef.createComponent(componentType);
|
|
402
|
+
this.assignInputs();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
assignInputs() {
|
|
406
|
+
if (this.componentRef && this._props) {
|
|
407
|
+
Object.assign(this.componentRef.instance, this._props);
|
|
408
|
+
this.componentRef.changeDetectorRef.markForCheck();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PropsHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
412
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: PropsHostComponent, isStandalone: true, selector: "props-host", inputs: { loadComponent: "loadComponent", props: "props" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
413
|
+
}
|
|
414
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PropsHostComponent, decorators: [{
|
|
415
|
+
type: Component,
|
|
416
|
+
args: [{
|
|
417
|
+
selector: 'props-host',
|
|
418
|
+
standalone: true,
|
|
419
|
+
template: '',
|
|
420
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
421
|
+
}]
|
|
422
|
+
}], propDecorators: { loadComponent: [{
|
|
423
|
+
type: Input
|
|
424
|
+
}], props: [{
|
|
425
|
+
type: Input
|
|
426
|
+
}] } });
|
|
427
|
+
|
|
428
|
+
class AttachmentPropsComponent extends BasePropsComponent {
|
|
429
|
+
constructor() {
|
|
430
|
+
super(...arguments);
|
|
431
|
+
this.$typedField = computed(() => {
|
|
432
|
+
const control = this.$control();
|
|
433
|
+
return AttachmentFieldHelperService.isAttachment(control)
|
|
434
|
+
? control.assignTo || ''
|
|
435
|
+
: control.field;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
setTypedField(val) {
|
|
439
|
+
const control = this.$control();
|
|
440
|
+
const context = this.$formContext();
|
|
441
|
+
if (AttachmentFieldHelperService.isAttachment(control)) {
|
|
442
|
+
AttachmentFieldHelperService.handleFieldNameChangeForAttachment(control, context, val);
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
control.field = val;
|
|
446
|
+
}
|
|
447
|
+
this.patchControl(control);
|
|
448
|
+
}
|
|
449
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AttachmentPropsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
450
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: AttachmentPropsComponent, isStandalone: true, selector: "attachment-props", usesInheritance: true, ngImport: i0, template: "<p>attachment-props works!</p>\r\n", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"] }); }
|
|
451
|
+
}
|
|
452
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AttachmentPropsComponent, decorators: [{
|
|
453
|
+
type: Component,
|
|
454
|
+
args: [{ selector: 'attachment-props', imports: [], template: "<p>attachment-props works!</p>\r\n", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"] }]
|
|
455
|
+
}] });
|
|
456
|
+
|
|
457
|
+
class ChecklistPropsComponent {
|
|
458
|
+
constructor() {
|
|
459
|
+
this.controlChange = new EventEmitter();
|
|
460
|
+
}
|
|
461
|
+
update() {
|
|
462
|
+
this.controlChange.emit(this.control);
|
|
463
|
+
}
|
|
464
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ChecklistPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
465
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: ChecklistPropsComponent, isStandalone: true, selector: "checklist-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>checklist-props works!</p>\r\n", styles: [""] }); }
|
|
466
|
+
}
|
|
467
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ChecklistPropsComponent, decorators: [{
|
|
468
|
+
type: Component,
|
|
469
|
+
args: [{ selector: 'checklist-props', imports: [], template: "<p>checklist-props works!</p>\r\n" }]
|
|
470
|
+
}], propDecorators: { control: [{
|
|
471
|
+
type: Input
|
|
472
|
+
}], controlChange: [{
|
|
473
|
+
type: Output
|
|
474
|
+
}] } });
|
|
475
|
+
|
|
476
|
+
class DatepickerPropsComponent {
|
|
477
|
+
constructor() {
|
|
478
|
+
this.controlChange = new EventEmitter();
|
|
479
|
+
}
|
|
480
|
+
update() {
|
|
481
|
+
this.controlChange.emit(this.control);
|
|
482
|
+
}
|
|
483
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DatepickerPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
484
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: DatepickerPropsComponent, isStandalone: true, selector: "datepicker-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>datepicker-props works!</p>\r\n", styles: [""] }); }
|
|
485
|
+
}
|
|
486
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DatepickerPropsComponent, decorators: [{
|
|
487
|
+
type: Component,
|
|
488
|
+
args: [{ selector: 'datepicker-props', imports: [], template: "<p>datepicker-props works!</p>\r\n" }]
|
|
489
|
+
}], propDecorators: { control: [{
|
|
490
|
+
type: Input
|
|
491
|
+
}], controlChange: [{
|
|
492
|
+
type: Output
|
|
493
|
+
}] } });
|
|
494
|
+
|
|
495
|
+
class DropdownPropsComponent extends BasePropsComponent {
|
|
496
|
+
constructor() {
|
|
497
|
+
super(...arguments);
|
|
498
|
+
this.jsonPayload = '';
|
|
499
|
+
this.httpVerbOptions$ = new BehaviorSubject([
|
|
500
|
+
{
|
|
501
|
+
value: 'HttpGet',
|
|
502
|
+
text: 'GET'
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
value: 'HttpPost',
|
|
506
|
+
text: 'POST'
|
|
507
|
+
}
|
|
508
|
+
]);
|
|
509
|
+
}
|
|
510
|
+
onPayloadJsonBlur() {
|
|
511
|
+
try {
|
|
512
|
+
const optionHttpPayload = JSON.parse(this.jsonPayload);
|
|
513
|
+
this.setControlProp('optionHttpPayload', optionHttpPayload);
|
|
514
|
+
}
|
|
515
|
+
catch (e) {
|
|
516
|
+
console.warn('⚠️ Invalid JSON payload');
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DropdownPropsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
520
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: DropdownPropsComponent, isStandalone: true, selector: "dropdown-props", usesInheritance: true, ngImport: i0, template: "<div class=\"setting-group dropdown-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Use API</label>\r\n <core-checkbox [ngModel]=\"$control().optionApiDriven\" (ngModelChange)=\"setControlProp('optionApiDriven', $event)\" />\r\n </div>\r\n\r\n @if (!!$control().optionApiDriven) {\r\n <div class=\"setting-row\">\r\n <label>API Endpoint</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().optionApi\" (ngModelChange)=\"setControlProp('optionApi', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>HTTP Verb</label>\r\n <div class=\"control-wrapper\">\r\n <core-dropdown [ngModel]=\"$control().optionHttpVerb\" (ngModelChange)=\"setControlProp('optionHttpVerb', $event)\" [paramMode]=\"true\"\r\n [options$]=\"httpVerbOptions$\">\r\n </core-dropdown>\r\n </div>\r\n </div>\r\n\r\n @if ($control().optionHttpVerb === 'HttpPost') {\r\n <div class=\"setting-row\">\r\n <label>Payload (JSON)</label>\r\n <div class=\"control-wrapper\">\r\n <textarea class=\"form-control\" rows=\"3\" [(ngModel)]=\"jsonPayload\" (ngModelChange)=\"onPayloadJsonBlur()\"></textarea>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"setting-row\">\r\n <label>Value Field</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().optionValueFrom\" (ngModelChange)=\"setControlProp('optionValueFrom', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Text Field</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().optionTextFrom\" (ngModelChange)=\"setControlProp('optionTextFrom', $event)\" />\r\n </div>\r\n }\r\n</div>", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CoreCheckboxComponent, selector: "core-checkbox", inputs: ["text", "tooltipPosition", "tooltip", "inputValue", "disabled"], outputs: ["onClick"] }, { kind: "component", type: CoreDropdownComponent, selector: "core-dropdown", inputs: ["getByIdObject$", "paramMode", "shownFrom", "options$", "height", "placeholder", "loading", "warningDisable", "clearDisable", "fitHeightWithItemCount", "itemHeight", "optionApiDriven", "optionApi", "optionHttpVerb", "optionHttpPayload", "optionValueFrom", "optionTextFrom"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
521
|
+
}
|
|
522
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: DropdownPropsComponent, decorators: [{
|
|
523
|
+
type: Component,
|
|
524
|
+
args: [{ selector: 'dropdown-props', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
525
|
+
NgStyle,
|
|
526
|
+
FormsModule,
|
|
527
|
+
CoreCheckboxComponent,
|
|
528
|
+
CoreDropdownComponent,
|
|
529
|
+
], template: "<div class=\"setting-group dropdown-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n <div class=\"setting-row optional-bool\">\r\n <label>Use API</label>\r\n <core-checkbox [ngModel]=\"$control().optionApiDriven\" (ngModelChange)=\"setControlProp('optionApiDriven', $event)\" />\r\n </div>\r\n\r\n @if (!!$control().optionApiDriven) {\r\n <div class=\"setting-row\">\r\n <label>API Endpoint</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().optionApi\" (ngModelChange)=\"setControlProp('optionApi', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>HTTP Verb</label>\r\n <div class=\"control-wrapper\">\r\n <core-dropdown [ngModel]=\"$control().optionHttpVerb\" (ngModelChange)=\"setControlProp('optionHttpVerb', $event)\" [paramMode]=\"true\"\r\n [options$]=\"httpVerbOptions$\">\r\n </core-dropdown>\r\n </div>\r\n </div>\r\n\r\n @if ($control().optionHttpVerb === 'HttpPost') {\r\n <div class=\"setting-row\">\r\n <label>Payload (JSON)</label>\r\n <div class=\"control-wrapper\">\r\n <textarea class=\"form-control\" rows=\"3\" [(ngModel)]=\"jsonPayload\" (ngModelChange)=\"onPayloadJsonBlur()\"></textarea>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"setting-row\">\r\n <label>Value Field</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().optionValueFrom\" (ngModelChange)=\"setControlProp('optionValueFrom', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row\">\r\n <label>Text Field</label>\r\n <input class=\"form-control\" type=\"text\" [ngModel]=\"$control().optionTextFrom\" (ngModelChange)=\"setControlProp('optionTextFrom', $event)\" />\r\n </div>\r\n }\r\n</div>", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"] }]
|
|
530
|
+
}] });
|
|
531
|
+
|
|
532
|
+
var dropdownProps_component = /*#__PURE__*/Object.freeze({
|
|
533
|
+
__proto__: null,
|
|
534
|
+
DropdownPropsComponent: DropdownPropsComponent
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
class GridBufferPropsComponent {
|
|
538
|
+
constructor() {
|
|
539
|
+
this.controlChange = new EventEmitter();
|
|
540
|
+
}
|
|
541
|
+
update() {
|
|
542
|
+
this.controlChange.emit(this.control);
|
|
543
|
+
}
|
|
544
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: GridBufferPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
545
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: GridBufferPropsComponent, isStandalone: true, selector: "grid-buffer-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>grid-buffer-props works!</p>\r\n", styles: [""] }); }
|
|
546
|
+
}
|
|
547
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: GridBufferPropsComponent, decorators: [{
|
|
548
|
+
type: Component,
|
|
549
|
+
args: [{ selector: 'grid-buffer-props', imports: [], template: "<p>grid-buffer-props works!</p>\r\n" }]
|
|
550
|
+
}], propDecorators: { control: [{
|
|
551
|
+
type: Input
|
|
552
|
+
}], controlChange: [{
|
|
553
|
+
type: Output
|
|
554
|
+
}] } });
|
|
555
|
+
|
|
556
|
+
class ListPropsComponent {
|
|
557
|
+
constructor() {
|
|
558
|
+
this.controlChange = new EventEmitter();
|
|
559
|
+
}
|
|
560
|
+
update() {
|
|
561
|
+
this.controlChange.emit(this.control);
|
|
562
|
+
}
|
|
563
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ListPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
564
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: ListPropsComponent, isStandalone: true, selector: "list-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>list-props works!</p>\r\n", styles: [""] }); }
|
|
565
|
+
}
|
|
566
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ListPropsComponent, decorators: [{
|
|
567
|
+
type: Component,
|
|
568
|
+
args: [{ selector: 'list-props', imports: [], template: "<p>list-props works!</p>\r\n" }]
|
|
569
|
+
}], propDecorators: { control: [{
|
|
570
|
+
type: Input
|
|
571
|
+
}], controlChange: [{
|
|
572
|
+
type: Output
|
|
573
|
+
}] } });
|
|
574
|
+
|
|
575
|
+
class MccPropsComponent {
|
|
576
|
+
constructor() {
|
|
577
|
+
this.controlChange = new EventEmitter();
|
|
578
|
+
}
|
|
579
|
+
update() {
|
|
580
|
+
this.controlChange.emit(this.control);
|
|
581
|
+
}
|
|
582
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MccPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
583
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: MccPropsComponent, isStandalone: true, selector: "mcc-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>mcc-props works!</p>\r\n", styles: [""] }); }
|
|
584
|
+
}
|
|
585
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MccPropsComponent, decorators: [{
|
|
586
|
+
type: Component,
|
|
587
|
+
args: [{ selector: 'mcc-props', imports: [], template: "<p>mcc-props works!</p>\r\n" }]
|
|
588
|
+
}], propDecorators: { control: [{
|
|
589
|
+
type: Input
|
|
590
|
+
}], controlChange: [{
|
|
591
|
+
type: Output
|
|
592
|
+
}] } });
|
|
593
|
+
|
|
594
|
+
class NumberPropsComponent {
|
|
595
|
+
constructor() {
|
|
596
|
+
this.controlChange = new EventEmitter();
|
|
597
|
+
}
|
|
598
|
+
update() {
|
|
599
|
+
this.controlChange.emit(this.control);
|
|
600
|
+
}
|
|
601
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: NumberPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
602
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: NumberPropsComponent, isStandalone: true, selector: "number-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<div class=\"number-props-container\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n</div>", styles: [""], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); }
|
|
603
|
+
}
|
|
604
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: NumberPropsComponent, decorators: [{
|
|
605
|
+
type: Component,
|
|
606
|
+
args: [{ selector: 'number-props', imports: [NgStyle], template: "<div class=\"number-props-container\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n</div>" }]
|
|
607
|
+
}], propDecorators: { control: [{
|
|
608
|
+
type: Input
|
|
609
|
+
}], controlChange: [{
|
|
610
|
+
type: Output
|
|
611
|
+
}] } });
|
|
612
|
+
|
|
613
|
+
var numberProps_component = /*#__PURE__*/Object.freeze({
|
|
614
|
+
__proto__: null,
|
|
615
|
+
NumberPropsComponent: NumberPropsComponent
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
class RadioGroupPropsComponent {
|
|
619
|
+
constructor() {
|
|
620
|
+
this.controlChange = new EventEmitter();
|
|
621
|
+
}
|
|
622
|
+
update() {
|
|
623
|
+
this.controlChange.emit(this.control);
|
|
624
|
+
}
|
|
625
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: RadioGroupPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
626
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: RadioGroupPropsComponent, isStandalone: true, selector: "radio-group-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>radio-group-props works!</p>\r\n", styles: [""] }); }
|
|
627
|
+
}
|
|
628
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: RadioGroupPropsComponent, decorators: [{
|
|
629
|
+
type: Component,
|
|
630
|
+
args: [{ selector: 'radio-group-props', imports: [], template: "<p>radio-group-props works!</p>\r\n" }]
|
|
631
|
+
}], propDecorators: { control: [{
|
|
632
|
+
type: Input
|
|
633
|
+
}], controlChange: [{
|
|
634
|
+
type: Output
|
|
635
|
+
}] } });
|
|
636
|
+
|
|
637
|
+
class SeekerPropsComponent {
|
|
638
|
+
constructor() {
|
|
639
|
+
this.controlChange = new EventEmitter();
|
|
640
|
+
}
|
|
641
|
+
update() {
|
|
642
|
+
this.controlChange.emit(this.control);
|
|
643
|
+
}
|
|
644
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SeekerPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
645
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: SeekerPropsComponent, isStandalone: true, selector: "seeker-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>seeker-props works!</p>\r\n", styles: [""] }); }
|
|
646
|
+
}
|
|
647
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SeekerPropsComponent, decorators: [{
|
|
648
|
+
type: Component,
|
|
649
|
+
args: [{ selector: 'seeker-props', imports: [], template: "<p>seeker-props works!</p>\r\n" }]
|
|
650
|
+
}], propDecorators: { control: [{
|
|
651
|
+
type: Input
|
|
652
|
+
}], controlChange: [{
|
|
653
|
+
type: Output
|
|
654
|
+
}] } });
|
|
655
|
+
|
|
656
|
+
class TextareaPropsComponent {
|
|
657
|
+
constructor() {
|
|
658
|
+
this.controlChange = new EventEmitter();
|
|
659
|
+
}
|
|
660
|
+
update() {
|
|
661
|
+
this.controlChange.emit(this.control);
|
|
662
|
+
}
|
|
663
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: TextareaPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
664
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: TextareaPropsComponent, isStandalone: true, selector: "textarea-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>textarea-props works!</p>\r\n", styles: [""] }); }
|
|
665
|
+
}
|
|
666
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: TextareaPropsComponent, decorators: [{
|
|
667
|
+
type: Component,
|
|
668
|
+
args: [{ selector: 'textarea-props', imports: [], template: "<p>textarea-props works!</p>\r\n" }]
|
|
669
|
+
}], propDecorators: { control: [{
|
|
670
|
+
type: Input
|
|
671
|
+
}], controlChange: [{
|
|
672
|
+
type: Output
|
|
673
|
+
}] } });
|
|
674
|
+
|
|
675
|
+
class UploaderPropsComponent {
|
|
676
|
+
constructor() {
|
|
677
|
+
this.controlChange = new EventEmitter();
|
|
678
|
+
}
|
|
679
|
+
update() {
|
|
680
|
+
this.controlChange.emit(this.control);
|
|
681
|
+
}
|
|
682
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: UploaderPropsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
683
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: UploaderPropsComponent, isStandalone: true, selector: "uploader-props", inputs: { control: "control" }, outputs: { controlChange: "controlChange" }, ngImport: i0, template: "<p>uploader-props works!</p>\r\n", styles: [""] }); }
|
|
684
|
+
}
|
|
685
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: UploaderPropsComponent, decorators: [{
|
|
686
|
+
type: Component,
|
|
687
|
+
args: [{ selector: 'uploader-props', imports: [], template: "<p>uploader-props works!</p>\r\n" }]
|
|
688
|
+
}], propDecorators: { control: [{
|
|
689
|
+
type: Input
|
|
690
|
+
}], controlChange: [{
|
|
691
|
+
type: Output
|
|
692
|
+
}] } });
|
|
693
|
+
|
|
694
|
+
class ValidatorEditorComponent extends BasePropsComponent {
|
|
695
|
+
constructor(mls) {
|
|
696
|
+
super(mls);
|
|
697
|
+
this.mls = mls;
|
|
698
|
+
// 🔹 Computed list based on controlType
|
|
699
|
+
this.$availableValidators = computed(() => this.coreFormDesignService.getSupportedValidators(this.$control()));
|
|
700
|
+
// 🔹 Signal for selected validators with their values
|
|
701
|
+
this.$selectedValidators = signal({});
|
|
702
|
+
effect(() => {
|
|
703
|
+
const c = this.$control(); // will re-run when selection changes
|
|
704
|
+
this.syncFromControl();
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
// ✅ Check if a validator is enabled
|
|
708
|
+
isChecked(v) {
|
|
709
|
+
return this.$selectedValidators()[v] !== undefined;
|
|
710
|
+
}
|
|
711
|
+
syncFromControl() {
|
|
712
|
+
const control = this.$control();
|
|
713
|
+
const validators = control.validators ?? [];
|
|
714
|
+
const validatorMap = {};
|
|
715
|
+
for (const v of validators) {
|
|
716
|
+
validatorMap[v.name] = v;
|
|
717
|
+
}
|
|
718
|
+
this.$selectedValidators.set(validatorMap);
|
|
719
|
+
}
|
|
720
|
+
toggleValidator(v) {
|
|
721
|
+
const current = { ...this.$selectedValidators() };
|
|
722
|
+
const requiresValue = ['minLength', 'maxLength', 'min', 'max', 'pattern'];
|
|
723
|
+
const isAlreadyChecked = !!current[v];
|
|
724
|
+
if (isAlreadyChecked) {
|
|
725
|
+
delete current[v]; // ❌ Remove validator
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
const validator = {
|
|
729
|
+
name: v,
|
|
730
|
+
errorMessage: this.coreFormDesignService.getErrorMessageFor(v),
|
|
731
|
+
validator: this.coreFormDesignService.getValidatorFn(v, null), // stub until value is filled
|
|
732
|
+
value: requiresValue.includes(v) ? '' : true
|
|
733
|
+
};
|
|
734
|
+
current[v] = validator;
|
|
735
|
+
}
|
|
736
|
+
this.$selectedValidators.set(current);
|
|
737
|
+
// ✅ Skip update if validator requires value but it's still empty
|
|
738
|
+
const shouldSkip = !isAlreadyChecked &&
|
|
739
|
+
requiresValue.includes(v) &&
|
|
740
|
+
current[v]?.value === '';
|
|
741
|
+
if (shouldSkip)
|
|
742
|
+
return;
|
|
743
|
+
const validatorArray = this.coreFormDesignService.toIValidatorArray(current);
|
|
744
|
+
this.setControlProp('validators', validatorArray);
|
|
745
|
+
}
|
|
746
|
+
updateValidatorValue(v, raw) {
|
|
747
|
+
const value = raw?.toString().trim();
|
|
748
|
+
const updated = { ...this.$selectedValidators() };
|
|
749
|
+
if (value === '') {
|
|
750
|
+
// 👇 Remove the validator if it was cleared
|
|
751
|
+
delete updated[v];
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
if (updated[v]) {
|
|
755
|
+
updated[v].value = value;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
this.$selectedValidators.set(updated);
|
|
759
|
+
// ✅ Only update the control if it's valid or fully removed
|
|
760
|
+
const validatorArray = this.coreFormDesignService.toIValidatorArray(updated);
|
|
761
|
+
this.setControlProp('validators', validatorArray);
|
|
762
|
+
}
|
|
763
|
+
isReadOnlyValidator(v) {
|
|
764
|
+
return this.$selectedValidators()[v]?.readOnly === true;
|
|
765
|
+
}
|
|
766
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ValidatorEditorComponent, deps: [{ token: MultiLanguageService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
767
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: ValidatorEditorComponent, isStandalone: true, selector: "validator-editor", usesInheritance: true, ngImport: i0, template: "<div class=\"setting-group validator-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n @for (v of $availableValidators(); track v) {\r\n <div class=\"setting-row\">\r\n\r\n <label>\r\n {{ v }}\r\n @if (isReadOnlyValidator(v)) {\r\n <i class=\"feather-lock tiny-lock\" title=\"Read-only system validator\"></i>\r\n }\r\n </label>\r\n\r\n <div class=\"control-wrapper\">\r\n <core-checkbox ngModel ngDefaultControl [ngModel]=\"isChecked(v)\" [disabled]=\"isReadOnlyValidator(v)\"\r\n [appTooltip]=\"isReadOnlyValidator(v) ? 'System validator' : ''\" (ngModelChange)=\"toggleValidator(v)\" />\r\n\r\n @if (isChecked(v) && ['minLength', 'maxLength', 'min', 'max'].includes(v)) {\r\n <input class=\"form-control validator-value\" [type]=\"'number'\"\r\n [value]=\"$selectedValidators()[v]?.value || ''\"\r\n (input)=\"updateValidatorValue(v, $any($event.target).value)\"\r\n [placeholder]=\"'Enter value' | translate: lang\" [disabled]=\"isReadOnlyValidator(v)\"\r\n [appTooltip]=\"isReadOnlyValidator(v) ? 'Default validator \u2014 cannot be modified' : ''\" />\r\n }\r\n\r\n @else if (isChecked(v) && v === 'pattern') {\r\n <input class=\"form-control validator-value\" type=\"text\" [value]=\"$selectedValidators()[v]?.value || ''\"\r\n (input)=\"updateValidatorValue(v, $any($event.target).value)\" [placeholder]=\"'Enter regex'\"\r\n [disabled]=\"isReadOnlyValidator(v)\"\r\n [appTooltip]=\"isReadOnlyValidator(v) ? 'Default validator \u2014 cannot be modified' : ''\" />\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "directive", type: TooltipDirective, selector: "[appTooltip]", inputs: ["color", "backgroundColor", "appTooltip", "showAnyway", "position"] }, { kind: "component", type: CoreCheckboxComponent, selector: "core-checkbox", inputs: ["text", "tooltipPosition", "tooltip", "inputValue", "disabled"], outputs: ["onClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
768
|
+
}
|
|
769
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ValidatorEditorComponent, decorators: [{
|
|
770
|
+
type: Component,
|
|
771
|
+
args: [{ selector: 'validator-editor', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
772
|
+
NgStyle,
|
|
773
|
+
FormsModule,
|
|
774
|
+
TranslatePipe,
|
|
775
|
+
TooltipDirective,
|
|
776
|
+
CoreCheckboxComponent
|
|
777
|
+
], template: "<div class=\"setting-group validator-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n @for (v of $availableValidators(); track v) {\r\n <div class=\"setting-row\">\r\n\r\n <label>\r\n {{ v }}\r\n @if (isReadOnlyValidator(v)) {\r\n <i class=\"feather-lock tiny-lock\" title=\"Read-only system validator\"></i>\r\n }\r\n </label>\r\n\r\n <div class=\"control-wrapper\">\r\n <core-checkbox ngModel ngDefaultControl [ngModel]=\"isChecked(v)\" [disabled]=\"isReadOnlyValidator(v)\"\r\n [appTooltip]=\"isReadOnlyValidator(v) ? 'System validator' : ''\" (ngModelChange)=\"toggleValidator(v)\" />\r\n\r\n @if (isChecked(v) && ['minLength', 'maxLength', 'min', 'max'].includes(v)) {\r\n <input class=\"form-control validator-value\" [type]=\"'number'\"\r\n [value]=\"$selectedValidators()[v]?.value || ''\"\r\n (input)=\"updateValidatorValue(v, $any($event.target).value)\"\r\n [placeholder]=\"'Enter value' | translate: lang\" [disabled]=\"isReadOnlyValidator(v)\"\r\n [appTooltip]=\"isReadOnlyValidator(v) ? 'Default validator \u2014 cannot be modified' : ''\" />\r\n }\r\n\r\n @else if (isChecked(v) && v === 'pattern') {\r\n <input class=\"form-control validator-value\" type=\"text\" [value]=\"$selectedValidators()[v]?.value || ''\"\r\n (input)=\"updateValidatorValue(v, $any($event.target).value)\" [placeholder]=\"'Enter regex'\"\r\n [disabled]=\"isReadOnlyValidator(v)\"\r\n [appTooltip]=\"isReadOnlyValidator(v) ? 'Default validator \u2014 cannot be modified' : ''\" />\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".setting-group{display:flex;flex-direction:column}.setting-row{display:flex;justify-content:flex-start;align-items:center;height:50px}.setting-row label{width:30%}.setting-row .control-wrapper,.setting-row input,.setting-row .readonly-field{width:65%}.setting-row .readonly-field{font-style:italic;color:#666}.setting-row input.validator-value{text-align:right}.setting-row input.validator-value.pattern{text-align:left}.setting-row .control-wrapper{display:flex;align-items:center;justify-content:flex-start}.validator-group .setting-row{justify-content:flex-start}.validator-group .setting-row input{margin-left:15px;width:100%}.tiny-lock{margin-left:6px;font-size:12px;color:#888}\n"] }]
|
|
778
|
+
}], ctorParameters: () => [{ type: MultiLanguageService }] });
|
|
779
|
+
|
|
780
|
+
class CalculatedPropsComponent extends BasePropsComponent {
|
|
781
|
+
constructor(mls) {
|
|
782
|
+
super(mls);
|
|
783
|
+
this.mls = mls;
|
|
784
|
+
this.$shortHtmlHintForCSharpSyntax = viewChild('shortHtmlHintForCSharpSyntax');
|
|
785
|
+
this.$shortHtmlHintForSqlSyntax = viewChild('shortHtmlHintForSqlSyntax');
|
|
786
|
+
this.$loading = signal(false);
|
|
787
|
+
this.$beingValidated = signal(false);
|
|
788
|
+
this.wfs = inject(CoreWorkflowService);
|
|
789
|
+
this.fds = inject(CoreFormDesignService);
|
|
790
|
+
this.dbs = inject(DbService);
|
|
791
|
+
this.gpt = inject(GptService);
|
|
792
|
+
this.alertService = inject(AlertService);
|
|
793
|
+
this.$currentControl = this.fds.$currentControl;
|
|
794
|
+
this.$sqlScriptError = signal(null);
|
|
795
|
+
this.$aiGeneratorOn = signal(false);
|
|
796
|
+
this.$selectedTables = signal([]);
|
|
797
|
+
this.$generatedScript = signal('');
|
|
798
|
+
this.$sqlPrompt = signal('');
|
|
799
|
+
this.$selectedTablesForAi = computed(() => {
|
|
800
|
+
const selectedTables = this.$selectedTables();
|
|
801
|
+
if (!selectedTables.length)
|
|
802
|
+
return 'No tables selected.';
|
|
803
|
+
let result = 'Consider the following database tables and columns:\n\n';
|
|
804
|
+
for (const table of selectedTables) {
|
|
805
|
+
result += `Table: ${table.table}\n`;
|
|
806
|
+
const activeColumns = table.columns.filter(c => c.isActive);
|
|
807
|
+
for (const col of activeColumns) {
|
|
808
|
+
const hint = col.hintForAi?.trim();
|
|
809
|
+
result += ` - ${col.column}` + (hint ? ` (${hint})` : '') + `\n`;
|
|
810
|
+
}
|
|
811
|
+
result += '\n';
|
|
812
|
+
}
|
|
813
|
+
return result.trim();
|
|
814
|
+
});
|
|
815
|
+
this.radioOptions$ = new BehaviorSubject([
|
|
816
|
+
{
|
|
817
|
+
value: 'in-memory',
|
|
818
|
+
text: 'In Memory',
|
|
819
|
+
htmlTooltip: this.$shortHtmlHintForCSharpSyntax()
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
value: 'sql-query',
|
|
823
|
+
text: 'SQL Query',
|
|
824
|
+
htmlTooltip: this.$shortHtmlHintForSqlSyntax()
|
|
825
|
+
},
|
|
826
|
+
]);
|
|
827
|
+
this.$fields = computed(() => {
|
|
828
|
+
const currentField = this.fds.$currentControl()?.field;
|
|
829
|
+
return this.wfs.$availableCols().filter(c => c.field !== currentField).map(c => ({ field: c.field }));
|
|
830
|
+
});
|
|
831
|
+
this.$placeholderForScriptInput = computed(() => {
|
|
832
|
+
switch (this.$currentControl()?.evaluationMode) {
|
|
833
|
+
case 'in-memory':
|
|
834
|
+
return 'Enter expression formula';
|
|
835
|
+
case 'sql-query':
|
|
836
|
+
return 'Enter SQL script';
|
|
837
|
+
default:
|
|
838
|
+
return 'Please select Evaluation Mode';
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
effect(() => {
|
|
842
|
+
const shortHtmlHintForCSharpSyntax = this.$shortHtmlHintForCSharpSyntax();
|
|
843
|
+
const shortHtmlHintForSqlSyntax = this.$shortHtmlHintForSqlSyntax();
|
|
844
|
+
if (!!shortHtmlHintForCSharpSyntax && !!shortHtmlHintForSqlSyntax) {
|
|
845
|
+
this.radioOptions$.next([
|
|
846
|
+
{
|
|
847
|
+
value: 'in-memory',
|
|
848
|
+
text: 'In Memory',
|
|
849
|
+
htmlTooltip: shortHtmlHintForCSharpSyntax
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
value: 'sql-query',
|
|
853
|
+
text: 'SQL Query',
|
|
854
|
+
htmlTooltip: shortHtmlHintForSqlSyntax
|
|
855
|
+
},
|
|
856
|
+
]);
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
loadDbSchema() {
|
|
861
|
+
const fullRequest = {};
|
|
862
|
+
this.subscriptions.push(this.dbs.syncAndGetWorkflowSchemaMetadata(fullRequest).pipe(catchError(() => of({ ok: false, status: 500 })))
|
|
863
|
+
.subscribe(x => {
|
|
864
|
+
if (x.ok && x.status === 200 && x.body?.statusCode === 200) {
|
|
865
|
+
this.wfs.$workflowDbSchema.set(x.body.innerBody.items);
|
|
866
|
+
}
|
|
867
|
+
}));
|
|
868
|
+
}
|
|
869
|
+
toTables(names) {
|
|
870
|
+
const allTables = this.wfs.$workflowDbSchema(); // All available tables
|
|
871
|
+
return allTables.filter(table => names.includes(table.table));
|
|
872
|
+
}
|
|
873
|
+
validateScript(e) {
|
|
874
|
+
e.stopPropagation();
|
|
875
|
+
const control = this.$currentControl();
|
|
876
|
+
if (!control) {
|
|
877
|
+
this.alertService.info(`No current control detected`);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (!control.script) {
|
|
881
|
+
this.alertService.info(`There is no script to validate for ${control.field}`);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (!control.returnType) {
|
|
885
|
+
this.alertService.info(`You should set return type for ${control.field}`);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const evalMode = this.$currentControl()?.evaluationMode;
|
|
889
|
+
if (!evalMode) {
|
|
890
|
+
this.alertService.error('Please select avaluation mode', alertOptions);
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const request = {
|
|
894
|
+
input: control.script,
|
|
895
|
+
context: this.getValidationContextByMode(evalMode),
|
|
896
|
+
purpose: 'validate-calculated-field',
|
|
897
|
+
isSystemPrompt: true
|
|
898
|
+
};
|
|
899
|
+
this.$beingValidated.set(true);
|
|
900
|
+
this.subscriptions.push(this.gpt.askGpt(request).pipe(catchError(err => {
|
|
901
|
+
this.$beingValidated.set(false);
|
|
902
|
+
this.alertService.error('Validation failed.');
|
|
903
|
+
return of({ ok: false, status: 500 });
|
|
904
|
+
})).subscribe(x => {
|
|
905
|
+
this.$beingValidated.set(false);
|
|
906
|
+
if (x.ok && x.status === 200 && x.body?.statusCode === 200) {
|
|
907
|
+
const reply = x.body.innerBody?.trim();
|
|
908
|
+
this.alertService.success(`GPT: ${reply}`);
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
this.alertService.error('Could not validate script.');
|
|
912
|
+
}
|
|
913
|
+
}));
|
|
914
|
+
}
|
|
915
|
+
generateScript() {
|
|
916
|
+
this.$aiGeneratorOn.update(current => !current);
|
|
917
|
+
}
|
|
918
|
+
applyGeneratedSql() {
|
|
919
|
+
const generated = this.$generatedScript();
|
|
920
|
+
if (!generated)
|
|
921
|
+
return;
|
|
922
|
+
this.setControlProp('script', generated);
|
|
923
|
+
this.alertService.success('Script applied from AI.');
|
|
924
|
+
this.$aiGeneratorOn.set(false);
|
|
925
|
+
}
|
|
926
|
+
submitScriptPrompt() {
|
|
927
|
+
const control = this.$currentControl();
|
|
928
|
+
const evalMode = control?.evaluationMode;
|
|
929
|
+
const prompt = this.$sqlPrompt().trim();
|
|
930
|
+
const dependsOn = control?.dependsOn ?? [];
|
|
931
|
+
const selectedTables = this.$selectedTables();
|
|
932
|
+
if (!evalMode) {
|
|
933
|
+
this.alertService.warn('Please select an evaluation mode.');
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (!prompt) {
|
|
937
|
+
this.alertService.warn('Please enter a description for the AI to generate your script.');
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
if (dependsOn.length === 0) {
|
|
941
|
+
this.alertService.warn('Please select at least one field that the script depends on.');
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (evalMode === 'sql-query') {
|
|
945
|
+
if (!selectedTables.length) {
|
|
946
|
+
this.alertService.warn('Please select at least one table.');
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const tablesWithNoHint = selectedTables
|
|
950
|
+
.filter(t => !t.hintForAi?.trim())
|
|
951
|
+
.map(t => t.table);
|
|
952
|
+
if (tablesWithNoHint.length > 0) {
|
|
953
|
+
this.alertService.warn(`Please provide a hint for the following table(s): ${tablesWithNoHint.join(', ')}`);
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
// 🧠 Construct prompt
|
|
958
|
+
const dependsOnLine = `The script should depend on: ${dependsOn.join(', ')}.`;
|
|
959
|
+
const contextIntro = this.getContextByMode(evalMode);
|
|
960
|
+
const tableContext = this.$selectedTablesForAi(); // nicely formatted
|
|
961
|
+
const fullPrompt = `${prompt}\n\n${dependsOnLine}\n\n${tableContext}`;
|
|
962
|
+
const request = {
|
|
963
|
+
input: fullPrompt,
|
|
964
|
+
context: contextIntro,
|
|
965
|
+
purpose: 'generate-calculated-field',
|
|
966
|
+
isSystemPrompt: true
|
|
967
|
+
};
|
|
968
|
+
this.$loading.set(true);
|
|
969
|
+
this.subscriptions.push(this.gpt.askGpt(request).pipe(catchError(err => {
|
|
970
|
+
this.$loading.set(false);
|
|
971
|
+
this.alertService.error('AI generation failed.');
|
|
972
|
+
return of({ ok: false, status: 500 });
|
|
973
|
+
})).subscribe(res => {
|
|
974
|
+
this.$loading.set(false);
|
|
975
|
+
if (res.ok && res.status === 200 && res.body?.statusCode === 200) {
|
|
976
|
+
const reply = res.body.innerBody?.trim();
|
|
977
|
+
if (reply) {
|
|
978
|
+
this.$generatedScript.set(reply);
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
this.alertService.warn('AI returned an empty result.');
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
this.alertService.error('AI could not generate a valid script.');
|
|
986
|
+
}
|
|
987
|
+
}));
|
|
988
|
+
}
|
|
989
|
+
tableCheckedChange(selected) {
|
|
990
|
+
this.$selectedTables.set(this.toTables(selected));
|
|
991
|
+
}
|
|
992
|
+
getContextByMode(mode) {
|
|
993
|
+
if (mode === 'sql-query') {
|
|
994
|
+
return [
|
|
995
|
+
`You use ${this.mls.lang$.value.toUpperCase() === 'VI' ? 'Vietnamese' : 'English'} for answer`,
|
|
996
|
+
"You are a SQL validator for a workflow engine.",
|
|
997
|
+
"User input must be a full SQL SELECT statement compatible with Dapper.",
|
|
998
|
+
"User input must not modify database schema",
|
|
999
|
+
"It must contain a FROM clause with a real table name.",
|
|
1000
|
+
"It must contain a UPDATE, DELETE or DROP clause.",
|
|
1001
|
+
"It must contain a WHERE clause.",
|
|
1002
|
+
"It must return exactly one value",
|
|
1003
|
+
"Examples of valid patterns include:",
|
|
1004
|
+
" SELECT CASE WHEN EXISTS (SELECT 1 FROM Users WHERE IsActive = 1) THEN 1 ELSE 0 END",
|
|
1005
|
+
" SELECT CASE WHEN @Amount > 1000 THEN @Amount ELSE 0 END -- when Amount is a parameter",
|
|
1006
|
+
"The query must be syntactically complete, no partial expressions or clauses.",
|
|
1007
|
+
`The query must return a type of ${this.$currentControl()?.returnType} JavaScript equivalent.`,
|
|
1008
|
+
"Do not wrap your output in triple backticks or markdown.",
|
|
1009
|
+
"Respond with only the raw SQL script.",
|
|
1010
|
+
];
|
|
1011
|
+
}
|
|
1012
|
+
return [
|
|
1013
|
+
`You use ${this.mls.lang$.value.toUpperCase() === 'VI' ? 'Vietnamese' : 'English'} for answer`,
|
|
1014
|
+
'You are a C# expression validator.',
|
|
1015
|
+
`Validate whether the expression returns a type of ${this.$currentControl()?.returnType} JavaScript equivalent result using C# syntax.`,
|
|
1016
|
+
'The result will be used inside an if-statement or condition tree.'
|
|
1017
|
+
];
|
|
1018
|
+
}
|
|
1019
|
+
getValidationContextByMode(mode) {
|
|
1020
|
+
if (mode === 'sql-query') {
|
|
1021
|
+
return [
|
|
1022
|
+
`You use ${this.mls.lang$.value.toUpperCase() === 'VI' ? 'Vietnamese' : 'English'} for answer`,
|
|
1023
|
+
"You're a SQL validator for a business rule engine.",
|
|
1024
|
+
"The input is a full SQL SELECT statement written by a human.",
|
|
1025
|
+
"Analyze if it meets these criteria:",
|
|
1026
|
+
"- Compatible with SQL Server and Dapper parameter style.",
|
|
1027
|
+
"- Has FROM clause with table name.",
|
|
1028
|
+
"- Has WHERE clause.",
|
|
1029
|
+
"- Uses @param style inputs.",
|
|
1030
|
+
"Return only a short answer like 'Valid ✅' or explain what's wrong in plain terms.",
|
|
1031
|
+
];
|
|
1032
|
+
}
|
|
1033
|
+
return [
|
|
1034
|
+
`You use ${this.mls.lang$.value.toUpperCase() === 'VI' ? 'Vietnamese' : 'English'} for answer`,
|
|
1035
|
+
"You're a validator for C# expressions and the result should be compatible with DynamicExpresso",
|
|
1036
|
+
`Analyze whether this expression is valid syntax and returns a type = ${this.$control().returnType}`,
|
|
1037
|
+
"If valid, say 'Valid ✅'. If not, explain the syntax issue.",
|
|
1038
|
+
];
|
|
1039
|
+
}
|
|
1040
|
+
validateSqlScript(input) {
|
|
1041
|
+
const trimmed = input.trim();
|
|
1042
|
+
if (!/^SELECT\b/i.test(trimmed)) {
|
|
1043
|
+
return 'Only SELECT queries are allowed.';
|
|
1044
|
+
}
|
|
1045
|
+
const destructivePattern = /^\s*(DELETE|DROP|UPDATE|INSERT|ALTER|TRUNCATE|EXEC|MERGE)\b/gi;
|
|
1046
|
+
if (destructivePattern.test(trimmed)) {
|
|
1047
|
+
return 'Dangerous SQL operations (DELETE, DROP, etc.) are not allowed.';
|
|
1048
|
+
}
|
|
1049
|
+
return null;
|
|
1050
|
+
}
|
|
1051
|
+
onScriptChange(script) {
|
|
1052
|
+
if (this.$control().evaluationMode === 'sql-query') {
|
|
1053
|
+
const error = this.validateSqlScript(script);
|
|
1054
|
+
this.setControlProp('isUnsafe', !!error);
|
|
1055
|
+
this.$sqlScriptError.set(error);
|
|
1056
|
+
this.setControlProp('script', script);
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
this.setControlProp('script', script);
|
|
1060
|
+
this.setControlProp('isUnsafe', false);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
ngAfterViewInit() {
|
|
1064
|
+
setTimeout(() => this.loadDbSchema());
|
|
1065
|
+
}
|
|
1066
|
+
openHintEditor(table, e) {
|
|
1067
|
+
}
|
|
1068
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CalculatedPropsComponent, deps: [{ token: MultiLanguageService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1069
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: CalculatedPropsComponent, isStandalone: true, selector: "calculated-props", viewQueries: [{ propertyName: "$shortHtmlHintForCSharpSyntax", first: true, predicate: ["shortHtmlHintForCSharpSyntax"], descendants: true, isSignal: true }, { propertyName: "$shortHtmlHintForSqlSyntax", first: true, predicate: ["shortHtmlHintForSqlSyntax"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"setting-group calculated-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n <div class=\"setting-row evaluation-mode-row\">\r\n <label class=\"setting-label\">Evaluation Mode</label>\r\n <core-radio-group [options$]=\"radioOptions$\" [vertical]=\"true\" [gapY]=\"8\"\r\n [ngModel]=\"$currentControl()?.evaluationMode\" (ngModelChange)=\"setControlProp('evaluationMode', $event)\">\r\n </core-radio-group>\r\n </div>\r\n\r\n <div class=\"setting-row depends-on-row\">\r\n <label class=\"setting-label\">Depends On</label>\r\n <core-sticker-collection [$items]=\"$fields()\" [$shownFrom]=\"'field'\"\r\n [$initialChecked]=\"$currentControl()?.dependsOn || []\" ($checkedChange)=\"setControlProp('dependsOn', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row return-type-row\">\r\n <label class=\"setting-label\">Return Type</label>\r\n <select class=\"form-control setting-input\" [ngModel]=\"$currentControl()?.returnType || 'number'\"\r\n (ngModelChange)=\"setControlProp('returnType', $event)\">\r\n <option value=\"boolean\">Boolean</option>\r\n <option value=\"number\">Number</option>\r\n <option value=\"string\">String</option>\r\n <option value=\"number[]\">Number[]</option>\r\n <option value=\"string[]\">String[]</option>\r\n <!-- Omitting 'Date' and 'object' -->\r\n </select>\r\n </div>\r\n\r\n @if (!!$currentControl()?.dependsOn && !!$currentControl()?.dependsOn!.length) {\r\n\r\n\r\n <div class=\"setting-row script-row\">\r\n <label class=\"setting-label\">Script {{ $beingValidated() ? 'is being validated...' : '' }}</label>\r\n <textarea [ngStyle]=\"{ minHeight: '150px', borderRadius: '8px' }\" [readonly]=\"!$currentControl()?.evaluationMode\"\r\n [disabled]=\"!$currentControl()?.evaluationMode\" class=\"form-control\"\r\n [class.form-control-disabled]=\"!$currentControl()?.evaluationMode\" rows=\"20\" [ngModel]=\"$currentControl()?.script\"\r\n (ngModelChange)=\"onScriptChange($event)\" [placeholder]=\"$placeholderForScriptInput()\">\r\n </textarea>\r\n @if (!!$sqlScriptError()) {\r\n <small class=\"text-danger\">{{ $sqlScriptError() }}</small>\r\n }\r\n\r\n\r\n @if (!!$currentControl()?.evaluationMode) {\r\n <div class=\"button-group\">\r\n <button class=\"btn btn-secondary validate-btn\" (click)=\"validateScript($event)\">Validate with AI</button>\r\n <button class=\"btn btn-primary validate-btn\" (click)=\"generateScript()\">\r\n {{ $aiGeneratorOn() ? 'Hide AI Prompt Editor' : 'Show AI Prompt Editor' }}\r\n </button>\r\n </div>\r\n }\r\n\r\n @if ($aiGeneratorOn()) {\r\n <div class=\"sql-generator-panel\">\r\n <div class=\"alert alert-info\" style=\"margin-top: 1rem;\">\r\n <strong>SQL Generator:</strong> Describe what you want the script to do, and AI will generate SQL for you.\r\n </div>\r\n\r\n @if ($currentControl()?.evaluationMode==='sql-query') {\r\n <div class=\"sql-generator-panel mt-4\">\r\n <label class=\"fw-bold mb-2\">Select Related Tables</label>\r\n\r\n <core-sticker-collection [$enableFilter]=\"true\" [$items]=\"wfs.$workflowDbSchema()\" [$shownFrom]=\"'table'\"\r\n [$tooltipFrom]=\"'hintForAi'\" [$initialChecked]=\"[]\" ($checkedChange)=\"tableCheckedChange($event)\">\r\n </core-sticker-collection>\r\n\r\n </div>\r\n }\r\n\r\n @if (!!$currentControl()?.evaluationMode) {\r\n <textarea class=\"form-control\" style=\"min-height: 100px; margin-top: 0.5rem; border-radius: 8px;\"\r\n [(ngModel)]=\"$sqlPrompt\" placeholder=\"e.g., Calculate leave days between FROM_DATE and TO_DATE\">\r\n </textarea>\r\n\r\n <div class=\"mt-2 d-flex gap-2 justify-content-end\">\r\n <button class=\"btn btn-success\" (click)=\"submitScriptPrompt()\">\r\n Generate Script\r\n </button>\r\n </div>\r\n }\r\n\r\n @if ($generatedScript()) {\r\n <div class=\"generated-sql-box mt-3\">\r\n <label class=\"fw-bold\">Generated by AI:</label>\r\n <pre class=\"bg-light p-3 rounded\" style=\"white-space: pre-wrap;\">{{$generatedScript()}}</pre>\r\n <button class=\"btn btn-sm btn-primary mt-2\" (click)=\"applyGeneratedSql()\">Apply to Script</button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n </div>\r\n\r\n\r\n\r\n }\r\n\r\n<ng-template #shortHtmlHintForCSharpSyntax>\r\n <div class=\"syntax-hint\">\r\n <strong>Example (C# Expression):</strong>\r\n <pre><code>FullName.ToUpper() + \" #\" + EmployeeCode</code></pre>\r\n <div class=\"note\">\r\n This expression will be evaluated dynamically using <code>DynamicExpresso</code>.<br>\r\n You can return any value from context.\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #shortHtmlHintForSqlSyntax>\r\n <div class=\"syntax-hint\">\r\n <strong>Example (SQL Query):</strong>\r\n <pre><code>SELECT EMPLOYEE_CODE FROM HU_EMPLOYEE WHERE ORG_ID = 123</code></pre>\r\n <div class=\"note\">\r\n Your SQL can return either a single value <em>or</em> a list.<br>\r\n The system will extract data from the <strong>first column of all rows</strong>.<br>\r\n Use standard SQL compatible with your database.\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n\r\n\r\n\r\n\r\n</div>", styles: [".setting-row{margin:30px 0}.button-group{margin-top:15px}.button-group button{height:35px;font-size:13px;border-radius:17.5px;margin-right:8px}.setting-divider{margin:1.5rem 0 .75rem;color:#555;border-top:1px dashed #ccc;padding-top:.5rem}.evaluation-mode-row{background-color:#ffecb3;border-radius:8px;width:180px;height:110px;padding:15px}.evaluation-mode-row label{margin-bottom:8px}.script-row{margin:30px 0}.syntax-hint{color:#666;background:#fff;border:1px solid #ccc;border-radius:6px;padding:12px;font-size:13px;max-width:320px;box-shadow:0 2px 6px #0000000d}.syntax-hint pre{margin:8px 0;background-color:#f8f8f8;padding:8px;border-radius:4px;font-family:monospace;white-space:pre-wrap;word-break:break-word}.syntax-hint .note{margin-top:6px;font-size:12px}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CoreStickerCollectionComponent, selector: "core-sticker-collection", inputs: ["$items", "$shownFrom", "$tooltipFrom", "$initialChecked", "$showMore", "$showMoreTooltip", "$hideShowMoreIfNotSelected", "$enableFilter"], outputs: ["$checkedChange", "$onShowMore"] }, { kind: "component", type: CoreRadioGroupComponent, selector: "core-radio-group", inputs: ["options$", "vertical", "columnCount", "gapY", "defaultValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1070
|
+
}
|
|
1071
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CalculatedPropsComponent, decorators: [{
|
|
1072
|
+
type: Component,
|
|
1073
|
+
args: [{ selector: 'calculated-props', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1074
|
+
NgStyle,
|
|
1075
|
+
FormsModule,
|
|
1076
|
+
CoreStickerCollectionComponent,
|
|
1077
|
+
CoreRadioGroupComponent,
|
|
1078
|
+
], template: "<div class=\"setting-group calculated-group\" [ngStyle]=\"{ padding: '15px' }\">\r\n\r\n <div class=\"setting-row evaluation-mode-row\">\r\n <label class=\"setting-label\">Evaluation Mode</label>\r\n <core-radio-group [options$]=\"radioOptions$\" [vertical]=\"true\" [gapY]=\"8\"\r\n [ngModel]=\"$currentControl()?.evaluationMode\" (ngModelChange)=\"setControlProp('evaluationMode', $event)\">\r\n </core-radio-group>\r\n </div>\r\n\r\n <div class=\"setting-row depends-on-row\">\r\n <label class=\"setting-label\">Depends On</label>\r\n <core-sticker-collection [$items]=\"$fields()\" [$shownFrom]=\"'field'\"\r\n [$initialChecked]=\"$currentControl()?.dependsOn || []\" ($checkedChange)=\"setControlProp('dependsOn', $event)\" />\r\n </div>\r\n\r\n <div class=\"setting-row return-type-row\">\r\n <label class=\"setting-label\">Return Type</label>\r\n <select class=\"form-control setting-input\" [ngModel]=\"$currentControl()?.returnType || 'number'\"\r\n (ngModelChange)=\"setControlProp('returnType', $event)\">\r\n <option value=\"boolean\">Boolean</option>\r\n <option value=\"number\">Number</option>\r\n <option value=\"string\">String</option>\r\n <option value=\"number[]\">Number[]</option>\r\n <option value=\"string[]\">String[]</option>\r\n <!-- Omitting 'Date' and 'object' -->\r\n </select>\r\n </div>\r\n\r\n @if (!!$currentControl()?.dependsOn && !!$currentControl()?.dependsOn!.length) {\r\n\r\n\r\n <div class=\"setting-row script-row\">\r\n <label class=\"setting-label\">Script {{ $beingValidated() ? 'is being validated...' : '' }}</label>\r\n <textarea [ngStyle]=\"{ minHeight: '150px', borderRadius: '8px' }\" [readonly]=\"!$currentControl()?.evaluationMode\"\r\n [disabled]=\"!$currentControl()?.evaluationMode\" class=\"form-control\"\r\n [class.form-control-disabled]=\"!$currentControl()?.evaluationMode\" rows=\"20\" [ngModel]=\"$currentControl()?.script\"\r\n (ngModelChange)=\"onScriptChange($event)\" [placeholder]=\"$placeholderForScriptInput()\">\r\n </textarea>\r\n @if (!!$sqlScriptError()) {\r\n <small class=\"text-danger\">{{ $sqlScriptError() }}</small>\r\n }\r\n\r\n\r\n @if (!!$currentControl()?.evaluationMode) {\r\n <div class=\"button-group\">\r\n <button class=\"btn btn-secondary validate-btn\" (click)=\"validateScript($event)\">Validate with AI</button>\r\n <button class=\"btn btn-primary validate-btn\" (click)=\"generateScript()\">\r\n {{ $aiGeneratorOn() ? 'Hide AI Prompt Editor' : 'Show AI Prompt Editor' }}\r\n </button>\r\n </div>\r\n }\r\n\r\n @if ($aiGeneratorOn()) {\r\n <div class=\"sql-generator-panel\">\r\n <div class=\"alert alert-info\" style=\"margin-top: 1rem;\">\r\n <strong>SQL Generator:</strong> Describe what you want the script to do, and AI will generate SQL for you.\r\n </div>\r\n\r\n @if ($currentControl()?.evaluationMode==='sql-query') {\r\n <div class=\"sql-generator-panel mt-4\">\r\n <label class=\"fw-bold mb-2\">Select Related Tables</label>\r\n\r\n <core-sticker-collection [$enableFilter]=\"true\" [$items]=\"wfs.$workflowDbSchema()\" [$shownFrom]=\"'table'\"\r\n [$tooltipFrom]=\"'hintForAi'\" [$initialChecked]=\"[]\" ($checkedChange)=\"tableCheckedChange($event)\">\r\n </core-sticker-collection>\r\n\r\n </div>\r\n }\r\n\r\n @if (!!$currentControl()?.evaluationMode) {\r\n <textarea class=\"form-control\" style=\"min-height: 100px; margin-top: 0.5rem; border-radius: 8px;\"\r\n [(ngModel)]=\"$sqlPrompt\" placeholder=\"e.g., Calculate leave days between FROM_DATE and TO_DATE\">\r\n </textarea>\r\n\r\n <div class=\"mt-2 d-flex gap-2 justify-content-end\">\r\n <button class=\"btn btn-success\" (click)=\"submitScriptPrompt()\">\r\n Generate Script\r\n </button>\r\n </div>\r\n }\r\n\r\n @if ($generatedScript()) {\r\n <div class=\"generated-sql-box mt-3\">\r\n <label class=\"fw-bold\">Generated by AI:</label>\r\n <pre class=\"bg-light p-3 rounded\" style=\"white-space: pre-wrap;\">{{$generatedScript()}}</pre>\r\n <button class=\"btn btn-sm btn-primary mt-2\" (click)=\"applyGeneratedSql()\">Apply to Script</button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n </div>\r\n\r\n\r\n\r\n }\r\n\r\n<ng-template #shortHtmlHintForCSharpSyntax>\r\n <div class=\"syntax-hint\">\r\n <strong>Example (C# Expression):</strong>\r\n <pre><code>FullName.ToUpper() + \" #\" + EmployeeCode</code></pre>\r\n <div class=\"note\">\r\n This expression will be evaluated dynamically using <code>DynamicExpresso</code>.<br>\r\n You can return any value from context.\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #shortHtmlHintForSqlSyntax>\r\n <div class=\"syntax-hint\">\r\n <strong>Example (SQL Query):</strong>\r\n <pre><code>SELECT EMPLOYEE_CODE FROM HU_EMPLOYEE WHERE ORG_ID = 123</code></pre>\r\n <div class=\"note\">\r\n Your SQL can return either a single value <em>or</em> a list.<br>\r\n The system will extract data from the <strong>first column of all rows</strong>.<br>\r\n Use standard SQL compatible with your database.\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n\r\n\r\n\r\n\r\n</div>", styles: [".setting-row{margin:30px 0}.button-group{margin-top:15px}.button-group button{height:35px;font-size:13px;border-radius:17.5px;margin-right:8px}.setting-divider{margin:1.5rem 0 .75rem;color:#555;border-top:1px dashed #ccc;padding-top:.5rem}.evaluation-mode-row{background-color:#ffecb3;border-radius:8px;width:180px;height:110px;padding:15px}.evaluation-mode-row label{margin-bottom:8px}.script-row{margin:30px 0}.syntax-hint{color:#666;background:#fff;border:1px solid #ccc;border-radius:6px;padding:12px;font-size:13px;max-width:320px;box-shadow:0 2px 6px #0000000d}.syntax-hint pre{margin:8px 0;background-color:#f8f8f8;padding:8px;border-radius:4px;font-family:monospace;white-space:pre-wrap;word-break:break-word}.syntax-hint .note{margin-top:6px;font-size:12px}\n"] }]
|
|
1079
|
+
}], ctorParameters: () => [{ type: MultiLanguageService }] });
|
|
1080
|
+
|
|
1081
|
+
class FieldSettingComponent extends BaseComponent {
|
|
1082
|
+
constructor() {
|
|
1083
|
+
super(...arguments);
|
|
1084
|
+
this.$isNested = input(false);
|
|
1085
|
+
this.coreFormDesignService = inject(CoreFormDesignService);
|
|
1086
|
+
this.enumControlType = EnumFormBaseContolType;
|
|
1087
|
+
this.renderer = inject(Renderer2);
|
|
1088
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
1089
|
+
// 🔹 Reactive normalized control (always derived from selected cell)
|
|
1090
|
+
this.$control = computed(() => {
|
|
1091
|
+
const cell = this.coreFormDesignService.$selectedCell();
|
|
1092
|
+
if (!cell?.control)
|
|
1093
|
+
throw new Error('No control selected!');
|
|
1094
|
+
return this.coreFormDesignService.normalize(cell.control);
|
|
1095
|
+
});
|
|
1096
|
+
// 🔹 Context (flat control list)
|
|
1097
|
+
this.$formContext = computed(() => this.coreFormDesignService.$formContext());
|
|
1098
|
+
this.componentLoaderMap = {
|
|
1099
|
+
'number-props': () => Promise.resolve().then(function () { return numberProps_component; }).then(m => m.NumberPropsComponent),
|
|
1100
|
+
'email-props': () => import('./ngx-histaff-alpha-email-props.component-Cf35Pexq.mjs').then(m => m.EmailPropsComponent),
|
|
1101
|
+
'dropdown-props': () => Promise.resolve().then(function () { return dropdownProps_component; }).then(m => m.DropdownPropsComponent),
|
|
1102
|
+
'form-array-props': () => import('./ngx-histaff-alpha-form-array-props.component-CCccglL5.mjs').then(m => m.FormArrayPropsComponent),
|
|
1103
|
+
// etc...
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
// 🔹 Optional trigger for downstream side-effects (no longer emits)
|
|
1107
|
+
onControlParamsChange() {
|
|
1108
|
+
const updated = this.$control();
|
|
1109
|
+
this.coreFormDesignService.patchControl(updated);
|
|
1110
|
+
}
|
|
1111
|
+
closeFieldSettingPanel() {
|
|
1112
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(false);
|
|
1113
|
+
}
|
|
1114
|
+
getFriendlyControlLabel(control) {
|
|
1115
|
+
if (!control)
|
|
1116
|
+
return 'Control';
|
|
1117
|
+
// 🧠 Detailed mapping: "controlType:type" → Friendly Name
|
|
1118
|
+
const fullTypeMap = {
|
|
1119
|
+
'TEXTBOX:string': 'String Box',
|
|
1120
|
+
'TEXTBOX:number': 'Number Box',
|
|
1121
|
+
'TEXTBOX:email': 'Email Field',
|
|
1122
|
+
'DROPDOWN': 'Dropdown',
|
|
1123
|
+
'CHECKLIST': 'Checklist',
|
|
1124
|
+
'SEEKER': 'Seeker',
|
|
1125
|
+
'DATEPICKER': 'Date Picker',
|
|
1126
|
+
'TEXTAREA': 'Text Area',
|
|
1127
|
+
'CHECKBOX': 'Checkbox'
|
|
1128
|
+
// Add more mappings as needed
|
|
1129
|
+
};
|
|
1130
|
+
const controlType = control.controlType?.toUpperCase() ?? '';
|
|
1131
|
+
const inputType = control.type?.toLowerCase();
|
|
1132
|
+
const fullKey = inputType ? `${controlType}:${inputType}` : controlType;
|
|
1133
|
+
return fullTypeMap[fullKey] ?? this.camelToTitle(control.controlType ?? 'Control');
|
|
1134
|
+
}
|
|
1135
|
+
camelToTitle(input) {
|
|
1136
|
+
return input
|
|
1137
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
1138
|
+
.replace(/^./, str => str.toUpperCase());
|
|
1139
|
+
}
|
|
1140
|
+
ngAfterViewInit() {
|
|
1141
|
+
this.listenerFn = this.renderer.listen('window', 'click', (e) => {
|
|
1142
|
+
this.cdr.markForCheck();
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
ngOnDestroy() {
|
|
1146
|
+
this.subscriptions.forEach(x => x?.unsubscribe());
|
|
1147
|
+
if (this.listenerFn)
|
|
1148
|
+
this.listenerFn();
|
|
1149
|
+
}
|
|
1150
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: FieldSettingComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1151
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: FieldSettingComponent, isStandalone: true, selector: "field-setting", inputs: { $isNested: { classPropertyName: "$isNested", publicName: "$isNested", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "<div class=\"field-setting-container\">\r\n\r\n @if (!!$control()) {\r\n <div class=\"panel-caption-header\">\r\n <div class=\"panel-caption\">\r\n Editing: {{ $control().type !== 'calculated' ? $control()?.controlType + ' - ' : '' }}{{ $control()?.field ||\r\n 'Unnamed' }}\r\n </div>\r\n <div class=\"close-wrapper pointer\" (click)=\"closeFieldSettingPanel()\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </div>\r\n\r\n\r\n <div class=\"field-setting-content\">\r\n\r\n\r\n @if ($control().type === 'calculated') {\r\n\r\n <div class=\"group-title mandatory\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-lock\"></i>\r\n </div>\r\n <div class=\"group-name\">Mandatory</div>\r\n </div>\r\n <mandatory-props></mandatory-props>\r\n\r\n <div class=\"group-title calculated\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-lock\"></i>\r\n </div>\r\n <div class=\"group-name\">Calculated Rule<i class=\"feather-help-circle\" [htmlTooltip]=\"aboutCalculatedField\"\r\n [maxWidthUnset]=\"true\"></i></div>\r\n </div>\r\n <calculated-props></calculated-props>\r\n\r\n\r\n } @else {\r\n\r\n <div class=\"group-title mandatory\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-lock\"></i>\r\n </div>\r\n <div class=\"group-name\">Mandatory</div>\r\n </div>\r\n <mandatory-props></mandatory-props>\r\n\r\n <!-- <div class=\"setting-divider\"></div> -->\r\n\r\n @if ($control().controlType !== 'FORM_ARRAY') {\r\n <div class=\"group-title optional\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-sliders\"></i>\r\n </div>\r\n <div class=\"group-name\">Optional</div>\r\n </div>\r\n <optional-props></optional-props>\r\n\r\n <!-- <div class=\"setting-divider\"></div> -->\r\n\r\n <div class=\"group-title validators\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-check-square\"></i>\r\n </div>\r\n <div class=\"group-name\">Validators</div>\r\n </div>\r\n <validator-editor></validator-editor>\r\n }\r\n\r\n @if (!($control().controlType === 'TEXTBOX' && ['string', 'text', 'email'].includes(($control().type ||\r\n '').toLowerCase()))) {\r\n\r\n <div class=\"group-title\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-check-square\"></i>\r\n </div>\r\n <div class=\"group-name\">{{ getFriendlyControlLabel($control()) }} Settings</div>\r\n </div>\r\n\r\n <props-host [loadComponent]=\"componentLoaderMap[coreFormDesignService.$currentPropsComponent()]\"\r\n [props]=\"{ control: $control(), controlChange: onControlParamsChange.bind(this) }\">\r\n </props-host>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n\r\n\r\n</div>\r\n\r\n<ng-template #aboutCalculatedField>\r\n\r\n <div class=\"calculated-field-rule\" style=\"padding: 1rem; border-radius: 8px; background: #000; width: 800px;\">\r\n <h4 style=\"margin-top: 0; color: #fff;\">Calculated Field Rule</h4>\r\n <p><strong>Calculated fields</strong> are invisible boolean fields evaluated based on form inputs and logic. They\r\n help drive dynamic workflow behavior, such as conditional routing or validation triggers.</p>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Key Properties</h5>\r\n <ul style=\"margin-top: 0;\">\r\n <li><strong>script:</strong> Expression to evaluate (e.g.,\r\n <code>leaveDays > 3 && status == 'Approved'</code>)</li>\r\n <li><strong>evaluationMode:</strong> <code>\"in-memory\"</code> or <code>\"sql-query\"</code></li>\r\n <li><strong>dependsOn[]:</strong> List of field names the script depends on</li>\r\n <li><strong>filterMode:</strong> <code>\"HardBlock\"</code> or <code>\"SoftWarn\"</code></li>\r\n </ul>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Evaluation Modes</h5>\r\n <ul style=\"margin-top: 0;\">\r\n <li>\r\n <strong>in-memory:</strong> Uses <code>DynamicExpresso</code> to evaluate C#-like logic at runtime<br />\r\n <small>Example: <code>leaveType == \"ANNUAL\" && leaveDays < 5</code></small>\r\n </li>\r\n <li>\r\n <strong>sql-query:</strong> Runs backend SQL to evaluate conditions via\r\n <code>SELECT CASE WHEN ... THEN 1 ELSE 0 END</code><br />\r\n <small>Example:\r\n <code>SELECT CASE WHEN QUOTA > 0 THEN 1 ELSE 0 END FROM LEAVE_QUOTA WHERE EMPLOYEE_ID = {{ '@' }}employeeId</code></small>\r\n </li>\r\n </ul>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Use Cases</h5>\r\n <ul style=\"margin-top: 0;\">\r\n <li>Validate complex logic without showing the field</li>\r\n <li>Route to a different workflow branch based on hidden logic</li>\r\n <li>Trigger warning or block submission based on business rules</li>\r\n </ul>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Example</h5>\r\n <pre style=\"color: #eee; padding: 0.5rem 1rem; border-radius: 4px; overflow-x: auto;\">\r\n {{ '{' }}\r\n \"script\": \"leaveDays > 3 && status == 'APPROVED'\",\r\n \"evaluationMode\": \"in-memory\",\r\n \"dependsOn\": [\"leaveDays\", \"status\"],\r\n \"filterMode\": \"HardBlock\"\r\n {{ '}' }}\r\n </pre>\r\n </div>\r\n\r\n\r\n</ng-template>", styles: [".field-setting-container{max-height:100%;overflow-x:hidden;overflow-y:auto}.field-setting-container .field-setting-content{padding:15px}.field-setting-container .panel-caption-header{display:flex;align-items:center;justify-content:space-between;padding:15px;height:60px;background-color:#f8fafc;border-bottom:1px solid #e2e8f0;color:#1f2937}.field-setting-container .panel-caption-header .panel-caption{flex-grow:1}.field-setting-container .panel-caption-header .close-wrapper{display:flex;align-items:center;justify-content:center;background-color:#f1f5f9;width:32px;height:32px;border-radius:50%;cursor:pointer;transition:background-color .2s}.field-setting-container .panel-caption-header .close-wrapper:hover{background-color:#e2e8f0}.field-setting-container .setting-divider{margin:1.5rem 0 .75rem;color:#555;border-top:1px dashed #ccc;padding-top:.5rem}.field-setting-container .group-title{display:flex;height:35px;align-items:center;gap:8px;background-color:#87ceeb;padding:6px 12px;font-weight:600;color:#1f2937;font-size:13px;margin:12px 0 4px;box-shadow:0 1px 2px #0000000d}.field-setting-container .group-title .icon-wrapper{display:flex;align-items:center;justify-content:center;width:18px;height:18px;color:#1f2937}.field-setting-container .group-title .icon-wrapper i{width:16px;height:16px;stroke-width:2}.field-setting-container .group-title.mandatory{background-color:#ffecb3}.field-setting-container .group-title.optional{background-color:#d0f0fd}.field-setting-container .group-title.validators{background-color:#e8f5e9}.field-setting-container .group-title .group-name{flex:1}.field-setting-container .group-title .group-name .feather-help-circle{display:inline-block;margin-left:8px}.field-setting-container .calculated-field-rule{font-family:system-ui,sans-serif;border:1px solid #ccc;padding:1rem;border-radius:8px}.field-setting-container .calculated-field-rule h2{margin-top:0;color:#2b3e50}.field-setting-container .calculated-field-rule pre{background:#eee;padding:.5rem 1rem;border-radius:4px;overflow-x:auto}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: MandatoryPropsComponent, selector: "mandatory-props" }, { kind: "component", type: OptionalPropsComponent, selector: "optional-props" }, { kind: "component", type: ValidatorEditorComponent, selector: "validator-editor" }, { kind: "component", type: PropsHostComponent, selector: "props-host", inputs: ["loadComponent", "props"] }, { kind: "component", type: CalculatedPropsComponent, selector: "calculated-props" }, { kind: "directive", type: HtmlTooltipDirective, selector: "[htmlTooltip]", inputs: ["htmlTooltip", "tooltipContext", "maxWidthUnset", "backgroundColor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1152
|
+
}
|
|
1153
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: FieldSettingComponent, decorators: [{
|
|
1154
|
+
type: Component,
|
|
1155
|
+
args: [{ selector: 'field-setting', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1156
|
+
FormsModule,
|
|
1157
|
+
NgStyle,
|
|
1158
|
+
MandatoryPropsComponent,
|
|
1159
|
+
OptionalPropsComponent,
|
|
1160
|
+
ValidatorEditorComponent,
|
|
1161
|
+
PropsHostComponent,
|
|
1162
|
+
AttachmentPropsComponent,
|
|
1163
|
+
ChecklistPropsComponent,
|
|
1164
|
+
DatepickerPropsComponent,
|
|
1165
|
+
DropdownPropsComponent,
|
|
1166
|
+
GridBufferPropsComponent,
|
|
1167
|
+
ListPropsComponent,
|
|
1168
|
+
MccPropsComponent,
|
|
1169
|
+
NumberPropsComponent,
|
|
1170
|
+
RadioGroupPropsComponent,
|
|
1171
|
+
SeekerPropsComponent,
|
|
1172
|
+
TextareaPropsComponent,
|
|
1173
|
+
UploaderPropsComponent,
|
|
1174
|
+
CalculatedPropsComponent,
|
|
1175
|
+
HtmlTooltipDirective
|
|
1176
|
+
], template: "<div class=\"field-setting-container\">\r\n\r\n @if (!!$control()) {\r\n <div class=\"panel-caption-header\">\r\n <div class=\"panel-caption\">\r\n Editing: {{ $control().type !== 'calculated' ? $control()?.controlType + ' - ' : '' }}{{ $control()?.field ||\r\n 'Unnamed' }}\r\n </div>\r\n <div class=\"close-wrapper pointer\" (click)=\"closeFieldSettingPanel()\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </div>\r\n\r\n\r\n <div class=\"field-setting-content\">\r\n\r\n\r\n @if ($control().type === 'calculated') {\r\n\r\n <div class=\"group-title mandatory\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-lock\"></i>\r\n </div>\r\n <div class=\"group-name\">Mandatory</div>\r\n </div>\r\n <mandatory-props></mandatory-props>\r\n\r\n <div class=\"group-title calculated\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-lock\"></i>\r\n </div>\r\n <div class=\"group-name\">Calculated Rule<i class=\"feather-help-circle\" [htmlTooltip]=\"aboutCalculatedField\"\r\n [maxWidthUnset]=\"true\"></i></div>\r\n </div>\r\n <calculated-props></calculated-props>\r\n\r\n\r\n } @else {\r\n\r\n <div class=\"group-title mandatory\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-lock\"></i>\r\n </div>\r\n <div class=\"group-name\">Mandatory</div>\r\n </div>\r\n <mandatory-props></mandatory-props>\r\n\r\n <!-- <div class=\"setting-divider\"></div> -->\r\n\r\n @if ($control().controlType !== 'FORM_ARRAY') {\r\n <div class=\"group-title optional\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-sliders\"></i>\r\n </div>\r\n <div class=\"group-name\">Optional</div>\r\n </div>\r\n <optional-props></optional-props>\r\n\r\n <!-- <div class=\"setting-divider\"></div> -->\r\n\r\n <div class=\"group-title validators\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-check-square\"></i>\r\n </div>\r\n <div class=\"group-name\">Validators</div>\r\n </div>\r\n <validator-editor></validator-editor>\r\n }\r\n\r\n @if (!($control().controlType === 'TEXTBOX' && ['string', 'text', 'email'].includes(($control().type ||\r\n '').toLowerCase()))) {\r\n\r\n <div class=\"group-title\">\r\n <div class=\"icon-wrapper\">\r\n <i class=\"feather-check-square\"></i>\r\n </div>\r\n <div class=\"group-name\">{{ getFriendlyControlLabel($control()) }} Settings</div>\r\n </div>\r\n\r\n <props-host [loadComponent]=\"componentLoaderMap[coreFormDesignService.$currentPropsComponent()]\"\r\n [props]=\"{ control: $control(), controlChange: onControlParamsChange.bind(this) }\">\r\n </props-host>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n\r\n\r\n</div>\r\n\r\n<ng-template #aboutCalculatedField>\r\n\r\n <div class=\"calculated-field-rule\" style=\"padding: 1rem; border-radius: 8px; background: #000; width: 800px;\">\r\n <h4 style=\"margin-top: 0; color: #fff;\">Calculated Field Rule</h4>\r\n <p><strong>Calculated fields</strong> are invisible boolean fields evaluated based on form inputs and logic. They\r\n help drive dynamic workflow behavior, such as conditional routing or validation triggers.</p>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Key Properties</h5>\r\n <ul style=\"margin-top: 0;\">\r\n <li><strong>script:</strong> Expression to evaluate (e.g.,\r\n <code>leaveDays > 3 && status == 'Approved'</code>)</li>\r\n <li><strong>evaluationMode:</strong> <code>\"in-memory\"</code> or <code>\"sql-query\"</code></li>\r\n <li><strong>dependsOn[]:</strong> List of field names the script depends on</li>\r\n <li><strong>filterMode:</strong> <code>\"HardBlock\"</code> or <code>\"SoftWarn\"</code></li>\r\n </ul>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Evaluation Modes</h5>\r\n <ul style=\"margin-top: 0;\">\r\n <li>\r\n <strong>in-memory:</strong> Uses <code>DynamicExpresso</code> to evaluate C#-like logic at runtime<br />\r\n <small>Example: <code>leaveType == \"ANNUAL\" && leaveDays < 5</code></small>\r\n </li>\r\n <li>\r\n <strong>sql-query:</strong> Runs backend SQL to evaluate conditions via\r\n <code>SELECT CASE WHEN ... THEN 1 ELSE 0 END</code><br />\r\n <small>Example:\r\n <code>SELECT CASE WHEN QUOTA > 0 THEN 1 ELSE 0 END FROM LEAVE_QUOTA WHERE EMPLOYEE_ID = {{ '@' }}employeeId</code></small>\r\n </li>\r\n </ul>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Use Cases</h5>\r\n <ul style=\"margin-top: 0;\">\r\n <li>Validate complex logic without showing the field</li>\r\n <li>Route to a different workflow branch based on hidden logic</li>\r\n <li>Trigger warning or block submission based on business rules</li>\r\n </ul>\r\n\r\n <h5 style=\"margin-bottom: 0.5rem; color: #fff;\">Example</h5>\r\n <pre style=\"color: #eee; padding: 0.5rem 1rem; border-radius: 4px; overflow-x: auto;\">\r\n {{ '{' }}\r\n \"script\": \"leaveDays > 3 && status == 'APPROVED'\",\r\n \"evaluationMode\": \"in-memory\",\r\n \"dependsOn\": [\"leaveDays\", \"status\"],\r\n \"filterMode\": \"HardBlock\"\r\n {{ '}' }}\r\n </pre>\r\n </div>\r\n\r\n\r\n</ng-template>", styles: [".field-setting-container{max-height:100%;overflow-x:hidden;overflow-y:auto}.field-setting-container .field-setting-content{padding:15px}.field-setting-container .panel-caption-header{display:flex;align-items:center;justify-content:space-between;padding:15px;height:60px;background-color:#f8fafc;border-bottom:1px solid #e2e8f0;color:#1f2937}.field-setting-container .panel-caption-header .panel-caption{flex-grow:1}.field-setting-container .panel-caption-header .close-wrapper{display:flex;align-items:center;justify-content:center;background-color:#f1f5f9;width:32px;height:32px;border-radius:50%;cursor:pointer;transition:background-color .2s}.field-setting-container .panel-caption-header .close-wrapper:hover{background-color:#e2e8f0}.field-setting-container .setting-divider{margin:1.5rem 0 .75rem;color:#555;border-top:1px dashed #ccc;padding-top:.5rem}.field-setting-container .group-title{display:flex;height:35px;align-items:center;gap:8px;background-color:#87ceeb;padding:6px 12px;font-weight:600;color:#1f2937;font-size:13px;margin:12px 0 4px;box-shadow:0 1px 2px #0000000d}.field-setting-container .group-title .icon-wrapper{display:flex;align-items:center;justify-content:center;width:18px;height:18px;color:#1f2937}.field-setting-container .group-title .icon-wrapper i{width:16px;height:16px;stroke-width:2}.field-setting-container .group-title.mandatory{background-color:#ffecb3}.field-setting-container .group-title.optional{background-color:#d0f0fd}.field-setting-container .group-title.validators{background-color:#e8f5e9}.field-setting-container .group-title .group-name{flex:1}.field-setting-container .group-title .group-name .feather-help-circle{display:inline-block;margin-left:8px}.field-setting-container .calculated-field-rule{font-family:system-ui,sans-serif;border:1px solid #ccc;padding:1rem;border-radius:8px}.field-setting-container .calculated-field-rule h2{margin-top:0;color:#2b3e50}.field-setting-container .calculated-field-rule pre{background:#eee;padding:.5rem 1rem;border-radius:4px;overflow-x:auto}\n"] }]
|
|
1177
|
+
}] });
|
|
1178
|
+
|
|
1179
|
+
var EnumLeftPanelMode;
|
|
1180
|
+
(function (EnumLeftPanelMode) {
|
|
1181
|
+
EnumLeftPanelMode["FieldCollection"] = "FieldCollection";
|
|
1182
|
+
EnumLeftPanelMode["Json"] = "Json";
|
|
1183
|
+
})(EnumLeftPanelMode || (EnumLeftPanelMode = {}));
|
|
1184
|
+
|
|
1185
|
+
class CoreFormArrayControlPaletteComponent {
|
|
1186
|
+
constructor() {
|
|
1187
|
+
this.coreFormDesignService = inject(CoreFormDesignService);
|
|
1188
|
+
this.controlOptions = [
|
|
1189
|
+
{
|
|
1190
|
+
flexSize: 12,
|
|
1191
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1192
|
+
type: 'string',
|
|
1193
|
+
field: 'stringBox',
|
|
1194
|
+
label: '',
|
|
1195
|
+
value: '',
|
|
1196
|
+
//readonly: true,
|
|
1197
|
+
placeholder: 'String box',
|
|
1198
|
+
headless: true,
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
flexSize: 12,
|
|
1202
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1203
|
+
type: 'email',
|
|
1204
|
+
field: 'email',
|
|
1205
|
+
label: '',
|
|
1206
|
+
value: '',
|
|
1207
|
+
//readonly: true,
|
|
1208
|
+
placeholder: 'Email',
|
|
1209
|
+
headless: true,
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
flexSize: 12,
|
|
1213
|
+
controlType: EnumFormBaseContolType.TEXTAREA,
|
|
1214
|
+
type: 'string',
|
|
1215
|
+
field: 'textarea',
|
|
1216
|
+
label: '',
|
|
1217
|
+
value: '',
|
|
1218
|
+
//readonly: true,
|
|
1219
|
+
textareaRows: 2,
|
|
1220
|
+
placeholder: 'Text area',
|
|
1221
|
+
headless: true,
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
flexSize: 12,
|
|
1225
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1226
|
+
type: 'number',
|
|
1227
|
+
field: 'number',
|
|
1228
|
+
label: '',
|
|
1229
|
+
value: 100,
|
|
1230
|
+
//readonly: true,
|
|
1231
|
+
placeholder: 'Number',
|
|
1232
|
+
headless: true,
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
flexSize: 12,
|
|
1236
|
+
controlType: EnumFormBaseContolType.CURRENCY,
|
|
1237
|
+
type: 'number',
|
|
1238
|
+
field: 'currency',
|
|
1239
|
+
label: '',
|
|
1240
|
+
value: '',
|
|
1241
|
+
//readonly: true,
|
|
1242
|
+
placeholder: 'Currency',
|
|
1243
|
+
headless: true,
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
flexSize: 12,
|
|
1247
|
+
controlType: EnumFormBaseContolType.DROPDOWN,
|
|
1248
|
+
type: 'any',
|
|
1249
|
+
field: 'dropdown',
|
|
1250
|
+
label: '',
|
|
1251
|
+
value: '',
|
|
1252
|
+
//readonly: true,
|
|
1253
|
+
dropdownOptions$: new BehaviorSubject([]),
|
|
1254
|
+
shownFrom: 'name',
|
|
1255
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1256
|
+
placeholder: 'Dropdown',
|
|
1257
|
+
headless: true,
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
flexSize: 12,
|
|
1261
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
1262
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
1263
|
+
type: 'any[]',
|
|
1264
|
+
field: 'employeeSeeker',
|
|
1265
|
+
label: '',
|
|
1266
|
+
value: ['Nguyễn A'],
|
|
1267
|
+
//readonly: true,
|
|
1268
|
+
boundFrom: 'id',
|
|
1269
|
+
shownFrom: 'fullname',
|
|
1270
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1271
|
+
placeholder: 'Employee seeker',
|
|
1272
|
+
headless: true,
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
flexSize: 12,
|
|
1276
|
+
controlType: EnumFormBaseContolType.CHECKLIST,
|
|
1277
|
+
type: 'any[]',
|
|
1278
|
+
field: 'checklist',
|
|
1279
|
+
label: '',
|
|
1280
|
+
value: [],
|
|
1281
|
+
//readonly: true,
|
|
1282
|
+
checklistOptions$: new BehaviorSubject([]),
|
|
1283
|
+
shownFrom: 'name',
|
|
1284
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1285
|
+
placeholder: 'Check list',
|
|
1286
|
+
headless: true,
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
flexSize: 12,
|
|
1290
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
1291
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
1292
|
+
multiMode: true,
|
|
1293
|
+
type: 'any[]',
|
|
1294
|
+
field: 'employeeSeekerMultiple',
|
|
1295
|
+
label: '',
|
|
1296
|
+
value: [],
|
|
1297
|
+
//readonly: true,
|
|
1298
|
+
objectList$: new BehaviorSubject([]),
|
|
1299
|
+
boundFrom: 'id',
|
|
1300
|
+
shownFrom: 'fullname',
|
|
1301
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1302
|
+
multiModeTableHeight: 100,
|
|
1303
|
+
placeholder: 'Employee seeker (multi mode)',
|
|
1304
|
+
headless: true,
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
flexSize: 12,
|
|
1308
|
+
controlType: EnumFormBaseContolType.DATEPICKER,
|
|
1309
|
+
type: 'date',
|
|
1310
|
+
field: 'datePicker',
|
|
1311
|
+
label: '',
|
|
1312
|
+
value: '',
|
|
1313
|
+
//readonly: true,
|
|
1314
|
+
placeholder: 'Date picker',
|
|
1315
|
+
showPlaceholder: true,
|
|
1316
|
+
headless: true,
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
flexSize: 12,
|
|
1320
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1321
|
+
type: 'time',
|
|
1322
|
+
field: 'timeBox',
|
|
1323
|
+
label: '',
|
|
1324
|
+
value: null,
|
|
1325
|
+
//readonly: true,
|
|
1326
|
+
placeholder: 'Time box',
|
|
1327
|
+
headless: true,
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
flexSize: 12,
|
|
1331
|
+
controlType: EnumFormBaseContolType.MONTHSELECTOR,
|
|
1332
|
+
type: 'string',
|
|
1333
|
+
field: 'monthSelector',
|
|
1334
|
+
label: '',
|
|
1335
|
+
value: '',
|
|
1336
|
+
//readonly: true,
|
|
1337
|
+
placeholder: 'Month selector',
|
|
1338
|
+
headless: true,
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
flexSize: 12,
|
|
1342
|
+
controlType: EnumFormBaseContolType.ATTACHMENT,
|
|
1343
|
+
type: 'string',
|
|
1344
|
+
field: 'attachment',
|
|
1345
|
+
label: '',
|
|
1346
|
+
value: '',
|
|
1347
|
+
//readonly: true,
|
|
1348
|
+
assignTo: 'unknown',
|
|
1349
|
+
placeholder: 'Attachment',
|
|
1350
|
+
headless: true,
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
flexSize: 12,
|
|
1354
|
+
controlType: EnumFormBaseContolType.CHECKBOX,
|
|
1355
|
+
type: 'boolean',
|
|
1356
|
+
field: 'checkbox',
|
|
1357
|
+
label: '',
|
|
1358
|
+
value: '',
|
|
1359
|
+
//readonly: true,
|
|
1360
|
+
placeholder: 'Checkbox',
|
|
1361
|
+
headless: true,
|
|
1362
|
+
}
|
|
1363
|
+
];
|
|
1364
|
+
this.form = new FormGroup({
|
|
1365
|
+
stringBox: new FormControl(),
|
|
1366
|
+
timeBox: new FormControl(),
|
|
1367
|
+
email: new FormControl(),
|
|
1368
|
+
textarea: new FormControl(),
|
|
1369
|
+
number: new FormControl(),
|
|
1370
|
+
currency: new FormControl(),
|
|
1371
|
+
dropdown: new FormControl(),
|
|
1372
|
+
employeeSeeker: new FormControl(),
|
|
1373
|
+
checklist: new FormControl(),
|
|
1374
|
+
employeeSeekerMultiple: new FormControl(),
|
|
1375
|
+
datePicker: new FormControl(),
|
|
1376
|
+
monthSelector: new FormControl(),
|
|
1377
|
+
attachment: new FormControl(),
|
|
1378
|
+
attachmentBuffer: new FormControl(),
|
|
1379
|
+
checkbox: new FormControl(),
|
|
1380
|
+
});
|
|
1381
|
+
this.checkError$ = new BehaviorSubject(false);
|
|
1382
|
+
}
|
|
1383
|
+
onDragStart(event, control) {
|
|
1384
|
+
this.coreFormDesignService.beginDragTemplate(control);
|
|
1385
|
+
}
|
|
1386
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormArrayControlPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1387
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: CoreFormArrayControlPaletteComponent, isStandalone: true, selector: "core-form-array-control-palette", ngImport: i0, template: "<ul>\r\n <!-- <div class=\"palette-debug\">\r\n <div>controlOptions length: {{ controlOptions.length }}</div>\r\n <pre>{{ controlOptions | json }}</pre>\r\n </div> -->\r\n @for (control of controlOptions; track $index) {\r\n <li class=\"palette-item\" draggable=\"true\" (dragstart)=\"onDragStart($event, control)\">\r\n <core-control-no-form-array [control]=\"control\" [form]=\"form\" [checkError$]=\"checkError$\" />\r\n </li>\r\n }\r\n</ul>", styles: [".palette-debug{font-size:12px;background:#f9f9f9;border:1px solid #ddd;padding:8px;border-radius:4px;color:#333;margin-bottom:8px;white-space:pre-wrap}ul{padding:0}ul li{margin-top:8px;margin-bottom:8px;margin-right:15px}\n"], dependencies: [{ kind: "component", type: CoreControlNoFormArrayComponent, selector: "core-control-no-form-array", inputs: ["control", "form", "checkError$", "rangeLimit"] }] }); }
|
|
1388
|
+
}
|
|
1389
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormArrayControlPaletteComponent, decorators: [{
|
|
1390
|
+
type: Component,
|
|
1391
|
+
args: [{ selector: 'core-form-array-control-palette', imports: [
|
|
1392
|
+
JsonPipe,
|
|
1393
|
+
CoreControlNoFormArrayComponent
|
|
1394
|
+
], template: "<ul>\r\n <!-- <div class=\"palette-debug\">\r\n <div>controlOptions length: {{ controlOptions.length }}</div>\r\n <pre>{{ controlOptions | json }}</pre>\r\n </div> -->\r\n @for (control of controlOptions; track $index) {\r\n <li class=\"palette-item\" draggable=\"true\" (dragstart)=\"onDragStart($event, control)\">\r\n <core-control-no-form-array [control]=\"control\" [form]=\"form\" [checkError$]=\"checkError$\" />\r\n </li>\r\n }\r\n</ul>", styles: [".palette-debug{font-size:12px;background:#f9f9f9;border:1px solid #ddd;padding:8px;border-radius:4px;color:#333;margin-bottom:8px;white-space:pre-wrap}ul{padding:0}ul li{margin-top:8px;margin-bottom:8px;margin-right:15px}\n"] }]
|
|
1395
|
+
}] });
|
|
1396
|
+
|
|
1397
|
+
class CoreFormGroupEditorComponent extends BaseComponent {
|
|
1398
|
+
constructor(mls) {
|
|
1399
|
+
super(mls);
|
|
1400
|
+
this.mls = mls;
|
|
1401
|
+
this.$isNested = input(false);
|
|
1402
|
+
this.$idAsInput = input();
|
|
1403
|
+
this.$id = input();
|
|
1404
|
+
this.$settingPanel = viewChild('settingPanel');
|
|
1405
|
+
this.$jsonMode = signal('selectedField');
|
|
1406
|
+
this.injectedFields$ = new BehaviorSubject([]);
|
|
1407
|
+
this.enumType = EnumFormDesignMode;
|
|
1408
|
+
this.enumLeftPanelMode = EnumLeftPanelMode;
|
|
1409
|
+
this.$leftPanelMode = computed(() => {
|
|
1410
|
+
if (this.coreFormDesignService.$fieldSettingPanelOpen()) {
|
|
1411
|
+
return this.enumLeftPanelMode.Json;
|
|
1412
|
+
}
|
|
1413
|
+
else {
|
|
1414
|
+
return this.enumLeftPanelMode.FieldCollection;
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
this.ds = inject(DomService);
|
|
1418
|
+
this.js = inject(JsonService);
|
|
1419
|
+
this.$zIndex = computed(() => {
|
|
1420
|
+
if (this.coreFormDesignService.$fieldSettingPanelOpen()) {
|
|
1421
|
+
return this.ds.getMaxZIndex() + 1;
|
|
1422
|
+
}
|
|
1423
|
+
else {
|
|
1424
|
+
return 0;
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
this.previewMode$ = new BehaviorSubject(EnumCorePageEditMode.CREATE);
|
|
1428
|
+
this.$loading = signal(false);
|
|
1429
|
+
this.coreFormDesignService = inject(CoreFormDesignService);
|
|
1430
|
+
this.renderer = inject(Renderer2);
|
|
1431
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
1432
|
+
this.router = inject(Router);
|
|
1433
|
+
this.route = inject(ActivatedRoute);
|
|
1434
|
+
this.alertService = inject(AlertService);
|
|
1435
|
+
this.applicationHelpService = inject(ApplicationHelpService);
|
|
1436
|
+
this.wfs = inject(CoreWorkflowService);
|
|
1437
|
+
this.dbs = inject(DbService);
|
|
1438
|
+
this.appConfigService = inject(AppConfigService);
|
|
1439
|
+
this.$hasCalculatedSection = computed(() => {
|
|
1440
|
+
return this.coreFormDesignService.$placeholderSections().some(s => !!s.forCalculatedFields);
|
|
1441
|
+
});
|
|
1442
|
+
this.formDesign = this.coreFormDesignService.formDesign;
|
|
1443
|
+
this.setControlProp = this.coreFormDesignService.setControlProp.bind(this.coreFormDesignService);
|
|
1444
|
+
this.isDragOverMap = {};
|
|
1445
|
+
this.idStream$ = new BehaviorSubject(undefined);
|
|
1446
|
+
this.getByIdStream$ = this.idStream$.pipe(filter(id => !!id && id !== '0'), distinctUntilChanged(), tap(() => {
|
|
1447
|
+
this.$loading.set(true);
|
|
1448
|
+
this.coreFormDesignService.editMode$.next(EnumCorePageEditMode.UPDATE);
|
|
1449
|
+
this.coreFormDesignService.$submitText.set(EnumTranslateKey.UI_EDIT_FORM_BUTTON_SAVE);
|
|
1450
|
+
}), switchMap(id => {
|
|
1451
|
+
return this.coreFormDesignService.getById(id)
|
|
1452
|
+
.pipe(catchError(err => {
|
|
1453
|
+
this.$loading.set(false);
|
|
1454
|
+
return of(err);
|
|
1455
|
+
}));
|
|
1456
|
+
}));
|
|
1457
|
+
this.form = new FormGroup({
|
|
1458
|
+
stringBox: new FormControl(),
|
|
1459
|
+
timeBox: new FormControl(),
|
|
1460
|
+
email: new FormControl(),
|
|
1461
|
+
textarea: new FormControl(),
|
|
1462
|
+
number: new FormControl(),
|
|
1463
|
+
currency: new FormControl(),
|
|
1464
|
+
dropdown: new FormControl(),
|
|
1465
|
+
employeeSeeker: new FormControl(),
|
|
1466
|
+
checklist: new FormControl(),
|
|
1467
|
+
employeeSeekerMultiple: new FormControl(),
|
|
1468
|
+
datePicker: new FormControl(),
|
|
1469
|
+
monthSelector: new FormControl(),
|
|
1470
|
+
attachment: new FormControl(),
|
|
1471
|
+
attachmentBuffer: new FormControl(),
|
|
1472
|
+
checkbox: new FormControl(),
|
|
1473
|
+
calculated: new FormControl(),
|
|
1474
|
+
formArray: new FormArray([
|
|
1475
|
+
new FormControl('')
|
|
1476
|
+
])
|
|
1477
|
+
});
|
|
1478
|
+
this.$sections = computed(() => {
|
|
1479
|
+
const hydrated = this.coreFormDesignService.rehydrateSections(this.coreFormDesignService.$placeholderSections());
|
|
1480
|
+
let sections = this.coreFormDesignService.toCoreFormSections(hydrated);
|
|
1481
|
+
// ✅ Inject hidden "id" field into the first cell row of the first section
|
|
1482
|
+
sections = this.coreFormDesignService.injectSystemControls(sections, this.coreFormDesignService.$afInstance().hasIdOfStringType ?? false);
|
|
1483
|
+
return sections;
|
|
1484
|
+
});
|
|
1485
|
+
this.checkError$ = new BehaviorSubject(false);
|
|
1486
|
+
this.controlCategories = [
|
|
1487
|
+
{
|
|
1488
|
+
name: 'Calculated fields',
|
|
1489
|
+
controls: [
|
|
1490
|
+
{
|
|
1491
|
+
flexSize: 12,
|
|
1492
|
+
controlType: EnumFormBaseContolType.CALCULATED,
|
|
1493
|
+
type: 'calculated',
|
|
1494
|
+
field: 'calculated',
|
|
1495
|
+
label: '',
|
|
1496
|
+
value: '',
|
|
1497
|
+
placeholder: 'Calculated',
|
|
1498
|
+
headless: true,
|
|
1499
|
+
}
|
|
1500
|
+
]
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
name: 'Text',
|
|
1504
|
+
controls: [
|
|
1505
|
+
{
|
|
1506
|
+
flexSize: 12,
|
|
1507
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1508
|
+
type: 'string',
|
|
1509
|
+
field: 'stringBox',
|
|
1510
|
+
label: '',
|
|
1511
|
+
value: '',
|
|
1512
|
+
placeholder: 'String box',
|
|
1513
|
+
headless: true,
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
flexSize: 12,
|
|
1517
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1518
|
+
type: 'email',
|
|
1519
|
+
field: 'email',
|
|
1520
|
+
label: '',
|
|
1521
|
+
value: '',
|
|
1522
|
+
placeholder: 'Email',
|
|
1523
|
+
headless: true,
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
flexSize: 12,
|
|
1527
|
+
controlType: EnumFormBaseContolType.TEXTAREA,
|
|
1528
|
+
type: 'string',
|
|
1529
|
+
field: 'textarea',
|
|
1530
|
+
label: '',
|
|
1531
|
+
value: '',
|
|
1532
|
+
textareaRows: 2,
|
|
1533
|
+
placeholder: 'Text area',
|
|
1534
|
+
headless: true,
|
|
1535
|
+
}
|
|
1536
|
+
]
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
name: 'Numerical',
|
|
1540
|
+
controls: [
|
|
1541
|
+
{
|
|
1542
|
+
flexSize: 12,
|
|
1543
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1544
|
+
type: 'number',
|
|
1545
|
+
field: 'number',
|
|
1546
|
+
label: '',
|
|
1547
|
+
value: 100,
|
|
1548
|
+
placeholder: 'Number',
|
|
1549
|
+
headless: true,
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
flexSize: 12,
|
|
1553
|
+
controlType: EnumFormBaseContolType.CURRENCY,
|
|
1554
|
+
type: 'number',
|
|
1555
|
+
field: 'currency',
|
|
1556
|
+
label: '',
|
|
1557
|
+
value: '',
|
|
1558
|
+
placeholder: 'Currency',
|
|
1559
|
+
headless: true,
|
|
1560
|
+
},
|
|
1561
|
+
]
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
name: 'Single selection',
|
|
1565
|
+
controls: [
|
|
1566
|
+
{
|
|
1567
|
+
flexSize: 12,
|
|
1568
|
+
controlType: EnumFormBaseContolType.DROPDOWN,
|
|
1569
|
+
type: 'any',
|
|
1570
|
+
field: 'dropdown',
|
|
1571
|
+
label: '',
|
|
1572
|
+
value: '',
|
|
1573
|
+
dropdownOptions$: new BehaviorSubject([]),
|
|
1574
|
+
shownFrom: 'name',
|
|
1575
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1576
|
+
placeholder: 'Dropdown',
|
|
1577
|
+
headless: true,
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
flexSize: 12,
|
|
1581
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
1582
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
1583
|
+
type: 'any[]',
|
|
1584
|
+
field: 'employeeSeeker',
|
|
1585
|
+
label: '',
|
|
1586
|
+
value: ['Nguyễn A'],
|
|
1587
|
+
boundFrom: 'id',
|
|
1588
|
+
shownFrom: 'fullname',
|
|
1589
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1590
|
+
placeholder: 'Employee seeker',
|
|
1591
|
+
headless: true,
|
|
1592
|
+
}
|
|
1593
|
+
]
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
name: 'Multiple Selection',
|
|
1597
|
+
controls: [
|
|
1598
|
+
// {
|
|
1599
|
+
// flexSize: 12,
|
|
1600
|
+
// controlType: EnumFormBaseContolType.GRIDBUFFER,
|
|
1601
|
+
// type: 'children',
|
|
1602
|
+
// field: 'gridbuffer',
|
|
1603
|
+
// label: '',
|
|
1604
|
+
// value: [],
|
|
1605
|
+
// placeholder: 'Grid buffer',
|
|
1606
|
+
// gridBufferFormSections: [
|
|
1607
|
+
// {
|
|
1608
|
+
// rows: [
|
|
1609
|
+
// [
|
|
1610
|
+
// {
|
|
1611
|
+
// flexSize: 6,
|
|
1612
|
+
// field: 'field1',
|
|
1613
|
+
// type: 'date',
|
|
1614
|
+
// value: '',
|
|
1615
|
+
// controlType: EnumFormBaseContolType.DATEPICKER,
|
|
1616
|
+
// label: 'field 1'
|
|
1617
|
+
// },
|
|
1618
|
+
// {
|
|
1619
|
+
// flexSize: 6,
|
|
1620
|
+
// field: 'field2',
|
|
1621
|
+
// type: 'number',
|
|
1622
|
+
// value: null,
|
|
1623
|
+
// controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1624
|
+
// label: 'field 2'
|
|
1625
|
+
// },
|
|
1626
|
+
// ]
|
|
1627
|
+
// ]
|
|
1628
|
+
// }
|
|
1629
|
+
// ],
|
|
1630
|
+
// gridBufferTableColumns: [
|
|
1631
|
+
// {
|
|
1632
|
+
// caption: 'id',
|
|
1633
|
+
// field: 'id',
|
|
1634
|
+
// hidden: true,
|
|
1635
|
+
// type: 'string',
|
|
1636
|
+
// align: 'left',
|
|
1637
|
+
// width: 0,
|
|
1638
|
+
// },
|
|
1639
|
+
// {
|
|
1640
|
+
// field: 'field1',
|
|
1641
|
+
// width: 200,
|
|
1642
|
+
// caption: 'field 1',
|
|
1643
|
+
// type: 'date',
|
|
1644
|
+
// pipe: EnumCoreTablePipeType.DATE,
|
|
1645
|
+
// align: 'center'
|
|
1646
|
+
// },
|
|
1647
|
+
// {
|
|
1648
|
+
// field: 'field2',
|
|
1649
|
+
// width: 100,
|
|
1650
|
+
// caption: 'field 2',
|
|
1651
|
+
// type: 'number',
|
|
1652
|
+
// align: 'right'
|
|
1653
|
+
// },
|
|
1654
|
+
// ],
|
|
1655
|
+
// headless: true,
|
|
1656
|
+
// tableHeight: 100,
|
|
1657
|
+
// liteMode: true,
|
|
1658
|
+
// },
|
|
1659
|
+
{
|
|
1660
|
+
flexSize: 12,
|
|
1661
|
+
controlType: EnumFormBaseContolType.CHECKLIST,
|
|
1662
|
+
type: 'any[]',
|
|
1663
|
+
field: 'checklist',
|
|
1664
|
+
label: '',
|
|
1665
|
+
value: [],
|
|
1666
|
+
checklistOptions$: new BehaviorSubject([]),
|
|
1667
|
+
shownFrom: 'name',
|
|
1668
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1669
|
+
placeholder: 'Check list',
|
|
1670
|
+
headless: true,
|
|
1671
|
+
},
|
|
1672
|
+
{
|
|
1673
|
+
flexSize: 12,
|
|
1674
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
1675
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
1676
|
+
multiMode: true,
|
|
1677
|
+
type: 'any[]',
|
|
1678
|
+
field: 'employeeSeekerMultiple',
|
|
1679
|
+
label: '',
|
|
1680
|
+
value: [],
|
|
1681
|
+
objectList$: new BehaviorSubject([]),
|
|
1682
|
+
boundFrom: 'id',
|
|
1683
|
+
shownFrom: 'fullname',
|
|
1684
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1685
|
+
multiModeTableHeight: 100,
|
|
1686
|
+
placeholder: 'Employee seeker (multi mode)',
|
|
1687
|
+
headless: true,
|
|
1688
|
+
}
|
|
1689
|
+
]
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
name: 'Date',
|
|
1693
|
+
controls: [
|
|
1694
|
+
{
|
|
1695
|
+
flexSize: 12,
|
|
1696
|
+
controlType: EnumFormBaseContolType.DATEPICKER,
|
|
1697
|
+
type: 'date',
|
|
1698
|
+
field: 'datePicker',
|
|
1699
|
+
label: '',
|
|
1700
|
+
value: '',
|
|
1701
|
+
placeholder: 'Date picker',
|
|
1702
|
+
showPlaceholder: true,
|
|
1703
|
+
headless: true,
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
flexSize: 12,
|
|
1707
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1708
|
+
type: 'time',
|
|
1709
|
+
field: 'timeBox',
|
|
1710
|
+
label: '',
|
|
1711
|
+
value: null,
|
|
1712
|
+
placeholder: 'Time box',
|
|
1713
|
+
headless: true,
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
flexSize: 12,
|
|
1717
|
+
controlType: EnumFormBaseContolType.MONTHSELECTOR,
|
|
1718
|
+
type: 'string',
|
|
1719
|
+
field: 'monthSelector',
|
|
1720
|
+
label: '',
|
|
1721
|
+
value: '',
|
|
1722
|
+
placeholder: 'Month selector',
|
|
1723
|
+
headless: true,
|
|
1724
|
+
},
|
|
1725
|
+
]
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
name: 'Other',
|
|
1729
|
+
controls: [
|
|
1730
|
+
{
|
|
1731
|
+
flexSize: 12,
|
|
1732
|
+
controlType: EnumFormBaseContolType.ATTACHMENT,
|
|
1733
|
+
type: 'string',
|
|
1734
|
+
field: 'attachment',
|
|
1735
|
+
label: '',
|
|
1736
|
+
value: '',
|
|
1737
|
+
assignTo: 'unknown',
|
|
1738
|
+
placeholder: 'Attachment',
|
|
1739
|
+
headless: true,
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
flexSize: 12,
|
|
1743
|
+
controlType: EnumFormBaseContolType.CHECKBOX,
|
|
1744
|
+
type: 'boolean',
|
|
1745
|
+
field: 'checkbox',
|
|
1746
|
+
label: '',
|
|
1747
|
+
value: '',
|
|
1748
|
+
placeholder: 'Checkbox',
|
|
1749
|
+
headless: true,
|
|
1750
|
+
},
|
|
1751
|
+
]
|
|
1752
|
+
},
|
|
1753
|
+
{
|
|
1754
|
+
name: 'Form Array',
|
|
1755
|
+
controls: [
|
|
1756
|
+
{
|
|
1757
|
+
flexSize: 12,
|
|
1758
|
+
controlType: EnumFormBaseContolType.FORM_ARRAY,
|
|
1759
|
+
type: 'children',
|
|
1760
|
+
field: 'formArray',
|
|
1761
|
+
label: '',
|
|
1762
|
+
value: '',
|
|
1763
|
+
placeholder: 'FormArray',
|
|
1764
|
+
headless: true,
|
|
1765
|
+
childrenSections: [
|
|
1766
|
+
{
|
|
1767
|
+
rows: [
|
|
1768
|
+
[
|
|
1769
|
+
{
|
|
1770
|
+
field: 'textBox',
|
|
1771
|
+
flexSize: 12,
|
|
1772
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
1773
|
+
type: 'text',
|
|
1774
|
+
value: '',
|
|
1775
|
+
label: ''
|
|
1776
|
+
}
|
|
1777
|
+
]
|
|
1778
|
+
]
|
|
1779
|
+
}
|
|
1780
|
+
]
|
|
1781
|
+
},
|
|
1782
|
+
]
|
|
1783
|
+
},
|
|
1784
|
+
];
|
|
1785
|
+
this.generateFieldNameFor = this.coreFormDesignService.generateFieldNameFor.bind(this.coreFormDesignService);
|
|
1786
|
+
this.generateLabelNameFor = this.coreFormDesignService.generateLabelNameFor.bind(this.coreFormDesignService);
|
|
1787
|
+
this.getAllControlsFromSections = this.coreFormDesignService.getAllControlsFromSections.bind(this.coreFormDesignService);
|
|
1788
|
+
this.getControl = this.coreFormDesignService.getControl.bind(this.coreFormDesignService);
|
|
1789
|
+
this.filteredCells = this.coreFormDesignService.filteredCells;
|
|
1790
|
+
this.coreFormDesignService.resetFormDesignState();
|
|
1791
|
+
effect(() => {
|
|
1792
|
+
const _ = this.coreFormDesignService.$placeholderSections();
|
|
1793
|
+
this.syncFormDesignWithSections();
|
|
1794
|
+
});
|
|
1795
|
+
effect(() => {
|
|
1796
|
+
this.idStream$.next(this.$id());
|
|
1797
|
+
});
|
|
1798
|
+
this.applicationHelpService.activeKey$.pipe(distinctUntilChanged()).subscribe(x => {
|
|
1799
|
+
switch (x) {
|
|
1800
|
+
case 'F3':
|
|
1801
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(true);
|
|
1802
|
+
break;
|
|
1803
|
+
case 'F6':
|
|
1804
|
+
this.coreFormDesignService.$mode.set(EnumFormDesignMode.Preview);
|
|
1805
|
+
break;
|
|
1806
|
+
case 'Escape':
|
|
1807
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(false);
|
|
1808
|
+
this.coreFormDesignService.$mode.set(EnumFormDesignMode.Default);
|
|
1809
|
+
break;
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
ngOnInit() {
|
|
1814
|
+
this.subscriptions.push(this.mls.lang$.subscribe(x => this.lang = x));
|
|
1815
|
+
if (this.appConfigService.WORKFLOW_ENABLE_DEBUG_MODE) {
|
|
1816
|
+
this.injectedFields$.next([
|
|
1817
|
+
{
|
|
1818
|
+
field: '___submitterEmployeeId',
|
|
1819
|
+
flexSize: 12,
|
|
1820
|
+
type: 'number',
|
|
1821
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
1822
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
1823
|
+
getByIdApi: '',
|
|
1824
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
1825
|
+
label: 'Simulated Requester',
|
|
1826
|
+
boundFrom: 'id',
|
|
1827
|
+
shownFrom: 'fullname',
|
|
1828
|
+
value: null,
|
|
1829
|
+
},
|
|
1830
|
+
{
|
|
1831
|
+
field: "___reason",
|
|
1832
|
+
type: "text",
|
|
1833
|
+
value: '',
|
|
1834
|
+
label: "UI_FORM_CONTROL_LABEL.REASON",
|
|
1835
|
+
flexSize: 12,
|
|
1836
|
+
controlType: EnumFormBaseContolType.TEXTAREA,
|
|
1837
|
+
textareaRows: 5,
|
|
1838
|
+
validators: [
|
|
1839
|
+
{
|
|
1840
|
+
name: 'required',
|
|
1841
|
+
validator: Validators.required,
|
|
1842
|
+
errorMessage: EnumTranslateKey.UI_FORM_CONTROL_ERROR_REQUIRED
|
|
1843
|
+
}
|
|
1844
|
+
],
|
|
1845
|
+
}
|
|
1846
|
+
]);
|
|
1847
|
+
}
|
|
1848
|
+
else {
|
|
1849
|
+
this.injectedFields$.next([
|
|
1850
|
+
{
|
|
1851
|
+
field: "___reason",
|
|
1852
|
+
type: "text",
|
|
1853
|
+
value: '',
|
|
1854
|
+
label: "UI_FORM_CONTROL_LABEL.REASON",
|
|
1855
|
+
flexSize: 12,
|
|
1856
|
+
controlType: EnumFormBaseContolType.TEXTAREA,
|
|
1857
|
+
textareaRows: 5,
|
|
1858
|
+
validators: [
|
|
1859
|
+
{
|
|
1860
|
+
name: 'required',
|
|
1861
|
+
validator: Validators.required,
|
|
1862
|
+
errorMessage: EnumTranslateKey.UI_FORM_CONTROL_ERROR_REQUIRED
|
|
1863
|
+
}
|
|
1864
|
+
],
|
|
1865
|
+
}
|
|
1866
|
+
]);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
switchJson() {
|
|
1870
|
+
if (this.$jsonMode() === 'full') {
|
|
1871
|
+
this.$jsonMode.set('selectedField');
|
|
1872
|
+
}
|
|
1873
|
+
else {
|
|
1874
|
+
this.$jsonMode.set('full');
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
onDragStart(event, control) {
|
|
1878
|
+
this.coreFormDesignService.beginDragTemplate(control);
|
|
1879
|
+
}
|
|
1880
|
+
onDragOver(event, section, control) {
|
|
1881
|
+
event.preventDefault(); // necessary to allow drop
|
|
1882
|
+
if ((!!section.forCalculatedFields && this.coreFormDesignService.$draggingControl()?.type === 'calculated') ||
|
|
1883
|
+
(!section.forCalculatedFields && this.coreFormDesignService.$draggingControl()?.type !== 'calculated')) {
|
|
1884
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
event.dataTransfer.dropEffect = 'none';
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
onDragEnter(placeholderId) {
|
|
1891
|
+
this.isDragOverMap[placeholderId] = true;
|
|
1892
|
+
}
|
|
1893
|
+
onDragLeave(placeholderId) {
|
|
1894
|
+
this.isDragOverMap[placeholderId] = false;
|
|
1895
|
+
}
|
|
1896
|
+
onDropIntoCell(event, sectionIndex, rowIndex, colIndex) {
|
|
1897
|
+
event.preventDefault();
|
|
1898
|
+
const template = this.coreFormDesignService.$currentControlTemplate();
|
|
1899
|
+
if (!template)
|
|
1900
|
+
return;
|
|
1901
|
+
const section = this.coreFormDesignService.$placeholderSections()[sectionIndex];
|
|
1902
|
+
if (!section.forCalculatedFields && template.type === 'calculated')
|
|
1903
|
+
return;
|
|
1904
|
+
if (!!section.forCalculatedFields && template.type !== 'calculated')
|
|
1905
|
+
return;
|
|
1906
|
+
const normalized = this.coreFormDesignService.normalize({
|
|
1907
|
+
...template,
|
|
1908
|
+
field: this.generateFieldNameFor(template),
|
|
1909
|
+
label: this.generateLabelNameFor(template),
|
|
1910
|
+
value: null
|
|
1911
|
+
});
|
|
1912
|
+
if (normalized.controlType === EnumFormBaseContolType.CALCULATED) {
|
|
1913
|
+
normalized.returnType = 'number';
|
|
1914
|
+
}
|
|
1915
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
1916
|
+
const section = sections[sectionIndex];
|
|
1917
|
+
const row = section?.rows[rowIndex];
|
|
1918
|
+
if (!row || row.cells.length <= colIndex)
|
|
1919
|
+
return sections;
|
|
1920
|
+
const usedFlex = row.cells.reduce((sum, cell) => sum + (cell?.control?.flexSize ?? 0), 0);
|
|
1921
|
+
const remainingFlex = 12 - usedFlex;
|
|
1922
|
+
const flexSize = Math.max(1, remainingFlex);
|
|
1923
|
+
normalized.flexSize = flexSize;
|
|
1924
|
+
const previous = row.cells[colIndex]?.control;
|
|
1925
|
+
if (previous && previous.flexSize !== undefined) {
|
|
1926
|
+
/*
|
|
1927
|
+
AttachmentFieldHelperService.handleAttachmentOverwrite(
|
|
1928
|
+
previous as IFormBaseControl,
|
|
1929
|
+
this.coreFormDesignService.getAllControlsFromSections()
|
|
1930
|
+
);
|
|
1931
|
+
*/
|
|
1932
|
+
}
|
|
1933
|
+
const previousField = row.cells[colIndex]?.control?.field ?? null;
|
|
1934
|
+
this.coreFormDesignService.replaceControl(previousField, normalized);
|
|
1935
|
+
// 🔹 INSERT FIRST
|
|
1936
|
+
row.cells[colIndex] = { ...row.cells[colIndex], control: normalized };
|
|
1937
|
+
this.coreFormDesignService.$selectedCell.set(row.cells[colIndex]);
|
|
1938
|
+
// 🔹 THEN CALL setup
|
|
1939
|
+
if (AttachmentFieldHelperService.isAttachment(normalized)) {
|
|
1940
|
+
const assignToField = AttachmentFieldHelperService.buildHiddenAssignToField(normalized.field);
|
|
1941
|
+
normalized.field = `${normalized.field}Buffer`;
|
|
1942
|
+
normalized.assignTo = assignToField.field;
|
|
1943
|
+
AttachmentFieldHelperService.injectAssignToFieldIntoRow(row, assignToField);
|
|
1944
|
+
}
|
|
1945
|
+
setTimeout(() => {
|
|
1946
|
+
this.coreFormDesignService.setControlPropDirectly('validators', normalized.validators ?? [], normalized);
|
|
1947
|
+
});
|
|
1948
|
+
return sections;
|
|
1949
|
+
});
|
|
1950
|
+
this.triggerUpdateSections();
|
|
1951
|
+
this.isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex] = false;
|
|
1952
|
+
this.coreFormDesignService.endDrag();
|
|
1953
|
+
this.openSettingsForCell(this.coreFormDesignService.$selectedCell(), sectionIndex, rowIndex, colIndex);
|
|
1954
|
+
}
|
|
1955
|
+
addCalculatedSection() {
|
|
1956
|
+
this.coreFormDesignService.$placeholderSections.update(sections => [
|
|
1957
|
+
{
|
|
1958
|
+
caption: `Calculated Section`,
|
|
1959
|
+
forCalculatedFields: true,
|
|
1960
|
+
hidden: true,
|
|
1961
|
+
rows: [{ cells: [{ flexSize: 12 }] }]
|
|
1962
|
+
},
|
|
1963
|
+
...sections,
|
|
1964
|
+
]);
|
|
1965
|
+
}
|
|
1966
|
+
addSection() {
|
|
1967
|
+
this.coreFormDesignService.$placeholderSections.update(sections => [
|
|
1968
|
+
...sections,
|
|
1969
|
+
{
|
|
1970
|
+
caption: `Section ${sections.length + 1}`,
|
|
1971
|
+
rows: [{ cells: [{ flexSize: 12 }] }]
|
|
1972
|
+
}
|
|
1973
|
+
]);
|
|
1974
|
+
}
|
|
1975
|
+
deleteSection(sectionIndex) {
|
|
1976
|
+
const sections = this.coreFormDesignService.$placeholderSections();
|
|
1977
|
+
const section = sections[sectionIndex];
|
|
1978
|
+
const hasControls = section?.rows?.some(row => row?.cells?.some(cell => !!cell?.control));
|
|
1979
|
+
if (hasControls) {
|
|
1980
|
+
const confirmed = confirm('This section contains controls. Are you sure you want to delete it?');
|
|
1981
|
+
if (!confirmed)
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
this.coreFormDesignService.$placeholderSections.update(prev => prev.filter((_, idx) => idx !== sectionIndex));
|
|
1985
|
+
}
|
|
1986
|
+
deleteRow(sectionIndex, rowIndex) {
|
|
1987
|
+
const sections = this.coreFormDesignService.$placeholderSections();
|
|
1988
|
+
const section = sections[sectionIndex];
|
|
1989
|
+
const row = section?.rows?.[rowIndex];
|
|
1990
|
+
if (!row)
|
|
1991
|
+
return; // safety check
|
|
1992
|
+
const hasControls = row.cells?.some(cell => !!cell?.control);
|
|
1993
|
+
if (hasControls) {
|
|
1994
|
+
const confirmed = confirm('This row contains controls. Are you sure you want to delete it?');
|
|
1995
|
+
if (!confirmed)
|
|
1996
|
+
return; // user canceled
|
|
1997
|
+
}
|
|
1998
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
1999
|
+
return sections.map((section, sIdx) => {
|
|
2000
|
+
if (sIdx !== sectionIndex)
|
|
2001
|
+
return section;
|
|
2002
|
+
return {
|
|
2003
|
+
...section,
|
|
2004
|
+
rows: (section.rows ?? []).filter((_, rIdx) => rIdx !== rowIndex)
|
|
2005
|
+
};
|
|
2006
|
+
});
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
addRow(sectionIndex) {
|
|
2010
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
2011
|
+
const section = sections[sectionIndex];
|
|
2012
|
+
if (section) {
|
|
2013
|
+
section.rows.push({ cells: [{ flexSize: 12 }] });
|
|
2014
|
+
}
|
|
2015
|
+
return sections;
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
addColumn(row) {
|
|
2019
|
+
if (!row || row.cells.length >= 4)
|
|
2020
|
+
return;
|
|
2021
|
+
const usedFlex = row.cells.reduce((sum, c) => sum + (c.flexSize ?? 0), 0);
|
|
2022
|
+
// ✅ Case 1: Normal case – still space available
|
|
2023
|
+
if (usedFlex < 12) {
|
|
2024
|
+
const remaining = 12 - usedFlex;
|
|
2025
|
+
row.cells.push({ flexSize: Math.min(3, remaining) });
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
// 💥 Case 2: Full row — try to rebalance
|
|
2029
|
+
if (usedFlex === 12) {
|
|
2030
|
+
const count = row.cells.length;
|
|
2031
|
+
if (count === 1) {
|
|
2032
|
+
// 12 → 6 + 6
|
|
2033
|
+
row.cells[0].flexSize = 6;
|
|
2034
|
+
row.cells.push({ flexSize: 6 });
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
if (count === 2) {
|
|
2038
|
+
// 6 + 6 → 4 + 4 + 4
|
|
2039
|
+
row.cells.forEach(c => (c.flexSize = 4));
|
|
2040
|
+
row.cells.push({ flexSize: 4 });
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
if (count === 3) {
|
|
2044
|
+
// 4 + 4 + 4 → 3 + 3 + 3 + 3
|
|
2045
|
+
row.cells.forEach(c => (c.flexSize = 3));
|
|
2046
|
+
row.cells.push({ flexSize: 3 });
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
// ❌ Already 4 cells, each 3 — can’t split further
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
// 🔧 Optional fallback logic for unknown edge cases
|
|
2053
|
+
const maxIndex = row.cells.reduce((maxI, c, i, arr) => (c.flexSize ?? 0) > (arr[maxI]?.flexSize ?? 0) ? i : maxI, 0);
|
|
2054
|
+
const maxCell = row.cells[maxIndex];
|
|
2055
|
+
if ((maxCell?.flexSize ?? 0) <= 3)
|
|
2056
|
+
return; // nothing to shrink
|
|
2057
|
+
maxCell.flexSize -= 3;
|
|
2058
|
+
row.cells.push({ flexSize: 3 });
|
|
2059
|
+
}
|
|
2060
|
+
onCorePageHeaderButtonClicked(e) {
|
|
2061
|
+
if (e.code === EnumCoreButtonVNSCode.HEADER_SWITCH_VIEW) {
|
|
2062
|
+
if (this.coreFormDesignService.$mode() === EnumFormDesignMode.Default) {
|
|
2063
|
+
const sections = this.coreFormDesignService.toCoreFormSections(this.coreFormDesignService.$placeholderSections());
|
|
2064
|
+
this.coreFormDesignService.$liveFormSections.set(sections);
|
|
2065
|
+
}
|
|
2066
|
+
setTimeout(() => this.coreFormDesignService.$mode.set(this.coreFormDesignService.$mode() === EnumFormDesignMode.Preview ? EnumFormDesignMode.Default : EnumFormDesignMode.Preview));
|
|
2067
|
+
}
|
|
2068
|
+
else if (e.code === EnumCoreButtonVNSCode.HEADER_SAVE) {
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
closePreview() {
|
|
2072
|
+
this.coreFormDesignService.$mode.set(EnumFormDesignMode.Default);
|
|
2073
|
+
}
|
|
2074
|
+
onPreviewSubmit(e) {
|
|
2075
|
+
alert(JSON.stringify(e, null, 2));
|
|
2076
|
+
}
|
|
2077
|
+
onCellClicked(sectionIndex, rowIndex, colIndex) {
|
|
2078
|
+
const row = this.coreFormDesignService.$placeholderSections()[sectionIndex].rows[rowIndex];
|
|
2079
|
+
if (!!row) {
|
|
2080
|
+
// Toggle selection
|
|
2081
|
+
if (!!row.cells && row.cells.length > colIndex) {
|
|
2082
|
+
row.cells[colIndex].selected = !row.cells[colIndex].selected;
|
|
2083
|
+
}
|
|
2084
|
+
// Only allow max 2 selections
|
|
2085
|
+
const selected = row.cells.filter(c => c?.selected);
|
|
2086
|
+
if (selected.length > 2) {
|
|
2087
|
+
row.cells.forEach(c => c && (c.selected = false));
|
|
2088
|
+
row[colIndex].selected = true;
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
canMergeCells(sectionIndex, rowIndex) {
|
|
2093
|
+
const row = this.coreFormDesignService.$placeholderSections()[sectionIndex].rows[rowIndex];
|
|
2094
|
+
const selected = row?.cells.map((c, i) => ({ c, i })).filter(x => x.c?.selected);
|
|
2095
|
+
if (selected?.length !== 2)
|
|
2096
|
+
return false;
|
|
2097
|
+
const [a, b] = selected;
|
|
2098
|
+
if (a.c.control || b.c.control)
|
|
2099
|
+
return false;
|
|
2100
|
+
return Math.abs(a.i - b.i) === 1; // Must be neighbors
|
|
2101
|
+
}
|
|
2102
|
+
mergeCells(sectionIndex, rowIndex) {
|
|
2103
|
+
const row = this.coreFormDesignService.$placeholderSections()[sectionIndex].rows[rowIndex];
|
|
2104
|
+
if (!row)
|
|
2105
|
+
return;
|
|
2106
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
2107
|
+
const selected = row.cells
|
|
2108
|
+
.map((c, i) => ({ c, i }))
|
|
2109
|
+
.filter(x => x.c?.selected);
|
|
2110
|
+
if (selected.length !== 2)
|
|
2111
|
+
return sections;
|
|
2112
|
+
const [a, b] = selected.sort((a, b) => a.i - b.i);
|
|
2113
|
+
const mergedFlex = (typeof a.c.flexSize === 'number' ? a.c.flexSize : 3) +
|
|
2114
|
+
(typeof b.c.flexSize === 'number' ? b.c.flexSize : 3);
|
|
2115
|
+
const mergedCell = {
|
|
2116
|
+
//control: { ...a.c.control, flexSize: mergedFlex }, // keep control data
|
|
2117
|
+
flexSize: mergedFlex,
|
|
2118
|
+
selected: false
|
|
2119
|
+
};
|
|
2120
|
+
row.cells.splice(a.i, 2, mergedCell); // replace both cells with one
|
|
2121
|
+
return sections;
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
openSettingsForCell(cell, sectionIndex, rowIndex, colIndex) {
|
|
2125
|
+
this.coreFormDesignService.$sectionIndex.set(sectionIndex);
|
|
2126
|
+
this.coreFormDesignService.$rowIndex.set(rowIndex);
|
|
2127
|
+
this.coreFormDesignService.$colIndex.set(colIndex);
|
|
2128
|
+
this.coreFormDesignService.$selectedCell.set({ ...cell });
|
|
2129
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(true);
|
|
2130
|
+
}
|
|
2131
|
+
deleteField(row, sectionIndex, rowIndex, colIndex) {
|
|
2132
|
+
if (!row)
|
|
2133
|
+
return;
|
|
2134
|
+
const dCell = row.cells[colIndex];
|
|
2135
|
+
const currentControl = dCell.control;
|
|
2136
|
+
if (!currentControl)
|
|
2137
|
+
return;
|
|
2138
|
+
const currentPlaceholderSections = this.coreFormDesignService.$placeholderSections();
|
|
2139
|
+
if (!currentPlaceholderSections[sectionIndex].rows[rowIndex])
|
|
2140
|
+
return;
|
|
2141
|
+
currentPlaceholderSections[sectionIndex].rows[rowIndex].cells[colIndex].control = undefined;
|
|
2142
|
+
this.coreFormDesignService.$selectedCell.set(null);
|
|
2143
|
+
this.coreFormDesignService.$placeholderSections.set([...currentPlaceholderSections]);
|
|
2144
|
+
}
|
|
2145
|
+
onCaptionEditEnd(index, event) {
|
|
2146
|
+
const newCaption = event.target.innerText.trim();
|
|
2147
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
2148
|
+
sections[index].caption = newCaption;
|
|
2149
|
+
return sections;
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
onFieldCaptionEditEnd(cell, event) {
|
|
2153
|
+
const element = event.target;
|
|
2154
|
+
const newLabel = element.innerText.trim();
|
|
2155
|
+
// Fallback if empty
|
|
2156
|
+
cell.control.label = newLabel || 'Unnamed Field';
|
|
2157
|
+
this.triggerUpdateSections();
|
|
2158
|
+
}
|
|
2159
|
+
insertSection() {
|
|
2160
|
+
}
|
|
2161
|
+
insertRow(sectionIndex, rowIndex) {
|
|
2162
|
+
const sections = this.coreFormDesignService.$placeholderSections();
|
|
2163
|
+
const section = sections[sectionIndex];
|
|
2164
|
+
if (!section)
|
|
2165
|
+
return;
|
|
2166
|
+
const newRow = {
|
|
2167
|
+
cells: [
|
|
2168
|
+
{ flexSize: 12 },
|
|
2169
|
+
]
|
|
2170
|
+
};
|
|
2171
|
+
section.rows.splice(rowIndex + 1, 0, newRow);
|
|
2172
|
+
this.coreFormDesignService.$placeholderSections.set([...sections]);
|
|
2173
|
+
}
|
|
2174
|
+
closeFieldSettingPanel() {
|
|
2175
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(false);
|
|
2176
|
+
this.coreFormDesignService.$selectedCell.set(null);
|
|
2177
|
+
this.coreFormDesignService.$currentControlTemplate.set(null);
|
|
2178
|
+
this.coreFormDesignService.$currentControl.set(null);
|
|
2179
|
+
}
|
|
2180
|
+
ngAfterViewInit() {
|
|
2181
|
+
setTimeout(() => {
|
|
2182
|
+
this.subscriptions.push(this.getByIdStream$.subscribe(x => {
|
|
2183
|
+
this.$loading.set(false);
|
|
2184
|
+
if (x.ok && x.status === 200 && x.body?.statusCode === 200) {
|
|
2185
|
+
this.coreFormDesignService.$afInstance.set(this.coreFormDesignService.mapFromAfInstanceDTO(x.body.innerBody));
|
|
2186
|
+
this.coreFormDesignService.syncPlaceholderSectionsFromInstance();
|
|
2187
|
+
this.coreFormDesignService.$shouldPatchMetadataForm.set(true);
|
|
2188
|
+
const saveLiveJson = this.coreFormDesignService.$saveLiveJson();
|
|
2189
|
+
setTimeout(() => this.wfs.$initialFormDesignJson.set(saveLiveJson));
|
|
2190
|
+
}
|
|
2191
|
+
}));
|
|
2192
|
+
if (!this.$idAsInput()) {
|
|
2193
|
+
this.subscriptions.push(this.route.params.subscribe(x => {
|
|
2194
|
+
let id = '0';
|
|
2195
|
+
try {
|
|
2196
|
+
id = atob(x['id']);
|
|
2197
|
+
this.coreFormDesignService.$afInstance.update(instance => ({
|
|
2198
|
+
...instance,
|
|
2199
|
+
id
|
|
2200
|
+
}));
|
|
2201
|
+
}
|
|
2202
|
+
catch (err) {
|
|
2203
|
+
if (isDevMode()) {
|
|
2204
|
+
this.alertService.error(err.toString(), noneAutoClosedAlertOptions);
|
|
2205
|
+
}
|
|
2206
|
+
return;
|
|
2207
|
+
}
|
|
2208
|
+
this.idStream$.next(id);
|
|
2209
|
+
}));
|
|
2210
|
+
}
|
|
2211
|
+
console.warn('InitialFormDesignJson on load', this.wfs.$initialFormDesignJson());
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
onControlParamsChange(updated) {
|
|
2215
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
2216
|
+
const cell = this.coreFormDesignService.$selectedCell();
|
|
2217
|
+
if (!cell)
|
|
2218
|
+
return sections;
|
|
2219
|
+
// Patch the selected cell
|
|
2220
|
+
Object.assign(cell.control, updated);
|
|
2221
|
+
return sections;
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
get normalizedSelectedControl() {
|
|
2225
|
+
const cell = this.coreFormDesignService.$selectedCell();
|
|
2226
|
+
if (!cell?.control) {
|
|
2227
|
+
throw new Error('No control is currently selected.');
|
|
2228
|
+
}
|
|
2229
|
+
return this.coreFormDesignService.normalize(cell.control);
|
|
2230
|
+
}
|
|
2231
|
+
triggerUpdateSections() {
|
|
2232
|
+
const newValue = this.coreFormDesignService.$placeholderSections();
|
|
2233
|
+
this.coreFormDesignService.$placeholderSections.set([...newValue]);
|
|
2234
|
+
}
|
|
2235
|
+
syncFormDesignWithSections() {
|
|
2236
|
+
const existingControls = Object.keys(this.formDesign.controls);
|
|
2237
|
+
const expectedControls = [];
|
|
2238
|
+
for (const section of this.coreFormDesignService.$placeholderSections()) {
|
|
2239
|
+
for (const row of section.rows) {
|
|
2240
|
+
for (const cell of row?.cells || []) {
|
|
2241
|
+
const control = cell?.control;
|
|
2242
|
+
if (control?.field) {
|
|
2243
|
+
expectedControls.push(control.field);
|
|
2244
|
+
if (!this.formDesign.contains(control.field)) {
|
|
2245
|
+
this.formDesign.addControl(control.field, new FormControl(control.value ?? null));
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
// 🧹 Remove controls that no longer exist
|
|
2252
|
+
for (const field of existingControls) {
|
|
2253
|
+
if (!expectedControls.includes(field)) {
|
|
2254
|
+
this.formDesign.removeControl(field);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
isRequired(control) {
|
|
2259
|
+
if (!control || !control.validators)
|
|
2260
|
+
return false;
|
|
2261
|
+
return control.validators.some((v) => v.name === 'required' || v.name === 'requiredTrue');
|
|
2262
|
+
}
|
|
2263
|
+
onFormDesignCreated(e) {
|
|
2264
|
+
console.log("onFormDesignCreated", e);
|
|
2265
|
+
}
|
|
2266
|
+
onLiveFormCreated(e) {
|
|
2267
|
+
console.log("onLiveFormCreated", e);
|
|
2268
|
+
}
|
|
2269
|
+
showMetadata() {
|
|
2270
|
+
this.coreFormDesignService.$showFormMetadata.set(true);
|
|
2271
|
+
}
|
|
2272
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormGroupEditorComponent, deps: [{ token: MultiLanguageService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2273
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: CoreFormGroupEditorComponent, isStandalone: true, selector: "core-form-group-editor", inputs: { $isNested: { classPropertyName: "$isNested", publicName: "$isNested", isSignal: true, isRequired: false, transformFunction: null }, $idAsInput: { classPropertyName: "$idAsInput", publicName: "$idAsInput", isSignal: true, isRequired: false, transformFunction: null }, $id: { classPropertyName: "$id", publicName: "$id", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "$settingPanel", first: true, predicate: ["settingPanel"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"core-form-group-editor-container\">\r\n\r\n <div class=\"form-design-right\">\r\n\r\n @if (coreFormDesignService.$mode() === enumType.Default) {\r\n <div class=\"form-wrapper\">\r\n\r\n @if (!$isNested() && !!coreFormDesignService.$isInWorkflowDesign()) {\r\n <form-metadata></form-metadata>\r\n }\r\n\r\n <form [formGroup]=\"formDesign\" autocomplete=\"off\">\r\n <div class=\"form-cells\"> \r\n\r\n <div class=\"form-tool-bar\">\r\n @if (!$isNested() && !$hasCalculatedSection()) {\r\n <button class=\"core-button-vns-container\" (click)=\"addCalculatedSection()\">+ Calculated fields</button>\r\n }\r\n <button class=\"core-button-vns-container\" (click)=\"addSection()\">+ Add Section</button>\r\n </div>\r\n \r\n @for (section of coreFormDesignService.$placeholderSections(); track $index; let sectionIndex = $index) {\r\n <div class=\"form-section-placeholder\">\r\n\r\n <div class=\"section-header\">\r\n\r\n <div class=\"section-img-wrapper\">\r\n <div class=\"section-img\" [class.calculated]=\"section.forCalculatedFields\"></div>\r\n </div>\r\n\r\n <label [attr.contenteditable]=\"!section.forCalculatedFields\"\r\n [htmlTooltip]=\"!!section.forCalculatedFields ? calculatedFieldsTooltip : null\"\r\n (blur)=\"onCaptionEditEnd(sectionIndex, $event)\"\r\n (keydown.enter)=\"onCaptionEditEnd(sectionIndex, $event); $event.preventDefault()\"\r\n >{{ section.caption }}</label>\r\n\r\n <div class=\"section-tool pointer\" (click)=\"deleteSection(sectionIndex)\" [appTooltip]=\"'Delete section'\" [showAnyway]=\"true\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n \r\n </div>\r\n \r\n @for (row of section.rows; track $index; let rowIndex = $index) {\r\n\r\n <div class=\"form-row-wrapper\">\r\n <div class=\"form-row\">\r\n @for (cell of filteredCells(row); track $index; let colIndex = $index) {\r\n <div class=\"form-cell drop-target\" \r\n [ngStyle]=\"{ flex: (cell.flexSize ?? 0) + ' 1 0%' }\"\r\n [attr.data-flex]=\"cell.flexSize ?? 0\"\r\n (drop)=\"onDropIntoCell($event, sectionIndex, rowIndex, colIndex)\"\r\n (dragover)=\"onDragOver($event, section, cell.control!)\"\r\n (dragenter)=\"onDragEnter(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n (dragleave)=\"onDragLeave(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n [class.selected]=\"cell?.selected\"\r\n [class.dragging-over]=\"isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]\"\r\n [class.has-control]=\"!!cell && !!cell.control && !!getControl(cell)\"\r\n (click)=\"onCellClicked(sectionIndex, rowIndex, colIndex)\"\r\n >\r\n\r\n\r\n @if (!isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]) {\r\n\r\n @if (!!cell && !!cell.control && !!getControl(cell)) {\r\n <label contenteditable=\"true\"\r\n (blur)=\"onFieldCaptionEditEnd(cell, $event)\"\r\n (keydown.enter)=\"onFieldCaptionEditEnd(cell, $event); $event.preventDefault()\"\r\n [class.d-none]=\"!!getControl(cell)?.hidden\"\r\n [class.required]=\"!!cell && !!cell.control && !!getControl(cell) && isRequired(getControl(cell))\"\r\n >{{ cell.control.label || 'label' }}</label>\r\n\r\n @if (cell.control.controlType==='CALCULATED') {\r\n <div class=\"control-wrapper\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\">\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n </div>\r\n } @else {\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n }\r\n \r\n <div class=\"field-toolbar\">\r\n <ul>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Show settings'\">\r\n <i class=\"feather-settings\"></i>\r\n </div>\r\n </li>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"deleteField(row, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Remove field from cell'\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </li>\r\n </ul>\r\n </div>\r\n \r\n } @else {\r\n <div class=\"calculated-placeholder\" [class.calculated]=\"!!section.forCalculatedFields\">\r\n {{ !!section.forCalculatedFields ? 'Drop calculated field here' : 'Drop here'}}\r\n </div>\r\n }\r\n\r\n @if (cell?.selected && canMergeCells(sectionIndex, rowIndex)) {\r\n <div class=\"merge-toolbar\">\r\n <button (click)=\"mergeCells(sectionIndex, rowIndex)\">\uD83D\uDD17 Merge</button>\r\n </div>\r\n }\r\n \r\n }\r\n\r\n </div>\r\n }\r\n </div>\r\n <div class=\"row-tool-bar\">\r\n <div class=\"icon-wrapper\" (click)=\"deleteRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Delete row'\" [showAnyway]=\"true\"><i class=\"feather-x\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addRow(sectionIndex)\" [appTooltip]=\"'Add row'\" [showAnyway]=\"true\"><i class=\"feather-arrow-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"insertRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Insert row'\" [showAnyway]=\"true\"><i class=\"feather-corner-right-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addColumn(row)\" [appTooltip]=\"'Add column'\" [showAnyway]=\"true\"><i class=\"feather-arrow-right\"></i></div>\r\n </div>\r\n </div>\r\n\r\n }\r\n </div>\r\n }\r\n \r\n \r\n </div>\r\n </form>\r\n </div>\r\n } @else if (!!$sections()) {\r\n\r\n @if(!!(injectedFields$ | async) && !!(injectedFields$ | async)!.length) {\r\n\r\n @if (!!coreFormDesignService.$afInstance().normalMode) {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" />\r\n } @else {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" ($onClose)=\"closePreview()\" />\r\n }\r\n\r\n } @else {\r\n <h2>COULD NOT UPDATE injectedFields$</h2>\r\n }\r\n\r\n } @else {\r\n <h2>$sections() empty / null / undefined</h2>\r\n }\r\n </div>\r\n</div>\r\n\r\n<ng-template #calculatedFieldsTooltip>\r\n <div style=\"max-width: 280px;\">\r\n <strong>Calculated Fields</strong><br />\r\n <p></p>\r\n These fields are hidden from the form and cannot be edited directly.<br /><br />\r\n - They are computed automatically during workflow execution.<br />\r\n - Each field depends on one or more standard input fields.<br />\r\n - The calculation is evaluated using either an SQL query engine (like <strong>Dapper</strong>) or an in-memory expression engine (like <strong>Dynamic Expresso</strong>).<br /><br />\r\n These results can be used to influence workflow logic, rule trees, or approval paths.\r\n </div>\r\n</ng-template>\r\n\r\n\r\n ", styles: ["@charset \"UTF-8\";.core-form-group-editor-container{position:relative;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));width:100%;overflow:hidden;background-color:#eff0f1;font-size:13px}.core-form-group-editor-container .core-button-vns-container{margin-right:8px!important}.core-form-group-editor-container .core-button-vns-container:last-child{background-color:#000;color:#fff;margin-right:0}.core-form-group-editor-container .af-metadata-overlay{position:fixed;top:60px;left:66px;background:#fff;box-shadow:.4rem 0 2rem #0000002e;z-index:1000;width:1214px;height:calc(100vh - 75px);overflow-y:auto;pointer-events:none}.core-form-group-editor-container .af-metadata-overlay.shown{pointer-events:auto}.core-form-group-editor-container *{border-radius:0}.core-form-group-editor-container ul,.core-form-group-editor-container li{padding:0}.core-form-group-editor-container li{max-width:200px}.core-form-group-editor-container ul div.space{display:block;height:15px}.core-form-group-editor-container li.full-width{max-width:100%}.core-form-group-editor-container .field-setting-panel{display:block;position:fixed;width:360px;height:100vh;top:0;right:-360px;background-color:#fff;box-shadow:.4rem 0 2rem #0000002e;transition:right .5s ease-out}.core-form-group-editor-container .field-setting-panel .close-wrapper{width:32px;height:32px;position:absolute;top:15px;right:15px;display:flex;background-color:#848484;border-radius:50%}.core-form-group-editor-container .field-setting-panel .close-wrapper i{width:16px;height:16px;font-size:18px;color:#848484}.core-form-group-editor-container .field-setting-panel .panel-caption{margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #ddd;width:100%;overflow:hidden;text-wrap:nowrap;text-overflow:ellipsis}.core-form-group-editor-container .field-setting-panel.calculated-type{width:500px;right:-500px}.core-form-group-editor-container .field-setting-panel.calculated-type textarea{border-radius:8px;height:150px}.core-form-group-editor-container .field-setting-panel.open{right:0}.core-form-group-editor-container .form-design-left{position:absolute;top:var(--size-core-page-header-height);bottom:0;left:0;z-index:1;width:360px;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));padding:15px;padding-right:calc(15px + var(--size-scrollbar-width));background-color:#87ceeb;overflow-y:hidden}.core-form-group-editor-container .form-design-left .category-name{color:#fff}.core-form-group-editor-container .form-design-left .grid-buffer-wrapper{background-color:#fff;padding:15px}.core-form-group-editor-container .form-design-left ul:last-child{padding-bottom:400px}.core-form-group-editor-container .form-design-left .category-name{margin-bottom:15px}.core-form-group-editor-container .form-design-left .json-preview{background-color:#fff;color:#333;padding:1rem 1.5rem;box-shadow:0 2px 6px #00000014;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;overflow-x:auto;line-height:1.5;width:330px;height:calc(100vh - 205px)}.core-form-group-editor-container .form-design-left button{height:35px;border-radius:0;margin-bottom:15px;color:#fff;border:none;font-size:13px;background-color:transparent}.core-form-group-editor-container .form-design-left:hover{overflow-y:auto;padding-right:15px}.core-form-group-editor-container .form-design-right{width:100%;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));overflow-y:auto}.core-form-group-editor-container .form-design-right .form-wrapper{width:100%;overflow-y:visible;background-color:#fff}.core-form-group-editor-container .form-design-right .form-wrapper button{margin-left:12px;padding:4px 10px;border:1px solid #ccc;cursor:pointer;transition:all .2s;width:110px}.core-form-group-editor-container .form-design-right .form-wrapper button:hover{background-color:#e4f0ff;border-color:#007bff;color:#007bff}.core-form-group-editor-container .form-design-right .form-wrapper .form-tool-bar{display:flex;align-items:center;justify-content:flex-end}.core-form-group-editor-container .form-design-right .form-wrapper .form-tool-bar>button{width:150px;margin:0}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells{display:flex;flex-direction:column;gap:32px;padding:20px 15px 15px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder{padding:12px 15px;border:1px dashed #ccc;background-color:#fff;box-shadow:0 2px 6px #0000000a}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header{display:flex;position:relative;align-items:center;justify-content:flex-start;font-weight:600;margin-bottom:12px;color:#333}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img{width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center;margin-right:8px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);background-color:transparent;border-radius:0}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool{position:absolute;right:5px;top:6px;display:none;z-index:2;color:gray}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool i{font-size:24px;color:#848484}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header:hover .section-tool{display:block}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .row-tool-bar{display:flex;align-items:center;justify-content:flex-end;margin-bottom:8px;height:24px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-row{display:flex;gap:15px;margin-bottom:15px;min-height:50px;background-color:#d3d3d3;padding:8px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell{flex:1;min-height:90px;background-color:#fcfcfc;border:1px dashed #ccc;position:relative;padding:20px 10px 10px;transition:border .2s ease-in-out}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell:before{content:attr(data-flex);position:absolute;top:2px;left:4px;font-size:10px;color:#999}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell label.required:after{content:\" *\";color:#ff040b}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar{position:absolute;right:7px;top:6px;display:none;z-index:2;color:gray}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul{display:flex}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div{display:flex;width:24px;height:24px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div i{font-size:18px;width:18px;height:18px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.has-control:hover .field-toolbar{display:flex}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.active-drop{border-color:#007bff;background-color:#eef6ff;transition:.2s}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .calculated-placeholder{display:flex;align-items:center;justify-content:center;background-color:#fff;color:#007bff;padding:15px 0;width:100%;cursor:default}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .calculated-placeholder.calculated{color:#ff4500}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper{width:24px;height:24px;border-radius:50%;padding:0;cursor:pointer;display:none;color:gray;border:1px solid gray;margin-left:8px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper i{font-size:18px;width:18px;height:18px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper:hover .icon-wrapper{display:flex}.core-form-group-editor-container .form-design-right .form-wrapper .form-cell.selected{background-color:#dff6dd}.core-form-group-editor-container .form-design-right .form-wrapper .merge-toolbar{text-align:center;margin-top:4px}.core-form-group-editor-container .form-design-right .form-wrapper.live-form{padding-bottom:15px}.core-form-group-editor-container .form-design-right .form-wrapper.live-form .live-form-header{padding:0 15px}.core-form-group-editor-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon{position:absolute;right:7px;top:6px;z-index:2;color:gray;cursor:pointer}.core-form-group-editor-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon i{font-size:18px}.core-form-group-editor-container .drop-target{min-height:50px;min-width:50px;border:2px dashed transparent;transition:border .2s ease}.core-form-group-editor-container .drop-target.active-drop{border-color:#007bff;background-color:#eaf4ff}.core-form-group-editor-container .no-padding{padding:0!important}.core-form-group-editor-container .modal-content-root{overflow:visible}\n", ".core-form-container{overflow-x:visible}.core-form-container>form .section{margin-top:var(--size-layout-block-cell-spacing)}.core-form-container>form .section:not(:first-child){margin-top:calc(var(--size-layout-block-cell-spacing) * 2)}.core-form-container>form .section .section-header-label{display:block;height:34px;line-height:34px;margin-bottom:15px;margin-left:12px}.core-form-container>form .section .section-header-label .section-img-wrapper{position:relative}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img{position:absolute;width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);border-radius:0;background-color:transparent}.core-form-container>form .section .section-header-label .section-calc-wrapper{width:34px;height:34px;display:flex;align-items:center;justify-content:center;background-color:#d3d3d3;border-radius:50%}.core-form-container>form .section .section-header-label .section-calc-wrapper i{width:24px;height:24px;font-size:24px;color:#1b4332}.core-form-container>form .section .section-header-label .section-caption{padding-left:40px;font-weight:700;color:#696969}.core-form-container .row{margin-left:var(--size-layout-block-cell-spacing) 0px;margin-right:var(--size-layout-block-cell-spacing) 0px}.core-form-container .row .grid-buffer{border:dotted 2px darkgray}.core-form-container .row .button-control{display:flex;align-items:flex-end}.core-form-container .form-row{margin:var(--size-layout-block-cell-spacing) 0px;display:flex;align-items:center;justify-content:center}.core-form-container .form-row>button{cursor:pointer;border-radius:0}.core-form-container .form-row>button:not(:first-child){margin-left:var(--size-layout-block-cell-spacing)}.core-form-container .dev-button{cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;padding:8px;width:120px;border-radius:18px;box-shadow:.4rem 0 2rem #0000002e}.core-form-container .dev-button:not(:last-child){margin-right:15px}.core-form-container .dev-button:first-child{background-color:#dff6dd;border:1px #9fdc9d solid}.core-form-container .dev-button:last-child{background-color:#fff4ce;border:1px #ffda6a solid}.core-form-container .payload-preview{display:block;width:calc(100% - 48px);height:200px;white-space:pre-wrap;overflow-x:hidden;overflow-y:auto;background-color:#dff6dd;padding:24px;margin:24px;text-indent:-58px;color:#333!important;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;line-height:1.5}.core-form-container .payload-preview.validator-preview{background-color:#fff4ce}.core-form-container .bottom-template-wrapper{padding-left:12px;padding-right:12px}.core-form-container .w-100{width:100%}.core-form-container .pr18{padding-right:18px}\n", ".core-button-vns-container{height:30px;display:flex;align-items:center;justify-content:center;min-width:30px}.core-button-vns-container .action-wrapper{height:30px!important;width:30px!important;display:flex;align-items:center;justify-content:center}.core-button-vns-container .action-wrapper:has(i:hover){background-color:#e7e7e7;border-radius:50%}.core-button-vns-container .btn-for-form{border:none;border-radius:0;background-color:transparent;color:#000;min-width:120px}.core-button-vns-container button.last-child{background-color:#000;color:#fff}.core-button-vns-container .action-wrapper.last-child{background-color:var(--color-basic-orange);color:#fff;border-radius:50%}.core-button-vns-container .action-wrapper.last-child:hover{background-color:var(--color-basic-orange);box-shadow:0 1rem 3rem #0000002e}.core-button-vns-container .temporary-unavailable{user-select:none;-moz-user-select:none;-webkit-user-select:none;cursor:not-allowed!important;opacity:.5}.core-button-vns-container .temporary-unavailable:hover{background-color:transparent!important}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: FormMetadataComponent, selector: "form-metadata" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: TooltipDirective, selector: "[appTooltip]", inputs: ["color", "backgroundColor", "appTooltip", "showAnyway", "position"] }, { kind: "component", type: CoreControlComponent, selector: "core-control", inputs: ["control", "form", "checkError$", "rangeLimit"] }, { kind: "component", type: LiveFormComponent, selector: "live-form", inputs: ["$designMode", "$forKickOff", "$workflowReactContext"], outputs: ["$onClose"] }, { kind: "directive", type: HtmlTooltipDirective, selector: "[htmlTooltip]", inputs: ["htmlTooltip", "tooltipContext", "maxWidthUnset", "backgroundColor"] }], animations: [slideFromTopFadeIn], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2274
|
+
}
|
|
2275
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormGroupEditorComponent, decorators: [{
|
|
2276
|
+
type: Component,
|
|
2277
|
+
args: [{ selector: 'core-form-group-editor', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
2278
|
+
NgStyle,
|
|
2279
|
+
AsyncPipe,
|
|
2280
|
+
NgTemplateOutlet,
|
|
2281
|
+
CoreFormArrayLayoutEditorComponent,
|
|
2282
|
+
CoreToastLoadingComponent,
|
|
2283
|
+
CorePageHeaderComponent,
|
|
2284
|
+
FormMetadataComponent,
|
|
2285
|
+
ReactiveFormsModule,
|
|
2286
|
+
TranslatePipe,
|
|
2287
|
+
TableCellPipe,
|
|
2288
|
+
TooltipDirective,
|
|
2289
|
+
HotKeysDirective,
|
|
2290
|
+
CoreFormComponent,
|
|
2291
|
+
CoreControlComponent,
|
|
2292
|
+
CoreDropdownComponent,
|
|
2293
|
+
CoreChecklistComponent,
|
|
2294
|
+
CoreFormControlSeekerComponent,
|
|
2295
|
+
CoreCheckboxComponent,
|
|
2296
|
+
CoreAttachmentComponent,
|
|
2297
|
+
CoreDatePickerComponent,
|
|
2298
|
+
CoreRadioGroupComponent,
|
|
2299
|
+
CoreMonthSelectorComponent,
|
|
2300
|
+
TooltipDirective,
|
|
2301
|
+
CoreCurrencyInputComponent,
|
|
2302
|
+
FieldSettingComponent,
|
|
2303
|
+
LiveFormComponent,
|
|
2304
|
+
HtmlTooltipDirective,
|
|
2305
|
+
], animations: [slideFromTopFadeIn], template: "<div class=\"core-form-group-editor-container\">\r\n\r\n <div class=\"form-design-right\">\r\n\r\n @if (coreFormDesignService.$mode() === enumType.Default) {\r\n <div class=\"form-wrapper\">\r\n\r\n @if (!$isNested() && !!coreFormDesignService.$isInWorkflowDesign()) {\r\n <form-metadata></form-metadata>\r\n }\r\n\r\n <form [formGroup]=\"formDesign\" autocomplete=\"off\">\r\n <div class=\"form-cells\"> \r\n\r\n <div class=\"form-tool-bar\">\r\n @if (!$isNested() && !$hasCalculatedSection()) {\r\n <button class=\"core-button-vns-container\" (click)=\"addCalculatedSection()\">+ Calculated fields</button>\r\n }\r\n <button class=\"core-button-vns-container\" (click)=\"addSection()\">+ Add Section</button>\r\n </div>\r\n \r\n @for (section of coreFormDesignService.$placeholderSections(); track $index; let sectionIndex = $index) {\r\n <div class=\"form-section-placeholder\">\r\n\r\n <div class=\"section-header\">\r\n\r\n <div class=\"section-img-wrapper\">\r\n <div class=\"section-img\" [class.calculated]=\"section.forCalculatedFields\"></div>\r\n </div>\r\n\r\n <label [attr.contenteditable]=\"!section.forCalculatedFields\"\r\n [htmlTooltip]=\"!!section.forCalculatedFields ? calculatedFieldsTooltip : null\"\r\n (blur)=\"onCaptionEditEnd(sectionIndex, $event)\"\r\n (keydown.enter)=\"onCaptionEditEnd(sectionIndex, $event); $event.preventDefault()\"\r\n >{{ section.caption }}</label>\r\n\r\n <div class=\"section-tool pointer\" (click)=\"deleteSection(sectionIndex)\" [appTooltip]=\"'Delete section'\" [showAnyway]=\"true\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n \r\n </div>\r\n \r\n @for (row of section.rows; track $index; let rowIndex = $index) {\r\n\r\n <div class=\"form-row-wrapper\">\r\n <div class=\"form-row\">\r\n @for (cell of filteredCells(row); track $index; let colIndex = $index) {\r\n <div class=\"form-cell drop-target\" \r\n [ngStyle]=\"{ flex: (cell.flexSize ?? 0) + ' 1 0%' }\"\r\n [attr.data-flex]=\"cell.flexSize ?? 0\"\r\n (drop)=\"onDropIntoCell($event, sectionIndex, rowIndex, colIndex)\"\r\n (dragover)=\"onDragOver($event, section, cell.control!)\"\r\n (dragenter)=\"onDragEnter(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n (dragleave)=\"onDragLeave(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n [class.selected]=\"cell?.selected\"\r\n [class.dragging-over]=\"isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]\"\r\n [class.has-control]=\"!!cell && !!cell.control && !!getControl(cell)\"\r\n (click)=\"onCellClicked(sectionIndex, rowIndex, colIndex)\"\r\n >\r\n\r\n\r\n @if (!isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]) {\r\n\r\n @if (!!cell && !!cell.control && !!getControl(cell)) {\r\n <label contenteditable=\"true\"\r\n (blur)=\"onFieldCaptionEditEnd(cell, $event)\"\r\n (keydown.enter)=\"onFieldCaptionEditEnd(cell, $event); $event.preventDefault()\"\r\n [class.d-none]=\"!!getControl(cell)?.hidden\"\r\n [class.required]=\"!!cell && !!cell.control && !!getControl(cell) && isRequired(getControl(cell))\"\r\n >{{ cell.control.label || 'label' }}</label>\r\n\r\n @if (cell.control.controlType==='CALCULATED') {\r\n <div class=\"control-wrapper\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\">\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n </div>\r\n } @else {\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n }\r\n \r\n <div class=\"field-toolbar\">\r\n <ul>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Show settings'\">\r\n <i class=\"feather-settings\"></i>\r\n </div>\r\n </li>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"deleteField(row, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Remove field from cell'\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </li>\r\n </ul>\r\n </div>\r\n \r\n } @else {\r\n <div class=\"calculated-placeholder\" [class.calculated]=\"!!section.forCalculatedFields\">\r\n {{ !!section.forCalculatedFields ? 'Drop calculated field here' : 'Drop here'}}\r\n </div>\r\n }\r\n\r\n @if (cell?.selected && canMergeCells(sectionIndex, rowIndex)) {\r\n <div class=\"merge-toolbar\">\r\n <button (click)=\"mergeCells(sectionIndex, rowIndex)\">\uD83D\uDD17 Merge</button>\r\n </div>\r\n }\r\n \r\n }\r\n\r\n </div>\r\n }\r\n </div>\r\n <div class=\"row-tool-bar\">\r\n <div class=\"icon-wrapper\" (click)=\"deleteRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Delete row'\" [showAnyway]=\"true\"><i class=\"feather-x\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addRow(sectionIndex)\" [appTooltip]=\"'Add row'\" [showAnyway]=\"true\"><i class=\"feather-arrow-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"insertRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Insert row'\" [showAnyway]=\"true\"><i class=\"feather-corner-right-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addColumn(row)\" [appTooltip]=\"'Add column'\" [showAnyway]=\"true\"><i class=\"feather-arrow-right\"></i></div>\r\n </div>\r\n </div>\r\n\r\n }\r\n </div>\r\n }\r\n \r\n \r\n </div>\r\n </form>\r\n </div>\r\n } @else if (!!$sections()) {\r\n\r\n @if(!!(injectedFields$ | async) && !!(injectedFields$ | async)!.length) {\r\n\r\n @if (!!coreFormDesignService.$afInstance().normalMode) {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" />\r\n } @else {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" ($onClose)=\"closePreview()\" />\r\n }\r\n\r\n } @else {\r\n <h2>COULD NOT UPDATE injectedFields$</h2>\r\n }\r\n\r\n } @else {\r\n <h2>$sections() empty / null / undefined</h2>\r\n }\r\n </div>\r\n</div>\r\n\r\n<ng-template #calculatedFieldsTooltip>\r\n <div style=\"max-width: 280px;\">\r\n <strong>Calculated Fields</strong><br />\r\n <p></p>\r\n These fields are hidden from the form and cannot be edited directly.<br /><br />\r\n - They are computed automatically during workflow execution.<br />\r\n - Each field depends on one or more standard input fields.<br />\r\n - The calculation is evaluated using either an SQL query engine (like <strong>Dapper</strong>) or an in-memory expression engine (like <strong>Dynamic Expresso</strong>).<br /><br />\r\n These results can be used to influence workflow logic, rule trees, or approval paths.\r\n </div>\r\n</ng-template>\r\n\r\n\r\n ", styles: ["@charset \"UTF-8\";.core-form-group-editor-container{position:relative;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));width:100%;overflow:hidden;background-color:#eff0f1;font-size:13px}.core-form-group-editor-container .core-button-vns-container{margin-right:8px!important}.core-form-group-editor-container .core-button-vns-container:last-child{background-color:#000;color:#fff;margin-right:0}.core-form-group-editor-container .af-metadata-overlay{position:fixed;top:60px;left:66px;background:#fff;box-shadow:.4rem 0 2rem #0000002e;z-index:1000;width:1214px;height:calc(100vh - 75px);overflow-y:auto;pointer-events:none}.core-form-group-editor-container .af-metadata-overlay.shown{pointer-events:auto}.core-form-group-editor-container *{border-radius:0}.core-form-group-editor-container ul,.core-form-group-editor-container li{padding:0}.core-form-group-editor-container li{max-width:200px}.core-form-group-editor-container ul div.space{display:block;height:15px}.core-form-group-editor-container li.full-width{max-width:100%}.core-form-group-editor-container .field-setting-panel{display:block;position:fixed;width:360px;height:100vh;top:0;right:-360px;background-color:#fff;box-shadow:.4rem 0 2rem #0000002e;transition:right .5s ease-out}.core-form-group-editor-container .field-setting-panel .close-wrapper{width:32px;height:32px;position:absolute;top:15px;right:15px;display:flex;background-color:#848484;border-radius:50%}.core-form-group-editor-container .field-setting-panel .close-wrapper i{width:16px;height:16px;font-size:18px;color:#848484}.core-form-group-editor-container .field-setting-panel .panel-caption{margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #ddd;width:100%;overflow:hidden;text-wrap:nowrap;text-overflow:ellipsis}.core-form-group-editor-container .field-setting-panel.calculated-type{width:500px;right:-500px}.core-form-group-editor-container .field-setting-panel.calculated-type textarea{border-radius:8px;height:150px}.core-form-group-editor-container .field-setting-panel.open{right:0}.core-form-group-editor-container .form-design-left{position:absolute;top:var(--size-core-page-header-height);bottom:0;left:0;z-index:1;width:360px;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));padding:15px;padding-right:calc(15px + var(--size-scrollbar-width));background-color:#87ceeb;overflow-y:hidden}.core-form-group-editor-container .form-design-left .category-name{color:#fff}.core-form-group-editor-container .form-design-left .grid-buffer-wrapper{background-color:#fff;padding:15px}.core-form-group-editor-container .form-design-left ul:last-child{padding-bottom:400px}.core-form-group-editor-container .form-design-left .category-name{margin-bottom:15px}.core-form-group-editor-container .form-design-left .json-preview{background-color:#fff;color:#333;padding:1rem 1.5rem;box-shadow:0 2px 6px #00000014;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;overflow-x:auto;line-height:1.5;width:330px;height:calc(100vh - 205px)}.core-form-group-editor-container .form-design-left button{height:35px;border-radius:0;margin-bottom:15px;color:#fff;border:none;font-size:13px;background-color:transparent}.core-form-group-editor-container .form-design-left:hover{overflow-y:auto;padding-right:15px}.core-form-group-editor-container .form-design-right{width:100%;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));overflow-y:auto}.core-form-group-editor-container .form-design-right .form-wrapper{width:100%;overflow-y:visible;background-color:#fff}.core-form-group-editor-container .form-design-right .form-wrapper button{margin-left:12px;padding:4px 10px;border:1px solid #ccc;cursor:pointer;transition:all .2s;width:110px}.core-form-group-editor-container .form-design-right .form-wrapper button:hover{background-color:#e4f0ff;border-color:#007bff;color:#007bff}.core-form-group-editor-container .form-design-right .form-wrapper .form-tool-bar{display:flex;align-items:center;justify-content:flex-end}.core-form-group-editor-container .form-design-right .form-wrapper .form-tool-bar>button{width:150px;margin:0}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells{display:flex;flex-direction:column;gap:32px;padding:20px 15px 15px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder{padding:12px 15px;border:1px dashed #ccc;background-color:#fff;box-shadow:0 2px 6px #0000000a}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header{display:flex;position:relative;align-items:center;justify-content:flex-start;font-weight:600;margin-bottom:12px;color:#333}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img{width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center;margin-right:8px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);background-color:transparent;border-radius:0}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool{position:absolute;right:5px;top:6px;display:none;z-index:2;color:gray}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool i{font-size:24px;color:#848484}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header:hover .section-tool{display:block}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .row-tool-bar{display:flex;align-items:center;justify-content:flex-end;margin-bottom:8px;height:24px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-row{display:flex;gap:15px;margin-bottom:15px;min-height:50px;background-color:#d3d3d3;padding:8px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell{flex:1;min-height:90px;background-color:#fcfcfc;border:1px dashed #ccc;position:relative;padding:20px 10px 10px;transition:border .2s ease-in-out}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell:before{content:attr(data-flex);position:absolute;top:2px;left:4px;font-size:10px;color:#999}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell label.required:after{content:\" *\";color:#ff040b}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar{position:absolute;right:7px;top:6px;display:none;z-index:2;color:gray}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul{display:flex}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div{display:flex;width:24px;height:24px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div i{font-size:18px;width:18px;height:18px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.has-control:hover .field-toolbar{display:flex}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.active-drop{border-color:#007bff;background-color:#eef6ff;transition:.2s}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .calculated-placeholder{display:flex;align-items:center;justify-content:center;background-color:#fff;color:#007bff;padding:15px 0;width:100%;cursor:default}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .calculated-placeholder.calculated{color:#ff4500}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper{width:24px;height:24px;border-radius:50%;padding:0;cursor:pointer;display:none;color:gray;border:1px solid gray;margin-left:8px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper i{font-size:18px;width:18px;height:18px}.core-form-group-editor-container .form-design-right .form-wrapper .form-cells .form-row-wrapper:hover .icon-wrapper{display:flex}.core-form-group-editor-container .form-design-right .form-wrapper .form-cell.selected{background-color:#dff6dd}.core-form-group-editor-container .form-design-right .form-wrapper .merge-toolbar{text-align:center;margin-top:4px}.core-form-group-editor-container .form-design-right .form-wrapper.live-form{padding-bottom:15px}.core-form-group-editor-container .form-design-right .form-wrapper.live-form .live-form-header{padding:0 15px}.core-form-group-editor-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon{position:absolute;right:7px;top:6px;z-index:2;color:gray;cursor:pointer}.core-form-group-editor-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon i{font-size:18px}.core-form-group-editor-container .drop-target{min-height:50px;min-width:50px;border:2px dashed transparent;transition:border .2s ease}.core-form-group-editor-container .drop-target.active-drop{border-color:#007bff;background-color:#eaf4ff}.core-form-group-editor-container .no-padding{padding:0!important}.core-form-group-editor-container .modal-content-root{overflow:visible}\n", ".core-form-container{overflow-x:visible}.core-form-container>form .section{margin-top:var(--size-layout-block-cell-spacing)}.core-form-container>form .section:not(:first-child){margin-top:calc(var(--size-layout-block-cell-spacing) * 2)}.core-form-container>form .section .section-header-label{display:block;height:34px;line-height:34px;margin-bottom:15px;margin-left:12px}.core-form-container>form .section .section-header-label .section-img-wrapper{position:relative}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img{position:absolute;width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);border-radius:0;background-color:transparent}.core-form-container>form .section .section-header-label .section-calc-wrapper{width:34px;height:34px;display:flex;align-items:center;justify-content:center;background-color:#d3d3d3;border-radius:50%}.core-form-container>form .section .section-header-label .section-calc-wrapper i{width:24px;height:24px;font-size:24px;color:#1b4332}.core-form-container>form .section .section-header-label .section-caption{padding-left:40px;font-weight:700;color:#696969}.core-form-container .row{margin-left:var(--size-layout-block-cell-spacing) 0px;margin-right:var(--size-layout-block-cell-spacing) 0px}.core-form-container .row .grid-buffer{border:dotted 2px darkgray}.core-form-container .row .button-control{display:flex;align-items:flex-end}.core-form-container .form-row{margin:var(--size-layout-block-cell-spacing) 0px;display:flex;align-items:center;justify-content:center}.core-form-container .form-row>button{cursor:pointer;border-radius:0}.core-form-container .form-row>button:not(:first-child){margin-left:var(--size-layout-block-cell-spacing)}.core-form-container .dev-button{cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;padding:8px;width:120px;border-radius:18px;box-shadow:.4rem 0 2rem #0000002e}.core-form-container .dev-button:not(:last-child){margin-right:15px}.core-form-container .dev-button:first-child{background-color:#dff6dd;border:1px #9fdc9d solid}.core-form-container .dev-button:last-child{background-color:#fff4ce;border:1px #ffda6a solid}.core-form-container .payload-preview{display:block;width:calc(100% - 48px);height:200px;white-space:pre-wrap;overflow-x:hidden;overflow-y:auto;background-color:#dff6dd;padding:24px;margin:24px;text-indent:-58px;color:#333!important;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;line-height:1.5}.core-form-container .payload-preview.validator-preview{background-color:#fff4ce}.core-form-container .bottom-template-wrapper{padding-left:12px;padding-right:12px}.core-form-container .w-100{width:100%}.core-form-container .pr18{padding-right:18px}\n", ".core-button-vns-container{height:30px;display:flex;align-items:center;justify-content:center;min-width:30px}.core-button-vns-container .action-wrapper{height:30px!important;width:30px!important;display:flex;align-items:center;justify-content:center}.core-button-vns-container .action-wrapper:has(i:hover){background-color:#e7e7e7;border-radius:50%}.core-button-vns-container .btn-for-form{border:none;border-radius:0;background-color:transparent;color:#000;min-width:120px}.core-button-vns-container button.last-child{background-color:#000;color:#fff}.core-button-vns-container .action-wrapper.last-child{background-color:var(--color-basic-orange);color:#fff;border-radius:50%}.core-button-vns-container .action-wrapper.last-child:hover{background-color:var(--color-basic-orange);box-shadow:0 1rem 3rem #0000002e}.core-button-vns-container .temporary-unavailable{user-select:none;-moz-user-select:none;-webkit-user-select:none;cursor:not-allowed!important;opacity:.5}.core-button-vns-container .temporary-unavailable:hover{background-color:transparent!important}\n"] }]
|
|
2306
|
+
}], ctorParameters: () => [{ type: MultiLanguageService }] });
|
|
2307
|
+
|
|
2308
|
+
class CoreFormArrayLayoutEditorComponent {
|
|
2309
|
+
constructor() {
|
|
2310
|
+
this.checkError$ = new BehaviorSubject(false);
|
|
2311
|
+
this.coreFormDesignService = inject(CoreFormDesignService);
|
|
2312
|
+
this.$sections = this.coreFormDesignService.$nestedDesignSections;
|
|
2313
|
+
this.form = this.coreFormDesignService.formDesign;
|
|
2314
|
+
}
|
|
2315
|
+
onClose() {
|
|
2316
|
+
this.coreFormDesignService.$showFormArrayDesign.set(false);
|
|
2317
|
+
}
|
|
2318
|
+
onMiniDrop(event, sectionIndex, rowIndex, colIndex) {
|
|
2319
|
+
const rawData = event?.dataTransfer?.getData('application/json');
|
|
2320
|
+
if (rawData) {
|
|
2321
|
+
const control = JSON.parse(rawData);
|
|
2322
|
+
// clone control để tránh mutate template gốc
|
|
2323
|
+
const newControl = structuredClone(control);
|
|
2324
|
+
// insert vào đúng chỗ
|
|
2325
|
+
const current = this.$sections();
|
|
2326
|
+
if (!current)
|
|
2327
|
+
return;
|
|
2328
|
+
current[sectionIndex].rows[rowIndex][colIndex] = newControl;
|
|
2329
|
+
// trigger signal cập nhật
|
|
2330
|
+
this.$sections.set([...current]);
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormArrayLayoutEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2334
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: CoreFormArrayLayoutEditorComponent, isStandalone: true, selector: "core-form-array-layout-editor", ngImport: i0, template: "<div class=\"core-form-array-layout-editor-container\">\r\n\r\n\r\n <div class=\"modal-container\">\r\n <div class=\"modal-content-root\">\r\n\r\n <div class=\"layout-header\">\r\n DESIGN FORM ARRAY LAYOUT\r\n <button class=\"btn btn-sm btn-outline-secondary\" (click)=\"onClose()\">\u2716</button>\r\n </div>\r\n\r\n <div class=\"layout-body\">\r\n <div class=\"json-holder\">\r\n <pre>{{ coreFormDesignService.$currentControlJson() }}</pre>\r\n </div>\r\n <div class=\"palete\">\r\n <core-form-array-control-palette />\r\n </div>\r\n <div class=\"sections-holder\">\r\n <core-form-group-editor [$idAsInput]=\"true\" [$isNested]=\"true\"/>\r\n </div>\r\n <div class=\"field-setting-holder\">\r\n <field-setting [$isNested]=\"true\"></field-setting>\r\n </div>\r\n \r\n <!-- @for (section of $sections(); track $index; let sectionIndex = $index) {\r\n <div class=\"section-card\">\r\n <div class=\"section-caption\">{{ section.caption || 'Untitled Section' }}</div>\r\n\r\n @for (row of section.rows; track $index; let rowIndex = $index) {\r\n <div class=\"row\">\r\n @for (control of row; track $index; let colIndex = $index) {\r\n <div\r\n class=\"form-cell mini-drop-target\"\r\n [attr.data-index]=\"$index\"\r\n miniDropTarget\r\n (miniDropped)=\"onMiniDrop($event, sectionIndex, rowIndex, colIndex)\"\r\n >\r\n <core-control-no-form-array\r\n [control]=\"control\"\r\n [form]=\"form\"\r\n [checkError$]=\"checkError$\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n } -->\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n\r\n</div>", styles: [".core-form-array-layout-editor-container{height:100%;width:100vw;display:flex;flex-direction:column}.layout-header{display:flex;justify-content:space-between;align-items:center;padding:15px;height:50px;background:#f7f7f7;border-bottom:1px solid #ddd;transform:translate(-15px,-15px);width:calc(100% + 30px);font-size:20px}.layout-body{display:flex;vertical-align:top}.layout-body .json-holder,.layout-body .palete{width:240px;display:inline-block}.layout-body .sections-holder{width:480px;display:inline-block}.layout-body .field-setting-holder{width:360px;display:inline-block}.layout-footer{padding:1rem;border-top:1px solid #ddd;text-align:right}\n"], dependencies: [{ kind: "component", type: CoreFormArrayControlPaletteComponent, selector: "core-form-array-control-palette" }, { kind: "component", type: CoreFormGroupEditorComponent, selector: "core-form-group-editor", inputs: ["$isNested", "$idAsInput", "$id"] }, { kind: "component", type: FieldSettingComponent, selector: "field-setting", inputs: ["$isNested"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2335
|
+
}
|
|
2336
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormArrayLayoutEditorComponent, decorators: [{
|
|
2337
|
+
type: Component,
|
|
2338
|
+
args: [{ selector: 'core-form-array-layout-editor', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
2339
|
+
CoreControlNoFormArrayComponent,
|
|
2340
|
+
CoreFormArrayControlPaletteComponent,
|
|
2341
|
+
CoreFormGroupEditorComponent,
|
|
2342
|
+
FieldSettingComponent
|
|
2343
|
+
], template: "<div class=\"core-form-array-layout-editor-container\">\r\n\r\n\r\n <div class=\"modal-container\">\r\n <div class=\"modal-content-root\">\r\n\r\n <div class=\"layout-header\">\r\n DESIGN FORM ARRAY LAYOUT\r\n <button class=\"btn btn-sm btn-outline-secondary\" (click)=\"onClose()\">\u2716</button>\r\n </div>\r\n\r\n <div class=\"layout-body\">\r\n <div class=\"json-holder\">\r\n <pre>{{ coreFormDesignService.$currentControlJson() }}</pre>\r\n </div>\r\n <div class=\"palete\">\r\n <core-form-array-control-palette />\r\n </div>\r\n <div class=\"sections-holder\">\r\n <core-form-group-editor [$idAsInput]=\"true\" [$isNested]=\"true\"/>\r\n </div>\r\n <div class=\"field-setting-holder\">\r\n <field-setting [$isNested]=\"true\"></field-setting>\r\n </div>\r\n \r\n <!-- @for (section of $sections(); track $index; let sectionIndex = $index) {\r\n <div class=\"section-card\">\r\n <div class=\"section-caption\">{{ section.caption || 'Untitled Section' }}</div>\r\n\r\n @for (row of section.rows; track $index; let rowIndex = $index) {\r\n <div class=\"row\">\r\n @for (control of row; track $index; let colIndex = $index) {\r\n <div\r\n class=\"form-cell mini-drop-target\"\r\n [attr.data-index]=\"$index\"\r\n miniDropTarget\r\n (miniDropped)=\"onMiniDrop($event, sectionIndex, rowIndex, colIndex)\"\r\n >\r\n <core-control-no-form-array\r\n [control]=\"control\"\r\n [form]=\"form\"\r\n [checkError$]=\"checkError$\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n } -->\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n\r\n</div>", styles: [".core-form-array-layout-editor-container{height:100%;width:100vw;display:flex;flex-direction:column}.layout-header{display:flex;justify-content:space-between;align-items:center;padding:15px;height:50px;background:#f7f7f7;border-bottom:1px solid #ddd;transform:translate(-15px,-15px);width:calc(100% + 30px);font-size:20px}.layout-body{display:flex;vertical-align:top}.layout-body .json-holder,.layout-body .palete{width:240px;display:inline-block}.layout-body .sections-holder{width:480px;display:inline-block}.layout-body .field-setting-holder{width:360px;display:inline-block}.layout-footer{padding:1rem;border-top:1px solid #ddd;text-align:right}\n"] }]
|
|
2344
|
+
}] });
|
|
2345
|
+
|
|
2346
|
+
class CoreFormDesignComponent extends BaseComponent {
|
|
2347
|
+
constructor(mls) {
|
|
2348
|
+
super(mls);
|
|
2349
|
+
this.mls = mls;
|
|
2350
|
+
this.$idAsInput = input();
|
|
2351
|
+
this.$id = input();
|
|
2352
|
+
this.$settingPanel = viewChild('settingPanel');
|
|
2353
|
+
this.$jsonMode = signal('selectedField');
|
|
2354
|
+
this.injectedFields$ = new BehaviorSubject([]);
|
|
2355
|
+
this.enumType = EnumFormDesignMode;
|
|
2356
|
+
this.enumLeftPanelMode = EnumLeftPanelMode;
|
|
2357
|
+
this.$leftPanelMode = computed(() => {
|
|
2358
|
+
if (this.coreFormDesignService.$fieldSettingPanelOpen()) {
|
|
2359
|
+
return this.enumLeftPanelMode.Json;
|
|
2360
|
+
}
|
|
2361
|
+
else {
|
|
2362
|
+
return this.enumLeftPanelMode.FieldCollection;
|
|
2363
|
+
}
|
|
2364
|
+
});
|
|
2365
|
+
this.ds = inject(DomService);
|
|
2366
|
+
this.js = inject(JsonService);
|
|
2367
|
+
this.$zIndex = computed(() => {
|
|
2368
|
+
if (this.coreFormDesignService.$fieldSettingPanelOpen()) {
|
|
2369
|
+
return this.ds.getMaxZIndex() + 1;
|
|
2370
|
+
}
|
|
2371
|
+
else {
|
|
2372
|
+
return 0;
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
this.previewMode$ = new BehaviorSubject(EnumCorePageEditMode.CREATE);
|
|
2376
|
+
this.$loading = signal(false);
|
|
2377
|
+
this.coreFormDesignService = inject(CoreFormDesignService);
|
|
2378
|
+
this.renderer = inject(Renderer2);
|
|
2379
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2380
|
+
this.router = inject(Router);
|
|
2381
|
+
this.route = inject(ActivatedRoute);
|
|
2382
|
+
this.alertService = inject(AlertService);
|
|
2383
|
+
this.applicationHelpService = inject(ApplicationHelpService);
|
|
2384
|
+
this.wfs = inject(CoreWorkflowService);
|
|
2385
|
+
this.dbs = inject(DbService);
|
|
2386
|
+
this.appConfigService = inject(AppConfigService);
|
|
2387
|
+
this.$hasCalculatedSection = computed(() => {
|
|
2388
|
+
return this.coreFormDesignService.$placeholderSections().some(s => !!s.forCalculatedFields);
|
|
2389
|
+
});
|
|
2390
|
+
this.formDesign = this.coreFormDesignService.formDesign;
|
|
2391
|
+
this.setControlProp = this.coreFormDesignService.setControlProp.bind(this.coreFormDesignService);
|
|
2392
|
+
this.isDragOverMap = {};
|
|
2393
|
+
this.idStream$ = new BehaviorSubject(undefined);
|
|
2394
|
+
this.getByIdStream$ = this.idStream$.pipe(filter(id => !!id && id !== '0'), distinctUntilChanged(), tap(() => {
|
|
2395
|
+
this.$loading.set(true);
|
|
2396
|
+
this.coreFormDesignService.editMode$.next(EnumCorePageEditMode.UPDATE);
|
|
2397
|
+
this.coreFormDesignService.$submitText.set(EnumTranslateKey.UI_EDIT_FORM_BUTTON_SAVE);
|
|
2398
|
+
}), switchMap(id => {
|
|
2399
|
+
return this.coreFormDesignService.getById(id)
|
|
2400
|
+
.pipe(catchError(err => {
|
|
2401
|
+
this.$loading.set(false);
|
|
2402
|
+
return of(err);
|
|
2403
|
+
}));
|
|
2404
|
+
}));
|
|
2405
|
+
this.form = new FormGroup({
|
|
2406
|
+
stringBox: new FormControl(),
|
|
2407
|
+
timeBox: new FormControl(),
|
|
2408
|
+
email: new FormControl(),
|
|
2409
|
+
textarea: new FormControl(),
|
|
2410
|
+
number: new FormControl(),
|
|
2411
|
+
currency: new FormControl(),
|
|
2412
|
+
dropdown: new FormControl(),
|
|
2413
|
+
employeeSeeker: new FormControl(),
|
|
2414
|
+
checklist: new FormControl(),
|
|
2415
|
+
employeeSeekerMultiple: new FormControl(),
|
|
2416
|
+
datePicker: new FormControl(),
|
|
2417
|
+
monthSelector: new FormControl(),
|
|
2418
|
+
attachment: new FormControl(),
|
|
2419
|
+
attachmentBuffer: new FormControl(),
|
|
2420
|
+
checkbox: new FormControl(),
|
|
2421
|
+
calculated: new FormControl(),
|
|
2422
|
+
formArray: new FormArray([
|
|
2423
|
+
new FormControl('')
|
|
2424
|
+
])
|
|
2425
|
+
});
|
|
2426
|
+
this.$sections = computed(() => {
|
|
2427
|
+
const hydrated = this.coreFormDesignService.rehydrateSections(this.coreFormDesignService.$placeholderSections());
|
|
2428
|
+
let sections = this.coreFormDesignService.toCoreFormSections(hydrated);
|
|
2429
|
+
// ✅ Inject hidden "id" field into the first cell row of the first section
|
|
2430
|
+
sections = this.coreFormDesignService.injectSystemControls(sections, this.coreFormDesignService.$afInstance().hasIdOfStringType ?? false);
|
|
2431
|
+
return sections;
|
|
2432
|
+
});
|
|
2433
|
+
this.checkError$ = new BehaviorSubject(false);
|
|
2434
|
+
this.controlCategories = [
|
|
2435
|
+
{
|
|
2436
|
+
name: 'Calculated fields',
|
|
2437
|
+
controls: [
|
|
2438
|
+
{
|
|
2439
|
+
flexSize: 12,
|
|
2440
|
+
controlType: EnumFormBaseContolType.CALCULATED,
|
|
2441
|
+
type: 'calculated',
|
|
2442
|
+
field: 'calculated',
|
|
2443
|
+
label: '',
|
|
2444
|
+
value: '',
|
|
2445
|
+
placeholder: 'Calculated',
|
|
2446
|
+
headless: true,
|
|
2447
|
+
}
|
|
2448
|
+
]
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
name: 'Text',
|
|
2452
|
+
controls: [
|
|
2453
|
+
{
|
|
2454
|
+
flexSize: 12,
|
|
2455
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
2456
|
+
type: 'string',
|
|
2457
|
+
field: 'stringBox',
|
|
2458
|
+
label: '',
|
|
2459
|
+
value: '',
|
|
2460
|
+
placeholder: 'String box',
|
|
2461
|
+
headless: true,
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
flexSize: 12,
|
|
2465
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
2466
|
+
type: 'email',
|
|
2467
|
+
field: 'email',
|
|
2468
|
+
label: '',
|
|
2469
|
+
value: '',
|
|
2470
|
+
placeholder: 'Email',
|
|
2471
|
+
headless: true,
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
flexSize: 12,
|
|
2475
|
+
controlType: EnumFormBaseContolType.TEXTAREA,
|
|
2476
|
+
type: 'string',
|
|
2477
|
+
field: 'textarea',
|
|
2478
|
+
label: '',
|
|
2479
|
+
value: '',
|
|
2480
|
+
textareaRows: 2,
|
|
2481
|
+
placeholder: 'Text area',
|
|
2482
|
+
headless: true,
|
|
2483
|
+
}
|
|
2484
|
+
]
|
|
2485
|
+
},
|
|
2486
|
+
{
|
|
2487
|
+
name: 'Numerical',
|
|
2488
|
+
controls: [
|
|
2489
|
+
{
|
|
2490
|
+
flexSize: 12,
|
|
2491
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
2492
|
+
type: 'number',
|
|
2493
|
+
field: 'number',
|
|
2494
|
+
label: '',
|
|
2495
|
+
value: 100,
|
|
2496
|
+
placeholder: 'Number',
|
|
2497
|
+
headless: true,
|
|
2498
|
+
},
|
|
2499
|
+
{
|
|
2500
|
+
flexSize: 12,
|
|
2501
|
+
controlType: EnumFormBaseContolType.CURRENCY,
|
|
2502
|
+
type: 'number',
|
|
2503
|
+
field: 'currency',
|
|
2504
|
+
label: '',
|
|
2505
|
+
value: '',
|
|
2506
|
+
placeholder: 'Currency',
|
|
2507
|
+
headless: true,
|
|
2508
|
+
},
|
|
2509
|
+
]
|
|
2510
|
+
},
|
|
2511
|
+
{
|
|
2512
|
+
name: 'Single selection',
|
|
2513
|
+
controls: [
|
|
2514
|
+
{
|
|
2515
|
+
flexSize: 12,
|
|
2516
|
+
controlType: EnumFormBaseContolType.DROPDOWN,
|
|
2517
|
+
type: 'any',
|
|
2518
|
+
field: 'dropdown',
|
|
2519
|
+
label: '',
|
|
2520
|
+
value: '',
|
|
2521
|
+
dropdownOptions$: new BehaviorSubject([]),
|
|
2522
|
+
shownFrom: 'name',
|
|
2523
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
2524
|
+
placeholder: 'Dropdown',
|
|
2525
|
+
headless: true,
|
|
2526
|
+
},
|
|
2527
|
+
{
|
|
2528
|
+
flexSize: 12,
|
|
2529
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
2530
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
2531
|
+
type: 'any[]',
|
|
2532
|
+
field: 'employeeSeeker',
|
|
2533
|
+
label: '',
|
|
2534
|
+
value: ['Nguyễn A'],
|
|
2535
|
+
boundFrom: 'id',
|
|
2536
|
+
shownFrom: 'fullname',
|
|
2537
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
2538
|
+
placeholder: 'Employee seeker',
|
|
2539
|
+
headless: true,
|
|
2540
|
+
}
|
|
2541
|
+
]
|
|
2542
|
+
},
|
|
2543
|
+
{
|
|
2544
|
+
name: 'Multiple Selection',
|
|
2545
|
+
controls: [
|
|
2546
|
+
// {
|
|
2547
|
+
// flexSize: 12,
|
|
2548
|
+
// controlType: EnumFormBaseContolType.GRIDBUFFER,
|
|
2549
|
+
// type: 'children',
|
|
2550
|
+
// field: 'gridbuffer',
|
|
2551
|
+
// label: '',
|
|
2552
|
+
// value: [],
|
|
2553
|
+
// placeholder: 'Grid buffer',
|
|
2554
|
+
// gridBufferFormSections: [
|
|
2555
|
+
// {
|
|
2556
|
+
// rows: [
|
|
2557
|
+
// [
|
|
2558
|
+
// {
|
|
2559
|
+
// flexSize: 6,
|
|
2560
|
+
// field: 'field1',
|
|
2561
|
+
// type: 'date',
|
|
2562
|
+
// value: '',
|
|
2563
|
+
// controlType: EnumFormBaseContolType.DATEPICKER,
|
|
2564
|
+
// label: 'field 1'
|
|
2565
|
+
// },
|
|
2566
|
+
// {
|
|
2567
|
+
// flexSize: 6,
|
|
2568
|
+
// field: 'field2',
|
|
2569
|
+
// type: 'number',
|
|
2570
|
+
// value: null,
|
|
2571
|
+
// controlType: EnumFormBaseContolType.TEXTBOX,
|
|
2572
|
+
// label: 'field 2'
|
|
2573
|
+
// },
|
|
2574
|
+
// ]
|
|
2575
|
+
// ]
|
|
2576
|
+
// }
|
|
2577
|
+
// ],
|
|
2578
|
+
// gridBufferTableColumns: [
|
|
2579
|
+
// {
|
|
2580
|
+
// caption: 'id',
|
|
2581
|
+
// field: 'id',
|
|
2582
|
+
// hidden: true,
|
|
2583
|
+
// type: 'string',
|
|
2584
|
+
// align: 'left',
|
|
2585
|
+
// width: 0,
|
|
2586
|
+
// },
|
|
2587
|
+
// {
|
|
2588
|
+
// field: 'field1',
|
|
2589
|
+
// width: 200,
|
|
2590
|
+
// caption: 'field 1',
|
|
2591
|
+
// type: 'date',
|
|
2592
|
+
// pipe: EnumCoreTablePipeType.DATE,
|
|
2593
|
+
// align: 'center'
|
|
2594
|
+
// },
|
|
2595
|
+
// {
|
|
2596
|
+
// field: 'field2',
|
|
2597
|
+
// width: 100,
|
|
2598
|
+
// caption: 'field 2',
|
|
2599
|
+
// type: 'number',
|
|
2600
|
+
// align: 'right'
|
|
2601
|
+
// },
|
|
2602
|
+
// ],
|
|
2603
|
+
// headless: true,
|
|
2604
|
+
// tableHeight: 100,
|
|
2605
|
+
// liteMode: true,
|
|
2606
|
+
// },
|
|
2607
|
+
{
|
|
2608
|
+
flexSize: 12,
|
|
2609
|
+
controlType: EnumFormBaseContolType.CHECKLIST,
|
|
2610
|
+
type: 'any[]',
|
|
2611
|
+
field: 'checklist',
|
|
2612
|
+
label: '',
|
|
2613
|
+
value: [],
|
|
2614
|
+
checklistOptions$: new BehaviorSubject([]),
|
|
2615
|
+
shownFrom: 'name',
|
|
2616
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
2617
|
+
placeholder: 'Check list',
|
|
2618
|
+
headless: true,
|
|
2619
|
+
},
|
|
2620
|
+
{
|
|
2621
|
+
flexSize: 12,
|
|
2622
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
2623
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
2624
|
+
multiMode: true,
|
|
2625
|
+
type: 'any[]',
|
|
2626
|
+
field: 'employeeSeekerMultiple',
|
|
2627
|
+
label: '',
|
|
2628
|
+
value: [],
|
|
2629
|
+
objectList$: new BehaviorSubject([]),
|
|
2630
|
+
boundFrom: 'id',
|
|
2631
|
+
shownFrom: 'fullname',
|
|
2632
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
2633
|
+
multiModeTableHeight: 100,
|
|
2634
|
+
placeholder: 'Employee seeker (multi mode)',
|
|
2635
|
+
headless: true,
|
|
2636
|
+
}
|
|
2637
|
+
]
|
|
2638
|
+
},
|
|
2639
|
+
{
|
|
2640
|
+
name: 'Date',
|
|
2641
|
+
controls: [
|
|
2642
|
+
{
|
|
2643
|
+
flexSize: 12,
|
|
2644
|
+
controlType: EnumFormBaseContolType.DATEPICKER,
|
|
2645
|
+
type: 'date',
|
|
2646
|
+
field: 'datePicker',
|
|
2647
|
+
label: '',
|
|
2648
|
+
value: '',
|
|
2649
|
+
placeholder: 'Date picker',
|
|
2650
|
+
showPlaceholder: true,
|
|
2651
|
+
headless: true,
|
|
2652
|
+
},
|
|
2653
|
+
{
|
|
2654
|
+
flexSize: 12,
|
|
2655
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
2656
|
+
type: 'time',
|
|
2657
|
+
field: 'timeBox',
|
|
2658
|
+
label: '',
|
|
2659
|
+
value: null,
|
|
2660
|
+
placeholder: 'Time box',
|
|
2661
|
+
headless: true,
|
|
2662
|
+
},
|
|
2663
|
+
{
|
|
2664
|
+
flexSize: 12,
|
|
2665
|
+
controlType: EnumFormBaseContolType.MONTHSELECTOR,
|
|
2666
|
+
type: 'string',
|
|
2667
|
+
field: 'monthSelector',
|
|
2668
|
+
label: '',
|
|
2669
|
+
value: '',
|
|
2670
|
+
placeholder: 'Month selector',
|
|
2671
|
+
headless: true,
|
|
2672
|
+
},
|
|
2673
|
+
]
|
|
2674
|
+
},
|
|
2675
|
+
{
|
|
2676
|
+
name: 'Other',
|
|
2677
|
+
controls: [
|
|
2678
|
+
{
|
|
2679
|
+
flexSize: 12,
|
|
2680
|
+
controlType: EnumFormBaseContolType.ATTACHMENT,
|
|
2681
|
+
type: 'string',
|
|
2682
|
+
field: 'attachment',
|
|
2683
|
+
label: '',
|
|
2684
|
+
value: '',
|
|
2685
|
+
assignTo: 'unknown',
|
|
2686
|
+
placeholder: 'Attachment',
|
|
2687
|
+
headless: true,
|
|
2688
|
+
},
|
|
2689
|
+
{
|
|
2690
|
+
flexSize: 12,
|
|
2691
|
+
controlType: EnumFormBaseContolType.CHECKBOX,
|
|
2692
|
+
type: 'boolean',
|
|
2693
|
+
field: 'checkbox',
|
|
2694
|
+
label: '',
|
|
2695
|
+
value: '',
|
|
2696
|
+
placeholder: 'Checkbox',
|
|
2697
|
+
headless: true,
|
|
2698
|
+
},
|
|
2699
|
+
]
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
name: 'Form Array',
|
|
2703
|
+
controls: [
|
|
2704
|
+
{
|
|
2705
|
+
flexSize: 12,
|
|
2706
|
+
controlType: EnumFormBaseContolType.FORM_ARRAY,
|
|
2707
|
+
type: 'children',
|
|
2708
|
+
field: 'formArray',
|
|
2709
|
+
label: '',
|
|
2710
|
+
value: '',
|
|
2711
|
+
placeholder: 'FormArray',
|
|
2712
|
+
headless: true,
|
|
2713
|
+
childrenSections: [
|
|
2714
|
+
{
|
|
2715
|
+
rows: [
|
|
2716
|
+
[
|
|
2717
|
+
{
|
|
2718
|
+
field: 'textBox',
|
|
2719
|
+
flexSize: 12,
|
|
2720
|
+
controlType: EnumFormBaseContolType.TEXTBOX,
|
|
2721
|
+
type: 'text',
|
|
2722
|
+
value: '',
|
|
2723
|
+
label: ''
|
|
2724
|
+
}
|
|
2725
|
+
]
|
|
2726
|
+
]
|
|
2727
|
+
}
|
|
2728
|
+
]
|
|
2729
|
+
},
|
|
2730
|
+
]
|
|
2731
|
+
},
|
|
2732
|
+
];
|
|
2733
|
+
/*
|
|
2734
|
+
onDropIntoCell(event: DragEvent, sectionIndex: number, rowIndex: number, colIndex: number): void {
|
|
2735
|
+
event.preventDefault();
|
|
2736
|
+
const template = this.coreFormDesignService.$currentControlTemplate();
|
|
2737
|
+
if (!template) return;
|
|
2738
|
+
|
|
2739
|
+
const section = this.coreFormDesignService.$placeholderSections()[sectionIndex];
|
|
2740
|
+
if (!section.forCalculatedFields && template.type === 'calculated') return;
|
|
2741
|
+
if (!!section.forCalculatedFields && template.type !== 'calculated') return;
|
|
2742
|
+
|
|
2743
|
+
const normalized = this.coreFormDesignService.normalize({
|
|
2744
|
+
...template,
|
|
2745
|
+
field: this.generateFieldNameFor(template),
|
|
2746
|
+
label: this.generateLabelNameFor(template),
|
|
2747
|
+
value: null
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2750
|
+
if (normalized.controlType === EnumFormBaseContolType.CALCULATED) {
|
|
2751
|
+
normalized.returnType = 'number'
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
2755
|
+
const section = sections[sectionIndex];
|
|
2756
|
+
const row = section?.rows[rowIndex];
|
|
2757
|
+
if (!row || row.cells.length <= colIndex) return sections;
|
|
2758
|
+
|
|
2759
|
+
const usedFlex = row.cells.reduce((sum, cell) => sum + (cell?.control?.flexSize ?? 0), 0);
|
|
2760
|
+
const remainingFlex = 12 - usedFlex;
|
|
2761
|
+
const flexSize = Math.max(1, remainingFlex);
|
|
2762
|
+
normalized.flexSize = flexSize;
|
|
2763
|
+
|
|
2764
|
+
const previous = row.cells[colIndex]?.control;
|
|
2765
|
+
|
|
2766
|
+
if (previous && previous.flexSize !== undefined) {
|
|
2767
|
+
|
|
2768
|
+
// AttachmentFieldHelperService.handleAttachmentOverwrite(
|
|
2769
|
+
// previous as IFormBaseControl,
|
|
2770
|
+
// this.coreFormDesignService.getAllControlsFromSections()
|
|
2771
|
+
// );
|
|
2772
|
+
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
const previousField = row.cells[colIndex]?.control?.field ?? null;
|
|
2776
|
+
this.coreFormDesignService.replaceControl(previousField, normalized);
|
|
2777
|
+
|
|
2778
|
+
// 🔹 INSERT FIRST
|
|
2779
|
+
row.cells[colIndex] = { ...row.cells[colIndex], control: normalized };
|
|
2780
|
+
this.coreFormDesignService.$selectedCell.set(row.cells[colIndex]);
|
|
2781
|
+
|
|
2782
|
+
// 🔹 THEN CALL setup
|
|
2783
|
+
if (AttachmentFieldHelperService.isAttachment(normalized)) {
|
|
2784
|
+
const assignToField = AttachmentFieldHelperService.buildHiddenAssignToField(normalized.field);
|
|
2785
|
+
normalized.field = `${normalized.field}Buffer`
|
|
2786
|
+
normalized.assignTo = assignToField.field;
|
|
2787
|
+
AttachmentFieldHelperService.injectAssignToFieldIntoRow(row, assignToField);
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
|
|
2791
|
+
setTimeout(() => {
|
|
2792
|
+
this.coreFormDesignService.setControlPropDirectly(
|
|
2793
|
+
'validators',
|
|
2794
|
+
normalized.validators ?? [],
|
|
2795
|
+
normalized
|
|
2796
|
+
);
|
|
2797
|
+
});
|
|
2798
|
+
|
|
2799
|
+
return sections;
|
|
2800
|
+
});
|
|
2801
|
+
|
|
2802
|
+
this.triggerUpdateSections();
|
|
2803
|
+
this.isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex] = false;
|
|
2804
|
+
this.coreFormDesignService.endDrag();
|
|
2805
|
+
this.openSettingsForCell(this.coreFormDesignService.$selectedCell()!, sectionIndex, rowIndex, colIndex)
|
|
2806
|
+
}
|
|
2807
|
+
*/
|
|
2808
|
+
this.generateFieldNameFor = this.coreFormDesignService.generateFieldNameFor.bind(this.coreFormDesignService);
|
|
2809
|
+
this.generateLabelNameFor = this.coreFormDesignService.generateLabelNameFor.bind(this.coreFormDesignService);
|
|
2810
|
+
this.getAllControlsFromSections = this.coreFormDesignService.getAllControlsFromSections.bind(this.coreFormDesignService);
|
|
2811
|
+
this.getControl = this.coreFormDesignService.getControl.bind(this.coreFormDesignService);
|
|
2812
|
+
this.filteredCells = this.coreFormDesignService.filteredCells;
|
|
2813
|
+
this.coreFormDesignService.resetFormDesignState();
|
|
2814
|
+
effect(() => {
|
|
2815
|
+
const _ = this.coreFormDesignService.$placeholderSections();
|
|
2816
|
+
this.syncFormDesignWithSections();
|
|
2817
|
+
});
|
|
2818
|
+
effect(() => {
|
|
2819
|
+
this.idStream$.next(this.$id());
|
|
2820
|
+
});
|
|
2821
|
+
this.applicationHelpService.activeKey$.pipe(distinctUntilChanged()).subscribe(x => {
|
|
2822
|
+
switch (x) {
|
|
2823
|
+
case 'F3':
|
|
2824
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(true);
|
|
2825
|
+
break;
|
|
2826
|
+
case 'F6':
|
|
2827
|
+
this.coreFormDesignService.$mode.set(EnumFormDesignMode.Preview);
|
|
2828
|
+
break;
|
|
2829
|
+
case 'Escape':
|
|
2830
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(false);
|
|
2831
|
+
this.coreFormDesignService.$mode.set(EnumFormDesignMode.Default);
|
|
2832
|
+
break;
|
|
2833
|
+
}
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
ngOnInit() {
|
|
2837
|
+
this.subscriptions.push(this.mls.lang$.subscribe(x => this.lang = x));
|
|
2838
|
+
if (this.appConfigService.WORKFLOW_ENABLE_DEBUG_MODE) {
|
|
2839
|
+
this.injectedFields$.next([
|
|
2840
|
+
{
|
|
2841
|
+
field: '___submitterEmployeeId',
|
|
2842
|
+
flexSize: 12,
|
|
2843
|
+
type: 'number',
|
|
2844
|
+
controlType: EnumFormBaseContolType.SEEKER,
|
|
2845
|
+
seekerSourceType: EnumCoreFormControlSeekerSourceType.EMPLOYEE_SEEK,
|
|
2846
|
+
getByIdApi: '',
|
|
2847
|
+
getByIdObject$: new BehaviorSubject({}),
|
|
2848
|
+
label: 'Simulated Requester',
|
|
2849
|
+
boundFrom: 'id',
|
|
2850
|
+
shownFrom: 'fullname',
|
|
2851
|
+
value: null,
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
field: "___reason",
|
|
2855
|
+
type: "text",
|
|
2856
|
+
value: '',
|
|
2857
|
+
label: "UI_FORM_CONTROL_LABEL.REASON",
|
|
2858
|
+
flexSize: 12,
|
|
2859
|
+
controlType: EnumFormBaseContolType.TEXTAREA,
|
|
2860
|
+
textareaRows: 5,
|
|
2861
|
+
validators: [
|
|
2862
|
+
{
|
|
2863
|
+
name: 'required',
|
|
2864
|
+
validator: Validators.required,
|
|
2865
|
+
errorMessage: EnumTranslateKey.UI_FORM_CONTROL_ERROR_REQUIRED
|
|
2866
|
+
}
|
|
2867
|
+
],
|
|
2868
|
+
}
|
|
2869
|
+
]);
|
|
2870
|
+
}
|
|
2871
|
+
else {
|
|
2872
|
+
this.injectedFields$.next([
|
|
2873
|
+
{
|
|
2874
|
+
field: "___reason",
|
|
2875
|
+
type: "text",
|
|
2876
|
+
value: '',
|
|
2877
|
+
label: "UI_FORM_CONTROL_LABEL.REASON",
|
|
2878
|
+
flexSize: 12,
|
|
2879
|
+
controlType: EnumFormBaseContolType.TEXTAREA,
|
|
2880
|
+
textareaRows: 5,
|
|
2881
|
+
validators: [
|
|
2882
|
+
{
|
|
2883
|
+
name: 'required',
|
|
2884
|
+
validator: Validators.required,
|
|
2885
|
+
errorMessage: EnumTranslateKey.UI_FORM_CONTROL_ERROR_REQUIRED
|
|
2886
|
+
}
|
|
2887
|
+
],
|
|
2888
|
+
}
|
|
2889
|
+
]);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
switchJson() {
|
|
2893
|
+
if (this.$jsonMode() === 'full') {
|
|
2894
|
+
this.$jsonMode.set('selectedField');
|
|
2895
|
+
}
|
|
2896
|
+
else {
|
|
2897
|
+
this.$jsonMode.set('full');
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
onDragStart(event, control) {
|
|
2901
|
+
this.coreFormDesignService.beginDragTemplate(control);
|
|
2902
|
+
}
|
|
2903
|
+
onDragOver(event, section, control) {
|
|
2904
|
+
event.preventDefault(); // necessary to allow drop
|
|
2905
|
+
if ((!!section.forCalculatedFields && this.coreFormDesignService.$draggingControl()?.type === 'calculated') ||
|
|
2906
|
+
(!section.forCalculatedFields && this.coreFormDesignService.$draggingControl()?.type !== 'calculated')) {
|
|
2907
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
2908
|
+
}
|
|
2909
|
+
else {
|
|
2910
|
+
event.dataTransfer.dropEffect = 'none';
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
onDragEnter(placeholderId) {
|
|
2914
|
+
this.isDragOverMap[placeholderId] = true;
|
|
2915
|
+
}
|
|
2916
|
+
onDragLeave(placeholderId) {
|
|
2917
|
+
this.isDragOverMap[placeholderId] = false;
|
|
2918
|
+
}
|
|
2919
|
+
addCalculatedSection() {
|
|
2920
|
+
this.coreFormDesignService.$placeholderSections.update(sections => [
|
|
2921
|
+
{
|
|
2922
|
+
caption: `Calculated Section`,
|
|
2923
|
+
forCalculatedFields: true,
|
|
2924
|
+
hidden: true,
|
|
2925
|
+
rows: [{ cells: [{ flexSize: 12 }] }]
|
|
2926
|
+
},
|
|
2927
|
+
...sections,
|
|
2928
|
+
]);
|
|
2929
|
+
}
|
|
2930
|
+
addSection() {
|
|
2931
|
+
this.coreFormDesignService.$placeholderSections.update(sections => [
|
|
2932
|
+
...sections,
|
|
2933
|
+
{
|
|
2934
|
+
caption: `Section ${sections.length + 1}`,
|
|
2935
|
+
rows: [{ cells: [{ flexSize: 12 }] }]
|
|
2936
|
+
}
|
|
2937
|
+
]);
|
|
2938
|
+
}
|
|
2939
|
+
deleteSection(sectionIndex) {
|
|
2940
|
+
const sections = this.coreFormDesignService.$placeholderSections();
|
|
2941
|
+
const section = sections[sectionIndex];
|
|
2942
|
+
const hasControls = section?.rows?.some(row => row?.cells?.some(cell => !!cell?.control));
|
|
2943
|
+
if (hasControls) {
|
|
2944
|
+
const confirmed = confirm('This section contains controls. Are you sure you want to delete it?');
|
|
2945
|
+
if (!confirmed)
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2948
|
+
this.coreFormDesignService.$placeholderSections.update(prev => prev.filter((_, idx) => idx !== sectionIndex));
|
|
2949
|
+
}
|
|
2950
|
+
deleteRow(sectionIndex, rowIndex) {
|
|
2951
|
+
const sections = this.coreFormDesignService.$placeholderSections();
|
|
2952
|
+
const section = sections[sectionIndex];
|
|
2953
|
+
const row = section?.rows?.[rowIndex];
|
|
2954
|
+
if (!row)
|
|
2955
|
+
return; // safety check
|
|
2956
|
+
const hasControls = row.cells?.some(cell => !!cell?.control);
|
|
2957
|
+
if (hasControls) {
|
|
2958
|
+
const confirmed = confirm('This row contains controls. Are you sure you want to delete it?');
|
|
2959
|
+
if (!confirmed)
|
|
2960
|
+
return; // user canceled
|
|
2961
|
+
}
|
|
2962
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
2963
|
+
return sections.map((section, sIdx) => {
|
|
2964
|
+
if (sIdx !== sectionIndex)
|
|
2965
|
+
return section;
|
|
2966
|
+
return {
|
|
2967
|
+
...section,
|
|
2968
|
+
rows: (section.rows ?? []).filter((_, rIdx) => rIdx !== rowIndex)
|
|
2969
|
+
};
|
|
2970
|
+
});
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
addRow(sectionIndex) {
|
|
2974
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
2975
|
+
const section = sections[sectionIndex];
|
|
2976
|
+
if (section) {
|
|
2977
|
+
section.rows.push({ cells: [{ flexSize: 12 }] });
|
|
2978
|
+
}
|
|
2979
|
+
return sections;
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
addColumn(row) {
|
|
2983
|
+
if (!row || row.cells.length >= 4)
|
|
2984
|
+
return;
|
|
2985
|
+
const usedFlex = row.cells.reduce((sum, c) => sum + (c.flexSize ?? 0), 0);
|
|
2986
|
+
// ✅ Case 1: Normal case – still space available
|
|
2987
|
+
if (usedFlex < 12) {
|
|
2988
|
+
const remaining = 12 - usedFlex;
|
|
2989
|
+
row.cells.push({ flexSize: Math.min(3, remaining) });
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
// 💥 Case 2: Full row — try to rebalance
|
|
2993
|
+
if (usedFlex === 12) {
|
|
2994
|
+
const count = row.cells.length;
|
|
2995
|
+
if (count === 1) {
|
|
2996
|
+
// 12 → 6 + 6
|
|
2997
|
+
row.cells[0].flexSize = 6;
|
|
2998
|
+
row.cells.push({ flexSize: 6 });
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
if (count === 2) {
|
|
3002
|
+
// 6 + 6 → 4 + 4 + 4
|
|
3003
|
+
row.cells.forEach(c => (c.flexSize = 4));
|
|
3004
|
+
row.cells.push({ flexSize: 4 });
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
if (count === 3) {
|
|
3008
|
+
// 4 + 4 + 4 → 3 + 3 + 3 + 3
|
|
3009
|
+
row.cells.forEach(c => (c.flexSize = 3));
|
|
3010
|
+
row.cells.push({ flexSize: 3 });
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
// ❌ Already 4 cells, each 3 — can’t split further
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
// 🔧 Optional fallback logic for unknown edge cases
|
|
3017
|
+
const maxIndex = row.cells.reduce((maxI, c, i, arr) => (c.flexSize ?? 0) > (arr[maxI]?.flexSize ?? 0) ? i : maxI, 0);
|
|
3018
|
+
const maxCell = row.cells[maxIndex];
|
|
3019
|
+
if ((maxCell?.flexSize ?? 0) <= 3)
|
|
3020
|
+
return; // nothing to shrink
|
|
3021
|
+
maxCell.flexSize -= 3;
|
|
3022
|
+
row.cells.push({ flexSize: 3 });
|
|
3023
|
+
}
|
|
3024
|
+
onCorePageHeaderButtonClicked(e) {
|
|
3025
|
+
if (e.code === EnumCoreButtonVNSCode.HEADER_SWITCH_VIEW) {
|
|
3026
|
+
if (this.coreFormDesignService.$mode() === EnumFormDesignMode.Default) {
|
|
3027
|
+
const sections = this.coreFormDesignService.toCoreFormSections(this.coreFormDesignService.$placeholderSections());
|
|
3028
|
+
this.coreFormDesignService.$liveFormSections.set(sections);
|
|
3029
|
+
}
|
|
3030
|
+
setTimeout(() => this.coreFormDesignService.$mode.set(this.coreFormDesignService.$mode() === EnumFormDesignMode.Preview ? EnumFormDesignMode.Default : EnumFormDesignMode.Preview));
|
|
3031
|
+
}
|
|
3032
|
+
else if (e.code === EnumCoreButtonVNSCode.HEADER_SAVE) {
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
closePreview() {
|
|
3036
|
+
this.coreFormDesignService.$mode.set(EnumFormDesignMode.Default);
|
|
3037
|
+
}
|
|
3038
|
+
onPreviewSubmit(e) {
|
|
3039
|
+
alert(JSON.stringify(e, null, 2));
|
|
3040
|
+
}
|
|
3041
|
+
onCellClicked(sectionIndex, rowIndex, colIndex) {
|
|
3042
|
+
const row = this.coreFormDesignService.$placeholderSections()[sectionIndex].rows[rowIndex];
|
|
3043
|
+
if (!!row) {
|
|
3044
|
+
// Toggle selection
|
|
3045
|
+
if (!!row.cells && row.cells.length > colIndex) {
|
|
3046
|
+
row.cells[colIndex].selected = !row.cells[colIndex].selected;
|
|
3047
|
+
}
|
|
3048
|
+
// Only allow max 2 selections
|
|
3049
|
+
const selected = row.cells.filter(c => c?.selected);
|
|
3050
|
+
if (selected.length > 2) {
|
|
3051
|
+
row.cells.forEach(c => c && (c.selected = false));
|
|
3052
|
+
row[colIndex].selected = true;
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
canMergeCells(sectionIndex, rowIndex) {
|
|
3057
|
+
const row = this.coreFormDesignService.$placeholderSections()[sectionIndex].rows[rowIndex];
|
|
3058
|
+
const selected = row?.cells.map((c, i) => ({ c, i })).filter(x => x.c?.selected);
|
|
3059
|
+
if (selected?.length !== 2)
|
|
3060
|
+
return false;
|
|
3061
|
+
const [a, b] = selected;
|
|
3062
|
+
if (a.c.control || b.c.control)
|
|
3063
|
+
return false;
|
|
3064
|
+
return Math.abs(a.i - b.i) === 1; // Must be neighbors
|
|
3065
|
+
}
|
|
3066
|
+
mergeCells(sectionIndex, rowIndex) {
|
|
3067
|
+
const row = this.coreFormDesignService.$placeholderSections()[sectionIndex].rows[rowIndex];
|
|
3068
|
+
if (!row)
|
|
3069
|
+
return;
|
|
3070
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
3071
|
+
const selected = row.cells
|
|
3072
|
+
.map((c, i) => ({ c, i }))
|
|
3073
|
+
.filter(x => x.c?.selected);
|
|
3074
|
+
if (selected.length !== 2)
|
|
3075
|
+
return sections;
|
|
3076
|
+
const [a, b] = selected.sort((a, b) => a.i - b.i);
|
|
3077
|
+
const mergedFlex = (typeof a.c.flexSize === 'number' ? a.c.flexSize : 3) +
|
|
3078
|
+
(typeof b.c.flexSize === 'number' ? b.c.flexSize : 3);
|
|
3079
|
+
const mergedCell = {
|
|
3080
|
+
//control: { ...a.c.control, flexSize: mergedFlex }, // keep control data
|
|
3081
|
+
flexSize: mergedFlex,
|
|
3082
|
+
selected: false
|
|
3083
|
+
};
|
|
3084
|
+
row.cells.splice(a.i, 2, mergedCell); // replace both cells with one
|
|
3085
|
+
return sections;
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
openSettingsForCell(cell, sectionIndex, rowIndex, colIndex) {
|
|
3089
|
+
this.coreFormDesignService.$sectionIndex.set(sectionIndex);
|
|
3090
|
+
this.coreFormDesignService.$rowIndex.set(rowIndex);
|
|
3091
|
+
this.coreFormDesignService.$colIndex.set(colIndex);
|
|
3092
|
+
this.coreFormDesignService.$selectedCell.set({ ...cell });
|
|
3093
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(true);
|
|
3094
|
+
}
|
|
3095
|
+
deleteField(row, sectionIndex, rowIndex, colIndex) {
|
|
3096
|
+
if (!row)
|
|
3097
|
+
return;
|
|
3098
|
+
const dCell = row.cells[colIndex];
|
|
3099
|
+
const currentControl = dCell.control;
|
|
3100
|
+
if (!currentControl)
|
|
3101
|
+
return;
|
|
3102
|
+
const currentPlaceholderSections = this.coreFormDesignService.$placeholderSections();
|
|
3103
|
+
if (!currentPlaceholderSections[sectionIndex].rows[rowIndex])
|
|
3104
|
+
return;
|
|
3105
|
+
currentPlaceholderSections[sectionIndex].rows[rowIndex].cells[colIndex].control = undefined;
|
|
3106
|
+
this.coreFormDesignService.$selectedCell.set(null);
|
|
3107
|
+
this.coreFormDesignService.$placeholderSections.set([...currentPlaceholderSections]);
|
|
3108
|
+
}
|
|
3109
|
+
onCaptionEditEnd(index, event) {
|
|
3110
|
+
const newCaption = event.target.innerText.trim();
|
|
3111
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
3112
|
+
sections[index].caption = newCaption;
|
|
3113
|
+
return sections;
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
onFieldCaptionEditEnd(cell, event) {
|
|
3117
|
+
const element = event.target;
|
|
3118
|
+
const newLabel = element.innerText.trim();
|
|
3119
|
+
// Fallback if empty
|
|
3120
|
+
cell.control.label = newLabel || 'Unnamed Field';
|
|
3121
|
+
this.triggerUpdateSections();
|
|
3122
|
+
}
|
|
3123
|
+
insertSection() {
|
|
3124
|
+
}
|
|
3125
|
+
insertRow(sectionIndex, rowIndex) {
|
|
3126
|
+
const sections = this.coreFormDesignService.$placeholderSections();
|
|
3127
|
+
const section = sections[sectionIndex];
|
|
3128
|
+
if (!section)
|
|
3129
|
+
return;
|
|
3130
|
+
const newRow = {
|
|
3131
|
+
cells: [
|
|
3132
|
+
{ flexSize: 12 },
|
|
3133
|
+
]
|
|
3134
|
+
};
|
|
3135
|
+
section.rows.splice(rowIndex + 1, 0, newRow);
|
|
3136
|
+
this.coreFormDesignService.$placeholderSections.set([...sections]);
|
|
3137
|
+
}
|
|
3138
|
+
closeFieldSettingPanel() {
|
|
3139
|
+
this.coreFormDesignService.$fieldSettingPanelOpen.set(false);
|
|
3140
|
+
this.coreFormDesignService.$selectedCell.set(null);
|
|
3141
|
+
this.coreFormDesignService.$currentControlTemplate.set(null);
|
|
3142
|
+
this.coreFormDesignService.$currentControl.set(null);
|
|
3143
|
+
}
|
|
3144
|
+
ngAfterViewInit() {
|
|
3145
|
+
setTimeout(() => {
|
|
3146
|
+
this.subscriptions.push(this.getByIdStream$.subscribe(x => {
|
|
3147
|
+
this.$loading.set(false);
|
|
3148
|
+
if (x.ok && x.status === 200 && x.body?.statusCode === 200) {
|
|
3149
|
+
this.coreFormDesignService.$afInstance.set(this.coreFormDesignService.mapFromAfInstanceDTO(x.body.innerBody));
|
|
3150
|
+
this.coreFormDesignService.syncPlaceholderSectionsFromInstance();
|
|
3151
|
+
this.coreFormDesignService.$shouldPatchMetadataForm.set(true);
|
|
3152
|
+
const saveLiveJson = this.coreFormDesignService.$saveLiveJson();
|
|
3153
|
+
setTimeout(() => this.wfs.$initialFormDesignJson.set(saveLiveJson));
|
|
3154
|
+
}
|
|
3155
|
+
}));
|
|
3156
|
+
if (!this.$idAsInput()) {
|
|
3157
|
+
this.subscriptions.push(this.route.params.subscribe(x => {
|
|
3158
|
+
let id = '0';
|
|
3159
|
+
try {
|
|
3160
|
+
id = atob(x['id']);
|
|
3161
|
+
this.coreFormDesignService.$afInstance.update(instance => ({
|
|
3162
|
+
...instance,
|
|
3163
|
+
id
|
|
3164
|
+
}));
|
|
3165
|
+
}
|
|
3166
|
+
catch (err) {
|
|
3167
|
+
if (isDevMode()) {
|
|
3168
|
+
this.alertService.error(err.toString(), noneAutoClosedAlertOptions);
|
|
3169
|
+
}
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
this.idStream$.next(id);
|
|
3173
|
+
}));
|
|
3174
|
+
}
|
|
3175
|
+
console.warn('InitialFormDesignJson on load', this.wfs.$initialFormDesignJson());
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
onControlParamsChange(updated) {
|
|
3179
|
+
this.coreFormDesignService.$placeholderSections.update(sections => {
|
|
3180
|
+
const cell = this.coreFormDesignService.$selectedCell();
|
|
3181
|
+
if (!cell)
|
|
3182
|
+
return sections;
|
|
3183
|
+
// Patch the selected cell
|
|
3184
|
+
Object.assign(cell.control, updated);
|
|
3185
|
+
return sections;
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
get normalizedSelectedControl() {
|
|
3189
|
+
const cell = this.coreFormDesignService.$selectedCell();
|
|
3190
|
+
if (!cell?.control) {
|
|
3191
|
+
throw new Error('No control is currently selected.');
|
|
3192
|
+
}
|
|
3193
|
+
return this.coreFormDesignService.normalize(cell.control);
|
|
3194
|
+
}
|
|
3195
|
+
triggerUpdateSections() {
|
|
3196
|
+
const newValue = this.coreFormDesignService.$placeholderSections();
|
|
3197
|
+
this.coreFormDesignService.$placeholderSections.set([...newValue]);
|
|
3198
|
+
}
|
|
3199
|
+
syncFormDesignWithSections() {
|
|
3200
|
+
const existingControls = Object.keys(this.formDesign.controls);
|
|
3201
|
+
const expectedControls = [];
|
|
3202
|
+
for (const section of this.coreFormDesignService.$placeholderSections()) {
|
|
3203
|
+
for (const row of section.rows) {
|
|
3204
|
+
for (const cell of row?.cells || []) {
|
|
3205
|
+
const control = cell?.control;
|
|
3206
|
+
if (control?.field) {
|
|
3207
|
+
expectedControls.push(control.field);
|
|
3208
|
+
if (!this.formDesign.contains(control.field)) {
|
|
3209
|
+
this.formDesign.addControl(control.field, new FormControl(control.value ?? null));
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
// 🧹 Remove controls that no longer exist
|
|
3216
|
+
for (const field of existingControls) {
|
|
3217
|
+
if (!expectedControls.includes(field)) {
|
|
3218
|
+
this.formDesign.removeControl(field);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
isRequired(control) {
|
|
3223
|
+
if (!control || !control.validators)
|
|
3224
|
+
return false;
|
|
3225
|
+
return control.validators.some((v) => v.name === 'required' || v.name === 'requiredTrue');
|
|
3226
|
+
}
|
|
3227
|
+
onFormDesignCreated(e) {
|
|
3228
|
+
console.log("onFormDesignCreated", e);
|
|
3229
|
+
}
|
|
3230
|
+
onLiveFormCreated(e) {
|
|
3231
|
+
console.log("onLiveFormCreated", e);
|
|
3232
|
+
}
|
|
3233
|
+
showMetadata() {
|
|
3234
|
+
this.coreFormDesignService.$showFormMetadata.set(true);
|
|
3235
|
+
}
|
|
3236
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormDesignComponent, deps: [{ token: MultiLanguageService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3237
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: CoreFormDesignComponent, isStandalone: true, selector: "core-form-design", inputs: { $idAsInput: { classPropertyName: "$idAsInput", publicName: "$idAsInput", isSignal: true, isRequired: false, transformFunction: null }, $id: { classPropertyName: "$id", publicName: "$id", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "$settingPanel", first: true, predicate: ["settingPanel"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"core-form-design-container fs-13\" [hotKeys]=\"['F3', 'F6', 'Escape']\">\r\n\r\n <core-page-header title=\"UI.COMPONENT_TITLE.WORKFLOW_FORM_DESIGN\" (buttonClick)=\"onCorePageHeaderButtonClicked($event)\"></core-page-header>\r\n\r\n <ng-template #formMetadata>\r\n <form-metadata></form-metadata>\r\n </ng-template>\r\n\r\n @if (coreFormDesignService.$showFormArrayDesign()) {\r\n <core-form-array-layout-editor />\r\n }\r\n\r\n @if (!coreFormDesignService.$isInWorkflowDesign()) {\r\n <aside class=\"af-metadata-overlay\" [@slideFromTopFadeIn]=\"coreFormDesignService.$showFormMetadata() ? 'in' : 'out'\"\r\n [class.shown]=\"coreFormDesignService.$showFormMetadata()\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"formMetadata\"></ng-container>\r\n </aside>\r\n }\r\n\r\n <div \r\n class=\"field-setting-panel\" \r\n [class.open]=\"coreFormDesignService.$fieldSettingPanelOpen()\" \r\n [class.calculated-type]=\"coreFormDesignService.$selectedCell()?.control?.type==='calculated'\"\r\n [ngStyle]=\"{ zIndex: $zIndex() }\"\r\n #settingPanel\r\n >\r\n @if (!!(this.coreFormDesignService.$selectedCell())) {\r\n <field-setting></field-setting>\r\n }\r\n </div>\r\n\r\n <div class=\"form-design-left\">\r\n\r\n @switch ($leftPanelMode()) {\r\n @case (enumLeftPanelMode.FieldCollection) {\r\n @for (category of controlCategories; track $index) {\r\n <div class=\"category-name\">\r\n {{ category.name }}\r\n </div>\r\n <ul>\r\n @for (control of category.controls; track $index) {\r\n <li draggable=\"true\" (dragstart)=\"onDragStart($event, control)\" [class.full-width]=\"control.controlType === 'SEEKER' || control.controlType === 'ATTACHMENT' || control.controlType === 'GRIDBUFFER'\">\r\n @if (control.controlType === 'GRIDBUFFER') {\r\n <div class=\"grid-buffer-wrapper\">\r\n <core-control [control]=\"control\" [form]=\"form\" [checkError$]=\"checkError$\"></core-control>\r\n </div>\r\n } @else {\r\n <core-control [control]=\"control\" [form]=\"form\" [checkError$]=\"checkError$\"></core-control>\r\n }\r\n </li>\r\n <div class=\"space\"></div>\r\n }\r\n </ul>\r\n }\r\n }\r\n @case (enumLeftPanelMode.Json) {\r\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"switchJson()\">{{ $jsonMode() }}</button>\r\n @if ($jsonMode()==='selectedField') {\r\n <pre class=\"json-preview\">{{ coreFormDesignService.$currentControlJson() }}</pre>\r\n } @else {\r\n <pre class=\"json-preview\">{{ coreFormDesignService.$saveLiveJson() }}</pre>\r\n }\r\n \r\n }\r\n }\r\n\r\n </div>\r\n\r\n <div class=\"form-design-right\">\r\n\r\n <core-form-group-editor [$idAsInput]=\"true\" />\r\n <!-- <pre>{{ $injectedFieldsJson() }}</pre> -->\r\n\r\n \r\n <!-- @if (coreFormDesignService.$mode() === enumType.Default) {\r\n <div class=\"form-wrapper\">\r\n\r\n @if (!!coreFormDesignService.$isInWorkflowDesign()) {\r\n <form-metadata></form-metadata>\r\n }\r\n\r\n <form [formGroup]=\"formDesign\" autocomplete=\"off\">\r\n <div class=\"form-cells\">\r\n\r\n <div class=\"form-tool-bar\">\r\n @if (!$hasCalculatedSection()) {\r\n <button class=\"core-button-vns-container\" (click)=\"addCalculatedSection()\">+ Calculated fields</button>\r\n }\r\n <button class=\"core-button-vns-container\" (click)=\"addSection()\">+ Add Section</button>\r\n </div>\r\n \r\n @for (section of coreFormDesignService.$placeholderSections(); track $index; let sectionIndex = $index) {\r\n <div class=\"form-section-placeholder\">\r\n\r\n <div class=\"section-header\">\r\n\r\n <div class=\"section-img-wrapper\">\r\n <div class=\"section-img\" [class.calculated]=\"section.forCalculatedFields\"></div>\r\n </div>\r\n\r\n <label [attr.contenteditable]=\"!section.forCalculatedFields\"\r\n [htmlTooltip]=\"!!section.forCalculatedFields ? calculatedFieldsTooltip : null\"\r\n (blur)=\"onCaptionEditEnd(sectionIndex, $event)\"\r\n (keydown.enter)=\"onCaptionEditEnd(sectionIndex, $event); $event.preventDefault()\"\r\n >{{ section.caption }}</label>\r\n\r\n <div class=\"section-tool pointer\" (click)=\"deleteSection(sectionIndex)\" [appTooltip]=\"'Delete section'\" [showAnyway]=\"true\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n \r\n </div>\r\n \r\n @for (row of section.rows; track $index; let rowIndex = $index) {\r\n\r\n <div class=\"form-row-wrapper\">\r\n <div class=\"form-row\">\r\n @for (cell of filteredCells(row); track $index; let colIndex = $index) {\r\n <div class=\"form-cell drop-target\" \r\n [ngStyle]=\"{ flex: (cell.flexSize ?? 0) + ' 1 0%' }\"\r\n [attr.data-flex]=\"cell.flexSize ?? 0\"\r\n (drop)=\"onDropIntoCell($event, sectionIndex, rowIndex, colIndex)\"\r\n (dragover)=\"onDragOver($event, section, cell.control!)\"\r\n (dragenter)=\"onDragEnter(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n (dragleave)=\"onDragLeave(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n [class.selected]=\"cell?.selected\"\r\n [class.dragging-over]=\"isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]\"\r\n [class.has-control]=\"!!cell && !!cell.control && !!getControl(cell)\"\r\n (click)=\"onCellClicked(sectionIndex, rowIndex, colIndex)\"\r\n >\r\n\r\n\r\n @if (!isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]) {\r\n\r\n @if (!!cell && !!cell.control && !!getControl(cell)) {\r\n <label contenteditable=\"true\"\r\n (blur)=\"onFieldCaptionEditEnd(cell, $event)\"\r\n (keydown.enter)=\"onFieldCaptionEditEnd(cell, $event); $event.preventDefault()\"\r\n [class.d-none]=\"!!getControl(cell)?.hidden\"\r\n [class.required]=\"!!cell && !!cell.control && !!getControl(cell) && isRequired(getControl(cell))\"\r\n >{{ cell.control.label || 'label' }}</label>\r\n\r\n @if (cell.control.controlType==='CALCULATED') {\r\n <div class=\"control-wrapper\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\">\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n </div>\r\n } @else {\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n }\r\n \r\n <div class=\"field-toolbar\">\r\n <ul>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Show settings'\">\r\n <i class=\"feather-settings\"></i>\r\n </div>\r\n </li>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"deleteField(row, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Remove field from cell'\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </li>\r\n </ul>\r\n </div>\r\n \r\n } @else {\r\n <div class=\"placeholder\" [class.calculated]=\"!!section.forCalculatedFields\">\r\n {{ !!section.forCalculatedFields ? 'Drop calculated field here' : 'Drop here'}}\r\n </div>\r\n }\r\n\r\n @if (cell?.selected && canMergeCells(sectionIndex, rowIndex)) {\r\n <div class=\"merge-toolbar\">\r\n <button (click)=\"mergeCells(sectionIndex, rowIndex)\">\uD83D\uDD17 Merge</button>\r\n </div>\r\n }\r\n \r\n }\r\n\r\n </div>\r\n }\r\n </div>\r\n <div class=\"row-tool-bar\">\r\n <div class=\"icon-wrapper\" (click)=\"deleteRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Delete row'\" [showAnyway]=\"true\"><i class=\"feather-x\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addRow(sectionIndex)\" [appTooltip]=\"'Add row'\" [showAnyway]=\"true\"><i class=\"feather-arrow-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"insertRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Insert row'\" [showAnyway]=\"true\"><i class=\"feather-corner-right-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addColumn(row)\" [appTooltip]=\"'Add column'\" [showAnyway]=\"true\"><i class=\"feather-arrow-right\"></i></div>\r\n </div>\r\n </div>\r\n\r\n }\r\n </div>\r\n }\r\n \r\n \r\n </div>\r\n </form>\r\n </div>\r\n } @else if (!!$sections()) {\r\n\r\n @if(!!(injectedFields$ | async) && !!(injectedFields$ | async)!.length) {\r\n\r\n @if (!!coreFormDesignService.$afInstance().normalMode) {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" />\r\n } @else {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" ($onClose)=\"closePreview()\" />\r\n }\r\n\r\n } @else {\r\n <h2>COULD NOT UPDATE injectedFields$</h2>\r\n }\r\n\r\n } @else {\r\n <h2>$sections() empty / null / undefined</h2>\r\n } -->\r\n\r\n\r\n \r\n </div>\r\n @if ($loading()) {\r\n <core-toast-loading></core-toast-loading>\r\n }\r\n\r\n <ng-template #calculatedFieldsTooltip>\r\n <div style=\"max-width: 280px;\">\r\n <strong>Calculated Fields</strong><br />\r\n <p></p>\r\n These fields are hidden from the form and cannot be edited directly.<br /><br />\r\n - They are computed automatically during workflow execution.<br />\r\n - Each field depends on one or more standard input fields.<br />\r\n - The calculation is evaluated using either an SQL query engine (like <strong>Dapper</strong>) or an in-memory expression engine (like <strong>Dynamic Expresso</strong>).<br /><br />\r\n In both cases, the result is a <code>0</code> or <code>1</code>, representing <strong>false</strong> or <strong>true</strong>.<br /><br />\r\n These results can be used to influence workflow logic, rule trees, or approval paths.\r\n </div>\r\n </ng-template>\r\n \r\n</div>", styles: ["@charset \"UTF-8\";.core-form-design-container{position:relative;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));width:100%;overflow:hidden;background-color:#eff0f1;font-size:13px}.core-form-design-container .core-button-vns-container{margin-right:8px!important}.core-form-design-container .core-button-vns-container:last-child{background-color:#000;color:#fff;margin-right:0}.core-form-design-container .af-metadata-overlay{position:fixed;top:60px;left:66px;background:#fff;box-shadow:.4rem 0 2rem #0000002e;z-index:1000;width:1214px;height:calc(100vh - 75px);overflow-y:auto;pointer-events:none}.core-form-design-container .af-metadata-overlay.shown{pointer-events:auto}.core-form-design-container *{border-radius:0}.core-form-design-container ul,.core-form-design-container li{padding:0}.core-form-design-container li{max-width:200px}.core-form-design-container ul div.space{display:block;height:15px}.core-form-design-container li.full-width{max-width:100%}.core-form-design-container .field-setting-panel{display:block;position:fixed;width:360px;height:100vh;top:0;right:-360px;background-color:#fff;box-shadow:.4rem 0 2rem #0000002e;transition:right .5s ease-out}.core-form-design-container .field-setting-panel .close-wrapper{width:32px;height:32px;position:absolute;top:15px;right:15px;display:flex;background-color:#848484;border-radius:50%}.core-form-design-container .field-setting-panel .close-wrapper i{width:16px;height:16px;font-size:18px;color:#848484}.core-form-design-container .field-setting-panel .panel-caption{margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #ddd;width:100%;overflow:hidden;text-wrap:nowrap;text-overflow:ellipsis}.core-form-design-container .field-setting-panel.calculated-type{width:500px;right:-500px}.core-form-design-container .field-setting-panel.calculated-type textarea{border-radius:8px;height:150px}.core-form-design-container .field-setting-panel.open{right:0}.core-form-design-container .form-design-left{position:absolute;top:var(--size-core-page-header-height);bottom:0;left:0;z-index:1;width:360px;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));padding:15px;padding-right:calc(15px + var(--size-scrollbar-width));background-color:#87ceeb;overflow-y:hidden}.core-form-design-container .form-design-left .category-name{color:#fff}.core-form-design-container .form-design-left .grid-buffer-wrapper{background-color:#fff;padding:15px}.core-form-design-container .form-design-left ul:last-child{padding-bottom:400px}.core-form-design-container .form-design-left .category-name{margin-bottom:15px}.core-form-design-container .form-design-left .json-preview{background-color:#fff;color:#333;padding:1rem 1.5rem;box-shadow:0 2px 6px #00000014;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;overflow-x:auto;line-height:1.5;width:330px;height:calc(100vh - 205px)}.core-form-design-container .form-design-left button{height:35px;border-radius:0;margin-bottom:15px;color:#fff;border:none;font-size:13px;background-color:transparent}.core-form-design-container .form-design-left:hover{overflow-y:auto;padding-right:15px}.core-form-design-container .form-design-right{width:100%;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));padding:15px 15px 15px 375px;overflow-y:auto}.core-form-design-container .form-design-right .form-wrapper{width:100%;overflow-y:visible;background-color:#fff}.core-form-design-container .form-design-right .form-wrapper button{margin-left:12px;padding:4px 10px;border:1px solid #ccc;cursor:pointer;transition:all .2s;width:110px}.core-form-design-container .form-design-right .form-wrapper button:hover{background-color:#e4f0ff;border-color:#007bff;color:#007bff}.core-form-design-container .form-design-right .form-wrapper .form-tool-bar{display:flex;align-items:center;justify-content:flex-end}.core-form-design-container .form-design-right .form-wrapper .form-tool-bar>button{width:150px;margin:0}.core-form-design-container .form-design-right .form-wrapper .form-cells{display:flex;flex-direction:column;gap:32px;padding:20px 15px 15px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder{padding:12px 15px;border:1px dashed #ccc;background-color:#fff;box-shadow:0 2px 6px #0000000a}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header{display:flex;position:relative;align-items:center;justify-content:flex-start;font-weight:600;margin-bottom:12px;color:#333}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img{width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center;margin-right:8px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);background-color:transparent;border-radius:0}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool{position:absolute;right:5px;top:6px;display:none;z-index:2;color:gray}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool i{font-size:24px;color:#848484}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header:hover .section-tool{display:block}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .row-tool-bar{display:flex;align-items:center;justify-content:flex-end;margin-bottom:8px;height:24px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-row{display:flex;gap:15px;margin-bottom:15px;min-height:50px;background-color:#d3d3d3;padding:8px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell{flex:1;min-height:90px;background-color:#fcfcfc;border:1px dashed #ccc;position:relative;padding:20px 10px 10px;transition:border .2s ease-in-out}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell:before{content:attr(data-flex);position:absolute;top:2px;left:4px;font-size:10px;color:#999}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell label.required:after{content:\" *\";color:#ff040b}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar{position:absolute;right:7px;top:6px;display:none;z-index:2;color:gray}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul{display:flex}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div{display:flex;width:24px;height:24px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div i{font-size:18px;width:18px;height:18px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.has-control:hover .field-toolbar{display:flex}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.active-drop{border-color:#007bff;background-color:#eef6ff;transition:.2s}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .placeholder{display:flex;align-items:center;justify-content:center;background-color:#fff;color:#007bff;padding:15px 0;width:100%;cursor:default}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .placeholder.calculated{color:#ff4500}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper{width:24px;height:24px;border-radius:50%;padding:0;cursor:pointer;display:none;color:gray;border:1px solid gray;margin-left:8px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper i{font-size:18px;width:18px;height:18px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper:hover .icon-wrapper{display:flex}.core-form-design-container .form-design-right .form-wrapper .form-cell.selected{background-color:#dff6dd}.core-form-design-container .form-design-right .form-wrapper .merge-toolbar{text-align:center;margin-top:4px}.core-form-design-container .form-design-right .form-wrapper.live-form{padding-bottom:15px}.core-form-design-container .form-design-right .form-wrapper.live-form .live-form-header{padding:0 15px}.core-form-design-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon{position:absolute;right:7px;top:6px;z-index:2;color:gray;cursor:pointer}.core-form-design-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon i{font-size:18px}.core-form-design-container .drop-target{min-height:50px;min-width:50px;border:2px dashed transparent;transition:border .2s ease}.core-form-design-container .drop-target.active-drop{border-color:#007bff;background-color:#eaf4ff}.core-form-design-container .no-padding{padding:0!important}.core-form-design-container .modal-content-root{overflow:visible}\n", ".core-form-container{overflow-x:visible}.core-form-container>form .section{margin-top:var(--size-layout-block-cell-spacing)}.core-form-container>form .section:not(:first-child){margin-top:calc(var(--size-layout-block-cell-spacing) * 2)}.core-form-container>form .section .section-header-label{display:block;height:34px;line-height:34px;margin-bottom:15px;margin-left:12px}.core-form-container>form .section .section-header-label .section-img-wrapper{position:relative}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img{position:absolute;width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);border-radius:0;background-color:transparent}.core-form-container>form .section .section-header-label .section-calc-wrapper{width:34px;height:34px;display:flex;align-items:center;justify-content:center;background-color:#d3d3d3;border-radius:50%}.core-form-container>form .section .section-header-label .section-calc-wrapper i{width:24px;height:24px;font-size:24px;color:#1b4332}.core-form-container>form .section .section-header-label .section-caption{padding-left:40px;font-weight:700;color:#696969}.core-form-container .row{margin-left:var(--size-layout-block-cell-spacing) 0px;margin-right:var(--size-layout-block-cell-spacing) 0px}.core-form-container .row .grid-buffer{border:dotted 2px darkgray}.core-form-container .row .button-control{display:flex;align-items:flex-end}.core-form-container .form-row{margin:var(--size-layout-block-cell-spacing) 0px;display:flex;align-items:center;justify-content:center}.core-form-container .form-row>button{cursor:pointer;border-radius:0}.core-form-container .form-row>button:not(:first-child){margin-left:var(--size-layout-block-cell-spacing)}.core-form-container .dev-button{cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;padding:8px;width:120px;border-radius:18px;box-shadow:.4rem 0 2rem #0000002e}.core-form-container .dev-button:not(:last-child){margin-right:15px}.core-form-container .dev-button:first-child{background-color:#dff6dd;border:1px #9fdc9d solid}.core-form-container .dev-button:last-child{background-color:#fff4ce;border:1px #ffda6a solid}.core-form-container .payload-preview{display:block;width:calc(100% - 48px);height:200px;white-space:pre-wrap;overflow-x:hidden;overflow-y:auto;background-color:#dff6dd;padding:24px;margin:24px;text-indent:-58px;color:#333!important;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;line-height:1.5}.core-form-container .payload-preview.validator-preview{background-color:#fff4ce}.core-form-container .bottom-template-wrapper{padding-left:12px;padding-right:12px}.core-form-container .w-100{width:100%}.core-form-container .pr18{padding-right:18px}\n", ".core-button-vns-container{height:30px;display:flex;align-items:center;justify-content:center;min-width:30px}.core-button-vns-container .action-wrapper{height:30px!important;width:30px!important;display:flex;align-items:center;justify-content:center}.core-button-vns-container .action-wrapper:has(i:hover){background-color:#e7e7e7;border-radius:50%}.core-button-vns-container .btn-for-form{border:none;border-radius:0;background-color:transparent;color:#000;min-width:120px}.core-button-vns-container button.last-child{background-color:#000;color:#fff}.core-button-vns-container .action-wrapper.last-child{background-color:var(--color-basic-orange);color:#fff;border-radius:50%}.core-button-vns-container .action-wrapper.last-child:hover{background-color:var(--color-basic-orange);box-shadow:0 1rem 3rem #0000002e}.core-button-vns-container .temporary-unavailable{user-select:none;-moz-user-select:none;-webkit-user-select:none;cursor:not-allowed!important;opacity:.5}.core-button-vns-container .temporary-unavailable:hover{background-color:transparent!important}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CoreFormGroupEditorComponent, selector: "core-form-group-editor", inputs: ["$isNested", "$idAsInput", "$id"] }, { kind: "component", type: CoreFormArrayLayoutEditorComponent, selector: "core-form-array-layout-editor" }, { kind: "component", type: CoreToastLoadingComponent, selector: "core-toast-loading" }, { kind: "component", type: CorePageHeaderComponent, selector: "core-page-header", inputs: ["instanceNumber", "shownItems", "title", "hideButtonGroup", "htmlTooltipRef"], outputs: ["buttonClick"] }, { kind: "component", type: FormMetadataComponent, selector: "form-metadata" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: HotKeysDirective, selector: "[hotKeys]", inputs: ["hotKeys"] }, { kind: "component", type: CoreControlComponent, selector: "core-control", inputs: ["control", "form", "checkError$", "rangeLimit"] }, { kind: "component", type: FieldSettingComponent, selector: "field-setting", inputs: ["$isNested"] }], animations: [slideFromTopFadeIn], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3238
|
+
}
|
|
3239
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: CoreFormDesignComponent, decorators: [{
|
|
3240
|
+
type: Component,
|
|
3241
|
+
args: [{ selector: 'core-form-design', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
3242
|
+
NgStyle,
|
|
3243
|
+
AsyncPipe,
|
|
3244
|
+
NgTemplateOutlet,
|
|
3245
|
+
CoreFormGroupEditorComponent,
|
|
3246
|
+
CoreFormArrayLayoutEditorComponent,
|
|
3247
|
+
CoreToastLoadingComponent,
|
|
3248
|
+
CorePageHeaderComponent,
|
|
3249
|
+
FormMetadataComponent,
|
|
3250
|
+
ReactiveFormsModule,
|
|
3251
|
+
TranslatePipe,
|
|
3252
|
+
TableCellPipe,
|
|
3253
|
+
TooltipDirective,
|
|
3254
|
+
HotKeysDirective,
|
|
3255
|
+
CoreFormComponent,
|
|
3256
|
+
CoreControlComponent,
|
|
3257
|
+
CoreDropdownComponent,
|
|
3258
|
+
CoreChecklistComponent,
|
|
3259
|
+
CoreFormControlSeekerComponent,
|
|
3260
|
+
CoreCheckboxComponent,
|
|
3261
|
+
CoreAttachmentComponent,
|
|
3262
|
+
CoreDatePickerComponent,
|
|
3263
|
+
CoreRadioGroupComponent,
|
|
3264
|
+
CoreMonthSelectorComponent,
|
|
3265
|
+
TooltipDirective,
|
|
3266
|
+
CoreCurrencyInputComponent,
|
|
3267
|
+
FieldSettingComponent,
|
|
3268
|
+
LiveFormComponent,
|
|
3269
|
+
HtmlTooltipDirective,
|
|
3270
|
+
], animations: [slideFromTopFadeIn], template: "<div class=\"core-form-design-container fs-13\" [hotKeys]=\"['F3', 'F6', 'Escape']\">\r\n\r\n <core-page-header title=\"UI.COMPONENT_TITLE.WORKFLOW_FORM_DESIGN\" (buttonClick)=\"onCorePageHeaderButtonClicked($event)\"></core-page-header>\r\n\r\n <ng-template #formMetadata>\r\n <form-metadata></form-metadata>\r\n </ng-template>\r\n\r\n @if (coreFormDesignService.$showFormArrayDesign()) {\r\n <core-form-array-layout-editor />\r\n }\r\n\r\n @if (!coreFormDesignService.$isInWorkflowDesign()) {\r\n <aside class=\"af-metadata-overlay\" [@slideFromTopFadeIn]=\"coreFormDesignService.$showFormMetadata() ? 'in' : 'out'\"\r\n [class.shown]=\"coreFormDesignService.$showFormMetadata()\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"formMetadata\"></ng-container>\r\n </aside>\r\n }\r\n\r\n <div \r\n class=\"field-setting-panel\" \r\n [class.open]=\"coreFormDesignService.$fieldSettingPanelOpen()\" \r\n [class.calculated-type]=\"coreFormDesignService.$selectedCell()?.control?.type==='calculated'\"\r\n [ngStyle]=\"{ zIndex: $zIndex() }\"\r\n #settingPanel\r\n >\r\n @if (!!(this.coreFormDesignService.$selectedCell())) {\r\n <field-setting></field-setting>\r\n }\r\n </div>\r\n\r\n <div class=\"form-design-left\">\r\n\r\n @switch ($leftPanelMode()) {\r\n @case (enumLeftPanelMode.FieldCollection) {\r\n @for (category of controlCategories; track $index) {\r\n <div class=\"category-name\">\r\n {{ category.name }}\r\n </div>\r\n <ul>\r\n @for (control of category.controls; track $index) {\r\n <li draggable=\"true\" (dragstart)=\"onDragStart($event, control)\" [class.full-width]=\"control.controlType === 'SEEKER' || control.controlType === 'ATTACHMENT' || control.controlType === 'GRIDBUFFER'\">\r\n @if (control.controlType === 'GRIDBUFFER') {\r\n <div class=\"grid-buffer-wrapper\">\r\n <core-control [control]=\"control\" [form]=\"form\" [checkError$]=\"checkError$\"></core-control>\r\n </div>\r\n } @else {\r\n <core-control [control]=\"control\" [form]=\"form\" [checkError$]=\"checkError$\"></core-control>\r\n }\r\n </li>\r\n <div class=\"space\"></div>\r\n }\r\n </ul>\r\n }\r\n }\r\n @case (enumLeftPanelMode.Json) {\r\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"switchJson()\">{{ $jsonMode() }}</button>\r\n @if ($jsonMode()==='selectedField') {\r\n <pre class=\"json-preview\">{{ coreFormDesignService.$currentControlJson() }}</pre>\r\n } @else {\r\n <pre class=\"json-preview\">{{ coreFormDesignService.$saveLiveJson() }}</pre>\r\n }\r\n \r\n }\r\n }\r\n\r\n </div>\r\n\r\n <div class=\"form-design-right\">\r\n\r\n <core-form-group-editor [$idAsInput]=\"true\" />\r\n <!-- <pre>{{ $injectedFieldsJson() }}</pre> -->\r\n\r\n \r\n <!-- @if (coreFormDesignService.$mode() === enumType.Default) {\r\n <div class=\"form-wrapper\">\r\n\r\n @if (!!coreFormDesignService.$isInWorkflowDesign()) {\r\n <form-metadata></form-metadata>\r\n }\r\n\r\n <form [formGroup]=\"formDesign\" autocomplete=\"off\">\r\n <div class=\"form-cells\">\r\n\r\n <div class=\"form-tool-bar\">\r\n @if (!$hasCalculatedSection()) {\r\n <button class=\"core-button-vns-container\" (click)=\"addCalculatedSection()\">+ Calculated fields</button>\r\n }\r\n <button class=\"core-button-vns-container\" (click)=\"addSection()\">+ Add Section</button>\r\n </div>\r\n \r\n @for (section of coreFormDesignService.$placeholderSections(); track $index; let sectionIndex = $index) {\r\n <div class=\"form-section-placeholder\">\r\n\r\n <div class=\"section-header\">\r\n\r\n <div class=\"section-img-wrapper\">\r\n <div class=\"section-img\" [class.calculated]=\"section.forCalculatedFields\"></div>\r\n </div>\r\n\r\n <label [attr.contenteditable]=\"!section.forCalculatedFields\"\r\n [htmlTooltip]=\"!!section.forCalculatedFields ? calculatedFieldsTooltip : null\"\r\n (blur)=\"onCaptionEditEnd(sectionIndex, $event)\"\r\n (keydown.enter)=\"onCaptionEditEnd(sectionIndex, $event); $event.preventDefault()\"\r\n >{{ section.caption }}</label>\r\n\r\n <div class=\"section-tool pointer\" (click)=\"deleteSection(sectionIndex)\" [appTooltip]=\"'Delete section'\" [showAnyway]=\"true\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n \r\n </div>\r\n \r\n @for (row of section.rows; track $index; let rowIndex = $index) {\r\n\r\n <div class=\"form-row-wrapper\">\r\n <div class=\"form-row\">\r\n @for (cell of filteredCells(row); track $index; let colIndex = $index) {\r\n <div class=\"form-cell drop-target\" \r\n [ngStyle]=\"{ flex: (cell.flexSize ?? 0) + ' 1 0%' }\"\r\n [attr.data-flex]=\"cell.flexSize ?? 0\"\r\n (drop)=\"onDropIntoCell($event, sectionIndex, rowIndex, colIndex)\"\r\n (dragover)=\"onDragOver($event, section, cell.control!)\"\r\n (dragenter)=\"onDragEnter(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n (dragleave)=\"onDragLeave(sectionIndex + '_' + rowIndex + '_' + colIndex)\"\r\n [class.selected]=\"cell?.selected\"\r\n [class.dragging-over]=\"isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]\"\r\n [class.has-control]=\"!!cell && !!cell.control && !!getControl(cell)\"\r\n (click)=\"onCellClicked(sectionIndex, rowIndex, colIndex)\"\r\n >\r\n\r\n\r\n @if (!isDragOverMap[sectionIndex + '_' + rowIndex + '_' + colIndex]) {\r\n\r\n @if (!!cell && !!cell.control && !!getControl(cell)) {\r\n <label contenteditable=\"true\"\r\n (blur)=\"onFieldCaptionEditEnd(cell, $event)\"\r\n (keydown.enter)=\"onFieldCaptionEditEnd(cell, $event); $event.preventDefault()\"\r\n [class.d-none]=\"!!getControl(cell)?.hidden\"\r\n [class.required]=\"!!cell && !!cell.control && !!getControl(cell) && isRequired(getControl(cell))\"\r\n >{{ cell.control.label || 'label' }}</label>\r\n\r\n @if (cell.control.controlType==='CALCULATED') {\r\n <div class=\"control-wrapper\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\">\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n </div>\r\n } @else {\r\n <core-control \r\n [control]=\"getControl(cell)!\" \r\n [form]=\"formDesign\"\r\n [checkError$]=\"checkError$\" />\r\n }\r\n \r\n <div class=\"field-toolbar\">\r\n <ul>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"openSettingsForCell(cell, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Show settings'\">\r\n <i class=\"feather-settings\"></i>\r\n </div>\r\n </li>\r\n <li>\r\n <div class=\"field-tool pointer\" (click)=\"deleteField(row, sectionIndex, rowIndex, colIndex)\" [appTooltip]=\"'Remove field from cell'\">\r\n <i class=\"feather-x\"></i>\r\n </div>\r\n </li>\r\n </ul>\r\n </div>\r\n \r\n } @else {\r\n <div class=\"placeholder\" [class.calculated]=\"!!section.forCalculatedFields\">\r\n {{ !!section.forCalculatedFields ? 'Drop calculated field here' : 'Drop here'}}\r\n </div>\r\n }\r\n\r\n @if (cell?.selected && canMergeCells(sectionIndex, rowIndex)) {\r\n <div class=\"merge-toolbar\">\r\n <button (click)=\"mergeCells(sectionIndex, rowIndex)\">\uD83D\uDD17 Merge</button>\r\n </div>\r\n }\r\n \r\n }\r\n\r\n </div>\r\n }\r\n </div>\r\n <div class=\"row-tool-bar\">\r\n <div class=\"icon-wrapper\" (click)=\"deleteRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Delete row'\" [showAnyway]=\"true\"><i class=\"feather-x\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addRow(sectionIndex)\" [appTooltip]=\"'Add row'\" [showAnyway]=\"true\"><i class=\"feather-arrow-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"insertRow(sectionIndex, rowIndex)\" [appTooltip]=\"'Insert row'\" [showAnyway]=\"true\"><i class=\"feather-corner-right-down\"></i></div>\r\n <div class=\"icon-wrapper\" (click)=\"addColumn(row)\" [appTooltip]=\"'Add column'\" [showAnyway]=\"true\"><i class=\"feather-arrow-right\"></i></div>\r\n </div>\r\n </div>\r\n\r\n }\r\n </div>\r\n }\r\n \r\n \r\n </div>\r\n </form>\r\n </div>\r\n } @else if (!!$sections()) {\r\n\r\n @if(!!(injectedFields$ | async) && !!(injectedFields$ | async)!.length) {\r\n\r\n @if (!!coreFormDesignService.$afInstance().normalMode) {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" />\r\n } @else {\r\n <live-form [$designMode]=\"true\" [$forKickOff]=\"true\" ($onClose)=\"closePreview()\" />\r\n }\r\n\r\n } @else {\r\n <h2>COULD NOT UPDATE injectedFields$</h2>\r\n }\r\n\r\n } @else {\r\n <h2>$sections() empty / null / undefined</h2>\r\n } -->\r\n\r\n\r\n \r\n </div>\r\n @if ($loading()) {\r\n <core-toast-loading></core-toast-loading>\r\n }\r\n\r\n <ng-template #calculatedFieldsTooltip>\r\n <div style=\"max-width: 280px;\">\r\n <strong>Calculated Fields</strong><br />\r\n <p></p>\r\n These fields are hidden from the form and cannot be edited directly.<br /><br />\r\n - They are computed automatically during workflow execution.<br />\r\n - Each field depends on one or more standard input fields.<br />\r\n - The calculation is evaluated using either an SQL query engine (like <strong>Dapper</strong>) or an in-memory expression engine (like <strong>Dynamic Expresso</strong>).<br /><br />\r\n In both cases, the result is a <code>0</code> or <code>1</code>, representing <strong>false</strong> or <strong>true</strong>.<br /><br />\r\n These results can be used to influence workflow logic, rule trees, or approval paths.\r\n </div>\r\n </ng-template>\r\n \r\n</div>", styles: ["@charset \"UTF-8\";.core-form-design-container{position:relative;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));width:100%;overflow:hidden;background-color:#eff0f1;font-size:13px}.core-form-design-container .core-button-vns-container{margin-right:8px!important}.core-form-design-container .core-button-vns-container:last-child{background-color:#000;color:#fff;margin-right:0}.core-form-design-container .af-metadata-overlay{position:fixed;top:60px;left:66px;background:#fff;box-shadow:.4rem 0 2rem #0000002e;z-index:1000;width:1214px;height:calc(100vh - 75px);overflow-y:auto;pointer-events:none}.core-form-design-container .af-metadata-overlay.shown{pointer-events:auto}.core-form-design-container *{border-radius:0}.core-form-design-container ul,.core-form-design-container li{padding:0}.core-form-design-container li{max-width:200px}.core-form-design-container ul div.space{display:block;height:15px}.core-form-design-container li.full-width{max-width:100%}.core-form-design-container .field-setting-panel{display:block;position:fixed;width:360px;height:100vh;top:0;right:-360px;background-color:#fff;box-shadow:.4rem 0 2rem #0000002e;transition:right .5s ease-out}.core-form-design-container .field-setting-panel .close-wrapper{width:32px;height:32px;position:absolute;top:15px;right:15px;display:flex;background-color:#848484;border-radius:50%}.core-form-design-container .field-setting-panel .close-wrapper i{width:16px;height:16px;font-size:18px;color:#848484}.core-form-design-container .field-setting-panel .panel-caption{margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid #ddd;width:100%;overflow:hidden;text-wrap:nowrap;text-overflow:ellipsis}.core-form-design-container .field-setting-panel.calculated-type{width:500px;right:-500px}.core-form-design-container .field-setting-panel.calculated-type textarea{border-radius:8px;height:150px}.core-form-design-container .field-setting-panel.open{right:0}.core-form-design-container .form-design-left{position:absolute;top:var(--size-core-page-header-height);bottom:0;left:0;z-index:1;width:360px;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));padding:15px;padding-right:calc(15px + var(--size-scrollbar-width));background-color:#87ceeb;overflow-y:hidden}.core-form-design-container .form-design-left .category-name{color:#fff}.core-form-design-container .form-design-left .grid-buffer-wrapper{background-color:#fff;padding:15px}.core-form-design-container .form-design-left ul:last-child{padding-bottom:400px}.core-form-design-container .form-design-left .category-name{margin-bottom:15px}.core-form-design-container .form-design-left .json-preview{background-color:#fff;color:#333;padding:1rem 1.5rem;box-shadow:0 2px 6px #00000014;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;overflow-x:auto;line-height:1.5;width:330px;height:calc(100vh - 205px)}.core-form-design-container .form-design-left button{height:35px;border-radius:0;margin-bottom:15px;color:#fff;border:none;font-size:13px;background-color:transparent}.core-form-design-container .form-design-left:hover{overflow-y:auto;padding-right:15px}.core-form-design-container .form-design-right{width:100%;height:calc(100vh - var(--size-header-height) - var(--size-layout-block-cell-spacing));padding:15px 15px 15px 375px;overflow-y:auto}.core-form-design-container .form-design-right .form-wrapper{width:100%;overflow-y:visible;background-color:#fff}.core-form-design-container .form-design-right .form-wrapper button{margin-left:12px;padding:4px 10px;border:1px solid #ccc;cursor:pointer;transition:all .2s;width:110px}.core-form-design-container .form-design-right .form-wrapper button:hover{background-color:#e4f0ff;border-color:#007bff;color:#007bff}.core-form-design-container .form-design-right .form-wrapper .form-tool-bar{display:flex;align-items:center;justify-content:flex-end}.core-form-design-container .form-design-right .form-wrapper .form-tool-bar>button{width:150px;margin:0}.core-form-design-container .form-design-right .form-wrapper .form-cells{display:flex;flex-direction:column;gap:32px;padding:20px 15px 15px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder{padding:12px 15px;border:1px dashed #ccc;background-color:#fff;box-shadow:0 2px 6px #0000000a}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header{display:flex;position:relative;align-items:center;justify-content:flex-start;font-weight:600;margin-bottom:12px;color:#333}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img{width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center;margin-right:8px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);background-color:transparent;border-radius:0}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool{position:absolute;right:5px;top:6px;display:none;z-index:2;color:gray}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header .section-tool i{font-size:24px;color:#848484}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-section-placeholder .section-header:hover .section-tool{display:block}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .row-tool-bar{display:flex;align-items:center;justify-content:flex-end;margin-bottom:8px;height:24px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-row{display:flex;gap:15px;margin-bottom:15px;min-height:50px;background-color:#d3d3d3;padding:8px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell{flex:1;min-height:90px;background-color:#fcfcfc;border:1px dashed #ccc;position:relative;padding:20px 10px 10px;transition:border .2s ease-in-out}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell:before{content:attr(data-flex);position:absolute;top:2px;left:4px;font-size:10px;color:#999}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell label.required:after{content:\" *\";color:#ff040b}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar{position:absolute;right:7px;top:6px;display:none;z-index:2;color:gray}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul{display:flex}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div{display:flex;width:24px;height:24px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell .field-toolbar ul li div i{font-size:18px;width:18px;height:18px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.has-control:hover .field-toolbar{display:flex}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .form-cell.active-drop{border-color:#007bff;background-color:#eef6ff;transition:.2s}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .placeholder{display:flex;align-items:center;justify-content:center;background-color:#fff;color:#007bff;padding:15px 0;width:100%;cursor:default}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .placeholder.calculated{color:#ff4500}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper{width:24px;height:24px;border-radius:50%;padding:0;cursor:pointer;display:none;color:gray;border:1px solid gray;margin-left:8px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper .icon-wrapper i{font-size:18px;width:18px;height:18px}.core-form-design-container .form-design-right .form-wrapper .form-cells .form-row-wrapper:hover .icon-wrapper{display:flex}.core-form-design-container .form-design-right .form-wrapper .form-cell.selected{background-color:#dff6dd}.core-form-design-container .form-design-right .form-wrapper .merge-toolbar{text-align:center;margin-top:4px}.core-form-design-container .form-design-right .form-wrapper.live-form{padding-bottom:15px}.core-form-design-container .form-design-right .form-wrapper.live-form .live-form-header{padding:0 15px}.core-form-design-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon{position:absolute;right:7px;top:6px;z-index:2;color:gray;cursor:pointer}.core-form-design-container .form-design-right .form-wrapper.live-form .live-form-header .preview-close-icon i{font-size:18px}.core-form-design-container .drop-target{min-height:50px;min-width:50px;border:2px dashed transparent;transition:border .2s ease}.core-form-design-container .drop-target.active-drop{border-color:#007bff;background-color:#eaf4ff}.core-form-design-container .no-padding{padding:0!important}.core-form-design-container .modal-content-root{overflow:visible}\n", ".core-form-container{overflow-x:visible}.core-form-container>form .section{margin-top:var(--size-layout-block-cell-spacing)}.core-form-container>form .section:not(:first-child){margin-top:calc(var(--size-layout-block-cell-spacing) * 2)}.core-form-container>form .section .section-header-label{display:block;height:34px;line-height:34px;margin-bottom:15px;margin-left:12px}.core-form-container>form .section .section-header-label .section-img-wrapper{position:relative}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img{position:absolute;width:34px;height:34px;border-radius:50%;background-color:#d3d3d3;float:left;background-image:url(/assets/images/info.svg);background-repeat:no-repeat;background-position:center}.core-form-container>form .section .section-header-label .section-img-wrapper .section-img.calculated{background-image:url(/assets/images/sql-icon.svg);border-radius:0;background-color:transparent}.core-form-container>form .section .section-header-label .section-calc-wrapper{width:34px;height:34px;display:flex;align-items:center;justify-content:center;background-color:#d3d3d3;border-radius:50%}.core-form-container>form .section .section-header-label .section-calc-wrapper i{width:24px;height:24px;font-size:24px;color:#1b4332}.core-form-container>form .section .section-header-label .section-caption{padding-left:40px;font-weight:700;color:#696969}.core-form-container .row{margin-left:var(--size-layout-block-cell-spacing) 0px;margin-right:var(--size-layout-block-cell-spacing) 0px}.core-form-container .row .grid-buffer{border:dotted 2px darkgray}.core-form-container .row .button-control{display:flex;align-items:flex-end}.core-form-container .form-row{margin:var(--size-layout-block-cell-spacing) 0px;display:flex;align-items:center;justify-content:center}.core-form-container .form-row>button{cursor:pointer;border-radius:0}.core-form-container .form-row>button:not(:first-child){margin-left:var(--size-layout-block-cell-spacing)}.core-form-container .dev-button{cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;padding:8px;width:120px;border-radius:18px;box-shadow:.4rem 0 2rem #0000002e}.core-form-container .dev-button:not(:last-child){margin-right:15px}.core-form-container .dev-button:first-child{background-color:#dff6dd;border:1px #9fdc9d solid}.core-form-container .dev-button:last-child{background-color:#fff4ce;border:1px #ffda6a solid}.core-form-container .payload-preview{display:block;width:calc(100% - 48px);height:200px;white-space:pre-wrap;overflow-x:hidden;overflow-y:auto;background-color:#dff6dd;padding:24px;margin:24px;text-indent:-58px;color:#333!important;font-family:Fira Code,monospace;font-size:.85rem;word-break:keep-all;line-height:1.5}.core-form-container .payload-preview.validator-preview{background-color:#fff4ce}.core-form-container .bottom-template-wrapper{padding-left:12px;padding-right:12px}.core-form-container .w-100{width:100%}.core-form-container .pr18{padding-right:18px}\n", ".core-button-vns-container{height:30px;display:flex;align-items:center;justify-content:center;min-width:30px}.core-button-vns-container .action-wrapper{height:30px!important;width:30px!important;display:flex;align-items:center;justify-content:center}.core-button-vns-container .action-wrapper:has(i:hover){background-color:#e7e7e7;border-radius:50%}.core-button-vns-container .btn-for-form{border:none;border-radius:0;background-color:transparent;color:#000;min-width:120px}.core-button-vns-container button.last-child{background-color:#000;color:#fff}.core-button-vns-container .action-wrapper.last-child{background-color:var(--color-basic-orange);color:#fff;border-radius:50%}.core-button-vns-container .action-wrapper.last-child:hover{background-color:var(--color-basic-orange);box-shadow:0 1rem 3rem #0000002e}.core-button-vns-container .temporary-unavailable{user-select:none;-moz-user-select:none;-webkit-user-select:none;cursor:not-allowed!important;opacity:.5}.core-button-vns-container .temporary-unavailable:hover{background-color:transparent!important}\n"] }]
|
|
3271
|
+
}], ctorParameters: () => [{ type: MultiLanguageService }] });
|
|
3272
|
+
|
|
3273
|
+
var coreFormDesign_component = /*#__PURE__*/Object.freeze({
|
|
3274
|
+
__proto__: null,
|
|
3275
|
+
CoreFormDesignComponent: CoreFormDesignComponent
|
|
3276
|
+
});
|
|
3277
|
+
|
|
3278
|
+
export { BasePropsComponent as B, CoreFormDesignComponent as C, coreFormDesign_component as c };
|
|
3279
|
+
//# sourceMappingURL=ngx-histaff-alpha-core-form-design.component-CGf-jOqR.mjs.map
|