@bolttech/form-engine-core 0.0.2-beta.6 → 0.0.3-beta.1

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,35 +2505,37 @@ 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;
2503
2513
  this.component = schemaComponent.component;
2504
2514
  this.path = path;
2505
2515
  this.children = children;
2506
- this.validations = schemaComponent.validations;
2507
- this.errorMessages = (_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages;
2508
- this.visibilityConditions = schemaComponent.visibilityConditions;
2509
- this.resetValues = schemaComponent.resetValues;
2510
- this.apiSchema = schemaComponent.api;
2511
- this.formatters = schemaComponent.formatters;
2512
- this.masks = schemaComponent.masks;
2516
+ this.validations = cloneDeep(schemaComponent.validations);
2517
+ this.errorMessages = cloneDeep((_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages);
2518
+ this.visibilityConditions = cloneDeep(schemaComponent.visibilityConditions);
2519
+ this.resetValues = cloneDeep(schemaComponent.resetValues);
2520
+ this.resetPropertyValues = cloneDeep(schemaComponent.resetPropertyValues);
2521
+ this.apiSchema = cloneDeep(schemaComponent.api);
2522
+ this.formatters = cloneDeep(schemaComponent.formatters);
2523
+ this.masks = cloneDeep(schemaComponent.masks);
2513
2524
  if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
2514
2525
  if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) this.valuePropName = mapper.events.setValue;
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$;
2521
2533
  this.dataSubject$ = dataSubject$;
2522
2534
  this.formValidNotification$ = formValidNotification$;
2523
- this._props = schemaComponent.props || {};
2535
+ this._props = cloneDeep(schemaComponent.props || {});
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)),
@@ -3083,20 +3081,22 @@ class FormCore {
3083
3081
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
3084
3082
  */
3085
3083
  constructor(entry) {
3086
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
3084
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
3087
3085
  this.templateSubscription$ = new Subscription();
3088
3086
  this.mappers = new Map();
3089
3087
  this.queuedFieldVisibilityEvents = new Map();
3090
3088
  this.queuedFieldResetValuesEvents = new Map();
3089
+ this.queuedFieldResetPropertyEvents = new Map();
3090
+ this.queuedInitialValues = new Map();
3091
3091
  this.schema = entry.schema;
3092
3092
  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) || {};
3093
+ this.action = entry.action || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.action);
3094
+ this.method = entry.method || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.method);
3095
+ this._iVars = entry.iVars || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.iVars) || {};
3097
3096
  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
3097
+ 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,
3098
+ 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,
3099
+ defaultLogVerbose: ((_h = entry.config) === null || _h === void 0 ? void 0 : _h.defaultLogVerbose) ? entry.config.defaultLogVerbose : DEFAULT_LOG_VERBOSE
3100
3100
  };
3101
3101
  (_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
3102
3102
  this.mappers.set(mapper.componentName, mapper);
@@ -3117,20 +3117,39 @@ class FormCore {
3117
3117
  event: 'ON_IVARS'
3118
3118
  });
3119
3119
  /*
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
3120
+ only emits event ON_FIELD_MOUNT if does not have initialValue, if has initialValue, initialValues class property setter will
3121
+ emit the value along with ON_FIELD_MOUNT event
3123
3122
  */
3123
+ const initialValues = entry.initialValues || ((_k = entry.schema) === null || _k === void 0 ? void 0 : _k.initialValues);
3124
3124
  this.fields.forEach((field, key) => {
3125
- field.emitEvents({
3126
- event: 'ON_FIELD_MOUNT'
3127
- });
3125
+ if (!initialValues || initialValues && !Object.keys(initialValues).includes(key)) {
3126
+ !(field === null || field === void 0 ? void 0 : field.value) && (field === null || field === void 0 ? void 0 : field.emitValue({
3127
+ value: '',
3128
+ event: 'ON_FIELD_MOUNT'
3129
+ }));
3130
+ }
3128
3131
  this.refreshTemplates({
3129
3132
  scope: 'fields',
3130
3133
  key,
3131
3134
  event: 'ON_FIELDS'
3132
3135
  });
3133
3136
  });
3137
+ this.setInitialValues(initialValues);
3138
+ }
3139
+ setInitialValues(payload) {
3140
+ if (payload) {
3141
+ Object.keys(payload).forEach(key => {
3142
+ const field = this.fields.get(key);
3143
+ if (!field || !(field === null || field === void 0 ? void 0 : field.visibility)) {
3144
+ this.queuedInitialValues.set(key, payload === null || payload === void 0 ? void 0 : payload[key]);
3145
+ } else {
3146
+ field.emitValue({
3147
+ value: payload === null || payload === void 0 ? void 0 : payload[key],
3148
+ event: 'ON_FIELD_MOUNT'
3149
+ });
3150
+ }
3151
+ });
3152
+ }
3134
3153
  }
