@websolutespa/bom-mixer-forms 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @websolutespa/bom-mixer-forms
2
2
 
3
+ ## 1.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added: useFormSchema
8
+
9
+ ## 1.8.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 15ad595: Added: Validators parametric error key
14
+
3
15
  ## 1.8.0
4
16
 
5
17
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -11,10 +11,10 @@ declare class EventEmitter {
11
11
  once(event: string, listener: (...args: any[]) => any): void;
12
12
  }
13
13
 
14
- declare class FormAbstractCollection<T extends FormControls> extends FormAbstract {
14
+ declare class FormAbstractCollection<U extends Record<string, FormValue> | FormValue[], T extends FormControls> extends FormAbstract<U> {
15
15
  controls_: T;
16
16
  constructor(controls: T, validators?: (FormValidator | FormValidator[]));
17
- initControl_(controlOrValue: FormAbstract | any, key: any): FormAbstract;
17
+ initControl_(controlOrValue: FormAbstract | FormValue, key: any): FormAbstract;
18
18
  protected setInitialOptions(options?: FormOptions): void;
19
19
  protected checkAsyncPropState_(key: keyof FormFlags, option?: FormActivator, root?: FormCollection): Promise<boolean>;
20
20
  updateState_(): void;
@@ -34,7 +34,7 @@ declare class FormAbstractCollection<T extends FormControls> extends FormAbstrac
34
34
  protected reduce_(callback: Function, result: any): any;
35
35
  protected all_(key: (keyof FormAbstract), value: any): boolean;
36
36
  protected any_(key: (keyof FormAbstract), value: any): boolean;
37
- protected map_(): FormAbstract[];
37
+ protected map_(): FormAbstract<any>[];
38
38
  get controls(): T;
39
39
  set controls(controls: T);
40
40
  set disabled(disabled: boolean);
@@ -42,12 +42,8 @@ declare class FormAbstractCollection<T extends FormControls> extends FormAbstrac
42
42
  set hidden(hidden: boolean);
43
43
  set submitted(submitted: boolean);
44
44
  set touched(touched: boolean);
45
- get value(): {
46
- [key: string]: FormValue;
47
- };
48
- set value(value: {
49
- [key: string]: FormValue;
50
- });
45
+ get value(): U;
46
+ set value(value: U);
51
47
  get errors(): {
52
48
  [key: string]: any;
53
49
  };
@@ -56,11 +52,11 @@ declare class FormAbstractCollection<T extends FormControls> extends FormAbstrac
56
52
  });
57
53
  }
58
54
 
