@bcweed/dynamic-form-lib 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.editorconfig +17 -0
  2. package/.postcssrc.json +5 -0
  3. package/.vscode/extensions.json +4 -0
  4. package/.vscode/launch.json +20 -0
  5. package/.vscode/tasks.json +42 -0
  6. package/AngularDynamicFormLibrary.esproj +14 -0
  7. package/CHANGELOG.md +16 -0
  8. package/README.md +15 -19
  9. package/angular.json +169 -0
  10. package/aspnetcore-https.js +37 -0
  11. package/karma.conf.js +44 -0
  12. package/obj/Debug/AngularDynamicFormLibrary.esproj.CoreCompileInputs.cache +0 -0
  13. package/obj/Debug/AngularDynamicFormLibrary.esproj.FileListAbsolute.txt +1 -0
  14. package/obj/Debug/package.g.props +56 -0
  15. package/package.json +65 -37
  16. package/postcss.config.js +6 -0
  17. package/projects/dynamic-form-lib/.postcssrc.json +5 -0
  18. package/projects/dynamic-form-lib/README.md +63 -0
  19. package/projects/dynamic-form-lib/ng-package.json +10 -0
  20. package/projects/dynamic-form-lib/package.json +27 -0
  21. package/projects/dynamic-form-lib/src/lib/README.md +424 -0
  22. package/projects/dynamic-form-lib/src/lib/SUMMARY.md +159 -0
  23. package/projects/dynamic-form-lib/src/lib/directives/dynamic-ref-directive.ts +13 -0
  24. package/projects/dynamic-form-lib/src/lib/dynamic-form-demo/dynamic-form-demo.component.html +75 -0
  25. package/projects/dynamic-form-lib/src/lib/dynamic-form-demo/dynamic-form-demo.component.scss +127 -0
  26. package/projects/dynamic-form-lib/src/lib/dynamic-form-demo/dynamic-form-demo.component.ts +50 -0
  27. package/projects/dynamic-form-lib/src/lib/dynamic-form-field/dynamic-form-field.component.html +234 -0
  28. package/projects/dynamic-form-lib/src/lib/dynamic-form-field/dynamic-form-field.component.scss +315 -0
  29. package/projects/dynamic-form-lib/src/lib/dynamic-form-field/dynamic-form-field.component.ts +233 -0
  30. package/projects/dynamic-form-lib/src/lib/dynamic-form.component.html +137 -0
  31. package/projects/dynamic-form-lib/src/lib/dynamic-form.component.scss +141 -0
  32. package/projects/dynamic-form-lib/src/lib/dynamic-form.component.ts +366 -0
  33. package/projects/dynamic-form-lib/src/lib/helpers/component-helper.ts +54 -0
  34. package/projects/dynamic-form-lib/src/lib/index.ts +10 -0
  35. package/projects/dynamic-form-lib/src/lib/models/dynamic-form-interfaces.ts +117 -0
  36. package/projects/dynamic-form-lib/src/lib/services/dynamic-form.service.spec.ts +302 -0
  37. package/projects/dynamic-form-lib/src/lib/services/dynamic-form.service.ts +325 -0
  38. package/projects/dynamic-form-lib/src/lib/style.scss +3 -0
  39. package/projects/dynamic-form-lib/src/lib/tailwind-output.css +0 -0
  40. package/projects/dynamic-form-lib/src/lib/validators/compare-validator.ts +26 -0
  41. package/projects/dynamic-form-lib/src/public-api.css +1 -0
  42. package/projects/dynamic-form-lib/src/public-api.ts +10 -0
  43. package/projects/dynamic-form-lib/tailwind.config.js +10 -0
  44. package/projects/dynamic-form-lib/tsconfig.lib.json +15 -0
  45. package/projects/dynamic-form-lib/tsconfig.lib.prod.json +11 -0
  46. package/projects/dynamic-form-lib/tsconfig.spec.json +15 -0
  47. package/public/favicon.ico +0 -0
  48. package/src/app/app-routing.module.ts +10 -0
  49. package/src/app/app.component.css +0 -0
  50. package/src/app/app.component.html +18 -0
  51. package/src/app/app.component.spec.ts +35 -0
  52. package/src/app/app.component.ts +1038 -0
  53. package/src/app/app.config.ts +45 -0
  54. package/src/app/app.module.ts +46 -0
  55. package/src/app/app.routes.ts +8 -0
  56. package/src/index.html +13 -0
  57. package/src/main.ts +16 -0
  58. package/src/styles.css +2 -0
  59. package/tailwind.config.js +10 -0
  60. package/tsconfig.app.json +15 -0
  61. package/tsconfig.json +32 -0
  62. package/tsconfig.spec.json +15 -0
  63. package/fesm2022/bcweed-dynamic-form-lib.mjs +0 -938
  64. package/fesm2022/bcweed-dynamic-form-lib.mjs.map +0 -1
  65. package/index.d.ts +0 -254
  66. /package/{src → projects/dynamic-form-lib/src}/lib/style.css +0 -0