3135
3154
  /**
3136
3155
  * Retrieves the internal variables (iVars) of the form.
@@ -3176,6 +3195,7 @@ class FormCore {
3176
3195
  validations,
3177
3196
  visibilityConditions,
3178
3197
  resetValues,
3198
+ resetPropertyValues,
3179
3199
  api
3180
3200
  }
3181
3201
  }, key) => {
@@ -3186,6 +3206,7 @@ class FormCore {
3186
3206
  validations,
3187
3207
  visibilityConditions,
3188
3208
  resetValues,
3209
+ resetPropertyValues,
3189
3210
  apiSchema: api
3190
3211
  };
3191
3212
  traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
@@ -3215,7 +3236,7 @@ class FormCore {
3215
3236
  case 'fields':
3216
3237
  {
3217
3238
  const field = this.fields.get(key);
3218
- if (!field) return console.warn(`failed to get value from ${key}`);
3239
+ if (!field) return console.info(`failed to get value from ${key}`);
3219
3240
  if (property === 'props' && path[0] === field.valuePropName) {
3220
3241
  return field.value;
3221
3242
  }
@@ -3243,7 +3264,7 @@ class FormCore {
3243
3264
  }) {
3244
3265
  const field = this.fields.get(key);
3245
3266
  if (!field) {
3246
- console.warn(`failed to update field ${key}`);
3267
+ console.info(`failed to update field ${key}`);
3247
3268
  return;
3248
3269
  }
3249
3270
  if (path.length > 0) {
@@ -3267,7 +3288,7 @@ class FormCore {
3267
3288
  } else if (typeof fieldProp === 'object' && !isNil(fieldProp)) {
3268
3289
  propState = Object.assign({}, fieldProp);
3269
3290
  } else {
3270
- console.warn(`invalid template property, skipping evaluation of ${field.name} with ${fieldProp}`);
3291
+ console.info(`invalid template property, skipping evaluation of ${field.name} with ${fieldProp}`);
3271
3292
  return;
3272
3293
  }
3273
3294
  set(propState, path, value);
@@ -3358,8 +3379,8 @@ class FormCore {
3358
3379
  try {
3359
3380
  return parse ? new Function(`return ${value}`)() : value;
3360
3381
  } catch (_a) {
3361
- console.warn(`unhandled parsing on ${expression} returning`);
3362
- console.warn(value);
3382
+ console.info(`unhandled parsing on ${expression} returning`);
3383
+ console.info(value);
3363
3384
  return value;
3364
3385
  }
3365
3386
  });
@@ -3441,33 +3462,67 @@ class FormCore {
3441
3462
  }, this.queuedFieldVisibilityEvents.get(field)));
3442
3463
  this.queuedFieldVisibilityEvents.delete(field);
3443
3464
  }
3465
+ if (this.queuedInitialValues.has(field)) {
3466
+ this.setInitialValues({
3467
+ [field]: this.queuedInitialValues.get(field)
3468
+ });
3469
+ this.queuedInitialValues.delete(field);
3470
+ }
3444
3471
  if (this.queuedFieldResetValuesEvents.has(field)) {
3445
3472
  this.setResetFieldValue(Object.assign({
3446
3473
  key: field
3447
3474
  }, this.queuedFieldResetValuesEvents.get(field)));
3448
3475
  this.queuedFieldResetValuesEvents.delete(field);
3449
3476
  }
3477
+ if (this.queuedFieldResetPropertyEvents.has(field)) {
3478
+ this.setResetPathValue(Object.assign({
3479
+ key: field
3480
+ }, this.queuedFieldResetPropertyEvents.get(field)));
3481
+ this.queuedFieldResetPropertyEvents.delete(field);
3482
+ }
3450
3483
  }
3451
3484
  /**
3452
3485
  * @internal
3453
3486
  * Update field visibility accordingly.
3454
3487
  *
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.
3488
+ * @param {object} options - options to set field visibility
3489
+ * @param {string} options.field - Field name to be updated.
3490
+ * @param {boolean} options.hasError - Condition to be used as visibility.
3491
+ * @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
3492
  */