59
- declare class FormArray extends FormAbstractCollection<FormAbstract[]> {
60
- constructor(controls?: FormAbstract[], validators?: FormValidator | FormValidator[], initialOptions?: FormOptions);
55
+ declare class FormArray<T extends FormValue[] = FormValue[]> extends FormAbstractCollection<T, FormArrayControls> {
56
+ constructor(controls?: FormArrayControls, validators?: FormValidator | FormValidator[], initialOptions?: FormOptions);
61
57
  forEach_(callback: (control: FormAbstract, key: number) => any): void;
62
- map_(): FormAbstract[];
63
- get value(): FormValue[];
58
+ map_(): FormArrayControls;
59
+ get value(): T;
64
60
  get length(): number;
65
61
  protected init(control: FormAbstract, key: number): void;
66
62
  set(control: FormAbstract, key: number): void;
@@ -69,25 +65,19 @@ declare class FormArray extends FormAbstractCollection<FormAbstract[]> {
69
65
  insert(control: FormAbstract, key: number): void;
70
66
  remove(control: FormAbstract): void;
71
67
  removeKey(key: number): void;
72
- at(key: number): FormAbstract;
68
+ at(key: number): FormAbstract<any>;
73
69
  }
74
- declare function formArray(controls?: FormAbstract[], validators?: FormValidator | FormValidator[]): FormArray;
70
+ declare function formArray(controls?: FormArrayControls, validators?: FormValidator | FormValidator[]): FormArray<any[]>;
75
71
 
76
72
  declare class FormControl extends FormAbstract {
77
73
  constructor(value?: FormValue, validators?: FormValidator | FormValidator[], initialOptions?: FormOptions);
78
74
  }
79
75
  declare function formControl(value?: FormValue, validators?: FormValidator | FormValidator[]): FormControl;
80
76
 
81
- declare class FormGroup extends FormAbstractCollection<{
82
- [key: string]: FormAbstract;
83
- }> {
84
- constructor(controls?: {
85
- [key: string]: FormAbstract | any;
86
- }, validators?: FormValidator | FormValidator[], initialOptions?: FormOptions);
77
+ declare class FormGroup<T extends Record<string, FormValue> = FormValue> extends FormAbstractCollection<T, FormGroupControls> {
78
+ constructor(controls?: FormGroupControls, validators?: FormValidator | FormValidator[], initialOptions?: FormOptions);
87
79
  }
88
- declare function formGroup(controls?: {
89
- [key: string]: FormAbstract | FormValue;
90
- }, validators?: FormValidator | FormValidator[]): FormGroup;
80
+ declare function formGroup(controls?: FormGroupControls, validators?: FormValidator | FormValidator[]): FormGroup<any>;
91
81
 
92
82
  type FormValue = any;
93
83
  type FormValueGroup = {
@@ -121,10 +111,10 @@ type FormOptions = {
121
111
  type ValidationError = {
122
112
  [key: string]: any;
123
113
  };
124
- type FormControls = {
125
- [key: string]: FormAbstract | FormValue;
126
- } | FormAbstract[];
127
- type FormCollection = FormAbstractCollection<FormControls>;
114
+ type FormGroupControls = Record<string, FormAbstract | FormValue>;
115
+ type FormArrayControls = FormAbstract[];
116
+ type FormControls = FormGroupControls | FormArrayControls;
117
+ type FormCollection = FormAbstractCollection<Record<string, any> | any[], FormControls>;
128
118
  type FormActivator = boolean | ((value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormCollection) => boolean | Promise<boolean>);
129
119
  type FormValidator = (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormCollection) => null | ValidationError;
130
120
  type FormAsyncValidator = (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormCollection) => Promise<null | ValidationError>;
@@ -144,9 +134,7 @@ type FormFlags = {
144
134
  untouched: boolean;
145
135
  unsubmitted: boolean;
146
136
  };
147
- type FormErrors = {
148
- [key: string]: any;
149
- };
137
+ type FormErrors = Record<string, any>;
150
138
  type FormValidationError = {
151
139
  key: string;
152
140
  value: FormValue;
@@ -176,18 +164,14 @@ type IFormBuilderControlSchema = {
176
164
  children?: IFormBuilderSchema;
177
165
  customData?: Record<string, any>;
178
166
  };
179
- type IFormBuilderGroupSchema = {
180
- [key: string]: IFormBuilderControlSchema;
181
- };
167
+ type IFormBuilderGroupSchema = Record<string, IFormBuilderControlSchema>;
182
168
  type IFormBuilderArraySchema = IFormBuilderControlSchema[];
183
- type IFormBuilderGroupValues = {
184
- [key: string]: FormGroup | FormArray | FormControl;
185
- };
169
+ type IFormBuilderGroupValues = Record<string, FormGroup | FormArray | FormControl>;
186
170
  type IFormBuilderSchema = IFormBuilderGroupSchema | IFormBuilderArraySchema;
187
171
 
188
- declare class FormAbstract extends EventEmitter {
172
+ declare class FormAbstract<T = any> extends EventEmitter {
189
173
  errors_: ValidationError;
190
- value_: FormValue;
174
+ value_: T | null;
191
175
  validators_: FormValidator[];
192
176
  state_: FormFlags;
193
177
  name?: string | number;
@@ -247,8 +231,8 @@ declare class FormAbstract extends EventEmitter {
247
231
  get pristine(): boolean;
248
232
  get untouched(): boolean;
249
233
  get unsubmitted(): boolean;
250
- get value(): any;
251
- set value(value: any);
234
+ get value(): T | null;
235
+ set value(value: T | null);
252
236
  }
253
237
 
254
238
  declare function validValue(value: FormValue): any;
@@ -261,32 +245,32 @@ declare function stringToValue(value: string | string[] | undefined, options?: I
261
245
  /**
262
246
  * an email pattern validator
263
247
  */
264
- declare function EmailValidator(): FormValidator;
248
+ declare function EmailValidator(key?: string): FormValidator;
265
249
 
266
250
  /**
267
251
  * an equality match validation on another field
268
252
  */
269
- declare function MatchValidator(getOtherValue: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => any): FormValidator;
253
+ declare function MatchValidator(getOtherValue: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => any, key?: string): FormValidator;
270
254
 
271
255
  /**
272
256
  * a max string length validator
273
257
  */
274
- declare function MaxLengthValidator(maxlength: number): FormValidator;
258
+ declare function MaxLengthValidator(maxlength: number, key?: string): FormValidator;
275
259
 
276
260
  /**
277
261
  * a max number value validator
278
262
  */
279
- declare function MaxValidator(max: number): FormValidator;
263
+ declare function MaxValidator(max: number, key?: string): FormValidator;
280
264
 
281
265
  /**
282
266
  * a min string length validator
283
267
  */
284
- declare function MinLengthValidator(minlength: number): FormValidator;
268
+ declare function MinLengthValidator(minlength: number, key?: string): FormValidator;
285
269
 
286
270
  /**
287
271
  * a min number value validator
288
272
  */
289
- declare function MinValidator(min: number): FormValidator;
273
+ declare function MinValidator(min: number, key?: string): FormValidator;
290
274
 
291
275
  /**
292
276
  * a null validator
@@ -296,27 +280,38 @@ declare function NullValidator(): FormValidator;
296
280
  /**
297
281
  * a regex pattern validator
298
282
  */
299
- declare function PatternValidator(pattern: string | RegExp): FormValidator;
283
+ declare function PatternValidator(pattern: string | RegExp, key?: string): FormValidator;
300
284
 
301
285
  /**
302
286
  * a required dependant on another field
303
287
  */
304
- declare function RequiredIfValidator(condition: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => boolean): FormValidator;
288
+ declare function RequiredIfValidator(condition: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => boolean, key?: string): FormValidator;
305
289
 
306
290
  /**
307
291
  * a required and true validator
308
292
  */
309
- declare function RequiredTrueValidator(): FormValidator;
293
+ declare function RequiredTrueValidator(key?: string): FormValidator;
310
294
 
311
295
  /**
312
296
  * a required validator
313
297
  */
314
- declare function RequiredValidator(): FormValidator;
298
+ declare function RequiredValidator(key?: string): FormValidator;
315
299
 
316
300
  declare function useControl<T>(control: FormAbstract): [FormState<T>, (value: T | null) => void, () => void, () => void, FormAbstract];
317
301
 
318
302
  declare function useForm<T, U extends (FormGroup | FormArray)>(factory: () => U, deps?: DependencyList): [FormState<T>, (value: any) => void, () => void, () => void, U];
319
303
 
320
- declare function useFormBuilder<T, U extends (FormGroup | FormArray)>(schema: IFormBuilderSchema, deps?: DependencyList): [FormState<T>, (value: Partial<T>) => void, () => void, () => void, U];
304
+ /**
305
+ * @deprecated Since version 1.9.0. Will be deleted in version 1.10.0. Use useFormSchema instead.
306
+ */
307
+ declare function useFormBuilder<T extends (Record<string, FormValue> | FormValue[]) = Record<string, FormValue>, U extends (T extends FormValue[] ? FormArray<T> : FormGroup<T>) = T extends FormValue[] ? FormArray<T> : FormGroup<T>>(schema: IFormBuilderSchema, deps?: DependencyList): [FormState<T>, (value: Partial<T>) => void, () => void, () => void, U];
308
+
309
+ declare function useFormSchema<T extends (Record<string, FormValue> | FormValue[]) = Record<string, FormValue>, U extends (T extends FormValue[] ? FormArray<T> : FormGroup<T>) = T extends FormValue[] ? FormArray<T> : FormGroup<T>>(schema: IFormBuilderSchema, deps?: DependencyList): {
310
+ state: FormState<T>;
311
+ setValue: (value: Partial<T>) => void;
312
+ setTouched: () => void;
313
+ reset: () => void;
314
+ form: U;
315
+ };
321
316
 
322
- export { ControlType, EmailValidator, FormAbstract, FormAbstractCollection, FormActivator, FormArray, FormAsyncValidator, FormCollection, FormControl, FormControls, FormErrors, FormFlags, FormGroup, FormOptions, FormState, FormValidationError, FormValidationErrors, FormValidator, FormValue, FormValueArray, FormValueGroup, IControlParam, IFormBuilderArraySchema, IFormBuilderControlSchema, IFormBuilderGroupSchema, IFormBuilderGroupValues, IFormBuilderSchema, IFormOption, MatchValidator, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, NullValidator, PatternValidator, RequiredIfValidator, RequiredTrueValidator, RequiredValidator, ValidationError, formArray, formControl, formGroup, mapErrors_, stringToValue, useControl, useForm, useFormBuilder, validValue, valueToString };
317
+ export { ControlType, EmailValidator, FormAbstract, FormAbstractCollection, FormActivator, FormArray, FormArrayControls, FormAsyncValidator, FormCollection, FormControl, FormControls, FormErrors, FormFlags, FormGroup, FormGroupControls, FormOptions, FormState, FormValidationError, FormValidationErrors, FormValidator, FormValue, FormValueArray, FormValueGroup, IControlParam, IFormBuilderArraySchema, IFormBuilderControlSchema, IFormBuilderGroupSchema, IFormBuilderGroupValues, IFormBuilderSchema, IFormOption, MatchValidator, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, NullValidator, PatternValidator, RequiredIfValidator, RequiredTrueValidator, RequiredValidator, ValidationError, formArray, formControl, formGroup, mapErrors_, stringToValue, useControl, useForm, useFormBuilder, useFormSchema, validValue, valueToString };
package/dist/index.js CHANGED
@@ -78,6 +78,7 @@ __export(src_exports, {
78
78
  useControl: () => useControl,
79
79
  useForm: () => useForm,
80
80
  useFormBuilder: () => useFormBuilder,
81
+ useFormSchema: () => useFormSchema,
81
82
  validValue: () => validValue,
82
83
  valueToString: () => valueToString
83
84
  });
@@ -864,11 +865,11 @@ function formGroup(controls = {}, validators) {
864
865
  }
865
866
 
866
867
  // src/forms/validators/email.validator.ts
867
- function EmailValidator() {
868
+ function EmailValidator(key = "email") {
868
869
  const regex = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
869
870
  return function(value) {
870
871
  if (typeof value === "string") {
871
- return regex.test(value) ? null : { email: true };
872
+ return regex.test(value) ? null : { [key]: true };
872
873
  } else {
873
874
  return null;
874
875
  }
@@ -876,23 +877,23 @@ function EmailValidator() {
876
877
  }
877
878
 
878
879
  // src/forms/validators/match.validator.ts
879
- function MatchValidator(getOtherValue) {
880
+ function MatchValidator(getOtherValue, key = "match") {
880
881
  return function(value, rootValue, control, root) {
881
882
  let otherValue = getOtherValue(value, rootValue, control, root);
882
883
  otherValue = validValue(otherValue);
883
- return value !== otherValue ? { match: { value, match: otherValue } } : null;
884
+ return value !== otherValue ? { [key]: { value, match: otherValue } } : null;
884
885
  };
885
886
  }
886
887
 
887
888
  // src/forms/validators/max-length.validator.ts
888
- function MaxLengthValidator(maxlength) {
889
+ function MaxLengthValidator(maxlength, key = "maxlength") {
889
890
  return function(value) {
890
891
  if (maxlength == null) {
891
892
  return null;
892
893
  }
893
894
  if (typeof value === "string") {
894
895
  const length = value ? value.length : 0;
895
- return length > maxlength ? { minlength: { requiredLength: maxlength, actualLength: length } } : null;
896
+ return length > maxlength ? { [key]: { requiredLength: maxlength, actualLength: length } } : null;
896
897
  } else {
897
898
  return null;
898
899
  }
@@ -900,25 +901,25 @@ function MaxLengthValidator(maxlength) {
900
901
  }
901
902
 
902
903
  // src/forms/validators/max.validator.ts
903
- function MaxValidator(max) {
904
+ function MaxValidator(max, key = "max") {
904
905
  return function(value) {
905
906
  value = typeof value === "string" ? parseFloat(value) : typeof value === "number" ? value : null;
906
907
  if (value == null || max == null) {
907
908
  return null;
908
909
  }
909
- return !isNaN(value) && value > max ? { max: { max, actual: value } } : null;
910
+ return !isNaN(value) && value > max ? { [key]: { max, actual: value } } : null;
910
911
  };
911
912
  }
912
913
 
913
914
  // src/forms/validators/min-length.validator.ts
914
- function MinLengthValidator(minlength) {
915
+ function MinLengthValidator(minlength, key = "minlength") {
915
916
  return function(value) {
916
917
  if (minlength == null) {
917
918
  return null;
918
919
  }
919
920
  if (typeof value === "string") {
920
921
  const length = value ? value.length : 0;
921
- return length < minlength ? { minlength: { requiredLength: minlength, actualLength: length } } : null;
922
+ return length < minlength ? { [key]: { requiredLength: minlength, actualLength: length } } : null;
922
923
  } else {
923
924
  return null;
924
925
  }
@@ -926,13 +927,13 @@ function MinLengthValidator(minlength) {
926
927
  }
927
928
 
928
929
  // src/forms/validators/min.validator.ts
929
- function MinValidator(min) {
930
+ function MinValidator(min, key = "min") {
930
931
  return function(value) {
931
932
  value = typeof value === "string" ? parseFloat(value) : typeof value === "number" ? value : null;
932
933
  if (value == null || min == null) {
933
934
  return null;
934
935
  }
935
- return !isNaN(value) && value < min ? { min: { min, actual: value } } : null;
936
+ return !isNaN(value) && value < min ? { [key]: { min, actual: value } } : null;
936
937
  };
937
938
  }
938
939
 
@@ -944,14 +945,14 @@ function NullValidator() {
944
945
  }
945
946
 
946
947
  // src/forms/validators/pattern.validator.ts
947
- function PatternValidator(pattern) {
948
+ function PatternValidator(pattern, key = "pattern") {
948
949
  return function(value) {
949
950
  if (pattern == null) {
950
951
  return null;
951
952
  }
952
953
  if (typeof value === "string") {
953
954
  const regex = patternToRegEx(pattern);
954
- return regex.test(value) ? null : { pattern: { requiredPattern: regex.toString(), actualValue: value } };
955
+ return regex.test(value) ? null : { [key]: { requiredPattern: regex.toString(), actualValue: value } };
955
956
  } else {
956
957
  return null;
957
958
  }
@@ -971,13 +972,13 @@ function patternToRegEx(pattern) {
971
972
 
972
973
  // src/forms/validators/required-if.validator.ts
973
974
  var import_bom_core3 = require("@websolutespa/bom-core");
974
- function RequiredIfValidator(condition) {
975
+ function RequiredIfValidator(condition, key = "required") {
975
976
  return function(value, rootValue, control, root) {
976
977
  if (Boolean(condition(value, rootValue, control, root)) === true) {
977
978
  if ((0, import_bom_core3.isArray)(value) || typeof value === "string") {
978
- return value.length === 0 ? { required: true } : null;
979
+ return value.length === 0 ? { [key]: true } : null;
979
980
  } else {
980
- return value == null ? { required: true } : null;
981
+ return value == null ? { [key]: true } : null;
981
982
  }
982
983
  } else {
983
984
  return null;
@@ -986,20 +987,20 @@ function RequiredIfValidator(condition) {
986
987
  }
987
988
 
988
989
  // src/forms/validators/required-true.validator.ts
989
- function RequiredTrueValidator() {
990
+ function RequiredTrueValidator(key = "required") {
990
991
  return function(value) {
991
- return value === true ? null : { required: true };
992
+ return value === true ? null : { [key]: true };
992
993
  };
993
994
  }
994
995
 
995
996
  // src/forms/validators/required.validator.ts
996
997
  var import_bom_core4 = require("@websolutespa/bom-core");
997
- function RequiredValidator() {
998
+ function RequiredValidator(key = "required") {
998
999
  return function(value) {
999
1000
  if ((0, import_bom_core4.isArray)(value) || typeof value === "string") {
1000
- return value.length === 0 ? { required: true } : null;
1001
+ return value.length === 0 ? { [key]: true } : null;
1001
1002
  } else {
1002
- return value == null ? { required: true } : null;
1003
+ return value == null ? { [key]: true } : null;
1003
1004
  }
1004
1005
  };
1005
1006
  }
@@ -1115,6 +1116,73 @@ function mapSchema_(schema) {
1115
1116
  return mapControl_(schema);
1116
1117
  }
1117
1118
  }
1119
+
1120
+ // src/hooks/useFormSchema/useFormSchema.ts
1121
+ var import_react4 = require("react");
1122
+ function useFormSchema(schema, deps = []) {
1123
+ const form = (0, import_react4.useMemo)(() => {
1124
+ if (Array.isArray(schema)) {
1125
+ return mapArray_2(schema);
1126
+ } else {
1127
+ return mapGroup_2(schema);
1128
+ }
1129
+ }, deps);
1130
+ const setValue = (0, import_react4.useCallback)((value) => {
1131
+ form.patch(value);
1132
+ }, deps);
1133
+ const setTouched = (0, import_react4.useCallback)(() => {
1134
+ form.touched = true;
1135
+ }, deps);
1136
+ const reset = (0, import_react4.useCallback)(() => {
1137
+ form.reset();
1138
+ }, deps);
1139
+ const [state, setState] = (0, import_react4.useState)({
1140
+ value: form.value,
1141
+ flags: form.state,
1142
+ errors: mapErrors_(form.errors)
1143
+ });
1144
+ (0, import_react4.useEffect)(() => {
1145
+ const onChange = (value) => {
1146
+ const newState = { value, flags: form.state, errors: mapErrors_(form.errors) };
1147
+ };
1148
+ form.on("change", onChange);
1149
+ form.revalidate_();
1150
+ return () => form.off("change", onChange);
1151
+ }, deps);
1152
+ return {
1153
+ form,
1154
+ state,
1155
+ setTouched,
1156
+ setValue,
1157
+ reset
1158
+ };
1159
+ }
1160
+ function mapGroup_2(children, schema) {
1161
+ const controls = {};
1162
+ Object.keys(children).forEach((key) => {
1163
+ controls[key] = mapSchema_2(__spreadValues({ name: key }, children[key]));
1164
+ });
1165
+ const group = new FormGroup(controls, schema == null ? void 0 : schema.validators, schema);
1166
+ return group;
1167
+ }
1168
+ function mapArray_2(children, schema) {
1169
+ const controls = children.map((x) => mapSchema_2(x));
1170
+ const array = new FormArray(controls, schema == null ? void 0 : schema.validators, schema);
1171
+ return array;
1172
+ }
1173
+ function mapControl_2(schema) {
1174
+ return new FormControl(schema.value, schema.validators, schema);
1175
+ }
1176
+ function mapSchema_2(schema) {
1177
+ switch (schema.schema) {
1178
+ case "group":
1179
+ return mapGroup_2(schema.children || {}, schema);
1180
+ case "array":
1181
+ return mapArray_2(schema.children || [], schema);
1182
+ default:
1183
+ return mapControl_2(schema);
1184
+ }
1185
+ }
1118
1186
  // Annotate the CommonJS export names for ESM import in node:
1119
1187
  0 && (module.exports = {
1120
1188
  EmailValidator,
@@ -1141,6 +1209,7 @@ function mapSchema_(schema) {
1141
1209
  useControl,
1142
1210
  useForm,
1143
1211
  useFormBuilder,
1212
+ useFormSchema,
1144
1213
  validValue,
1145
1214
  valueToString
1146
1215
  });
package/dist/index.mjs CHANGED
@@ -816,11 +816,11 @@ function formGroup(controls = {}, validators) {
816
816
  }
817
817
 
818
818
  // src/forms/validators/email.validator.ts
819
- function EmailValidator() {
819
+ function EmailValidator(key = "email") {
820
820
  const regex = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
821
821
  return function(value) {
822
822
  if (typeof value === "string") {
823
- return regex.test(value) ? null : { email: true };
823
+ return regex.test(value) ? null : { [key]: true };
824
824
  } else {
825
825
  return null;
826
826
  }
@@ -828,23 +828,23 @@ function EmailValidator() {
828
828
  }
829
829
 
830
830
  // src/forms/validators/match.validator.ts
831
- function MatchValidator(getOtherValue) {
831
+ function MatchValidator(getOtherValue, key = "match") {
832
832
  return function(value, rootValue, control, root) {
833
833
  let otherValue = getOtherValue(value, rootValue, control, root);
834
834
  otherValue = validValue(otherValue);
835
- return value !== otherValue ? { match: { value, match: otherValue } } : null;
835
+ return value !== otherValue ? { [key]: { value, match: otherValue } } : null;
836
836
  };
837
837
  }
838
838
 
839
839
  // src/forms/validators/max-length.validator.ts
840
- function MaxLengthValidator(maxlength) {
840
+ function MaxLengthValidator(maxlength, key = "maxlength") {
841
841
  return function(value) {
842
842
  if (maxlength == null) {
843
843
  return null;
844
844
  }
845
845
  if (typeof value === "string") {
846
846
  const length = value ? value.length : 0;
847
- return length > maxlength ? { minlength: { requiredLength: maxlength, actualLength: length } } : null;
847
+ return length > maxlength ? { [key]: { requiredLength: maxlength, actualLength: length } } : null;
848
848
  } else {
849
849
  return null;
850
850
  }
@@ -852,25 +852,25 @@ function MaxLengthValidator(maxlength) {
852
852
  }
853
853
 
854
854
  // src/forms/validators/max.validator.ts
855
- function MaxValidator(max) {
855
+ function MaxValidator(max, key = "max") {
856
856
  return function(value) {
857
857
  value = typeof value === "string" ? parseFloat(value) : typeof value === "number" ? value : null;
858
858
  if (value == null || max == null) {
859
859
  return null;
860
860
  }
861
- return !isNaN(value) && value > max ? { max: { max, actual: value } } : null;
861
+ return !isNaN(value) && value > max ? { [key]: { max, actual: value } } : null;
862
862
  };
863
863
  }
864
864
 
865
865
  // src/forms/validators/min-length.validator.ts
866
- function MinLengthValidator(minlength) {
866
+ function MinLengthValidator(minlength, key = "minlength") {
867
867
  return function(value) {
868
868
  if (minlength == null) {
869
869
  return null;
870
870
  }
871
871
  if (typeof value === "string") {
872
872
  const length = value ? value.length : 0;
873
- return length < minlength ? { minlength: { requiredLength: minlength, actualLength: length } } : null;
873
+ return length < minlength ? { [key]: { requiredLength: minlength, actualLength: length } } : null;
874
874
  } else {
875
875
  return null;
876
876
  }
@@ -878,13 +878,13 @@ function MinLengthValidator(minlength) {
878
878
  }
879
879
 
880
880
  // src/forms/validators/min.validator.ts
881
- function MinValidator(min) {
881
+ function MinValidator(min, key = "min") {
882
882
  return function(value) {
883
883
  value = typeof value === "string" ? parseFloat(value) : typeof value === "number" ? value : null;
884
884
  if (value == null || min == null) {
885
885
  return null;
886
886
  }
887
- return !isNaN(value) && value < min ? { min: { min, actual: value } } : null;
887
+ return !isNaN(value) && value < min ? { [key]: { min, actual: value } } : null;
888
888
  };
889
889
  }
890
890
 
@@ -896,14 +896,14 @@ function NullValidator() {
896
896
  }
897
897
 
898
898
  // src/forms/validators/pattern.validator.ts
899
- function PatternValidator(pattern) {
899
+ function PatternValidator(pattern, key = "pattern") {
900
900
  return function(value) {
901
901
  if (pattern == null) {
902
902
  return null;
903
903
  }
904
904
  if (typeof value === "string") {
905
905
  const regex = patternToRegEx(pattern);
906
- return regex.test(value) ? null : { pattern: { requiredPattern: regex.toString(), actualValue: value } };
906
+ return regex.test(value) ? null : { [key]: { requiredPattern: regex.toString(), actualValue: value } };
907
907
  } else {
908
908
  return null;
909
909
  }
@@ -923,13 +923,13 @@ function patternToRegEx(pattern) {
923
923
 
924
924
  // src/forms/validators/required-if.validator.ts
925
925
  import { isArray } from "@websolutespa/bom-core";
926
- function RequiredIfValidator(condition) {
926
+ function RequiredIfValidator(condition, key = "required") {
927
927
  return function(value, rootValue, control, root) {
928
928
  if (Boolean(condition(value, rootValue, control, root)) === true) {
929
929
  if (isArray(value) || typeof value === "string") {
930
- return value.length === 0 ? { required: true } : null;
930
+ return value.length === 0 ? { [key]: true } : null;
931
931
  } else {
932
- return value == null ? { required: true } : null;
932
+ return value == null ? { [key]: true } : null;
933
933
  }
934
934
  } else {
935
935
  return null;
@@ -938,20 +938,20 @@ function RequiredIfValidator(condition) {
938
938
  }
939
939
 
940
940
  // src/forms/validators/required-true.validator.ts
941
- function RequiredTrueValidator() {
941
+ function RequiredTrueValidator(key = "required") {
942
942
  return function(value) {
943
- return value === true ? null : { required: true };
943
+ return value === true ? null : { [key]: true };
944
944
  };
945
945
  }
946
946
 
947
947
  // src/forms/validators/required.validator.ts
948
948
  import { isArray as isArray2 } from "@websolutespa/bom-core";
949
- function RequiredValidator() {
949
+ function RequiredValidator(key = "required") {
950
950
  return function(value) {
951
951
  if (isArray2(value) || typeof value === "string") {
952
- return value.length === 0 ? { required: true } : null;
952
+ return value.length === 0 ? { [key]: true } : null;
953
953
  } else {
954
- return value == null ? { required: true } : null;
954
+ return value == null ? { [key]: true } : null;
955
955
  }
956
956
  };
957
957
  }
@@ -1067,6 +1067,73 @@ function mapSchema_(schema) {
1067
1067
  return mapControl_(schema);
1068
1068
  }
1069
1069
  }
1070
+
1071
+ // src/hooks/useFormSchema/useFormSchema.ts
1072
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useState as useState4 } from "react";
1073
+ function useFormSchema(schema, deps = []) {
1074
+ const form = useMemo3(() => {
1075
+ if (Array.isArray(schema)) {
1076
+ return mapArray_2(schema);
1077
+ } else {
1078
+ return mapGroup_2(schema);
1079
+ }
1080
+ }, deps);
1081
+ const setValue = useCallback4((value) => {
1082
+ form.patch(value);
1083
+ }, deps);
1084
+ const setTouched = useCallback4(() => {
1085
+ form.touched = true;
1086
+ }, deps);
1087
+ const reset = useCallback4(() => {
1088
+ form.reset();
1089
+ }, deps);
1090
+ const [state, setState] = useState4({
1091
+ value: form.value,
1092
+ flags: form.state,
1093
+ errors: mapErrors_(form.errors)
1094
+ });
1095
+ useEffect4(() => {
1096
+ const onChange = (value) => {
1097
+ const newState = { value, flags: form.state, errors: mapErrors_(form.errors) };
1098
+ };
1099
+ form.on("change", onChange);
1100
+ form.revalidate_();
1101
+ return () => form.off("change", onChange);
1102
+ }, deps);
1103
+ return {
1104
+ form,
1105
+ state,
1106
+ setTouched,
1107
+ setValue,
1108
+ reset
1109
+ };
1110
+ }
1111
+ function mapGroup_2(children, schema) {
1112
+ const controls = {};
1113
+ Object.keys(children).forEach((key) => {
1114
+ controls[key] = mapSchema_2(__spreadValues({ name: key }, children[key]));
1115
+ });
1116
+ const group = new FormGroup(controls, schema == null ? void 0 : schema.validators, schema);
1117
+ return group;
1118
+ }
1119
+ function mapArray_2(children, schema) {
1120
+ const controls = children.map((x) => mapSchema_2(x));
1121
+ const array = new FormArray(controls, schema == null ? void 0 : schema.validators, schema);
1122
+ return array;
1123
+ }
1124
+ function mapControl_2(schema) {
1125
+ return new FormControl(schema.value, schema.validators, schema);
1126
+ }
1127
+ function mapSchema_2(schema) {
1128
+ switch (schema.schema) {
1129
+ case "group":
1130
+ return mapGroup_2(schema.children || {}, schema);
1131
+ case "array":
1132
+ return mapArray_2(schema.children || [], schema);
1133
+ default:
1134
+ return mapControl_2(schema);
1135
+ }
1136
+ }
1070
1137
  export {
1071
1138
  EmailValidator,
1072
1139
  FormAbstract,
@@ -1092,6 +1159,7 @@ export {
1092
1159
  useControl,
1093
1160
  useForm,
1094
1161
  useFormBuilder,
1162
+ useFormSchema,
1095
1163
  validValue,
1096
1164
  valueToString
1097
1165
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-mixer-forms",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Mixer Forms module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -3,7 +3,10 @@ import { FormAbstract } from './form-abstract';
3
3
  import { FormControl } from './form-control';
4
4
  import { FormActivator, FormCollection, FormControls, FormFlags, FormOptions, FormValidator, FormValue } from './types';
5
5
 
6
- export class FormAbstractCollection<T extends FormControls> extends FormAbstract {
6
+ export class FormAbstractCollection<
7
+ U extends Record<string, FormValue> | FormValue[],
8
+ T extends FormControls> extends FormAbstract<U
9
+ > {
7
10
 
8
11
  controls_: T;
9
12
 
@@ -16,7 +19,7 @@ export class FormAbstractCollection<T extends FormControls> extends FormAbstract
16
19
  });
17
20
  }
18
21
 
19
- initControl_(controlOrValue: FormAbstract | any, key: any): FormAbstract {
22
+ initControl_(controlOrValue: FormAbstract | FormValue, key: any): FormAbstract {
20
23
  const control = controlOrValue instanceof FormAbstract ? controlOrValue : new FormControl(controlOrValue);
21
24
  control.name = key;
22
25
  control.parent = this;
@@ -283,18 +286,18 @@ export class FormAbstractCollection<T extends FormControls> extends FormAbstract
283
286
  });
284
287
  }
285
288
 
286
- get value(): { [key: string]: FormValue } {
287
- return this.reduce_((result: { [key: string]: FormValue }, control: FormAbstract, key: string) => {
289
+ get value(): U {
290
+ return this.reduce_((result: U, control: FormAbstract, key: string) => {
288
291
  if (control.enabled) {
289
- result[key] = control.value;
292
+ result[key as keyof U] = control.value;
290
293
  }
291
294
  return result;
292
295
  }, {});
293
296
  }
294
297
 
295
- set value(value: { [key: string]: FormValue }) {
298
+ set value(value: U) {
296
299
  this.forEach_((control: FormAbstract, key: string) => {
297
- control.value = value[key];
300
+ control.value = value[key as keyof U];
298
301
  });
299
302
  }
300
303
 
@@ -3,11 +3,11 @@ import { EventEmitter } from './event-emitter';
3
3
  import { ControlType, FormActivator, FormCollection, FormFlags, FormOptions, FormValidator, FormValue, ValidationError } from './types';
4
4
  import { validValue } from './utils';
5
5
 
6
- export class FormAbstract extends EventEmitter {
6
+ export class FormAbstract<T = any> extends EventEmitter {
7
7
 
8
8
  // callbacks_: Function[] = [];
9
9
  errors_: ValidationError;
10
- value_: FormValue;
10
+ value_: T | null;
11
11
  validators_: FormValidator[];
12
12
  state_: FormFlags;
13
13
  name?: string | number;
@@ -1,10 +1,10 @@
1
1
  import { FormAbstract } from './form-abstract';
2
2
  import { FormAbstractCollection } from './form-abstract-collection';
3
- import { FormOptions, FormValidator, FormValue } from './types';
3
+ import { FormArrayControls, FormOptions, FormValidator, FormValue } from './types';
4
4
 
5
- export class FormArray extends FormAbstractCollection<FormAbstract[]> {
5
+ export class FormArray<T extends FormValue[] = FormValue[]> extends FormAbstractCollection<T, FormArrayControls> {
6
6
 
7
- constructor(controls: FormAbstract[] = [], validators?: FormValidator | FormValidator[], initialOptions?: FormOptions) {
7
+ constructor(controls: FormArrayControls = [], validators?: FormValidator | FormValidator[], initialOptions?: FormOptions) {
8
8
  super(controls, validators);
9
9
  this.setInitialOptions(initialOptions);
10
10
  // this.revalidate_();
@@ -18,8 +18,8 @@ export class FormArray extends FormAbstractCollection<FormAbstract[]> {
18
18
  return this.controls;
19
19
  }
20
20
 
21
- get value(): FormValue[] {
22
- return this.reduce_((result: FormValue[], control: FormAbstract, key: number) => {
21
+ get value(): T {
22
+ return this.reduce_((result: T, control: FormAbstract, key: number) => {
23
23
  result[key] = control.value;
24
24
  return result;
25
25
  }, []); // init as array
@@ -73,6 +73,6 @@ export class FormArray extends FormAbstractCollection<FormAbstract[]> {
73
73
  }
74
74
  }
75
75
 
76
- export function formArray(controls: FormAbstract[] = [], validators?: FormValidator | FormValidator[]) {
76
+ export function formArray(controls: FormArrayControls = [], validators?: FormValidator | FormValidator[]) {
77
77
  return new FormArray(controls, validators);
78
78
  }
@@ -1,15 +1,14 @@
1
- import { FormAbstract } from './form-abstract';
2
1
  import { FormAbstractCollection } from './form-abstract-collection';
3
- import { FormOptions, FormValidator, FormValue } from './types';
2
+ import { FormGroupControls, FormOptions, FormValidator, FormValue } from './types';
4
3
 
5
- export class FormGroup extends FormAbstractCollection<{ [key: string]: FormAbstract }> {
6
- constructor(controls: { [key: string]: FormAbstract | any } = {}, validators?: FormValidator | FormValidator[], initialOptions?: FormOptions) {
4
+ export class FormGroup<T extends Record<string, FormValue> = FormValue> extends FormAbstractCollection<T, FormGroupControls> {
5
+ constructor(controls: FormGroupControls = {}, validators?: FormValidator | FormValidator[], initialOptions?: FormOptions) {
7
6
  super(controls, validators);
8
7
  this.setInitialOptions(initialOptions);
9
8
  // this.revalidate_();
10
9
  }
11
10
  }
12
11
 
13
- export function formGroup(controls: { [key: string]: FormAbstract | FormValue } = {}, validators?: FormValidator | FormValidator[]) {
12
+ export function formGroup(controls: FormGroupControls = {}, validators?: FormValidator | FormValidator[]) {
14
13
  return new FormGroup(controls, validators);
15
14
  }
@@ -34,8 +34,10 @@ export type ValidationError = {
34
34
  [key: string]: any
35
35
  };
36
36
 
37
- export type FormControls = { [key: string]: FormAbstract | FormValue } | FormAbstract[];
38
- export type FormCollection = FormAbstractCollection<FormControls>;
37
+ export type FormGroupControls = Record<string, FormAbstract | FormValue>;
38
+ export type FormArrayControls = FormAbstract[];
39
+ export type FormControls = FormGroupControls | FormArrayControls;
40
+ export type FormCollection = FormAbstractCollection<Record<string, any> | any[], FormControls>;
39
41
  export type FormActivator = boolean | ((value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormCollection) => boolean | Promise<boolean>);
40
42
  export type FormValidator = (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormCollection) => null | ValidationError;
41
43
  export type FormAsyncValidator = (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormCollection) => Promise<null | ValidationError>;
@@ -57,7 +59,7 @@ export type FormFlags = {
57
59
  unsubmitted: boolean;
58
60
  };
59
61
  // export type FormFlags = { [key: string]: boolean };
60
- export type FormErrors = { [key: string]: any };
62
+ export type FormErrors = Record<string, any>;
61
63
  export type FormValidationError = { key: string, value: FormValue };
62
64
  export type FormValidationErrors = FormValidationError[];
63
65
 
@@ -86,7 +88,7 @@ export type IFormBuilderControlSchema = {
86
88
  customData?: Record<string, any>;
87
89
  };
88
90
 
89
- export type IFormBuilderGroupSchema = { [key: string]: IFormBuilderControlSchema };
91
+ export type IFormBuilderGroupSchema = Record<string, IFormBuilderControlSchema>;
90
92
  export type IFormBuilderArraySchema = IFormBuilderControlSchema[];
91
- export type IFormBuilderGroupValues = { [key: string]: FormGroup | FormArray | FormControl };
93
+ export type IFormBuilderGroupValues = Record<string, FormGroup | FormArray | FormControl>;
92
94
  export type IFormBuilderSchema = IFormBuilderGroupSchema | IFormBuilderArraySchema;
@@ -3,11 +3,11 @@ import { FormValidator } from '../types';
3
3
  /**
4
4
  * an email pattern validator
5
5
  */
6
- export function EmailValidator(): FormValidator {
6
+ export function EmailValidator(key: string = 'email'): FormValidator {
7
7
  const regex = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
8
8
  return function (value) {
9
9
  if (typeof value === 'string') {
10
- return regex.test(value) ? null : { email: true };
10
+ return regex.test(value) ? null : { [key]: true };
11
11
  } else {
12
12
  return null;
13
13
  }
@@ -5,10 +5,13 @@ import { validValue } from '../utils';
5
5
  /**
6
6
  * an equality match validation on another field
7
7
  */
8
- export function MatchValidator(getOtherValue: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => any): FormValidator {
8
+ export function MatchValidator(
9
+ getOtherValue: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => any,
10
+ key: string = 'match'
11
+ ): FormValidator {
9
12
  return function (value, rootValue, control, root) {
10
13
  let otherValue = getOtherValue(value, rootValue, control, root);
11
14
  otherValue = validValue(otherValue);
12
- return value !== otherValue ? { match: { value: value, match: otherValue } } : null;
15
+ return value !== otherValue ? { [key]: { value: value, match: otherValue } } : null;
13
16
  };
14
17
  }
@@ -3,14 +3,14 @@ import { FormValidator } from '../types';
3
3
  /**
4
4
  * a max string length validator
5
5
  */
6
- export function MaxLengthValidator(maxlength: number): FormValidator {
6
+ export function MaxLengthValidator(maxlength: number, key: string = 'maxlength'): FormValidator {
7
7
  return function (value) {
8
8
  if (maxlength == null) {
9
9
  return null;
10
10
  }
11
11
  if (typeof value === 'string') {
12
12
  const length = value ? value.length : 0;
13
- return length > maxlength ? { minlength: { requiredLength: maxlength, actualLength: length } } : null;
13
+ return length > maxlength ? { [key]: { requiredLength: maxlength, actualLength: length } } : null;
14
14
  } else {
15
15
  return null;
16
16
  }
@@ -3,12 +3,12 @@ import { FormValidator } from '../types';
3
3
  /**
4
4
  * a max number value validator
5
5
  */
6
- export function MaxValidator(max: number): FormValidator {
6
+ export function MaxValidator(max: number, key: string = 'max'): FormValidator {
7
7
  return function (value) {
8
8
  value = typeof value === 'string' ? parseFloat(value) : typeof value === 'number' ? value : null;
9
9
  if (value == null || max == null) { // loose
10
10
  return null;
11
11
  }
12
- return !isNaN(value) && value > max ? { max: { max: max, actual: value } } : null;
12
+ return !isNaN(value) && value > max ? { [key]: { max: max, actual: value } } : null;
13
13
  };
14
14
  }
@@ -3,14 +3,14 @@ import { FormValidator } from '../types';
3
3
  /**
4
4
  * a min string length validator
5
5
  */
6
- export function MinLengthValidator(minlength: number): FormValidator {
6
+ export function MinLengthValidator(minlength: number, key: string = 'minlength'): FormValidator {
7
7
  return function (value) {
8
8
  if (minlength == null) {
9
9
  return null;
10
10
  }
11
11
  if (typeof value === 'string') {
12
12
  const length = value ? value.length : 0;
13
- return length < minlength ? { minlength: { requiredLength: minlength, actualLength: length } } : null;
13
+ return length < minlength ? { [key]: { requiredLength: minlength, actualLength: length } } : null;
14
14
  } else {
15
15
  return null;
16
16
  }
@@ -3,12 +3,12 @@ import { FormValidator } from '../types';
3
3
  /**
4
4
  * a min number value validator
5
5
  */
6
- export function MinValidator(min: number): FormValidator {
6
+ export function MinValidator(min: number, key: string = 'min'): FormValidator {
7
7
  return function (value) {
8
8
  value = typeof value === 'string' ? parseFloat(value) : typeof value === 'number' ? value : null;
9
9
  if (value == null || min == null) { // loose
10
10
  return null;
11
11
  }
12
- return !isNaN(value) && value < min ? { min: { min: min, actual: value } } : null;
12
+ return !isNaN(value) && value < min ? { [key]: { min: min, actual: value } } : null;
13
13
  };
14
14
  }
@@ -3,14 +3,14 @@ import { FormValidator } from '../types';
3
3
  /**
4
4
  * a regex pattern validator
5
5
  */
6
- export function PatternValidator(pattern: string | RegExp): FormValidator {
6
+ export function PatternValidator(pattern: string | RegExp, key: string = 'pattern'): FormValidator {
7
7
  return function (value) {
8
8
  if (pattern == null) { // loose
9
9
  return null;
10
10
  }
11
11
  if (typeof value === 'string') {
12
12
  const regex = patternToRegEx(pattern);
13
- return regex.test(value) ? null : { pattern: { requiredPattern: regex.toString(), actualValue: value } };
13
+ return regex.test(value) ? null : { [key]: { requiredPattern: regex.toString(), actualValue: value } };
14
14
  } else {
15
15
  return null;
16
16
  }
@@ -5,14 +5,14 @@ import { FormValidator, FormValue } from '../types';
5
5
  /**
6
6
  * a required dependant on another field
7
7
  */
8
- export function RequiredIfValidator(condition: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => boolean): FormValidator {
8
+ export function RequiredIfValidator(condition: (value: FormValue, rootValue: FormValue, control?: FormAbstract, root?: FormAbstract) => boolean, key: string = 'required'): FormValidator {
9
9
  return function (value, rootValue, control, root) {
10
10
  // console.log('RequiredIfValidator', value, Boolean(condition(value, rootValue, control, root)));
11
11
  if (Boolean(condition(value, rootValue, control, root)) === true) {
12
12
  if (isArray(value) || typeof value === 'string') {
13
- return (value.length === 0) ? { required: true } : null;
13
+ return (value.length === 0) ? { [key]: true } : null;
14
14
  } else {
15
- return value == null ? { required: true } : null;
15
+ return value == null ? { [key]: true } : null;
16
16
  }
17
17
  } else {
18
18
  return null;
@@ -3,9 +3,9 @@ import { FormValidator } from '../types';
3
3
  /**
4
4
  * a required and true validator
5
5
  */
6
- export function RequiredTrueValidator(): FormValidator {
6
+ export function RequiredTrueValidator(key: string = 'required'): FormValidator {
7
7
  return function (value) {
8
8
  // console.log('RequiredTrueValidator', value, value === true ? null : { required: true });
9
- return value === true ? null : { required: true };
9
+ return value === true ? null : { [key]: true };
10
10
  };
11
11
  }
@@ -4,13 +4,13 @@ import { FormValidator } from '../types';
4
4
  /**
5
5
  * a required validator
6
6
  */
7
- export function RequiredValidator(): FormValidator {
7
+ export function RequiredValidator(key: string = 'required'): FormValidator {
8
8
  return function (value) {
9
9
  // console.log('RequiredValidator', value, (value == null || value.length === 0) ? { required: true } : null);
10
10
  if (isArray(value) || typeof value === 'string') {
11
- return (value.length === 0) ? { required: true } : null;
11
+ return (value.length === 0) ? { [key]: true } : null;
12
12
  } else {
13
- return value == null ? { required: true } : null;
13
+ return value == null ? { [key]: true } : null;
14
14
  }
15
15
  };
16
16
  // return (value == null || value.length === 0) ? 'required' : null;
@@ -1,4 +1,5 @@
1
1
  export { useControl } from './useControl/useControl';
2
2
  export { useForm } from './useForm/useForm';
3
3
  export { useFormBuilder } from './useFormBuilder/useFormBuilder';
4
+ export { useFormSchema } from './useFormSchema/useFormSchema';
4
5
 
@@ -1,8 +1,14 @@
1
1
  import { DependencyList, useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import { FormArray, FormControl, FormGroup, FormState, mapErrors_ } from '../../forms';
3
- import { IFormBuilderArraySchema, IFormBuilderControlSchema, IFormBuilderGroupSchema, IFormBuilderGroupValues, IFormBuilderSchema } from '../../forms/types';
3
+ import { FormValue, IFormBuilderArraySchema, IFormBuilderControlSchema, IFormBuilderGroupSchema, IFormBuilderGroupValues, IFormBuilderSchema } from '../../forms/types';
4
4
 
5
- export function useFormBuilder<T, U extends (FormGroup | FormArray)>(
5
+ /**
6
+ * @deprecated Since version 1.9.0. Will be deleted in version 1.10.0. Use useFormSchema instead.
7
+ */
8
+ export function useFormBuilder<
9
+ T extends (Record<string, FormValue> | FormValue[]) = Record<string, FormValue>,
10
+ U extends (T extends FormValue[] ? FormArray<T> : FormGroup<T>) = T extends FormValue[] ? FormArray<T> : FormGroup<T>
11
+ >(
6
12
  schema: IFormBuilderSchema,
7
13
  deps: DependencyList = []
8
14
  ): [FormState<T>, (value: Partial<T>) => void, () => void, () => void, U] {
@@ -0,0 +1,105 @@
1
+ import { DependencyList, useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { FormArray, FormControl, FormGroup, FormState, mapErrors_ } from '../../forms';
3
+ import { FormValue, IFormBuilderArraySchema, IFormBuilderControlSchema, IFormBuilderGroupSchema, IFormBuilderGroupValues, IFormBuilderSchema } from '../../forms/types';
4
+
5
+ export function useFormSchema<
6
+ T extends (Record<string, FormValue> | FormValue[]) = Record<string, FormValue>,
7
+ U extends (T extends FormValue[] ? FormArray<T> : FormGroup<T>) = T extends FormValue[] ? FormArray<T> : FormGroup<T>
8
+ >(
9
+ schema: IFormBuilderSchema,
10
+ deps: DependencyList = []
11
+ ): {
12
+ state: FormState<T>,
13
+ setValue: (value: Partial<T>) => void,
14
+ setTouched: () => void,
15
+ reset: () => void,
16
+ form: U,
17
+ } {
18
+ const form: U = useMemo<U>(() => {
19
+ // console.log(' useFormSchema.rebuild', schema);
20
+ if (Array.isArray(schema)) {
21
+ return mapArray_(schema) as U;
22
+ } else {
23
+ return mapGroup_(schema) as U;
24
+ }
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ }, deps);
27
+
28
+ const setValue = useCallback((value: Partial<T>) => {
29
+ form.patch(value);
30
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31
+ }, deps);
32
+
33
+ const setTouched = useCallback(() => {
34
+ form.touched = true;
35
+ // eslint-disable-next-line react-hooks/exhaustive-deps
36
+ }, deps);
37
+
38
+ const reset = useCallback(() => {
39
+ form.reset();
40
+ // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ }, deps);
42
+
43
+ const [state, setState] = useState<FormState<T>>({
44
+ value: form.value as T,
45
+ flags: form.state,
46
+ errors: mapErrors_(form.errors),
47
+ });
48
+
49
+ useEffect(() => {
50
+ const onChange = (value: T) => {
51
+ // console.log(' useFormSchema.onChange', value);
52
+ const newState = { value, flags: form.state, errors: mapErrors_(form.errors) };
53
+ // setState(newState);
54
+ };
55
+ form.on('change', onChange);
56
+ // form.validateAndChange_();
57
+ form.revalidate_();
58
+ // const value = form.value;
59
+ // form.reset();
60
+ // form.patch(value);
61
+ // console.log('subscribe');
62
+ return () => form.off('change', onChange);
63
+ // eslint-disable-next-line react-hooks/exhaustive-deps
64
+ }, deps);
65
+
66
+ // console.log(' useFormSchema', form.value);
67
+
68
+ return {
69
+ form,
70
+ state,
71
+ setTouched,
72
+ setValue,
73
+ reset,
74
+ };
75
+ }
76
+
77
+ function mapGroup_(children: IFormBuilderGroupSchema, schema?: IFormBuilderControlSchema): FormGroup {
78
+ const controls: IFormBuilderGroupValues = {};
79
+ Object.keys(children).forEach(key => {
80
+ controls[key] = mapSchema_({ name: key, ...children[key] });
81
+ });
82
+ const group = new FormGroup(controls, schema?.validators, schema);
83
+ return group;
84
+ }
85
+
86
+ function mapArray_(children: IFormBuilderControlSchema[], schema?: IFormBuilderControlSchema): FormArray {
87
+ const controls = children.map(x => mapSchema_(x));
88
+ const array = new FormArray(controls, schema?.validators, schema);
89
+ return array;
90
+ }
91
+
92
+ function mapControl_(schema: IFormBuilderControlSchema): FormControl {
93
+ return new FormControl(schema.value, schema.validators, schema);
94
+ }
95
+
96
+ function mapSchema_(schema: IFormBuilderControlSchema): FormGroup | FormArray | FormControl {
97
+ switch (schema.schema) {
98
+ case 'group':
99
+ return mapGroup_(schema.children as IFormBuilderGroupSchema || {}, schema);
100
+ case 'array':
101
+ return mapArray_(schema.children as IFormBuilderArraySchema || [], schema);
102
+ default:
103
+ return mapControl_(schema);
104
+ }
105
+ }