@bolttech/form-engine-core 0.0.2-beta.7 → 0.0.3-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Subject, Subscription, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map, distinctUntilKeyChanged } from 'rxjs';
1
+ import { Subject, Subscription, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map, distinctUntilKeyChanged, distinctUntilChanged } from 'rxjs';
2
2
  import creditCardType from 'credit-card-type';
3
3
  import { isNumber as isNumber$1, isFunction, cloneDeep, isEqual, get, isNil, set } from 'lodash';
4
4
  import { getCurrencySymbol } from '@gaignoux/currency';
@@ -51,6 +51,8 @@ const TEMPLATE_REGEX_DELIMITATOR = /\$\{((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\}
51
51
  const TEMPLATE_REGEX_OPERATOR_SPLITTER = /\s*(\|\||&&|!)\s*/g;
52
52
  const TEMPLATE_REGEX_OPERATOR_MATCHER = /^\|\||&&|!$/;
53
53
  const TEMPLATE_AVALIABLE_SCOPES = ['fields', 'iVars'];
54
+ const ALLOWED_RESET_PROPS_MUTATIONS = ['api', 'apiSchema', 'props', 'validations', 'visibilityConditions', 'resetValues'];
55
+ const DEFAULT_LOG_VERBOSE = false;
54
56
 
55
57
  /**
56
58
  * Makes an HTTP request using XMLHttpRequest.
@@ -66,9 +68,16 @@ const TEMPLATE_AVALIABLE_SCOPES = ['fields', 'iVars'];
66
68
  * const response = await makeRequest('GET', 'https://api.example.com/data');
67
69
  * ```
68
70
  */
69
- function makeRequest(method, url, headers, body) {
71
+ function makeRequest(method, url, headers, body, queryParams) {
70
72
  return new Promise(function (resolve, reject) {
71
73
  const xhr = new XMLHttpRequest();
74
+ if (queryParams) {
75
+ const newUrl = new URL(url);
76
+ Object.keys(queryParams).forEach(param => {
77
+ newUrl.searchParams.append(param, queryParams[param]);
78
+ });
79
+ url = newUrl.toString();
80
+ }
72
81
  xhr.open(method, url, true);
73
82
  if (headers) {
74
83
  Object.keys(headers).forEach(header => {
@@ -2467,7 +2476,7 @@ class FormField {
2467
2476
  * @param {string[]} options.children - An array of children fields names.
2468
2477
  * @param {Function} options.validateVisibility - A function to validate the visibility of the field.
2469
2478
  * @param {Function} options.resetValue - A function to reset the field value.
2470
- * @param {unknown} [options.initialValue] - The initial value of the form field.
2479
+ * @param {Function} options.resetProperty - A function to reset a field property.
2471
2480
  * @param {Subject<{ key: string }>} options.templateSubject$ - A subject for template updates.
2472
2481
  * @param {Subject<TFieldEvent>} options.fieldEventSubject$, - Subject for basic event mapped field emissions, except onData to form instance
2473
2482
  * @param {Subject<{ key: string; event: TEvents }>} options.dataSubject$, - Subject to emit onData events to form instance
@@ -2482,7 +2491,7 @@ class FormField {
2482
2491
  children,
2483
2492
  validateVisibility,
2484
2493
  resetValue,
2485
- initialValue,
2494
+ resetProperty,
2486
2495
  templateSubject$,
2487
2496
  fieldEventSubject$,
2488
2497
  dataSubject$,
@@ -2496,7 +2505,8 @@ class FormField {
2496
2505
  this.originalSchema = cloneDeep(schemaComponent);
2497
2506
  this.config = {
2498
2507
  defaultAPIdebounceTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) : DEFAULT_API_DEBOUNCE_TIME,
2499
- defaultStateRefreshTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME
2508
+ defaultStateRefreshTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME,
2509
+ defaultLogVerbose: (config === null || config === void 0 ? void 0 : config.defaultLogVerbose) ? config.defaultLogVerbose : DEFAULT_LOG_VERBOSE
2500
2510
  };
2501
2511
  this.name = schemaComponent.name;
2502
2512
  this.nameToSubmit = schemaComponent.nameToSubmit;
@@ -2507,6 +2517,7 @@ class FormField {
2507
2517
  this.errorMessages = cloneDeep((_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages);
2508
2518
  this.visibilityConditions = cloneDeep(schemaComponent.visibilityConditions);
2509
2519
  this.resetValues = cloneDeep(schemaComponent.resetValues);
2520
+ this.resetPropertyValues = cloneDeep(schemaComponent.resetPropertyValues);
2510
2521
  this.apiSchema = cloneDeep(schemaComponent.api);
2511
2522
  this.formatters = cloneDeep(schemaComponent.formatters);
2512
2523
  this.masks = cloneDeep(schemaComponent.masks);
@@ -2515,6 +2526,7 @@ class FormField {
2515
2526
  this.mapper = mapper;
2516
2527
  this.validateVisibility = validateVisibility;
2517
2528
  this.resetValue = resetValue;
2529
+ this.resetProperty = resetProperty;
2518
2530
  this.getFormValues = getFormValues;
2519
2531
  this.templateSubject$ = templateSubject$;
2520
2532
  this.fieldEventSubject$ = fieldEventSubject$;
@@ -2524,7 +2536,6 @@ class FormField {
2524
2536
  this._metadata = '';
2525
2537
  this.errorsString = '';
2526
2538
  this.errorsList = [];
2527
- this.initialValue = initialValue || (this.valuePropName && !String(this.props[this.valuePropName]).includes('${') ? this.props[this.valuePropName] : null);
2528
2539
  this._visibility = true;
2529
2540
  this._api = {
2530
2541
  default: {
@@ -2544,7 +2555,6 @@ class FormField {
2544
2555
  this._mounted = true;
2545
2556
  this._valid = false;
2546
2557
  this.initializeObservers();
2547
- this.value = this.initialValue || '';
2548
2558
  }
2549
2559
  /**
2550
2560
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
@@ -2688,23 +2698,6 @@ class FormField {
2688
2698
  set visibility(visible) {
2689
2699
  if (typeof visible === 'undefined' || visible === this.visibility) return;
2690
2700
  this._visibility = visible;
2691
- /**
2692
- * I was sure I would not require to gambiarra, but..
2693
- * in order to ignore an hidden value on a form submit
2694
- * or revalidate it when it comes back to visibility
2695
- * I needed to...
2696
- * I don't recommend setting private properties like this
2697
- * this will force the field to be valid when it's hidden
2698
- * and trigger the validation when it's visible
2699
- */
2700
- if (!this.visibility) {
2701
- this.value = '';
2702
- this.valid = true;
2703
- } else {
2704
- this.setFieldValidity({
2705
- event: 'ON_FIELD_MOUNT'
2706
- });
2707
- }
2708
2701
  this.visibilitySubject$.next(this.visibility);
2709
2702
  this.templateSubject$.next({
2710
2703
  scope: 'fields',
@@ -2814,6 +2807,7 @@ class FormField {
2814
2807
  * @returns {void}
2815
2808
  */
2816
2809
  emitValue(prop) {
2810
+ if (!this.visibility) return;
2817
2811
  this.value = prop.value;
2818
2812
  this.emitEvents({
2819
2813
  event: prop.event
@@ -2843,6 +2837,10 @@ class FormField {
2843
2837
  event,
2844
2838
  key: this.name
2845
2839
  });
2840
+ this.resetProperty({
2841
+ event,
2842
+ key: this.name
2843
+ });
2846
2844
  this.apiEventQueueSubject$.next({
2847
2845
  event
2848
2846
  });
@@ -2956,7 +2954,7 @@ class FormField {
2956
2954
  const {
2957
2955
  status,
2958
2956
  response
2959
- } = yield makeRequest(config.method, config.url, config.headers, config.body);
2957
+ } = yield makeRequest(config.method, config.url, config.headers, config.body, config.queryParams);
2960
2958
  const callbackTransform = (_k = config.transform) === null || _k === void 0 ? void 0 : _k.callback;
2961
2959
  const apiResponseData = callbackTransform ? callbackTransform({
2962
2960
  payload: JSON.parse(String(response)),
@@ -3042,6 +3040,9 @@ class FormField {
3042
3040
  this.errorSubject$.unsubscribe();
3043
3041
  this.apiSubject$.unsubscribe();
3044
3042
  this.apiEventQueueSubject$.unsubscribe();
3043
+ this.formValidNotification$.next({
3044
+ fieldTrigger: this.name
3045
+ });
3045
3046
  }
3046
3047
  /**
3047
3048
  * Subscribes to changes in the field state and executes the provided callback function.
@@ -3083,20 +3084,22 @@ class FormCore {
3083
3084
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
3084
3085
  */
3085
3086
  constructor(entry) {
3086
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
3087
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
3087
3088
  this.templateSubscription$ = new Subscription();
3088
3089
  this.mappers = new Map();
3089
3090
  this.queuedFieldVisibilityEvents = new Map();
3090
3091
  this.queuedFieldResetValuesEvents = new Map();
3092
+ this.queuedFieldResetPropertyEvents = new Map();
3093
+ this.queuedInitialValues = new Map();
3091
3094
  this.schema = entry.schema;
3092
3095
  this.fields = new Map();
3093
- this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
3094
- this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
3095
- this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
3096
- this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
3096
+ this.action = entry.action || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.action);
3097
+ this.method = entry.method || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.method);
3098
+ this._iVars = entry.iVars || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.iVars) || {};
3097
3099
  this.config = {
3098
- defaultAPIdebounceTimeMS: Number((_e = entry.config) === null || _e === void 0 ? void 0 : _e.defaultAPIdebounceTimeMS) ? Number((_f = entry.config) === null || _f === void 0 ? void 0 : _f.defaultAPIdebounceTimeMS) : DEFAULT_API_DEBOUNCE_TIME,
3099
- defaultStateRefreshTimeMS: Number((_g = entry.config) === null || _g === void 0 ? void 0 : _g.defaultStateRefreshTimeMS) ? Number((_h = entry.config) === null || _h === void 0 ? void 0 : _h.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME
3100
+ defaultAPIdebounceTimeMS: Number((_d = entry.config) === null || _d === void 0 ? void 0 : _d.defaultAPIdebounceTimeMS) ? Number((_e = entry.config) === null || _e === void 0 ? void 0 : _e.defaultAPIdebounceTimeMS) : DEFAULT_API_DEBOUNCE_TIME,
3101
+ defaultStateRefreshTimeMS: Number((_f = entry.config) === null || _f === void 0 ? void 0 : _f.defaultStateRefreshTimeMS) ? Number((_g = entry.config) === null || _g === void 0 ? void 0 : _g.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME,
3102
+ defaultLogVerbose: ((_h = entry.config) === null || _h === void 0 ? void 0 : _h.defaultLogVerbose) ? entry.config.defaultLogVerbose : DEFAULT_LOG_VERBOSE
3100
3103
  };
3101
3104
  (_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
3102
3105
  this.mappers.set(mapper.componentName, mapper);
@@ -3117,20 +3120,39 @@ class FormCore {
3117
3120
  event: 'ON_IVARS'
3118
3121
  });
3119
3122
  /*
3120
- mount events needs to occur on form level, only when all the fields are instantiated
3121
- is it possible to apply all the side effects that occur globally, same effect occur
3122
- onto refreshFields
3123
+ only emits event ON_FIELD_MOUNT if does not have initialValue, if has initialValue, initialValues class property setter will
3124
+ emit the value along with ON_FIELD_MOUNT event
3123
3125
  */
3126
+ const initialValues = entry.initialValues || ((_k = entry.schema) === null || _k === void 0 ? void 0 : _k.initialValues);
3124
3127
  this.fields.forEach((field, key) => {
3125
- field.emitEvents({
3126
- event: 'ON_FIELD_MOUNT'
3127
- });
3128
+ if (!initialValues || initialValues && !Object.keys(initialValues).includes(key)) {
3129
+ !(field === null || field === void 0 ? void 0 : field.value) && (field === null || field === void 0 ? void 0 : field.emitValue({
3130
+ value: '',
3131
+ event: 'ON_FIELD_MOUNT'
3132
+ }));
3133
+ }
3128
3134
  this.refreshTemplates({
3129
3135
  scope: 'fields',
3130
3136
  key,
3131
3137
  event: 'ON_FIELDS'
3132
3138
  });
3133
3139
  });
3140
+ this.setInitialValues(initialValues);
3141
+ }
3142
+ setInitialValues(payload) {
3143
+ if (payload) {
3144
+ Object.keys(payload).forEach(key => {
3145
+ const field = this.fields.get(key);
3146
+ if (!field || !(field === null || field === void 0 ? void 0 : field.visibility)) {
3147
+ this.queuedInitialValues.set(key, payload === null || payload === void 0 ? void 0 : payload[key]);
3148
+ } else {
3149
+ field.emitValue({
3150
+ value: payload === null || payload === void 0 ? void 0 : payload[key],
3151
+ event: 'ON_FIELD_MOUNT'
3152
+ });
3153
+ }
3154
+ });
3155
+ }
3134
3156
  }
3135
3157
  /**
3136
3158
  * Retrieves the internal variables (iVars) of the form.
@@ -3176,6 +3198,7 @@ class FormCore {
3176
3198
  validations,
3177
3199
  visibilityConditions,
3178
3200
  resetValues,
3201
+ resetPropertyValues,
3179
3202
  api
3180
3203
  }
3181
3204
  }, key) => {
@@ -3186,6 +3209,7 @@ class FormCore {
3186
3209
  validations,
3187
3210
  visibilityConditions,
3188
3211
  resetValues,
3212
+ resetPropertyValues,
3189
3213
  apiSchema: api
3190
3214
  };
3191
3215
  traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
@@ -3215,7 +3239,7 @@ class FormCore {
3215
3239
  case 'fields':
3216
3240
  {
3217
3241
  const field = this.fields.get(key);
3218
- if (!field) return console.warn(`failed to get value from ${key}`);
3242
+ if (!field) return this.config.defaultLogVerbose && console.warn(`failed to get value from ${key}`);
3219
3243
  if (property === 'props' && path[0] === field.valuePropName) {
3220
3244
  return field.value;
3221
3245
  }
@@ -3243,7 +3267,7 @@ class FormCore {
3243
3267
  }) {
3244
3268
  const field = this.fields.get(key);
3245
3269
  if (!field) {
3246
- console.warn(`failed to update field ${key}`);
3270
+ this.config.defaultLogVerbose && console.warn(`failed to update field ${key}`);
3247
3271
  return;
3248
3272
  }
3249
3273
  if (path.length > 0) {
@@ -3267,7 +3291,7 @@ class FormCore {
3267
3291
  } else if (typeof fieldProp === 'object' && !isNil(fieldProp)) {
3268
3292
  propState = Object.assign({}, fieldProp);
3269
3293
  } else {
3270
- console.warn(`invalid template property, skipping evaluation of ${field.name} with ${fieldProp}`);
3294
+ this.config.defaultLogVerbose && console.warn(`invalid template property, skipping evaluation of ${field.name} with ${fieldProp}`);
3271
3295
  return;
3272
3296
  }
3273
3297
  set(propState, path, value);
@@ -3358,8 +3382,8 @@ class FormCore {
3358
3382
  try {
3359
3383
  return parse ? new Function(`return ${value}`)() : value;
3360
3384
  } catch (_a) {
3361
- console.warn(`unhandled parsing on ${expression} returning`);
3362
- console.warn(value);
3385
+ this.config.defaultLogVerbose && console.warn(`unhandled parsing on ${expression} returning`);
3386
+ this.config.defaultLogVerbose && console.warn(value);
3363
3387
  return value;
3364
3388
  }
3365
3389
  });
@@ -3441,33 +3465,67 @@ class FormCore {
3441
3465
  }, this.queuedFieldVisibilityEvents.get(field)));
3442
3466
  this.queuedFieldVisibilityEvents.delete(field);
3443
3467
  }
3468
+ if (this.queuedInitialValues.has(field)) {
3469
+ this.setInitialValues({
3470
+ [field]: this.queuedInitialValues.get(field)
3471
+ });
3472
+ this.queuedInitialValues.delete(field);
3473
+ }
3444
3474
  if (this.queuedFieldResetValuesEvents.has(field)) {
3445
3475
  this.setResetFieldValue(Object.assign({
3446
3476
  key: field
3447
3477
  }, this.queuedFieldResetValuesEvents.get(field)));
3448
3478
  this.queuedFieldResetValuesEvents.delete(field);
3449
3479
  }
3480
+ if (this.queuedFieldResetPropertyEvents.has(field)) {
3481
+ this.setResetPathValue(Object.assign({
3482
+ key: field
3483
+ }, this.queuedFieldResetPropertyEvents.get(field)));
3484
+ this.queuedFieldResetPropertyEvents.delete(field);
3485
+ }
3450
3486
  }
3451
3487
  /**
3452
3488
  * @internal
3453
3489
  * Update field visibility accordingly.
3454
3490
  *
3455
- * @param {string} field - Field name to be updated.
3456
- * @param {boolean} hasError - Condition to be used as visibility.
3457
- * @param {boolean|undefined} showOnlyIfTrue - Flag to be considered when update field visibility. If it's true, then considered error, if it's false, always considered the opposite.
3491
+ * @param {object} options - options to set field visibility
3492
+ * @param {string} options.field - Field name to be updated.
3493
+ * @param {boolean} options.hasError - Condition to be used as visibility.
3494
+ * @param {boolean|undefined} options.showOnlyIfTrue - Flag to be considered when update field visibility. If it's true, then considered error, if it's false, always considered the opposite.
3458
3495
  */
3459
3496
  setFieldVisibility({
3460
3497
  field,
3461
3498
  hasError,
3462
3499
  showOnlyIfTrue
3463
3500
  }) {
3464
- if (!this.fields.has(field)) {
3501
+ const fieldInstance = this.fields.get(field);
3502
+ if (!fieldInstance) {
3465
3503
  this.queuedFieldVisibilityEvents.set(field, {
3466
3504
  hasError,
3467
3505
  showOnlyIfTrue
3468
3506
  });
3469
3507
  } else {
3470
- this.fields.get(field).visibility = showOnlyIfTrue ? hasError : !hasError;
3508
+ const visibility = showOnlyIfTrue ? hasError : !hasError;
3509
+ fieldInstance.visibility = visibility;
3510
+ /**
3511
+ * I was sure I would not require to gambiarra, but..
3512
+ * in order to ignore an hidden value on a form submit
3513
+ * or revalidate it when it comes back to visibility
3514
+ * I needed to...
3515
+ * I don't recommend setting private properties like this
3516
+ * this will force the field to be valid when it's hidden
3517
+ * and trigger the validation when it's visible
3518
+ */
3519
+ if (fieldInstance.visibility) {
3520
+ fieldInstance === null || fieldInstance === void 0 ? void 0 : fieldInstance.emitValue({
3521
+ value: this.queuedInitialValues.get(field) || '',
3522
+ event: 'ON_FIELD_MOUNT'
3523
+ });
3524
+ this.queuedInitialValues.delete(field);
3525
+ } else {
3526
+ fieldInstance.value = '';
3527
+ fieldInstance.valid = true;
3528
+ }
3471
3529
  }
3472
3530
  }
3473
3531
  /**
@@ -3512,8 +3570,9 @@ class FormCore {
3512
3570
  * @internal
3513
3571
  * Update field value and emit change and cleared event.
3514
3572
  *
3515
- * @param {string} key - Field name to be updated.
3516
- * @param {unknown} value - Value to be inserted into field.
3573
+ * @param {options} options to reset the field value
3574
+ * @param {string} options.key - Field name to be updated.
3575
+ * @param {unknown} options.value - Value to be inserted into field.
3517
3576
  */
3518
3577
  setResetFieldValue({
3519
3578
  key,
@@ -3527,9 +3586,6 @@ class FormCore {
3527
3586
  const field = this.fields.get(key);
3528
3587
  field.emitValue({
3529
3588
  value: value,
3530
- event: 'ON_FIELD_CHANGE'
3531
- });
3532
- field.emitEvents({
3533
3589
  event: 'ON_FIELD_CLEARED'
3534
3590
  });
3535
3591
  }
@@ -3577,6 +3633,76 @@ class FormCore {
3577
3633
  });
3578
3634
  });
3579
3635
  }
3636
+ /**
3637
+ * @internal
3638
+ * Update field property and emit template change.
3639
+ *
3640
+ * @param {object} options - Options for resetting field property
3641
+ * @param {string} options.key - Field name to be updated.
3642
+ * @param {string} options.property - field property to change.
3643
+ * @param {string} options.path - field property path to change.
3644
+ * @param {unknown} options.value - Value to be inserted into field.
3645
+ */
3646
+ setResetPathValue({
3647
+ key,
3648
+ property,
3649
+ path,
3650
+ value
3651
+ }) {
3652
+ if (!this.fields.has(key)) {
3653
+ this.queuedFieldResetPropertyEvents.set(key, {
3654
+ property,
3655
+ path,
3656
+ value
3657
+ });
3658
+ } else {
3659
+ this.setValue({
3660
+ key,
3661
+ property,
3662
+ path: path.split('.'),
3663
+ value: value,
3664
+ event: 'ON_RESET'
3665
+ });
3666
+ }
3667
+ }
3668
+ /**
3669
+ * Resets field properties based on reset conditions defined in the schema.
3670
+ *
3671
+ * @param {object} options - Options for resetting field property.
3672
+ * @param {TEvents} options.event - The event triggering the reset.
3673
+ * @param {string} options.key - The key of the field.
3674
+ */
3675
+ resetProperty({
3676
+ event,
3677
+ key
3678
+ }) {
3679
+ const field = this.fields.get(key);
3680
+ const structResetPath = field === null || field === void 0 ? void 0 : field.resetPropertyValues;
3681
+ if (!structResetPath || !(structResetPath === null || structResetPath === void 0 ? void 0 : structResetPath.some(config => config.events.includes(event)))) return;
3682
+ structResetPath.forEach(structElement => {
3683
+ if (!structElement.events.includes(event)) return;
3684
+ if (!ALLOWED_RESET_PROPS_MUTATIONS.includes(structElement.property)) return;
3685
+ if (!structElement.validations) {
3686
+ return this.setResetPathValue({
3687
+ key: structElement.field,
3688
+ path: structElement.path,
3689
+ property: structElement.property,
3690
+ value: structElement.resettledValue
3691
+ });
3692
+ }
3693
+ Object.keys(structElement.validations).forEach(validationKey => {
3694
+ const error = handleValidation(field.value, structElement.validations, validations, validationKey);
3695
+ if (!error) {
3696
+ this.setResetPathValue({
3697
+ key: structElement.field,
3698
+ path: structElement.path,
3699
+ property: structElement.property,
3700
+ value: structElement.resettledValue
3701
+ });
3702
+ }
3703
+ });
3704
+ });
3705
+ }
3580
3706
  /**
3581
3707
  * Adds a field onto the form instance regardless there is a schema or not
3582
3708
  *
@@ -3587,7 +3713,7 @@ class FormCore {
3587
3713
  fieldSchema,
3588
3714
  mapperElement
3589
3715
  }) {
3590
- var _a, _b, _c;
3716
+ var _a;
3591
3717
  if (this.fields.has(fieldSchema.name)) {
3592
3718
  throw new Error(`field name ${fieldSchema.name} already defined`);
3593
3719
  }
@@ -3599,8 +3725,8 @@ class FormCore {
3599
3725
  children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
3600
3726
  validateVisibility: this.validateVisibility.bind(this),
3601
3727
  resetValue: this.resetValue.bind(this),
3728
+ resetProperty: this.resetProperty.bind(this),
3602
3729
  getFormValues: this.getFormValues.bind(this),
3603
- initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
3604
3730
  templateSubject$: this.templateSubject$,
3605
3731
  fieldEventSubject$: this.fieldEventSubject$,
3606
3732
  dataSubject$: this.dataSubject$,
@@ -3610,13 +3736,16 @@ class FormCore {
3610
3736
  this.subscribeTemplates();
3611
3737
  this.refreshTemplates({
3612
3738
  scope: 'fields',
3613
- event: 'ON_FIELDS',
3614
- key: fieldSchema.name
3739
+ event: 'ON_FIELDS'
3615
3740
  });
3741
+ if (!this.queuedInitialValues.has(fieldSchema.name)) {
3742
+ const field = this.fields.get(fieldSchema.name);
3743
+ !(field === null || field === void 0 ? void 0 : field.value) && (field === null || field === void 0 ? void 0 : field.emitValue({
3744
+ value: '',
3745
+ event: 'ON_FIELD_MOUNT'
3746
+ }));
3747
+ }
3616
3748
  this.checkFieldEventQueues(fieldSchema.name);
3617
- (_c = this.fields.get(fieldSchema.name)) === null || _c === void 0 ? void 0 : _c.emitEvents({
3618
- event: 'ON_FIELD_MOUNT'
3619
- });
3620
3749
  }
3621
3750
  removeField({
3622
3751
  key
@@ -3640,7 +3769,7 @@ class FormCore {
3640
3769
  serializeStructure(struct, path) {
3641
3770
  if (!struct) return;
3642
3771
  struct.forEach(structElement => {
3643
- var _a, _b, _c;
3772
+ var _a, _b;
3644
3773
  const currField = this.fields.get(structElement.name);
3645
3774
  if (!currField) {
3646
3775
  let mapper;
@@ -3657,7 +3786,7 @@ class FormCore {
3657
3786
  children: structElement.children ? structElement.children.map(el => el.name) : [],
3658
3787
  validateVisibility: this.validateVisibility.bind(this),
3659
3788
  resetValue: this.resetValue.bind(this),
3660
- initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[structElement.name],
3789
+ resetProperty: this.resetProperty.bind(this),
3661
3790
  templateSubject$: this.templateSubject$,
3662
3791
  fieldEventSubject$: this.fieldEventSubject$,
3663
3792
  dataSubject$: this.dataSubject$,
@@ -3666,7 +3795,7 @@ class FormCore {
3666
3795
  getFormValues: this.getFormValues.bind(this)
3667
3796
  }));
3668
3797
  } else {
3669
- currField.children = ((_c = structElement === null || structElement === void 0 ? void 0 : structElement.children) === null || _c === void 0 ? void 0 : _c.map(el => el.name)) || (currField === null || currField === void 0 ? void 0 : currField.children) || [];
3798
+ currField.children = ((_b = structElement === null || structElement === void 0 ? void 0 : structElement.children) === null || _b === void 0 ? void 0 : _b.map(el => el.name)) || (currField === null || currField === void 0 ? void 0 : currField.children) || [];
3670
3799
  currField.path = path;
3671
3800
  currField.originalSchema = structElement;
3672
3801
  currField.templateSubject$ = this.templateSubject$;
@@ -3834,12 +3963,12 @@ class FormCore {
3834
3963
  this.submitSubject$.next(values);
3835
3964
  }
3836
3965
  destroy() {
3966
+ this.fields.forEach(field => field.destroyField());
3837
3967
  this.submitSubject$.unsubscribe();
3838
3968
  this.templateSubscription$.unsubscribe();
3839
3969
  this.fieldEventSubject$.unsubscribe();
3840
3970
  this.dataSubject$.unsubscribe();
3841
3971
  this.formValidNotification$.unsubscribe();
3842
- this.fields.forEach(field => field.destroyField());
3843
3972
  }
3844
3973
  }
3845
3974
  /**
@@ -3878,14 +4007,15 @@ class FormGroup {
3878
4007
  * Creates an instance of FormGroup.
3879
4008
  */
3880
4009
  constructor(entry) {
3881
- var _a, _b, _c, _d;
4010
+ var _a, _b, _c, _d, _e;
3882
4011
  this.destroy = () => {
3883
4012
  this.forms.forEach(form => form.destroy());
3884
4013
  };
3885
4014
  this.forms = new Map();
3886
4015
  this.config = {
3887
4016
  defaultAPIdebounceTimeMS: Number((_a = entry === null || entry === void 0 ? void 0 : entry.config) === null || _a === void 0 ? void 0 : _a.defaultAPIdebounceTimeMS) ? Number((_b = entry === null || entry === void 0 ? void 0 : entry.config) === null || _b === void 0 ? void 0 : _b.defaultAPIdebounceTimeMS) : DEFAULT_API_DEBOUNCE_TIME,
3888
- defaultStateRefreshTimeMS: Number((_c = entry === null || entry === void 0 ? void 0 : entry.config) === null || _c === void 0 ? void 0 : _c.defaultStateRefreshTimeMS) ? Number((_d = entry === null || entry === void 0 ? void 0 : entry.config) === null || _d === void 0 ? void 0 : _d.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME
4017
+ defaultStateRefreshTimeMS: Number((_c = entry === null || entry === void 0 ? void 0 : entry.config) === null || _c === void 0 ? void 0 : _c.defaultStateRefreshTimeMS) ? Number((_d = entry === null || entry === void 0 ? void 0 : entry.config) === null || _d === void 0 ? void 0 : _d.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME,
4018
+ defaultLogVerbose: ((_e = entry === null || entry === void 0 ? void 0 : entry.config) === null || _e === void 0 ? void 0 : _e.defaultLogVerbose) ? entry.config.defaultLogVerbose : DEFAULT_LOG_VERBOSE
3889
4019
  };
3890
4020
  }
3891
4021
  /**
@@ -4031,7 +4161,7 @@ class FormGroup {
4031
4161
  if (sub) {
4032
4162
  acc[formId] = sub;
4033
4163
  } else {
4034
- console.warn(`failed to register form id ${formId}`);
4164
+ this.config.defaultLogVerbose && console.warn(`failed to register form id ${formId}`);
4035
4165
  }
4036
4166
  return acc;
4037
4167
  }, {});
@@ -4049,11 +4179,11 @@ class FormGroup {
4049
4179
  }), map(() => {
4050
4180
  var _a;
4051
4181
  return ((_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.isValid) === false ? false : true;
4052
- }));
4182
+ }), distinctUntilChanged());
4053
4183
  if (sub) {
4054
4184
  acc[formId] = sub;
4055
4185
  } else {
4056
- console.warn(`failed to register validation subscription form id ${formId}`);
4186
+ this.config.defaultLogVerbose && console.warn(`failed to register validation subscription form id ${formId}`);
4057
4187
  }
4058
4188
  return acc;
4059
4189
  }, {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "0.0.2-beta.7",
3
+ "version": "0.0.3-beta.2",
4
4
  "module": "./index.esm.js",
5
5
  "type": "module",
6
6
  "main": "./index.esm.js",
@@ -5,4 +5,6 @@ declare const TEMPLATE_REGEX_DELIMITATOR: RegExp;
5
5
  declare const TEMPLATE_REGEX_OPERATOR_SPLITTER: RegExp;
6
6
  declare const TEMPLATE_REGEX_OPERATOR_MATCHER: RegExp;
7
7
  declare const TEMPLATE_AVALIABLE_SCOPES: readonly ["fields", "iVars"];
8
- export { DEFAULT_API_DEBOUNCE_TIME, DEFAULT_STATE_REFRESH_TIME, TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR, TEMPLATE_REGEX_DELIMITATOR, TEMPLATE_REGEX_OPERATOR_SPLITTER, TEMPLATE_REGEX_OPERATOR_MATCHER, TEMPLATE_AVALIABLE_SCOPES, };
8
+ declare const ALLOWED_RESET_PROPS_MUTATIONS: ("api" | "apiSchema" | "props" | "validations" | "visibilityConditions" | "resetValues")[];
9
+ declare const DEFAULT_LOG_VERBOSE = false;
10
+ export { DEFAULT_API_DEBOUNCE_TIME, DEFAULT_STATE_REFRESH_TIME, DEFAULT_LOG_VERBOSE, TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR, TEMPLATE_REGEX_DELIMITATOR, TEMPLATE_REGEX_OPERATOR_SPLITTER, TEMPLATE_REGEX_OPERATOR_MATCHER, TEMPLATE_AVALIABLE_SCOPES, ALLOWED_RESET_PROPS_MUTATIONS, };
@@ -17,7 +17,7 @@ import { OutgoingHttpHeaders } from 'http2';
17
17
  * const response = await makeRequest('GET', 'https://api.example.com/data');
18
18
  * ```
19
19
  */
20
- declare function makeRequest(method: string, url: string, headers?: OutgoingHttpHeaders, body?: Record<string, unknown>): Promise<TApiResponsePayload>;
20
+ declare function makeRequest(method: string, url: string, headers?: OutgoingHttpHeaders, body?: Record<string, unknown>, queryParams?: Record<string, string>): Promise<TApiResponsePayload>;
21
21
  /**
22
22
  * Extracts keys enclosed in `${}` from a given expression.
23
23
  *
@@ -1,5 +1,5 @@
1
1
  import { TMapper } from '../types/mapper';
2
- import { TApiEvent, TFormatters, TMasks, TProps, TResetValueMethods, TSchemaFormConfig, TValidations, TVisibility } from '../types/schema';
2
+ import { TApiEvent, TFormatters, TMasks, TProps, TResetPathMethods, TResetValueMethods, TSchemaFormConfig, TValidations, TVisibility } from '../types/schema';
3
3
  /**
4
4
  * @interface IComponentSchema
5
5
  * Represents the schema for a component within a form.
@@ -58,6 +58,7 @@ interface IComponentSchema {
58
58
  api?: TApiEvent;
59
59
  visibilityConditions?: TVisibility[];
60
60
  resetValues?: TResetValueMethods[];
61
+ resetPropertyValues?: TResetPathMethods[];
61
62
  formatters?: TFormatters;
62
63
  masks?: TMasks;
63
64
  children?: IComponentSchema[];
@@ -1,5 +1,5 @@
1
1
  import { Observable, Subject, Subscription } from 'rxjs';
2
- import { TApiConfig, TApiEvent, TApiResponse, TErrorMessages, TFormatters, TMasks, TResetValueMethods, TSchemaFormConfig, TValidations, TVisibility } from '../types/schema';
2
+ import { TApiConfig, TApiEvent, TApiResponse, TErrorMessages, TFormatters, TMasks, TResetPathMethods, TResetValueMethods, TSchemaFormConfig, TValidations, TVisibility } from '../types/schema';
3
3
  import { IComponentSchema, IComponentSchemaAsFormField } from '../interfaces/schema';
4
4
  import { IState } from '../interfaces/state';
5
5
  import { TEvents, TFieldEvent, TFormValidationPayload, TValueChangeEvent } from '../types/event';
@@ -20,12 +20,12 @@ declare class FormField {
20
20
  validations?: TValidations;
21
21
  visibilityConditions?: TVisibility[];
22
22
  resetValues?: TResetValueMethods[];
23
+ resetPropertyValues?: TResetPathMethods[];
23
24
  errorMessages?: TErrorMessages;
24
25
  apiSchema?: TApiEvent;
25
26
  formatters?: TFormatters;
26
27
  masks?: TMasks;
27
28
  valuePropName?: string;
28
- initialValue?: unknown;
29
29
  config: Required<TSchemaFormConfig>;
30
30
  mapper: TMapper<unknown>;
31
31
  errorsString: string;
@@ -65,6 +65,10 @@ declare class FormField {
65
65
  event: TEvents;
66
66
  key: string;
67
67
  }) => void;
68
+ resetProperty: (payload: {
69
+ event: TEvents;
70
+ key: string;
71
+ }) => void;
68
72
  getFormValues: () => TFormValues<unknown>;
69
73
  valueChangeEvent: TValueChangeEvent;
70
74
  /**
@@ -77,7 +81,7 @@ declare class FormField {
77
81
  * @param {string[]} options.children - An array of children fields names.
78
82
  * @param {Function} options.validateVisibility - A function to validate the visibility of the field.
79
83
  * @param {Function} options.resetValue - A function to reset the field value.
80
- * @param {unknown} [options.initialValue] - The initial value of the form field.
84
+ * @param {Function} options.resetProperty - A function to reset a field property.
81
85
  * @param {Subject<{ key: string }>} options.templateSubject$ - A subject for template updates.
82
86
  * @param {Subject<TFieldEvent>} options.fieldEventSubject$, - Subject for basic event mapped field emissions, except onData to form instance
83
87
  * @param {Subject<{ key: string; event: TEvents }>} options.dataSubject$, - Subject to emit onData events to form instance
@@ -85,7 +89,7 @@ declare class FormField {
85
89
  * @param {TMapper<unknown>} options.mapper, - component generic mapper containing render parameters for adapters
86
90
  * @param {() => TFormValues<unknown>} options.getFormValues, - form instance function that builds onData parameter payload from fields
87
91
  */
88
- constructor({ schemaComponent, config, path, children, validateVisibility, resetValue, initialValue, templateSubject$, fieldEventSubject$, dataSubject$, formValidNotification$, mapper, getFormValues, }: {
92
+ constructor({ schemaComponent, config, path, children, validateVisibility, resetValue, resetProperty, templateSubject$, fieldEventSubject$, dataSubject$, formValidNotification$, mapper, getFormValues, }: {
89
93
  schemaComponent: IComponentSchema;
90
94
  config?: TSchemaFormConfig;
91
95
  path?: string;
@@ -98,7 +102,10 @@ declare class FormField {
98
102
  event: TEvents;
99
103
  key: string;
100
104
  }) => void;
101
- initialValue?: unknown;
105
+ resetProperty: (payload: {
106
+ event: TEvents;
107
+ key: string;
108
+ }) => void;
102
109
  templateSubject$: Subject<TTemplateEvent>;
103
110
  fieldEventSubject$: Subject<TFieldEvent>;
104
111
  dataSubject$: Subject<{
@@ -13,7 +13,6 @@ import { TEMPLATE_AVALIABLE_SCOPES } from '../constants/constants';
13
13
  declare class FormCore {
14
14
  schema?: IFormSchema;
15
15
  fields: Map<string, IFormField>;
16
- initialValues?: Record<string, unknown>;
17
16
  private _iVars;
18
17
  templateSubject$: Subject<TTemplateEvent>;
19
18
  templateSubscription$: Subscription;
@@ -37,6 +36,12 @@ declare class FormCore {
37
36
  queuedFieldResetValuesEvents: Map<string, {
38
37
  value: unknown;
39
38
  }>;
39
+ queuedFieldResetPropertyEvents: Map<string, {
40
+ property: string;
41
+ path: string;
42
+ value: unknown;
43
+ }>;
44
+ queuedInitialValues: Map<string, unknown>;
40
45
  /**
41
46
  * Creates an instance of FormCore.
42
47
  *
@@ -49,6 +54,7 @@ declare class FormCore {
49
54
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
50
55
  */
51
56
  constructor(entry: TFormEntry & Omit<IFormSchema, 'components'>);
57
+ setInitialValues(payload: Record<string, unknown> | undefined): void;
52
58
  /**
53
59
  * Retrieves the internal variables (iVars) of the form.
54
60
  *
@@ -155,9 +161,10 @@ declare class FormCore {
155
161
  * @internal
156
162
  * Update field visibility accordingly.
157
163
  *
158
- * @param {string} field - Field name to be updated.
159
- * @param {boolean} hasError - Condition to be used as visibility.
160
- * @param {boolean|undefined} showOnlyIfTrue - Flag to be considered when update field visibility. If it's true, then considered error, if it's false, always considered the opposite.
164
+ * @param {object} options - options to set field visibility
165
+ * @param {string} options.field - Field name to be updated.
166
+ * @param {boolean} options.hasError - Condition to be used as visibility.
167
+ * @param {boolean|undefined} options.showOnlyIfTrue - Flag to be considered when update field visibility. If it's true, then considered error, if it's false, always considered the opposite.
161
168
  */
162
169
  private setFieldVisibility;
163
170
  /**
@@ -175,8 +182,9 @@ declare class FormCore {
175
182
  * @internal
176
183
  * Update field value and emit change and cleared event.
177
184
  *
178
- * @param {string} key - Field name to be updated.
179
- * @param {unknown} value - Value to be inserted into field.
185
+ * @param {options} options to reset the field value
186
+ * @param {string} options.key - Field name to be updated.
187
+ * @param {unknown} options.value - Value to be inserted into field.
180
188
  */
181
189
  private setResetFieldValue;
182
190
  /**
@@ -190,6 +198,28 @@ declare class FormCore {
190
198
  event: TEvents;
191
199
  key: string;
192
200
  }): void;
201
+ /**
202
+ * @internal
203
+ * Update field property and emit template change.
204
+ *
205
+ * @param {object} options - Options for resetting field property
206
+ * @param {string} options.key - Field name to be updated.
207
+ * @param {string} options.property - field property to change.
208
+ * @param {string} options.path - field property path to change.
209
+ * @param {unknown} options.value - Value to be inserted into field.
210
+ */
211
+ private setResetPathValue;
212
+ /**
213
+ * Resets field properties based on reset conditions defined in the schema.
214
+ *
215
+ * @param {object} options - Options for resetting field property.
216
+ * @param {TEvents} options.event - The event triggering the reset.
217
+ * @param {string} options.key - The key of the field.
218
+ */
219
+ resetProperty({ event, key }: {
220
+ event: TEvents;
221
+ key: string;
222
+ }): void;
193
223
  /**
194
224
  * Adds a field onto the form instance regardless there is a schema or not
195
225
  *
@@ -19,7 +19,7 @@ type TEvents = 'ON_FIELD_MOUNT' | 'ON_FIELD_CHANGE' | 'ON_FIELD_BLUR' | 'ON_FIEL
19
19
  * const event: TMutationEvents = 'ON_VALUE';
20
20
  * ```
21
21
  */
22
- type TMutationEvents = 'ON_VALUE' | 'ON_PROPS' | 'ON_VISIBILITY' | 'ON_API' | 'ON_IVARS' | 'ON_FIELDS';
22
+ type TMutationEvents = 'ON_VALUE' | 'ON_PROPS' | 'ON_VISIBILITY' | 'ON_API' | 'ON_IVARS' | 'ON_FIELDS' | 'ON_RESET';
23
23
  declare enum TMutationEnum {
24
24
  ON_VALUE = "value",
25
25
  ON_PROPS = "props",
@@ -4,6 +4,7 @@ import { TCurrencyCode, TCurrencyLocalCode } from '@gaignoux/currency';
4
4
  import { OutgoingHttpHeaders } from 'http2';
5
5
  import { TEvents } from './event';
6
6
  import { TFormValues } from './form';
7
+ import { ALLOWED_RESET_PROPS_MUTATIONS } from '../constants/constants';
7
8
  /**
8
9
  * @type TLengthValidation
9
10
  * Represents the validation rules based on the length of the input.
@@ -665,6 +666,14 @@ type TResetValueMethods = Omit<TVisibility, 'showOnlyIfTrue' | 'validations'> &
665
666
  resettledValue: unknown[] | unknown;
666
667
  validations?: TSchemaValidation;
667
668
  };
669
+ type TResetPathMethods = {
670
+ property: typeof ALLOWED_RESET_PROPS_MUTATIONS[number];
671
+ path: string;
672
+ field: string;
673
+ resettledValue: unknown;
674
+ validations: TSchemaValidation;
675
+ events: Partial<TEvents>[];
676
+ };
668
677
  /**
669
678
  * @type TApiConfig
670
679
  * Represents the configuration for an API request.
@@ -672,6 +681,7 @@ type TResetValueMethods = Omit<TVisibility, 'showOnlyIfTrue' | 'validations'> &
672
681
  * @property {'GET' | 'POST'} method - The HTTP method for the request.
673
682
  * @property {string} url - The URL for the request.
674
683
  * @property {OutgoingHttpHeaders} [headers] - The headers for the request.
684
+ * @property {Record<string, string>} queryParams - query parameters for request.
675
685
  * @property {string} [resultPath] - The path to extract the result from the response.
676
686
  * @property {unknown} [fallbackValue] - The fallback value if the request fails.
677
687
  * @property {TSchemaValidation} preConditions - validation conditions to execute the API call
@@ -697,6 +707,7 @@ type TApiConfig = {
697
707
  url: string;
698
708
  body?: Record<string, unknown>;
699
709
  headers?: OutgoingHttpHeaders;
710
+ queryParams?: Record<string, string>;
700
711
  resultPath?: string;
701
712
  fallbackValue?: unknown;
702
713
  preConditions?: TSchemaValidation;
@@ -815,5 +826,6 @@ type TApiResponse = {
815
826
  type TSchemaFormConfig = {
816
827
  defaultAPIdebounceTimeMS?: number;
817
828
  defaultStateRefreshTimeMS?: number;
829
+ defaultLogVerbose?: boolean;
818
830
  };
819
- export { TApiConfig, TErrorMessages, TValidations, TMasks, TProps, TResetValueMethods, TFormatters, TValidationMethods, TGenericValidationRule, TSchemaValidation, TEvent, TVisibility, TApiEvent, TApiResponsePayload, TApiResponse, TSchemaFormConfig, TLengthValidation, TCreditCardMatch, TDocumentValidation, TCallbackValidation, TBetweenValidation, TMaskGeneric, TSplitterFormatterValue, TCurrencyMask, TDateOperatorsValidation, TConditionsValidationSet, TConditionsValidation, TMultipleValidation, TAvailableValidations, TDateValidation, TBetweenDatesValidation, TDateFormatsValidation, TDateInterval, };
831
+ export { TApiConfig, TErrorMessages, TValidations, TMasks, TProps, TResetValueMethods, TResetPathMethods, TFormatters, TValidationMethods, TGenericValidationRule, TSchemaValidation, TEvent, TVisibility, TApiEvent, TApiResponsePayload, TApiResponse, TSchemaFormConfig, TLengthValidation, TCreditCardMatch, TDocumentValidation, TCallbackValidation, TBetweenValidation, TMaskGeneric, TSplitterFormatterValue, TCurrencyMask, TDateOperatorsValidation, TConditionsValidationSet, TConditionsValidation, TMultipleValidation, TAvailableValidations, TDateValidation, TBetweenDatesValidation, TDateFormatsValidation, TDateInterval, };