3459
3493
  setFieldVisibility({
3460
3494
  field,
3461
3495
  hasError,
3462
3496
  showOnlyIfTrue
3463
3497
  }) {
3464
- if (!this.fields.has(field)) {
3498
+ const fieldInstance = this.fields.get(field);
3499
+ if (!fieldInstance) {
3465
3500
  this.queuedFieldVisibilityEvents.set(field, {
3466
3501
  hasError,
3467
3502
  showOnlyIfTrue
3468
3503
  });
3469
3504
  } else {
3470
- this.fields.get(field).visibility = showOnlyIfTrue ? hasError : !hasError;
3505
+ const visibility = showOnlyIfTrue ? hasError : !hasError;
3506
+ fieldInstance.visibility = visibility;
3507
+ /**
3508
+ * I was sure I would not require to gambiarra, but..
3509
+ * in order to ignore an hidden value on a form submit
3510
+ * or revalidate it when it comes back to visibility
3511
+ * I needed to...
3512
+ * I don't recommend setting private properties like this
3513
+ * this will force the field to be valid when it's hidden
3514
+ * and trigger the validation when it's visible
3515
+ */
3516
+ if (fieldInstance.visibility) {
3517
+ fieldInstance === null || fieldInstance === void 0 ? void 0 : fieldInstance.emitValue({
3518
+ value: this.queuedInitialValues.get(field) || '',
3519
+ event: 'ON_FIELD_MOUNT'
3520
+ });
3521
+ this.queuedInitialValues.delete(field);
3522
+ } else {
3523
+ fieldInstance.value = '';
3524
+ fieldInstance.valid = true;
3525
+ }
3471
3526
  }
3472
3527
  }
3473
3528
  /**
@@ -3512,8 +3567,9 @@ class FormCore {
3512
3567
  * @internal
3513
3568
  * Update field value and emit change and cleared event.
3514
3569
  *
3515
- * @param {string} key - Field name to be updated.
3516
- * @param {unknown} value - Value to be inserted into field.
3570
+ * @param {options} options to reset the field value
3571
+ * @param {string} options.key - Field name to be updated.
3572
+ * @param {unknown} options.value - Value to be inserted into field.
3517
3573
  */
3518
3574
  setResetFieldValue({
3519
3575
  key,
@@ -3527,9 +3583,6 @@ class FormCore {
3527
3583
  const field = this.fields.get(key);
3528
3584
  field.emitValue({
3529
3585
  value: value,
3530
- event: 'ON_FIELD_CHANGE'
3531
- });
3532
- field.emitEvents({
3533
3586
  event: 'ON_FIELD_CLEARED'
3534
3587
  });
3535
3588
  }
@@ -3577,6 +3630,76 @@ class FormCore {
3577
3630
  });
3578
3631
  });
3579
3632
  }