@@ -0,0 +1,141 @@
1
+ .dynamic-form-container {
2
+ width: 100%;
3
+
4
+ p-message .p-message.light-font .p-message-text {
5
+ font-weight: normal;
6
+ }
7
+
8
+ .form-description {
9
+ color: #666;
10
+ margin-bottom: 1rem;
11
+ }
12
+
13
+ .form-fields-container {
14
+ margin-bottom: 1rem;
15
+
16
+ .row {
17
+ margin-bottom: 0.5rem;
18
+ }
19
+ }
20
+
21
+ .form-actions {
22
+ margin-top: 2rem;
23
+ padding-top: 1rem;
24
+ border-top: 1px solid #dee2e6;
25
+
26
+ button {
27
+ margin-left: 0.5rem;
28
+ }
29
+
30
+ .btn-cancel {
31
+ background-color: #6c757d;
32
+ border-color: #6c757d;
33
+
34
+ &:hover {
35
+ background-color: #5a6268;
36
+ border-color: #545b62;
37
+ }
38
+ }
39
+ }
40
+ // New styles for converted Tailwind classes
41
+ .section-wrapper {
42
+ width: 100%;
43
+ display: flex;
44
+ justify-content: center;
45
+ }
46
+
47
+ .section-content {
48
+ width: 100%;
49
+ display: flex;
50
+ flex-wrap: wrap;
51
+ gap: 1rem;
52
+ justify-content: center;
53
+ padding-top: 16px;
54
+ }
55
+
56
+ .section-title {
57
+ width: 100%;
58
+ text-align:left;
59
+ font-size:24px;
60
+ font-weight:600;
61
+ margin-top:8px;
62
+ margin-bottom:16px;
63
+ }
64
+
65
+ .message-container {
66
+ width: 100%;
67
+ display: block;
68
+ margin-top: 0.5rem;
69
+ margin-bottom: 0.5rem;
70
+ text-align: left;
71
+ }
72
+
73
+ .fields-wrapper {
74
+ width: 100%;
75
+ display: flex;
76
+ justify-content: center;
77
+ }
78
+
79
+ .fields-grid {
80
+ width: 100%;
81
+ display: flex;
82
+ flex-wrap: wrap;
83
+ gap: 1rem;
84
+ justify-content: left;
85
+ }
86
+
87
+ .field-container {
88
+ width: 100%;
89
+ text-align: left;
90
+ }
91
+
92
+ .field-container-2col {
93
+ width: 100%;
94
+ text-align: left;
95
+ }
96
+
97
+ .field-container-1col {
98
+ width: 100%;
99
+ text-align: left;
100
+ }
101
+
102
+ .button-container {
103
+ width: 100%;
104
+ display: flex;
105
+ justify-content: flex-end;
106
+ }
107
+
108
+ .button-group {
109
+ display: flex;
110
+ gap: 0.5rem;
111
+ }
112
+ }
113
+
114
+ // Responsive adjustments
115
+ @media (min-width: 1024px) {
116
+ .dynamic-form-container {
117
+
118
+ .field-container {
119
+ width: calc(50% - 0.5rem);
120
+ }
121
+
122
+ .field-container-2col {
123
+ width: calc(50% - 0.5rem);
124
+ }
125
+
126
+ .field-container-1col {
127
+ width: 100%;
128
+ }
129
+ }
130
+ }
131
+
132
+ @media (max-width: 768px) {
133
+ .dynamic-form-container {
134
+ .form-actions {
135
+ button {
136
+ width: 100%;
137
+ margin: 0.25rem 0;
138
+ }
139
+ }
140
+ }
141
+ }
@@ -0,0 +1,366 @@
1
+ import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ViewChildren, QueryList } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
4
+ import { Subject } from 'rxjs';
5
+ import { takeUntil } from 'rxjs/operators';
6
+ import { DynamicFormConfig, DynamicFormSection, DynamicFormState, DynamicFormValue, DynamicFormError, DynamicFormField, DynamicFormFrameGroup } from './models/dynamic-form-interfaces';
7
+ import { DynamicFormFieldComponent } from './dynamic-form-field/dynamic-form-field.component';
8
+ import { DynamicFormService } from './services/dynamic-form.service';
9
+ import { ButtonModule } from 'primeng/button';
10
+ import { inject } from '@angular/core';
11
+ import { MessageModule } from 'primeng/message';
12
+ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
13
+
14
+ @Component({
15
+ selector: 'app-dynamic-form',
16
+ standalone: true,
17
+ imports: [CommonModule, ReactiveFormsModule, DynamicFormFieldComponent, ButtonModule, MessageModule],
18
+ templateUrl: './dynamic-form.component.html',
19
+ styleUrls: ['./dynamic-form.component.scss']
20
+ })
21
+ export class DynamicFormComponent implements OnInit, OnDestroy {
22
+ // Inject dependencies using field initializers (this IS the injection context)
23
+ private fb = inject(FormBuilder);
24
+ private dynamicFormService = inject(DynamicFormService);
25
+
26
+ @Input() config!: DynamicFormConfig;
27
+ @Input() initialValues?: DynamicFormValue;
28
+ @Input() allowHelpOverlay = false;
29
+ @Input() allowHelpSideBar = false;
30
+
31
+ @Output() formSubmit = new EventEmitter<DynamicFormValue>();
32
+ @Output() formCancel = new EventEmitter<void>();
33
+ @Output() formStateChange = new EventEmitter<DynamicFormState>();
34
+ @ViewChildren(DynamicFormFieldComponent) fieldComponents!: QueryList<DynamicFormFieldComponent>;
35
+
36
+ componentMap!: Map<string, DynamicFormFieldComponent>;
37
+ fieldMap!: Map<string, DynamicFormField>;
38
+ form!: FormGroup;
39
+ formState!: DynamicFormState;
40
+ sortedFields: DynamicFormField[] = [];
41
+ sortedSections: DynamicFormSection[] = [];
42
+ sortedFrames: DynamicFormFrameGroup[] = [];
43
+ private destroy$ = new Subject<void>();
44
+ isExpanded = false;
45
+ isSmallMedia = false;
46
+ isMediumMedia = false;
47
+
48
+ // Empty constructor - no code needed here
49
+ constructor(private responsive: BreakpointObserver) {}
50
+
51
+
52
+
53
+ ngOnInit(): void {
54
+ this.responsive.observe([Breakpoints.Medium, Breakpoints.Small, Breakpoints.XSmall])
55
+ .subscribe(result => {
56
+
57
+
58
+ if (result.matches) {
59
+ this.isSmallMedia = true;
60
+
61
+ }
62
+ else {
63
+ this.isSmallMedia = false;
64
+
65
+ }
66
+ });
67
+
68
+ this.initializeForm();
69
+ this.subscribeToFormChanges();
70
+ }
71
+
72
+ ngOnDestroy(): void {
73
+ this.destroy$.next();
74
+ this.destroy$.complete();
75
+ }
76
+
77
+ ngAfterViewInit() {
78
+ this.componentMap = new Map(
79
+ this.fieldComponents.map(comp => [comp.field.id, comp])
80
+ );
81
+ }
82
+
83
+ private initializeForm(): void {
84
+
85
+ //sort frames
86
+ // Sort sections and fields by order
87
+ this.sortedFrames = [...this.config.frames].sort((a, b) =>
88
+ (a.order ?? 0) - (b.order ?? 0)
89
+ );
90
+ this.sortedFrames.forEach((frame) => {
91
+ frame.sections = [...frame.sections].sort((a, b) =>
92
+ (a.order ?? 0) - (b.order ?? 0)
93
+ );
94
+ frame.sections.forEach((section) => {
95
+ section.fields = [...section.fields].sort((a, b) =>
96
+ (a.order ?? 0) - (b.order ?? 0)
97
+ );
98
+ });
99
+ });
100
+
101
+
102
+ // Build field map for O(1) lookup
103
+ this.fieldMap = new Map();
104
+ this.sortedFrames.forEach(frame => {
105
+ frame.sections.forEach(section => {
106
+ section.fields.forEach(field => {
107
+ this.fieldMap.set(field.id, field);
108
+ });
109
+ });
110
+ });
111
+
112
+ // Build form controls
113
+ const group: { [key: string]: any } = {};
114
+
115
+ this.sortedFrames.forEach(frame => {
116
+ frame.sections.forEach(section => {
117
+ section.fields.forEach(field => {
118
+ const validators = field.disabled === true ? [] : this.dynamicFormService.getValidators(field);
119
+ const initialValue = this.getInitialValue(field);
120
+
121
+ group[field.id] = [
122
+ { value: initialValue, disabled: field.disabled ?? false },
123
+ validators
124
+ ];
125
+
126
+ });
127
+ });
128
+ });
129
+
130
+ this.form = this.fb.group(group);
131
+
132
+ // Trigger all click events on form controls
133
+ this.triggerAllClickEvents();
134
+
135
+ // Initialize form state
136
+ this.formState = {
137
+ values: this.form.value,
138
+ errors: [],
139
+ touched: {},
140
+ isSubmitting: false,
141
+ isValid: this.form.valid
142
+ };
143
+ if (this.form.valid) {
144
+ this.markAllAsTouched();
145
+ }
146
+ this.emitFormState();
147
+
148
+ }
149
+
150
+ private triggerAllClickEvents(): void {
151
+ this.sortedFrames.forEach(frame => {
152
+ frame.sections.forEach(section => {
153
+ section.fields.forEach(field => {
154
+ const control = this.form.get(field.id);
155
+ if (control && field.onClick) {
156
+ // Trigger the onClick event with current control value
157
+ field.onClick(control.value, this.form);
158
+ }
159
+ });
160
+ });
161
+ })
162
+ }
163
+
164
+ private getInitialValue(field: DynamicFormField): any {
165
+ if (this.initialValues && this.initialValues[field.id] !== undefined) {
166
+ //return this.initialValues[field.id];
167
+ if (field.type == "date") {
168
+ if (this.initialValues[field.id] instanceof Date && !isNaN(this.initialValues[field.id].getTime())) {
169
+ console.log(field.id + " is already a valid Date object");
170
+ return this.initialValues[field.id];
171
+ }
172
+ else {
173
+ console.log(field.id + " is not a valid Date object, attempting to parse");
174
+ return new Date(this.initialValues[field.id]);
175
+ }
176
+ }
177
+ else {
178
+ return this.initialValues[field.id];
179
+ }
180
+ }
181
+ return field.value ?? this.getDefaultValue(field.type);
182
+ }
183
+
184
+ private getDefaultValue(type: string): any {
185
+ switch (type) {
186
+ case 'checkbox':
187
+ return null;
188
+ case 'number':
189
+ return null;
190
+ case 'hidden':
191
+ return 0;
192
+ default:
193
+ return '';
194
+ }
195
+ }
196
+
197
+ private subscribeToFormChanges(): void {
198
+ this.form.valueChanges
199
+ .pipe(takeUntil(this.destroy$))
200
+ .subscribe(values => {
201
+ this.updateFormState();
202
+ });
203
+
204
+ this.form.statusChanges
205
+ .pipe(takeUntil(this.destroy$))
206
+ .subscribe(() => {
207
+ this.updateFormState();
208
+ });
209
+ }
210
+
211
+ private updateFormState(): void {
212
+ const touched: { [key: string]: boolean } = {};
213
+ const errors: DynamicFormError[] = [];
214
+
215
+ this.fieldMap.forEach((field, fieldId) => {
216
+ const control = this.form.get(fieldId);
217
+ if (control) {
218
+ touched[fieldId] = control.touched;
219
+
220
+ if (control.invalid && control.touched) {
221
+ const fieldErrors = this.dynamicFormService.getErrorMessages(field, control.errors);
222
+ errors.push(...fieldErrors);
223
+ }
224
+ }
225
+ });
226
+
227
+ this.formState = {
228
+ values: this.form.value,
229
+ errors,
230
+ touched,
231
+ isSubmitting: this.formState.isSubmitting,
232
+ isValid: this.form.valid
233
+ };
234
+
235
+ this.emitFormState();
236
+ }
237
+
238
+ private emitFormState(): void {
239
+ this.formStateChange.emit({ ...this.formState });
240
+ }
241
+ showCancelButton(): boolean {
242
+ if (!this.config.hideCancelButton) {
243
+ return true;
244
+ }
245
+ return !this.config.hideCancelButton();
246
+ }
247
+
248
+ showSubmitButton(): boolean {
249
+ if (!this.config.hideSubmitButton) {
250
+ return true;
251
+ }
252
+ return !this.config.hideSubmitButton();
253
+ }
254
+
255
+ isFieldVisible(field: DynamicFormField): boolean {
256
+ if (!field.conditionalDisplay) {
257
+ return true;
258
+ }
259
+ return field.conditionalDisplay(this.form.value);
260
+ }
261
+
262
+ isSectionVisible(section: DynamicFormSection): boolean {
263
+ if (!section.conditionalDisplay) {
264
+ return true;
265
+ }
266
+ return section.conditionalDisplay(this.form.value);
267
+ }
268
+
269
+ isFrameVisible(frame: DynamicFormFrameGroup): boolean {
270
+ if (!frame.conditionalDisplay) {
271
+ return true;
272
+ }
273
+ return frame.conditionalDisplay(this.form.value);
274
+ }
275
+
276
+ onSubmit(): void {
277
+ if (this.form.invalid) {
278
+ this.markAllAsTouched();
279
+ this.updateFormState();
280
+ for (const frame of this.sortedFrames) {
281
+ for (const section of frame.sections) {
282
+ for (const field of section.fields) {
283
+ const control = this.form.get(field.id);
284
+ if (control && !control.valid) {
285
+ const component = this.componentMap.get(field.id);
286
+ if (component) {
287
+ component.focusOn();
288
+ return;
289
+ }
290
+ }
291
+ }
292
+ }
293
+ }
294
+ return;
295
+ }
296
+
297
+ this.formState.isSubmitting = true;
298
+ this.emitFormState();
299
+
300
+ const formValue = this.form.value;
301
+ this.formSubmit.emit(formValue);
302
+
303
+ // Reset submitting state after emission
304
+ setTimeout(() => {
305
+ this.formState.isSubmitting = false;
306
+ this.emitFormState();
307
+ }, 0);
308
+ }
309
+
310
+ onCancel(): void {
311
+ this.formCancel.emit();
312
+ }
313
+
314
+ private markAllAsTouched(): void {
315
+ Object.keys(this.form.controls).forEach(key => {
316
+ const control = this.form.get(key);
317
+ control?.markAsTouched();
318
+ });
319
+ }
320
+
321
+ getFieldErrors(fieldId: string): string[] {
322
+ const control = this.form.get(fieldId);
323
+ if (!control || !control.errors || !control.touched) {
324
+ return [];
325
+ }
326
+
327
+ const field = this.fieldMap.get(fieldId);
328
+ if (!field) {
329
+ return [];
330
+ }
331
+
332
+ return this.dynamicFormService.getErrorMessages(field, control.errors).map(e => e.message);
333
+ }
334
+
335
+ reset(): void {
336
+ this.form.reset();
337
+ this.formState = {
338
+ values: this.form.value,
339
+ errors: [],
340
+ touched: {},
341
+ isSubmitting: false,
342
+ isValid: this.form.valid
343
+ };
344
+ this.emitFormState();
345
+ }
346
+
347
+ patchValues(values: DynamicFormValue): void {
348
+ this.form.patchValue(values);
349
+ if (this.form.valid) {
350
+ this.markAllAsTouched();
351
+ }
352
+
353
+ }
354
+
355
+ getFormValue(): DynamicFormValue {
356
+ return this.form.value;
357
+ }
358
+
359
+ get submitLabel(): string {
360
+ return this.config.submitLabel ?? 'Submit';
361
+ }
362
+
363
+ get cancelLabel(): string {
364
+ return this.config.cancelLabel ?? 'Cancel';
365
+ }
366
+ }
@@ -0,0 +1,54 @@
1
+ import { ElementRef, Injector, Type } from '@angular/core';
2
+
3
+ export function getComponentFromElement<T>(
4
+ el: ElementRef,
5
+ injector: Injector,
6
+ componentType: Type<T>
7
+ ): T | null {
8
+ try {
9
+ // Get the native element
10
+ const nativeElement = el.nativeElement;
11
+
12
+ // Access Angular's internal context from the element
13
+ const context = (nativeElement as any).__ngContext__;
14
+
15
+ if (!context) {
16
+ return null;
17
+ }
18
+
19
+ // The context is an array or number that references the LView
20
+ // We need to traverse up to find the component instance
21
+ let lView: any;
22
+ if (typeof context === 'number') {
23
+ // Context is an index into the LView array
24
+ lView = (nativeElement as any).__ngContext__;
25
+ } else if (Array.isArray(context)) {
26
+ lView = context;
27
+ }
28
+
29
+ // Try to find the parent component by traversing the DOM tree
30
+ let currentElement = nativeElement.parentElement;
31
+ while (currentElement) {
32
+ const elementContext = (currentElement as any).__ngContext__;
33
+ if (elementContext) {
34
+ // Check if this element has a component instance
35
+ const lViewArray = Array.isArray(elementContext) ? elementContext : null;
36
+ if (lViewArray) {
37
+ // LView[8] typically contains the component instance
38
+ for (let i = 8; i < lViewArray.length; i++) {
39
+ const item = lViewArray[i];
40
+ if (item && item instanceof componentType) {
41
+ return item as T;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ currentElement = currentElement.parentElement;
47
+ }
48
+
49
+ return null;
50
+ } catch (error) {
51
+ console.error('Error getting component from element:', error);
52
+ return null;
53
+ }
54
+ }
@@ -0,0 +1,10 @@
1
+ // Components
2
+ export * from './dynamic-form.component';
3
+ export * from './dynamic-form-field/dynamic-form-field.component';
4
+ export * from './dynamic-form-demo/dynamic-form-demo.component';
5
+
6
+ // Services
7
+ export * from './services/dynamic-form.service';
8
+
9
+ // Re-export models for convenience
10
+ export * from './models/dynamic-form-interfaces';
@@ -0,0 +1,117 @@
1
+ import { FormGroup } from '@angular/forms';
2
+ export interface DynamicFormField {
3
+ id: string;
4
+ label: string;
5
+ superTitle?: string; //????
6
+ type: 'text' | 'number' | 'email' | 'select' | 'checkbox' | 'radio' | 'multi-checkbox' | 'multi-radio' | 'date' | 'textarea' | 'hidden' | 'phone';
7
+ value?: any;
8
+ decimals?: number;
9
+ placeholder?: string; //normal placeholder for input
10
+ popInfo?: string; //drawer or popover info for the field
11
+ leftPreInput?: string; // left side mid aligned pre phrase for input
12
+ helperInfo?: string; //????
13
+ hint?: string; // hint message shown below the field
14
+ toolTip?: string; // tooltip for the field
15
+ postWarning?: string; // warning message shown after the field
16
+ postInfo?: string; // info message shown after the field
17
+ required?: boolean; // for basic required validation
18
+ disabled?: boolean; // for disabling the field
19
+ options?: DynamicFormOption[];
20
+ validations?: DynamicFormValidation[];
21
+ order?: number;
22
+ cssClass?: string;
23
+ dependsOn?: string;
24
+ fieldWidth?: string;
25
+ inputWidth?: string;
26
+ labelWidth?: string;
27
+ min?: number;
28
+ max?: number;
29
+ conditionalDisplay?: (formValues: any) => boolean;
30
+ conditionalDisabled?: () => boolean;
31
+ onChange?: (value: any, form: FormGroup) => void;
32
+ onClick?: (value: any, form: FormGroup) => void;
33
+ }
34
+
35
+ export interface DynamicFormFrameGroup {
36
+ sections: DynamicFormSection[];
37
+ cssClass?: string;
38
+ innerFrameCssClass?: string;
39
+ titleContainerCssClass?: string;
40
+ titlePrefixCssClass?: string;
41
+ titleCssClass?: string;
42
+ id: string;
43
+ title?: string;
44
+ titleIcon?: string;
45
+ titlePrefix?: string;
46
+ description?: string;
47
+ order?: number;
48
+ conditionalDisplay?: (formValues: any) => boolean;
49
+
50
+ }
51
+
52
+ export interface DynamicFormSection {
53
+ id: string;
54
+ title?: string;
55
+ description?: string;
56
+ order?: number;
57
+ fields: DynamicFormField[];
58
+ preWarning?: string; // warning message shown before the section
59
+ postWarning?: string; // warning message shown after the section
60
+ preInfo?: string; // info message shown before the section
61
+ postInfo?: string; // info message shown after the section
62
+ conditionalDisplay?: (formValues: any) => boolean;
63
+ conditionalDisabled?: () => boolean;
64
+
65
+
66
+ }
67
+ export interface DynamicFormOption {
68
+ label: string;
69
+ toolTip?: string;
70
+ helpInfo?: string;
71
+ errorInfo?: string;
72
+ displayError?: () => boolean;
73
+ hint?: string;
74
+ value: any;
75
+ }
76
+
77
+ export interface DynamicFormValidation {
78
+ type: 'required' | 'minLength' | 'maxLength' | 'pattern' | 'min' | 'max' | 'email' | 'custom' | 'compare';
79
+ value?: any;
80
+ message: string;
81
+ compareToField?: string; // For 'compare' validation
82
+ validator?: (value: any) => boolean;
83
+ }
84
+
85
+ export interface DynamicFormConfig {
86
+ id: string;
87
+ title?: string;
88
+ description?: string;
89
+ frames: DynamicFormFrameGroup[];
90
+ hideSubmitButton?: () => boolean; // function to determine if the submit button should be hidden
91
+ hideCancelButton?: () => boolean; // function to determine if the cancel button should be hidden
92
+ submitLabel?: string; // label for the submit button
93
+ cancelLabel?: string; // label for the cancel button
94
+ submitIcon?: string; // icon for the submit button
95
+ cancelIcon?: string; // icon for the cancel button
96
+ preWarning?: string; // warning message shown before the form
97
+ postWarning?: string; // warning message shown after the form
98
+ preInfo?: string; // info message shown before the form
99
+ postInfo?: string; // info message shown after the form
100
+ }
101
+
102
+ export interface DynamicFormValue {
103
+ [key: string]: any;
104
+ }
105
+
106
+ export interface DynamicFormError {
107
+ fieldId: string;
108
+ message: string;
109
+ }
110
+
111
+ export interface DynamicFormState {
112
+ values: DynamicFormValue;
113
+ errors: DynamicFormError[];
114
+ touched: { [key: string]: boolean };
115
+ isSubmitting: boolean;
116
+ isValid: boolean;
117
+ }