@masterteam/forms 0.0.8 → 0.0.9
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/dynamic-form/index.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
2
|
import { OnDestroy } from '@angular/core';
|
|
3
3
|
import { ControlValueAccessor, Validator, FormGroup, AbstractControl, ValidationErrors } from '@angular/forms';
|
|
4
4
|
import { DynamicFormConfig } from '@masterteam/components';
|
|
5
5
|
|
|
6
6
|
declare class DynamicForm implements OnDestroy, ControlValueAccessor, Validator {
|
|
7
7
|
private fb;
|
|
8
|
-
readonly formConfig:
|
|
8
|
+
readonly formConfig: _angular_core.InputSignal<DynamicFormConfig>;
|
|
9
9
|
form: FormGroup<{}>;
|
|
10
10
|
private onChange;
|
|
11
11
|
private onTouched;
|
|
12
12
|
private onValidatorChange;
|
|
13
13
|
private formSubscription?;
|
|
14
|
-
private
|
|
14
|
+
private fieldStates;
|
|
15
|
+
readonly hiddenFields: _angular_core.Signal<Record<string, boolean>>;
|
|
15
16
|
constructor();
|
|
16
17
|
registerOnValidatorChange(fn: () => void): void;
|
|
17
18
|
registerOnChange(fn: (value: any) => void): void;
|
|
@@ -19,10 +20,13 @@ declare class DynamicForm implements OnDestroy, ControlValueAccessor, Validator
|
|
|
19
20
|
private buildForm;
|
|
20
21
|
writeValue(value: any): void;
|
|
21
22
|
validate(_control: AbstractControl): ValidationErrors | null;
|
|
23
|
+
private evaluateAllRelations;
|
|
24
|
+
private applyFieldState;
|
|
25
|
+
private doesRelationMatch;
|
|
22
26
|
private buildValidators;
|
|
23
27
|
ngOnDestroy(): void;
|
|
24
|
-
static ɵfac:
|
|
25
|
-
static ɵcmp:
|
|
28
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm, never>;
|
|
29
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicForm, "mt-dynamic-form", never, { "formConfig": { "alias": "formConfig"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
export { DynamicForm };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, input, effect, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { inject, input, signal, computed, effect, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
@@ -15,9 +15,17 @@ class DynamicForm {
|
|
|
15
15
|
onChange = (_value) => { };
|
|
16
16
|
onTouched = () => { };
|
|
17
17
|
onValidatorChange = () => { };
|
|
18
|
-
// private disabled = false;
|
|
19
18
|
formSubscription;
|
|
20
|
-
|
|
19
|
+
fieldStates = signal({}, ...(ngDevMode ? [{ debugName: "fieldStates" }] : []));
|
|
20
|
+
// Computed signal for field visibility - better performance
|
|
21
|
+
hiddenFields = computed(() => {
|
|
22
|
+
const states = this.fieldStates();
|
|
23
|
+
const hidden = {};
|
|
24
|
+
Object.keys(states).forEach((key) => {
|
|
25
|
+
hidden[key] = states[key].hidden;
|
|
26
|
+
});
|
|
27
|
+
return hidden;
|
|
28
|
+
}, ...(ngDevMode ? [{ debugName: "hiddenFields" }] : []));
|
|
21
29
|
constructor() {
|
|
22
30
|
// Effect to rebuild form when config changes
|
|
23
31
|
effect(() => {
|
|
@@ -34,17 +42,22 @@ class DynamicForm {
|
|
|
34
42
|
this.onTouched = fn;
|
|
35
43
|
}
|
|
36
44
|
buildForm() {
|
|
37
|
-
// Clean up previous subscriptions
|
|
38
45
|
if (this.formSubscription) {
|
|
39
46
|
this.formSubscription.unsubscribe();
|
|
40
47
|
}
|
|
41
|
-
if (this.statusSubscription) {
|
|
42
|
-
this.statusSubscription.unsubscribe();
|
|
43
|
-
}
|
|
44
48
|
const formControls = {};
|
|
49
|
+
const fieldStateMap = {};
|
|
45
50
|
this.formConfig().sections.forEach((section) => {
|
|
46
51
|
section.fields.forEach((field) => {
|
|
47
52
|
if (field.key) {
|
|
53
|
+
const hidden = !!field.hidden;
|
|
54
|
+
const disabled = !!field.disabled;
|
|
55
|
+
fieldStateMap[field.key] = {
|
|
56
|
+
hidden,
|
|
57
|
+
disabled,
|
|
58
|
+
initialHidden: hidden,
|
|
59
|
+
initialDisabled: disabled,
|
|
60
|
+
};
|
|
48
61
|
const validators = this.buildValidators(field.validators || []);
|
|
49
62
|
// Check if field key contains dot notation (e.g., "name.ar", "name.en")
|
|
50
63
|
if (field.key.includes('.')) {
|
|
@@ -73,10 +86,14 @@ class DynamicForm {
|
|
|
73
86
|
});
|
|
74
87
|
});
|
|
75
88
|
this.form = this.fb.group(formControls);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
this.fieldStates.set(fieldStateMap);
|
|
90
|
+
// Subscribe to form value changes and evaluate all relations
|
|
91
|
+
this.formSubscription = this.form.valueChanges.subscribe(() => {
|
|
92
|
+
this.evaluateAllRelations();
|
|
93
|
+
this.onChange(this.form.value);
|
|
79
94
|
});
|
|
95
|
+
// Evaluate initial relations state
|
|
96
|
+
this.evaluateAllRelations();
|
|
80
97
|
// Trigger initial validation state
|
|
81
98
|
this.onValidatorChange();
|
|
82
99
|
}
|
|
@@ -84,6 +101,7 @@ class DynamicForm {
|
|
|
84
101
|
writeValue(value) {
|
|
85
102
|
if (value && this.form) {
|
|
86
103
|
this.form.patchValue(value, { emitEvent: false });
|
|
104
|
+
this.evaluateAllRelations();
|
|
87
105
|
}
|
|
88
106
|
}
|
|
89
107
|
validate(_control) {
|
|
@@ -93,6 +111,91 @@ class DynamicForm {
|
|
|
93
111
|
// Return the form's errors if invalid, null if valid
|
|
94
112
|
return this.form.valid ? null : { invalidForm: true };
|
|
95
113
|
}
|
|
114
|
+
evaluateAllRelations() {
|
|
115
|
+
// Reset all fields to initial state
|
|
116
|
+
this.fieldStates.update((current) => {
|
|
117
|
+
const resetState = {};
|
|
118
|
+
Object.keys(current).forEach((key) => {
|
|
119
|
+
const state = current[key];
|
|
120
|
+
resetState[key] = {
|
|
121
|
+
...state,
|
|
122
|
+
hidden: state.initialHidden,
|
|
123
|
+
disabled: state.initialDisabled,
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
return resetState;
|
|
127
|
+
});
|
|
128
|
+
// Evaluate all field relations
|
|
129
|
+
this.formConfig().sections.forEach((section) => {
|
|
130
|
+
section.fields.forEach((field) => {
|
|
131
|
+
if (!field.key || !field.relations?.length) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Check each relation
|
|
135
|
+
field.relations.forEach((relation) => {
|
|
136
|
+
const dependencyControl = this.form.get(relation.key);
|
|
137
|
+
if (!dependencyControl) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// If relation matches, apply the action
|
|
141
|
+
if (this.doesRelationMatch(dependencyControl.value, relation.value)) {
|
|
142
|
+
this.applyFieldState(field.key, relation.action);
|
|
143
|
+
// Handle form control enable/disable
|
|
144
|
+
if (relation.action === 'disable' || relation.action === 'enable') {
|
|
145
|
+
const control = this.form.get(field.key);
|
|
146
|
+
if (control) {
|
|
147
|
+
if (relation.action === 'disable') {
|
|
148
|
+
control.disable({ emitEvent: false });
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
control.enable({ emitEvent: false });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
applyFieldState(fieldKey, action) {
|
|
161
|
+
this.fieldStates.update((current) => {
|
|
162
|
+
const state = current[fieldKey];
|
|
163
|
+
if (!state)
|
|
164
|
+
return current;
|
|
165
|
+
let hidden = state.hidden;
|
|
166
|
+
let disabled = state.disabled;
|
|
167
|
+
switch (action) {
|
|
168
|
+
case 'hide':
|
|
169
|
+
hidden = true;
|
|
170
|
+
break;
|
|
171
|
+
case 'show':
|
|
172
|
+
hidden = false;
|
|
173
|
+
break;
|
|
174
|
+
case 'disable':
|
|
175
|
+
disabled = true;
|
|
176
|
+
break;
|
|
177
|
+
case 'enable':
|
|
178
|
+
disabled = false;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
if (state.hidden === hidden && state.disabled === disabled) {
|
|
182
|
+
return current;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
...current,
|
|
186
|
+
[fieldKey]: { ...state, hidden, disabled },
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
doesRelationMatch(sourceValue, targetValue) {
|
|
191
|
+
if (Array.isArray(sourceValue)) {
|
|
192
|
+
if (Array.isArray(targetValue)) {
|
|
193
|
+
return targetValue.every((value) => sourceValue.includes(value));
|
|
194
|
+
}
|
|
195
|
+
return sourceValue.includes(targetValue);
|
|
196
|
+
}
|
|
197
|
+
return sourceValue === targetValue;
|
|
198
|
+
}
|
|
96
199
|
buildValidators(validatorConfigs) {
|
|
97
200
|
const validators = [];
|
|
98
201
|
validatorConfigs.forEach((config) => {
|
|
@@ -146,13 +249,9 @@ class DynamicForm {
|
|
|
146
249
|
return validators;
|
|
147
250
|
}
|
|
148
251
|
ngOnDestroy() {
|
|
149
|
-
// Clean up subscriptions
|
|
150
252
|
if (this.formSubscription) {
|
|
151
253
|
this.formSubscription.unsubscribe();
|
|
152
254
|
}
|
|
153
|
-
if (this.statusSubscription) {
|
|
154
|
-
this.statusSubscription.unsubscribe();
|
|
155
|
-
}
|
|
156
255
|
}
|
|
157
256
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
158
257
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: DynamicForm, isStandalone: true, selector: "mt-dynamic-form", inputs: { formConfig: { classPropertyName: "formConfig", publicName: "formConfig", isSignal: true, isRequired: true, transformFunction: null } }, providers: [
|
|
@@ -166,7 +265,7 @@ class DynamicForm {
|
|
|
166
265
|
useExisting: forwardRef(() => DynamicForm),
|
|
167
266
|
multi: true,
|
|
168
267
|
},
|
|
169
|
-
], ngImport: i0, template: "<form [formGroup]=\"form\">\n <div\n class=\"flex flex-col gap-6 max-w-4xl mx-auto p-4\"\n [class]=\"formConfig().layout?.containerClass\"\n >\n @for (\n section of formConfig().sections | sort: \"order\";\n track section.key || $index\n ) {\n <div\n [class]=\"\n section.cssClass ||\n formConfig().layout?.sectionClass ||\n 'flex flex-col gap-4'\n \"\n >\n @if (section.type === \"header\" && section.label) {\n <h3\n [class]=\"\n section?.headerClass ||\n 'text-xl font-semibold text-gray-800 border-b-2 border-gray-200 pb-2 mb-4'\n \"\n >\n {{ section.label }}\n </h3>\n }\n\n <div\n [class]=\"\n section?.bodyClass ||\n 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start'\n \"\n >\n @for (field of section.fields | sort: \"order\"; track field.key) {\n <div\n [class]=\"\n field.cssClass ||\n formConfig().layout?.fieldClass ||\n 'flex flex-col gap-1'\n \"\n [hidden]=\"field.
|
|
268
|
+
], ngImport: i0, template: "<form [formGroup]=\"form\">\n <div\n class=\"flex flex-col gap-6 max-w-4xl mx-auto p-4\"\n [class]=\"formConfig().layout?.containerClass\"\n >\n @for (\n section of formConfig().sections | sort: \"order\";\n track section.key || $index\n ) {\n <div\n [class]=\"\n section.cssClass ||\n formConfig().layout?.sectionClass ||\n 'flex flex-col gap-4'\n \"\n >\n @if (section.type === \"header\" && section.label) {\n <h3\n [class]=\"\n section?.headerClass ||\n 'text-xl font-semibold text-gray-800 border-b-2 border-gray-200 pb-2 mb-4'\n \"\n >\n {{ section.label }}\n </h3>\n }\n\n <div\n [class]=\"\n section?.bodyClass ||\n 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start'\n \"\n >\n @for (field of section.fields | sort: \"order\"; track field.key) {\n <div\n [class]=\"\n field.cssClass ||\n formConfig().layout?.fieldClass ||\n 'flex flex-col gap-1'\n \"\n [hidden]=\"hiddenFields()[field.key!]\"\n >\n @if (field.key!.includes(\".\")) {\n <!-- Handle nested fields (e.g., name.ar, name.en) -->\n @let parentKey = field.key!.split(\".\")[0];\n @let childKey = field.key!.split(\".\")[1];\n <form [formGroup]=\"form.get(parentKey)\">\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"childKey\"\n />\n </form>\n } @else {\n <!-- Handle regular fields -->\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"field.key!\"\n />\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: "component", type: DynamicField, selector: "mt-dynamic-field", inputs: ["fieldConfig", "fieldName"] }, { kind: "pipe", type: SortPipe, name: "sort" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
170
269
|
}
|
|
171
270
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: DynamicForm, decorators: [{
|
|
172
271
|
type: Component,
|
|
@@ -181,7 +280,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
181
280
|
useExisting: forwardRef(() => DynamicForm),
|
|
182
281
|
multi: true,
|
|
183
282
|
},
|
|
184
|
-
], template: "<form [formGroup]=\"form\">\n <div\n class=\"flex flex-col gap-6 max-w-4xl mx-auto p-4\"\n [class]=\"formConfig().layout?.containerClass\"\n >\n @for (\n section of formConfig().sections | sort: \"order\";\n track section.key || $index\n ) {\n <div\n [class]=\"\n section.cssClass ||\n formConfig().layout?.sectionClass ||\n 'flex flex-col gap-4'\n \"\n >\n @if (section.type === \"header\" && section.label) {\n <h3\n [class]=\"\n section?.headerClass ||\n 'text-xl font-semibold text-gray-800 border-b-2 border-gray-200 pb-2 mb-4'\n \"\n >\n {{ section.label }}\n </h3>\n }\n\n <div\n [class]=\"\n section?.bodyClass ||\n 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start'\n \"\n >\n @for (field of section.fields | sort: \"order\"; track field.key) {\n <div\n [class]=\"\n field.cssClass ||\n formConfig().layout?.fieldClass ||\n 'flex flex-col gap-1'\n \"\n [hidden]=\"field.
|
|
283
|
+
], template: "<form [formGroup]=\"form\">\n <div\n class=\"flex flex-col gap-6 max-w-4xl mx-auto p-4\"\n [class]=\"formConfig().layout?.containerClass\"\n >\n @for (\n section of formConfig().sections | sort: \"order\";\n track section.key || $index\n ) {\n <div\n [class]=\"\n section.cssClass ||\n formConfig().layout?.sectionClass ||\n 'flex flex-col gap-4'\n \"\n >\n @if (section.type === \"header\" && section.label) {\n <h3\n [class]=\"\n section?.headerClass ||\n 'text-xl font-semibold text-gray-800 border-b-2 border-gray-200 pb-2 mb-4'\n \"\n >\n {{ section.label }}\n </h3>\n }\n\n <div\n [class]=\"\n section?.bodyClass ||\n 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start'\n \"\n >\n @for (field of section.fields | sort: \"order\"; track field.key) {\n <div\n [class]=\"\n field.cssClass ||\n formConfig().layout?.fieldClass ||\n 'flex flex-col gap-1'\n \"\n [hidden]=\"hiddenFields()[field.key!]\"\n >\n @if (field.key!.includes(\".\")) {\n <!-- Handle nested fields (e.g., name.ar, name.en) -->\n @let parentKey = field.key!.split(\".\")[0];\n @let childKey = field.key!.split(\".\")[1];\n <form [formGroup]=\"form.get(parentKey)\">\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"childKey\"\n />\n </form>\n } @else {\n <!-- Handle regular fields -->\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"field.key!\"\n />\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n</form>\n" }]
|
|
185
284
|
}], ctorParameters: () => [] });
|
|
186
285
|
|
|
187
286
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"masterteam-forms-dynamic-form.mjs","sources":["../../../../packages/masterteam/forms/dynamic-form/dynamic-form.ts","../../../../packages/masterteam/forms/dynamic-form/dynamic-form.html","../../../../packages/masterteam/forms/dynamic-form/masterteam-forms-dynamic-form.ts"],"sourcesContent":["import {\n Component,\n input,\n effect,\n ChangeDetectionStrategy,\n OnDestroy,\n forwardRef,\n inject,\n} from '@angular/core';\nimport {\n FormBuilder,\n FormGroup,\n ReactiveFormsModule,\n Validators,\n AbstractControl,\n ValidatorFn,\n ControlValueAccessor,\n NG_VALUE_ACCESSOR,\n Validator,\n ValidationErrors,\n NG_VALIDATORS,\n} from '@angular/forms';\nimport { CommonModule } from '@angular/common';\nimport { DynamicField } from '@masterteam/forms/dynamic-field';\nimport { SortPipe } from '@masterteam/forms/pipes';\n// import { SortPipe } from '../pipes/sort.pipe';\nimport {\n DynamicFormConfig,\n ValidatorConfig,\n createCustomValidator,\n wrapValidatorWithMessage,\n} from '@masterteam/components';\nimport { Subscription } from 'rxjs';\n\n@Component({\n selector: 'mt-dynamic-form',\n standalone: true,\n imports: [CommonModule, ReactiveFormsModule, DynamicField, SortPipe],\n templateUrl: './dynamic-form.html',\n styleUrls: ['./dynamic-form.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DynamicForm),\n multi: true,\n },\n {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => DynamicForm),\n multi: true,\n },\n ],\n})\nexport class DynamicForm implements OnDestroy, ControlValueAccessor, Validator {\n private fb = inject(FormBuilder);\n\n readonly formConfig = input.required<DynamicFormConfig>();\n\n form = new FormGroup({});\n\n // ControlValueAccessor implementation\n private onChange = (_value: any) => {};\n private onTouched = () => {};\n private onValidatorChange = () => {};\n // private disabled = false;\n private formSubscription?: Subscription;\n private statusSubscription?: Subscription;\n\n constructor() {\n // Effect to rebuild form when config changes\n effect(() => {\n this.buildForm();\n });\n }\n\n registerOnValidatorChange(fn: () => void): void {\n this.onValidatorChange = fn;\n }\n registerOnChange(fn: (value: any) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n private buildForm() {\n // Clean up previous subscriptions\n if (this.formSubscription) {\n this.formSubscription.unsubscribe();\n }\n if (this.statusSubscription) {\n this.statusSubscription.unsubscribe();\n }\n\n const formControls: { [key: string]: AbstractControl } = {};\n\n this.formConfig().sections.forEach((section) => {\n section.fields.forEach((field) => {\n if (field.key) {\n const validators = this.buildValidators(field.validators || []);\n\n // Check if field key contains dot notation (e.g., \"name.ar\", \"name.en\")\n if (field.key.includes('.')) {\n const [parentKey, childKey] = field.key.split('.');\n\n // Create or get the parent FormGroup\n if (!formControls[parentKey]) {\n formControls[parentKey] = this.fb.group({});\n }\n\n const parentGroup = formControls[parentKey] as FormGroup;\n\n // Add the child control to the parent group\n const control = this.fb.control(\n {\n value: field.defaultValue,\n disabled: field.disabled || false,\n },\n validators,\n );\n\n parentGroup.addControl(childKey, control);\n } else {\n // Regular field without nesting\n const control = this.fb.control(\n {\n value: field.defaultValue,\n disabled: field.disabled || false,\n },\n validators,\n );\n formControls[field.key] = control;\n }\n }\n });\n });\n\n this.form = this.fb.group(formControls);\n\n // Subscribe to form value changes\n this.formSubscription = this.form.valueChanges.subscribe((value) => {\n this.onChange(value);\n });\n\n // Trigger initial validation state\n this.onValidatorChange();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: any): void {\n if (value && this.form) {\n this.form.patchValue(value, { emitEvent: false });\n }\n }\n\n validate(_control: AbstractControl): ValidationErrors | null {\n if (!this.form) {\n return null;\n }\n // Return the form's errors if invalid, null if valid\n return this.form.valid ? null : { invalidForm: true };\n }\n\n private buildValidators(validatorConfigs: ValidatorConfig[]): ValidatorFn[] {\n const validators: ValidatorFn[] = [];\n\n validatorConfigs.forEach((config) => {\n let validator: ValidatorFn | null = null;\n\n switch (config.type) {\n case 'required':\n validator = Validators.required;\n break;\n case 'email':\n validator = Validators.email;\n break;\n case 'minLength':\n if (config.value) {\n validator = Validators.minLength(config.value);\n }\n break;\n case 'maxLength':\n if (config.value) {\n validator = Validators.maxLength(config.value);\n }\n break;\n case 'min':\n if (config.value !== undefined) {\n validator = Validators.min(config.value);\n }\n break;\n case 'max':\n if (config.value !== undefined) {\n validator = Validators.max(config.value);\n }\n break;\n case 'pattern':\n if (config.value) {\n validator = Validators.pattern(config.value);\n }\n break;\n case 'custom':\n if (config.customValidator) {\n validators.push(\n createCustomValidator(config.customValidator, config.message),\n );\n }\n break;\n }\n\n // If we have a validator and a custom message, wrap it\n if (validator && config.message) {\n validators.push(\n wrapValidatorWithMessage(validator, config.type, config.message),\n );\n } else if (validator) {\n validators.push(validator);\n }\n });\n\n return validators;\n }\n\n ngOnDestroy(): void {\n // Clean up subscriptions\n if (this.formSubscription) {\n this.formSubscription.unsubscribe();\n }\n if (this.statusSubscription) {\n this.statusSubscription.unsubscribe();\n }\n }\n}\n","<form [formGroup]=\"form\">\n <div\n class=\"flex flex-col gap-6 max-w-4xl mx-auto p-4\"\n [class]=\"formConfig().layout?.containerClass\"\n >\n @for (\n section of formConfig().sections | sort: \"order\";\n track section.key || $index\n ) {\n <div\n [class]=\"\n section.cssClass ||\n formConfig().layout?.sectionClass ||\n 'flex flex-col gap-4'\n \"\n >\n @if (section.type === \"header\" && section.label) {\n <h3\n [class]=\"\n section?.headerClass ||\n 'text-xl font-semibold text-gray-800 border-b-2 border-gray-200 pb-2 mb-4'\n \"\n >\n {{ section.label }}\n </h3>\n }\n\n <div\n [class]=\"\n section?.bodyClass ||\n 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start'\n \"\n >\n @for (field of section.fields | sort: \"order\"; track field.key) {\n <div\n [class]=\"\n field.cssClass ||\n formConfig().layout?.fieldClass ||\n 'flex flex-col gap-1'\n \"\n [hidden]=\"field.hidden\"\n >\n @if (field.key!.includes(\".\")) {\n <!-- Handle nested fields (e.g., name.ar, name.en) -->\n @let parentKey = field.key!.split(\".\")[0];\n @let childKey = field.key!.split(\".\")[1];\n <form [formGroup]=\"form.get(parentKey)\">\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"childKey\"\n />\n </form>\n } @else {\n <!-- Handle regular fields -->\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"field.key!\"\n />\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n</form>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"masterteam-forms-dynamic-form.mjs","sources":["../../../../packages/masterteam/forms/dynamic-form/dynamic-form.ts","../../../../packages/masterteam/forms/dynamic-form/dynamic-form.html","../../../../packages/masterteam/forms/dynamic-form/masterteam-forms-dynamic-form.ts"],"sourcesContent":["import {\n Component,\n input,\n effect,\n ChangeDetectionStrategy,\n OnDestroy,\n forwardRef,\n inject,\n signal,\n computed,\n} from '@angular/core';\nimport {\n FormBuilder,\n FormGroup,\n ReactiveFormsModule,\n Validators,\n AbstractControl,\n ValidatorFn,\n ControlValueAccessor,\n NG_VALUE_ACCESSOR,\n Validator,\n ValidationErrors,\n NG_VALIDATORS,\n} from '@angular/forms';\nimport { CommonModule } from '@angular/common';\nimport { DynamicField } from '@masterteam/forms/dynamic-field';\nimport { SortPipe } from '@masterteam/forms/pipes';\n// import { SortPipe } from '../pipes/sort.pipe';\nimport {\n DynamicFormConfig,\n ValidatorConfig,\n createCustomValidator,\n wrapValidatorWithMessage,\n FieldRelationAction,\n FieldState,\n} from '@masterteam/components';\nimport { Subscription } from 'rxjs';\n\n@Component({\n selector: 'mt-dynamic-form',\n standalone: true,\n imports: [CommonModule, ReactiveFormsModule, DynamicField, SortPipe],\n templateUrl: './dynamic-form.html',\n styleUrls: ['./dynamic-form.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DynamicForm),\n multi: true,\n },\n {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => DynamicForm),\n multi: true,\n },\n ],\n})\nexport class DynamicForm implements OnDestroy, ControlValueAccessor, Validator {\n private fb = inject(FormBuilder);\n\n readonly formConfig = input.required<DynamicFormConfig>();\n\n form = new FormGroup({});\n\n // ControlValueAccessor implementation\n private onChange = (_value: any) => {};\n private onTouched = () => {};\n private onValidatorChange = () => {};\n private formSubscription?: Subscription;\n private fieldStates = signal<Record<string, FieldState>>({});\n\n // Computed signal for field visibility - better performance\n readonly hiddenFields = computed(() => {\n const states = this.fieldStates();\n const hidden: Record<string, boolean> = {};\n Object.keys(states).forEach((key) => {\n hidden[key] = states[key].hidden;\n });\n return hidden;\n });\n\n constructor() {\n // Effect to rebuild form when config changes\n effect(() => {\n this.buildForm();\n });\n }\n\n registerOnValidatorChange(fn: () => void): void {\n this.onValidatorChange = fn;\n }\n registerOnChange(fn: (value: any) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n private buildForm() {\n if (this.formSubscription) {\n this.formSubscription.unsubscribe();\n }\n\n const formControls: { [key: string]: AbstractControl } = {};\n const fieldStateMap: Record<string, FieldState> = {};\n\n this.formConfig().sections.forEach((section) => {\n section.fields.forEach((field) => {\n if (field.key) {\n const hidden = !!field.hidden;\n const disabled = !!field.disabled;\n fieldStateMap[field.key] = {\n hidden,\n disabled,\n initialHidden: hidden,\n initialDisabled: disabled,\n };\n const validators = this.buildValidators(field.validators || []);\n\n // Check if field key contains dot notation (e.g., \"name.ar\", \"name.en\")\n if (field.key.includes('.')) {\n const [parentKey, childKey] = field.key.split('.');\n\n // Create or get the parent FormGroup\n if (!formControls[parentKey]) {\n formControls[parentKey] = this.fb.group({});\n }\n\n const parentGroup = formControls[parentKey] as FormGroup;\n\n // Add the child control to the parent group\n const control = this.fb.control(\n {\n value: field.defaultValue,\n disabled: field.disabled || false,\n },\n validators,\n );\n\n parentGroup.addControl(childKey, control);\n } else {\n // Regular field without nesting\n const control = this.fb.control(\n {\n value: field.defaultValue,\n disabled: field.disabled || false,\n },\n validators,\n );\n formControls[field.key] = control;\n }\n }\n });\n });\n\n this.form = this.fb.group(formControls);\n this.fieldStates.set(fieldStateMap);\n\n // Subscribe to form value changes and evaluate all relations\n this.formSubscription = this.form.valueChanges.subscribe(() => {\n this.evaluateAllRelations();\n this.onChange(this.form.value);\n });\n\n // Evaluate initial relations state\n this.evaluateAllRelations();\n\n // Trigger initial validation state\n this.onValidatorChange();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: any): void {\n if (value && this.form) {\n this.form.patchValue(value, { emitEvent: false });\n this.evaluateAllRelations();\n }\n }\n\n validate(_control: AbstractControl): ValidationErrors | null {\n if (!this.form) {\n return null;\n }\n // Return the form's errors if invalid, null if valid\n return this.form.valid ? null : { invalidForm: true };\n }\n\n private evaluateAllRelations() {\n // Reset all fields to initial state\n this.fieldStates.update((current) => {\n const resetState: Record<string, FieldState> = {};\n Object.keys(current).forEach((key) => {\n const state = current[key];\n resetState[key] = {\n ...state,\n hidden: state.initialHidden,\n disabled: state.initialDisabled,\n };\n });\n return resetState;\n });\n\n // Evaluate all field relations\n this.formConfig().sections.forEach((section) => {\n section.fields.forEach((field) => {\n if (!field.key || !field.relations?.length) {\n return;\n }\n\n // Check each relation\n field.relations.forEach((relation) => {\n const dependencyControl = this.form.get(relation.key);\n if (!dependencyControl) {\n return;\n }\n\n // If relation matches, apply the action\n if (this.doesRelationMatch(dependencyControl.value, relation.value)) {\n this.applyFieldState(field.key!, relation.action);\n\n // Handle form control enable/disable\n if (relation.action === 'disable' || relation.action === 'enable') {\n const control = this.form.get(field.key!);\n if (control) {\n if (relation.action === 'disable') {\n control.disable({ emitEvent: false });\n } else {\n control.enable({ emitEvent: false });\n }\n }\n }\n }\n });\n });\n });\n }\n\n private applyFieldState(fieldKey: string, action: FieldRelationAction) {\n this.fieldStates.update((current) => {\n const state = current[fieldKey];\n if (!state) return current;\n\n let hidden = state.hidden;\n let disabled = state.disabled;\n\n switch (action) {\n case 'hide':\n hidden = true;\n break;\n case 'show':\n hidden = false;\n break;\n case 'disable':\n disabled = true;\n break;\n case 'enable':\n disabled = false;\n break;\n }\n\n if (state.hidden === hidden && state.disabled === disabled) {\n return current;\n }\n\n return {\n ...current,\n [fieldKey]: { ...state, hidden, disabled },\n };\n });\n }\n\n private doesRelationMatch(sourceValue: any, targetValue: any): boolean {\n if (Array.isArray(sourceValue)) {\n if (Array.isArray(targetValue)) {\n return targetValue.every((value) => sourceValue.includes(value));\n }\n return sourceValue.includes(targetValue);\n }\n return sourceValue === targetValue;\n }\n\n private buildValidators(validatorConfigs: ValidatorConfig[]): ValidatorFn[] {\n const validators: ValidatorFn[] = [];\n\n validatorConfigs.forEach((config) => {\n let validator: ValidatorFn | null = null;\n\n switch (config.type) {\n case 'required':\n validator = Validators.required;\n break;\n case 'email':\n validator = Validators.email;\n break;\n case 'minLength':\n if (config.value) {\n validator = Validators.minLength(config.value);\n }\n break;\n case 'maxLength':\n if (config.value) {\n validator = Validators.maxLength(config.value);\n }\n break;\n case 'min':\n if (config.value !== undefined) {\n validator = Validators.min(config.value);\n }\n break;\n case 'max':\n if (config.value !== undefined) {\n validator = Validators.max(config.value);\n }\n break;\n case 'pattern':\n if (config.value) {\n validator = Validators.pattern(config.value);\n }\n break;\n case 'custom':\n if (config.customValidator) {\n validators.push(\n createCustomValidator(config.customValidator, config.message),\n );\n }\n break;\n }\n\n // If we have a validator and a custom message, wrap it\n if (validator && config.message) {\n validators.push(\n wrapValidatorWithMessage(validator, config.type, config.message),\n );\n } else if (validator) {\n validators.push(validator);\n }\n });\n\n return validators;\n }\n\n ngOnDestroy(): void {\n if (this.formSubscription) {\n this.formSubscription.unsubscribe();\n }\n }\n}\n","<form [formGroup]=\"form\">\n <div\n class=\"flex flex-col gap-6 max-w-4xl mx-auto p-4\"\n [class]=\"formConfig().layout?.containerClass\"\n >\n @for (\n section of formConfig().sections | sort: \"order\";\n track section.key || $index\n ) {\n <div\n [class]=\"\n section.cssClass ||\n formConfig().layout?.sectionClass ||\n 'flex flex-col gap-4'\n \"\n >\n @if (section.type === \"header\" && section.label) {\n <h3\n [class]=\"\n section?.headerClass ||\n 'text-xl font-semibold text-gray-800 border-b-2 border-gray-200 pb-2 mb-4'\n \"\n >\n {{ section.label }}\n </h3>\n }\n\n <div\n [class]=\"\n section?.bodyClass ||\n 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start'\n \"\n >\n @for (field of section.fields | sort: \"order\"; track field.key) {\n <div\n [class]=\"\n field.cssClass ||\n formConfig().layout?.fieldClass ||\n 'flex flex-col gap-1'\n \"\n [hidden]=\"hiddenFields()[field.key!]\"\n >\n @if (field.key!.includes(\".\")) {\n <!-- Handle nested fields (e.g., name.ar, name.en) -->\n @let parentKey = field.key!.split(\".\")[0];\n @let childKey = field.key!.split(\".\")[1];\n <form [formGroup]=\"form.get(parentKey)\">\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"childKey\"\n />\n </form>\n } @else {\n <!-- Handle regular fields -->\n <mt-dynamic-field\n [fieldConfig]=\"field\"\n [fieldName]=\"field.key!\"\n />\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n</form>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|