3633
+ /**
3634
+ * @internal
3635
+ * Update field property and emit template change.
3636
+ *
3637
+ * @param {object} options - Options for resetting field property
3638
+ * @param {string} options.key - Field name to be updated.
3639
+ * @param {string} options.property - field property to change.
3640
+ * @param {string} options.path - field property path to change.
3641
+ * @param {unknown} options.value - Value to be inserted into field.
3642
+ */
3643
+ setResetPathValue({
3644
+ key,
3645
+ property,
3646
+ path,
3647
+ value
3648
+ }) {
3649
+ if (!this.fields.has(key)) {
3650
+ this.queuedFieldResetPropertyEvents.set(key, {
3651
+ property,
3652
+ path,
3653
+ value
3654
+ });
3655
+ } else {
3656
+ this.setValue({
3657
+ key,
3658
+ property,
3659
+ path: path.split('.'),
3660
+ value: value,
3661
+ event: 'ON_RESET'
3662
+ });
3663
+ }
3664
+ }
3665
+ /**
3666
+ * Resets field properties based on reset conditions defined in the schema.
3667
+ *
3668
+ * @param {object} options - Options for resetting field property.
3669
+ * @param {TEvents} options.event - The event triggering the reset.
3670
+ * @param {string} options.key - The key of the field.
3671
+ */
3672
+ resetProperty({
3673
+ event,
3674
+ key
3675
+ }) {
3676
+ const field = this.fields.get(key);
3677
+ const structResetPath = field === null || field === void 0 ? void 0 : field.resetPropertyValues;
3678
+ if (!structResetPath || !(structResetPath === null || structResetPath === void 0 ? void 0 : structResetPath.some(config => config.events.includes(event)))) return;
3679
+ structResetPath.forEach(structElement => {
3680
+ if (!structElement.events.includes(event)) return;
3681
+ if (!ALLOWED_RESET_PROPS_MUTATIONS.includes(structElement.property)) return;
3682
+ if (!structElement.validations) {
3683
+ return this.setResetPathValue({
3684
+ key: structElement.field,
3685
+ path: structElement.path,
3686
+ property: structElement.property,
3687
+ value: structElement.resettledValue
3688
+ });
3689
+ }
3690
+ Object.keys(structElement.validations).forEach(validationKey => {
3691
+ const error = handleValidation(field.value, structElement.validations, validations, validationKey);
3692
+ if (!error) {
3693
+ this.setResetPathValue({
3694
+ key: structElement.field,
3695
+ path: structElement.path,
3696
+ property: structElement.property,
3697
+ value: structElement.resettledValue
3698
+ });
3699
+ }
3700
+ });
3701
+ });
3702
+ }
3580
3703
  /**
3581
3704
  * Adds a field onto the form instance regardless there is a schema or not
3582
3705
  *
@@ -3587,7 +3710,7 @@ class FormCore {
3587
3710
  fieldSchema,
3588
3711
  mapperElement
3589
3712
  }) {
3590
- var _a, _b, _c;
3713
+ var _a;
3591
3714
  if (this.fields.has(fieldSchema.name)) {
3592
3715
  throw new Error(`field name ${fieldSchema.name} already defined`);
3593
3716
  }
@@ -3599,8 +3722,8 @@ class FormCore {
3599
3722
  children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
3600
3723
  validateVisibility: this.validateVisibility.bind(this),
3601
3724
  resetValue: this.resetValue.bind(this),
3725
+ resetProperty: this.resetProperty.bind(this),
3602
3726
  getFormValues: this.getFormValues.bind(this),
3603
- initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
3604
3727
  templateSubject$: this.templateSubject$,
3605
3728
  fieldEventSubject$: this.fieldEventSubject$,
3606
3729
  dataSubject$: this.dataSubject$,
@@ -3610,13 +3733,16 @@ class FormCore {
3610
3733
  this.subscribeTemplates();
3611
3734
  this.refreshTemplates({
3612
3735
  scope: 'fields',
3613
- event: 'ON_FIELDS',
3614
- key: fieldSchema.name
3736
+ event: 'ON_FIELDS'
3615
3737
  });
3738
+ if (!this.queuedInitialValues.has(fieldSchema.name)) {
3739
+ const field = this.fields.get(fieldSchema.name);
3740
+ !(field === null || field === void 0 ? void 0 : field.value) && (field === null || field === void 0 ? void 0 : field.emitValue({
3741
+ value: '',
3742
+ event: 'ON_FIELD_MOUNT'
3743
+ }));
3744
+ }
3616
3745
  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
3746
  }
3621
3747
  removeField({
3622
3748
  key
@@ -3640,7 +3766,7 @@ class FormCore {
3640
3766
  serializeStructure(struct, path) {
3641
3767
  if (!struct) return;
3642
3768
  struct.forEach(structElement => {
3643
- var _a, _b, _c;
3769
+ var _a, _b;
3644
3770
  const currField = this.fields.get(structElement.name);
3645
3771
  if (!currField) {
3646
3772
  let mapper;
@@ -3657,7 +3783,7 @@ class FormCore {
3657
3783
  children: structElement.children ? structElement.children.map(el => el.name) : [],
3658
3784
  validateVisibility: this.validateVisibility.bind(this),
3659
3785
  resetValue: this.resetValue.bind(this),
3660
- initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[structElement.name],
3786
+ resetProperty: this.resetProperty.bind(this),
3661
3787
  templateSubject$: this.templateSubject$,
3662
3788
  fieldEventSubject$: this.fieldEventSubject$,
3663
3789
  dataSubject$: this.dataSubject$,
@@ -3666,7 +3792,7 @@ class FormCore {
3666
3792
  getFormValues: this.getFormValues.bind(this)
3667
3793
  }));
3668
3794
  } 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) || [];
3795
+ 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
3796
  currField.path = path;
3671
3797
  currField.originalSchema = structElement;
3672
3798
  currField.templateSubject$ = this.templateSubject$;
@@ -3878,14 +4004,15 @@ class FormGroup {
3878
4004
  * Creates an instance of FormGroup.
3879
4005
  */
3880
4006
  constructor(entry) {
3881
- var _a, _b, _c, _d;
4007
+ var _a, _b, _c, _d, _e;
3882
4008
  this.destroy = () => {
3883
4009
  this.forms.forEach(form => form.destroy());
3884
4010
  };
3885
4011
  this.forms = new Map();
3886
4012
  this.config = {
3887
4013
  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
4014
+ 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,
4015
+ 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
4016
  };
3890
4017
  }
3891
4018
  /**
@@ -4049,7 +4176,7 @@ class FormGroup {
4049
4176
  }), map(() => {
4050
4177
  var _a;
4051
4178
  return ((_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.isValid) === false ? false : true;
4052
- }));
4179
+ }), distinctUntilChanged());
4053
4180
  if (sub) {
4054
4181
  acc[formId] = sub;
4055
4182
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "0.0.2-beta.6",
3
+ "version": "0.0.3-beta.1",
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, };