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

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 } from 'rxjs';
1
+ import { Subject, Subscription, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map, distinctUntilKeyChanged } 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';
@@ -76,11 +76,10 @@ function makeRequest(method, url, headers, body) {
76
76
  });
77
77
  }
78
78
  xhr.onload = function () {
79
- if (xhr.status >= 200 && xhr.status < 300) {
80
- resolve(xhr.responseText);
81
- } else {
82
- reject(xhr.statusText);
83
- }
79
+ resolve({
80
+ status: xhr.status,
81
+ response: xhr.responseText
82
+ });
84
83
  };
85
84
  xhr.onerror = function () {
86
85
  reject(xhr.statusText);
@@ -2470,6 +2469,11 @@ class FormField {
2470
2469
  * @param {Function} options.resetValue - A function to reset the field value.
2471
2470
  * @param {unknown} [options.initialValue] - The initial value of the form field.
2472
2471
  * @param {Subject<{ key: string }>} options.templateSubject$ - A subject for template updates.
2472
+ * @param {Subject<TFieldEvent>} options.fieldEventSubject$, - Subject for basic event mapped field emissions, except onData to form instance
2473
+ * @param {Subject<{ key: string; event: TEvents }>} options.dataSubject$, - Subject to emit onData events to form instance
2474
+ * @param {Subject<{ key: string }>} options.formValidNotification$, - Subject to emit field valid change to form instance
2475
+ * @param {TMapper<unknown>} options.mapper, - component generic mapper containing render parameters for adapters
2476
+ * @param {() => TFormValues<unknown>} options.getFormValues, - form instance function that builds onData parameter payload from fields
2473
2477
  */
2474
2478
  constructor({
2475
2479
  schemaComponent,
@@ -2482,7 +2486,9 @@ class FormField {
2482
2486
  templateSubject$,
2483
2487
  fieldEventSubject$,
2484
2488
  dataSubject$,
2485
- mapper
2489
+ formValidNotification$,
2490
+ mapper,
2491
+ getFormValues
2486
2492
  }) {
2487
2493
  var _a, _b, _c, _d, _e, _f, _g;
2488
2494
  this.valueSubscription$ = new Subscription();
@@ -2497,42 +2503,46 @@ class FormField {
2497
2503
  this.component = schemaComponent.component;
2498
2504
  this.path = path;
2499
2505
  this.children = children;
2500
- this.validations = schemaComponent.validations;
2501
- this.errorMessages = (_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages;
2502
- this.visibilityConditions = schemaComponent.visibilityConditions;
2503
- this.resetValues = schemaComponent.resetValues;
2504
- this.apiSchema = schemaComponent.api;
2505
- this.formatters = schemaComponent.formatters;
2506
- this.masks = schemaComponent.masks;
2506
+ this.validations = cloneDeep(schemaComponent.validations);
2507
+ this.errorMessages = cloneDeep((_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages);
2508
+ this.visibilityConditions = cloneDeep(schemaComponent.visibilityConditions);
2509
+ this.resetValues = cloneDeep(schemaComponent.resetValues);
2510
+ this.apiSchema = cloneDeep(schemaComponent.api);
2511
+ this.formatters = cloneDeep(schemaComponent.formatters);
2512
+ this.masks = cloneDeep(schemaComponent.masks);
2507
2513
  if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
2508
2514
  if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) this.valuePropName = mapper.events.setValue;
2509
2515
  this.mapper = mapper;
2510
2516
  this.validateVisibility = validateVisibility;
2511
2517
  this.resetValue = resetValue;
2518
+ this.getFormValues = getFormValues;
2512
2519
  this.templateSubject$ = templateSubject$;
2513
2520
  this.fieldEventSubject$ = fieldEventSubject$;
2514
2521
  this.dataSubject$ = dataSubject$;
2515
- this._props = schemaComponent.props || {};
2522
+ this.formValidNotification$ = formValidNotification$;
2523
+ this._props = cloneDeep(schemaComponent.props || {});
2516
2524
  this._metadata = '';
2517
2525
  this.errorsString = '';
2518
2526
  this.errorsList = [];
2519
- this.initialValue = initialValue;
2527
+ this.initialValue = initialValue || (this.valuePropName && !String(this.props[this.valuePropName]).includes('${') ? this.props[this.valuePropName] : null);
2520
2528
  this._visibility = true;
2521
2529
  this._api = {
2522
2530
  default: {
2523
- response: ((_e = (_d = (_c = this.apiSchema) === null || _c === void 0 ? void 0 : _c.defaultConfig) === null || _d === void 0 ? void 0 : _d.config) === null || _e === void 0 ? void 0 : _e.fallbackValue) || ''
2531
+ response: ((_e = (_d = (_c = this.apiSchema) === null || _c === void 0 ? void 0 : _c.defaultConfig) === null || _d === void 0 ? void 0 : _d.config) === null || _e === void 0 ? void 0 : _e.fallbackValue) || '',
2532
+ status: null
2524
2533
  },
2525
2534
  named: ((_f = this.apiSchema) === null || _f === void 0 ? void 0 : _f.configs) && Object.keys((_g = this.apiSchema) === null || _g === void 0 ? void 0 : _g.configs).reduce((acc, curr) => {
2526
2535
  var _a, _b;
2527
2536
  acc[curr] = {
2528
- response: ((_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[curr].config.fallbackValue) || ''
2537
+ response: ((_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[curr].config.fallbackValue) || '',
2538
+ status: null
2529
2539
  };
2530
2540
  return acc;
2531
2541
  }, {})
2532
2542
  };
2533
2543
  this._errors = {};
2534
- this._valid = false;
2535
2544
  this._mounted = true;
2545
+ this._valid = false;
2536
2546
  this.initializeObservers();
2537
2547
  this.value = this.initialValue || '';
2538
2548
  }
@@ -2689,7 +2699,7 @@ class FormField {
2689
2699
  */
2690
2700
  if (!this.visibility) {
2691
2701
  this.value = '';
2692
- this._valid = true;
2702
+ this.valid = true;
2693
2703
  } else {
2694
2704
  this.setFieldValidity({
2695
2705
  event: 'ON_FIELD_MOUNT'
@@ -2702,6 +2712,17 @@ class FormField {
2702
2712
  event: 'ON_VISIBILITY'
2703
2713
  });
2704
2714
  }
2715
+ /**
2716
+ * sets valid field state and notifies form instance via formValidNotification$
2717
+ */
2718
+ set valid(valid) {
2719
+ if (this._valid !== valid) {
2720
+ this._valid = valid;
2721
+ this.formValidNotification$.next({
2722
+ fieldTrigger: this.name
2723
+ });
2724
+ }
2725
+ }
2705
2726
  /**
2706
2727
  * Retrieves the validity status of the form field.
2707
2728
  *
@@ -2843,7 +2864,7 @@ class FormField {
2843
2864
  var _a, _b, _c, _d;
2844
2865
  if (!this.validations || !this.visibility) {
2845
2866
  this.errors = {};
2846
- this._valid = true;
2867
+ this.valid = true;
2847
2868
  return;
2848
2869
  }
2849
2870
  let valid = true;
@@ -2865,7 +2886,7 @@ class FormField {
2865
2886
  delete errors[validationKey];
2866
2887
  }
2867
2888
  });
2868
- this._valid = valid;
2889
+ this.valid = valid;
2869
2890
  if ((_c = (_b = this.validations) === null || _b === void 0 ? void 0 : _b.eventMessages) === null || _c === void 0 ? void 0 : _c[event]) {
2870
2891
  const eventMessages = {};
2871
2892
  (_d = this.validations.eventMessages[event]) === null || _d === void 0 ? void 0 : _d.forEach(method => {
@@ -2930,14 +2951,28 @@ class FormField {
2930
2951
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2931
2952
  return __awaiter(this, void 0, void 0, function* () {
2932
2953
  const configRequest = config => __awaiter(this, void 0, void 0, function* () {
2954
+ var _k;
2933
2955
  try {
2934
- const responseData = yield makeRequest(config.method, config.url, config.headers, config.body);
2935
- const apiResponseData = JSON.parse(String(responseData));
2936
- const response = config.resultPath ? get(apiResponseData, config.resultPath) : apiResponseData;
2956
+ const {
2957
+ status,
2958
+ response
2959
+ } = yield makeRequest(config.method, config.url, config.headers, config.body);
2960
+ const callbackTransform = (_k = config.transform) === null || _k === void 0 ? void 0 : _k.callback;
2961
+ const apiResponseData = callbackTransform ? callbackTransform({
2962
+ payload: JSON.parse(String(response)),
2963
+ formValues: this.getFormValues()
2964
+ }) : JSON.parse(String(response));
2965
+ const responseReturn = config.resultPath ? get(apiResponseData, config.resultPath) : apiResponseData;
2937
2966
  // this.apiResponseData = { response };
2938
- return response;
2967
+ return {
2968
+ response: responseReturn,
2969
+ status
2970
+ };
2939
2971
  } catch (e) {
2940
- return !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error';
2972
+ return {
2973
+ response: !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error',
2974
+ status: 500
2975
+ };
2941
2976
  }
2942
2977
  });
2943
2978
  if (!((_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.defaultConfig) === null || _b === void 0 ? void 0 : _b.events.includes(event)) && !(((_c = this.apiSchema) === null || _c === void 0 ? void 0 : _c.configs) && Object.keys((_d = this.apiSchema) === null || _d === void 0 ? void 0 : _d.configs).some(key => {
@@ -2950,9 +2985,13 @@ class FormField {
2950
2985
  };
2951
2986
  const config = (_e = this.apiSchema.defaultConfig) === null || _e === void 0 ? void 0 : _e.config;
2952
2987
  if (config && ((_g = (_f = this.apiSchema) === null || _f === void 0 ? void 0 : _f.defaultConfig) === null || _g === void 0 ? void 0 : _g.events.includes(event)) && this.checkApiRequestValidations(config)) {
2953
- const response = yield configRequest(config);
2988
+ const {
2989
+ response,
2990
+ status
2991
+ } = yield configRequest(config);
2954
2992
  responses.default = {
2955
- response
2993
+ response,
2994
+ status
2956
2995
  };
2957
2996
  }
2958
2997
  if (((_h = this.apiSchema) === null || _h === void 0 ? void 0 : _h.configs) && Object.keys((_j = this.apiSchema) === null || _j === void 0 ? void 0 : _j.configs).some(key => {
@@ -2964,14 +3003,18 @@ class FormField {
2964
3003
  @TODO handle promises with error
2965
3004
  */
2966
3005
  const result = yield Promise.all(Object.keys(this.apiSchema.configs).map(configKey => __awaiter(this, void 0, void 0, function* () {
2967
- var _k, _l, _m, _o;
2968
- const config = (_l = (_k = this.apiSchema) === null || _k === void 0 ? void 0 : _k.configs) === null || _l === void 0 ? void 0 : _l[configKey].config;
2969
- if (config && ((_o = (_m = this.apiSchema) === null || _m === void 0 ? void 0 : _m.configs) === null || _o === void 0 ? void 0 : _o[configKey].events.includes(event)) && this.checkApiRequestValidations(config)) {
2970
- const response = yield configRequest(config);
3006
+ var _l, _m, _o, _p;
3007
+ const config = (_m = (_l = this.apiSchema) === null || _l === void 0 ? void 0 : _l.configs) === null || _m === void 0 ? void 0 : _m[configKey].config;
3008
+ if (config && ((_p = (_o = this.apiSchema) === null || _o === void 0 ? void 0 : _o.configs) === null || _p === void 0 ? void 0 : _p[configKey].events.includes(event)) && this.checkApiRequestValidations(config)) {
3009
+ const {
3010
+ response,
3011
+ status
3012
+ } = yield configRequest(config);
2971
3013
  return {
2972
3014
  name: configKey,
2973
3015
  result: {
2974
- response
3016
+ response,
3017
+ status
2975
3018
  }
2976
3019
  };
2977
3020
  }
@@ -3043,6 +3086,8 @@ class FormCore {
3043
3086
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
3044
3087
  this.templateSubscription$ = new Subscription();
3045
3088
  this.mappers = new Map();
3089
+ this.queuedFieldVisibilityEvents = new Map();
3090
+ this.queuedFieldResetValuesEvents = new Map();
3046
3091
  this.schema = entry.schema;
3047
3092
  this.fields = new Map();
3048
3093
  this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
@@ -3062,6 +3107,7 @@ class FormCore {
3062
3107
  this.fieldEventSubject$ = new Subject();
3063
3108
  this.dataSubject$ = new Subject();
3064
3109
  this.mountSubject$ = new Subject();
3110
+ this.formValidNotification$ = new Subject();
3065
3111
  this.subscribedTemplates = [];
3066
3112
  this.schema && this.serializeStructure(this.schema.components);
3067
3113
  this.schema && this.subscribeTemplates();
@@ -3171,7 +3217,7 @@ class FormCore {
3171
3217
  const field = this.fields.get(key);
3172
3218
  if (!field) return console.warn(`failed to get value from ${key}`);
3173
3219
  if (property === 'props' && path[0] === field.valuePropName) {
3174
- return field.stateValue;
3220
+ return field.value;
3175
3221
  }
3176
3222
  return path.length > 0 ? get(field[property], path) : field[property];
3177
3223
  }
@@ -3383,6 +3429,25 @@ class FormCore {
3383
3429
  }
3384
3430
  });
3385
3431
  }
3432
+ /**
3433
+ * executes events that were stored due to field unavaliability
3434
+ *
3435
+ * @param {string} field field to check
3436
+ */
3437
+ checkFieldEventQueues(field) {
3438
+ if (this.queuedFieldVisibilityEvents.has(field)) {
3439
+ this.setFieldVisibility(Object.assign({
3440
+ field: field
3441
+ }, this.queuedFieldVisibilityEvents.get(field)));
3442
+ this.queuedFieldVisibilityEvents.delete(field);
3443
+ }
3444
+ if (this.queuedFieldResetValuesEvents.has(field)) {
3445
+ this.setResetFieldValue(Object.assign({
3446
+ key: field
3447
+ }, this.queuedFieldResetValuesEvents.get(field)));
3448
+ this.queuedFieldResetValuesEvents.delete(field);
3449
+ }
3450
+ }
3386
3451
  /**
3387
3452
  * @internal
3388
3453
  * Update field visibility accordingly.
@@ -3391,14 +3456,16 @@ class FormCore {
3391
3456
  * @param {boolean} hasError - Condition to be used as visibility.
3392
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.
3393
3458
  */
3394
- setFieldVisibility(field, hasError, showOnlyIfTrue) {
3459
+ setFieldVisibility({
3460
+ field,
3461
+ hasError,
3462
+ showOnlyIfTrue
3463
+ }) {
3395
3464
  if (!this.fields.has(field)) {
3396
- /*
3397
- * check with João the need for a global mount event for the form
3398
- * to avoid not displaying this message in some cases (AsFormField)
3399
- * suggestion: create subscriber to targetField.ON_FIELD_MOUNT
3400
- */
3401
- console.warn(`failed to update visibility onto field ${field}`);
3465
+ this.queuedFieldVisibilityEvents.set(field, {
3466
+ hasError,
3467
+ showOnlyIfTrue
3468
+ });
3402
3469
  } else {
3403
3470
  this.fields.get(field).visibility = showOnlyIfTrue ? hasError : !hasError;
3404
3471
  }
@@ -3425,10 +3492,18 @@ class FormCore {
3425
3492
  const error = handleValidation(field.value, structElement.validations, validations, validationKey);
3426
3493
  if (Array.isArray(structElement.fields)) {
3427
3494
  structElement.fields.forEach(fieldKey => {
3428
- this.setFieldVisibility(fieldKey, error, !!(field.value && structElement.showOnlyIfTrue));
3495
+ this.setFieldVisibility({
3496
+ field: fieldKey,
3497
+ hasError: error,
3498
+ showOnlyIfTrue: !!(field.value && structElement.showOnlyIfTrue)
3499
+ });
3429
3500
  });
3430
3501
  } else if (structElement.fields) {
3431
- this.setFieldVisibility(structElement.fields, error, !!(field.value && structElement.showOnlyIfTrue));
3502
+ this.setFieldVisibility({
3503
+ field: structElement.fields,
3504
+ hasError: error,
3505
+ showOnlyIfTrue: !!(field.value && structElement.showOnlyIfTrue)
3506
+ });
3432
3507
  }
3433
3508
  });
3434
3509
  });
@@ -3440,9 +3515,14 @@ class FormCore {
3440
3515
  * @param {string} key - Field name to be updated.
3441
3516
  * @param {unknown} value - Value to be inserted into field.
3442
3517
  */
3443
- setResetFieldValue(key, value) {
3518
+ setResetFieldValue({
3519
+ key,
3520
+ value
3521
+ }) {
3444
3522
  if (!this.fields.has(key)) {
3445
- console.warn(`failed to reset value onto field ${key}`);
3523
+ this.queuedFieldResetValuesEvents.set(key, {
3524
+ value
3525
+ });
3446
3526
  } else {
3447
3527
  const field = this.fields.get(key);
3448
3528
  field.emitValue({
@@ -3474,10 +3554,16 @@ class FormCore {
3474
3554
  if (Array.isArray(structElement.fields)) {
3475
3555
  structElement.fields.forEach((fieldKey, index) => {
3476
3556
  const resettledValue = Array.isArray(structElement.resettledValue) ? structElement.resettledValue[index] : structElement.resettledValue;
3477
- this.setResetFieldValue(fieldKey, resettledValue);
3557
+ this.setResetFieldValue({
3558
+ key: fieldKey,
3559
+ value: resettledValue
3560
+ });
3478
3561
  });
3479
3562
  } else if (structElement.fields) {
3480
- this.setResetFieldValue(structElement.fields, structElement.resettledValue);
3563
+ this.setResetFieldValue({
3564
+ key: structElement.fields,
3565
+ value: structElement.resettledValue
3566
+ });
3481
3567
  }
3482
3568
  };
3483
3569
  if (!structElement.validations) {
@@ -3513,10 +3599,12 @@ class FormCore {
3513
3599
  children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
3514
3600
  validateVisibility: this.validateVisibility.bind(this),
3515
3601
  resetValue: this.resetValue.bind(this),
3602
+ getFormValues: this.getFormValues.bind(this),
3516
3603
  initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
3517
3604
  templateSubject$: this.templateSubject$,
3518
3605
  fieldEventSubject$: this.fieldEventSubject$,
3519
3606
  dataSubject$: this.dataSubject$,
3607
+ formValidNotification$: this.formValidNotification$,
3520
3608
  config: this.config
3521
3609
  }));
3522
3610
  this.subscribeTemplates();
@@ -3525,6 +3613,7 @@ class FormCore {
3525
3613
  event: 'ON_FIELDS',
3526
3614
  key: fieldSchema.name
3527
3615
  });
3616
+ this.checkFieldEventQueues(fieldSchema.name);
3528
3617
  (_c = this.fields.get(fieldSchema.name)) === null || _c === void 0 ? void 0 : _c.emitEvents({
3529
3618
  event: 'ON_FIELD_MOUNT'
3530
3619
  });
@@ -3572,7 +3661,9 @@ class FormCore {
3572
3661
  templateSubject$: this.templateSubject$,
3573
3662
  fieldEventSubject$: this.fieldEventSubject$,
3574
3663
  dataSubject$: this.dataSubject$,
3575
- config: this.config
3664
+ formValidNotification$: this.formValidNotification$,
3665
+ config: this.config,
3666
+ getFormValues: this.getFormValues.bind(this)
3576
3667
  }));
3577
3668
  } else {
3578
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) || [];
@@ -3608,6 +3699,7 @@ class FormCore {
3608
3699
  (_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.emitEvents({
3609
3700
  event: 'ON_FIELD_MOUNT'
3610
3701
  });
3702
+ this.checkFieldEventQueues(key);
3611
3703
  }
3612
3704
  });
3613
3705
  this.subscribedTemplates.forEach(el => {
@@ -3682,7 +3774,7 @@ class FormCore {
3682
3774
  }
3683
3775
  /**
3684
3776
  *
3685
- * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
3777
+ * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call onData
3686
3778
  */
3687
3779
  subscribeData(callback) {
3688
3780
  const sub = this.dataSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), map(({
@@ -3701,6 +3793,21 @@ class FormCore {
3701
3793
  });
3702
3794
  return sub;
3703
3795
  }
3796
+ /**
3797
+ *
3798
+ * @param {(payload: TFormValidationPayload) => void} callback callback function to call onValid
3799
+ */
3800
+ subscribeFormValidation(callback) {
3801
+ const sub = this.formValidNotification$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
3802
+ fieldTrigger
3803
+ }) => ({
3804
+ fieldTrigger,
3805
+ valid: this.isValid
3806
+ })), distinctUntilKeyChanged('valid')).subscribe({
3807
+ next: callback
3808
+ });
3809
+ return sub;
3810
+ }
3704
3811
  /**
3705
3812
  * Submits the form by triggering form field events and invoking the onSubmit callback.
3706
3813
  */
@@ -3731,6 +3838,7 @@ class FormCore {
3731
3838
  this.templateSubscription$.unsubscribe();
3732
3839
  this.fieldEventSubject$.unsubscribe();
3733
3840
  this.dataSubject$.unsubscribe();
3841
+ this.formValidNotification$.unsubscribe();
3734
3842
  this.fields.forEach(field => field.destroyField());
3735
3843
  }
3736
3844
  }
@@ -3769,8 +3877,16 @@ class FormGroup {
3769
3877
  /**
3770
3878
  * Creates an instance of FormGroup.
3771
3879
  */
3772
- constructor() {
3880
+ constructor(entry) {
3881
+ var _a, _b, _c, _d;
3882
+ this.destroy = () => {
3883
+ this.forms.forEach(form => form.destroy());
3884
+ };
3773
3885
  this.forms = new Map();
3886
+ this.config = {
3887
+ 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
3889
+ };
3774
3890
  }
3775
3891
  /**
3776
3892
  * Creates an empty form with given index
@@ -3784,7 +3900,8 @@ class FormGroup {
3784
3900
  }) {
3785
3901
  const formInstance = new FormCore({
3786
3902
  index,
3787
- mappers
3903
+ mappers,
3904
+ config: this.config
3788
3905
  });
3789
3906
  this.addForm({
3790
3907
  key: index,
@@ -3805,6 +3922,9 @@ class FormGroup {
3805
3922
  this.checkIndexes({
3806
3923
  key
3807
3924
  });
3925
+ if (!formInstance.config) {
3926
+ formInstance.config = this.config;
3927
+ }
3808
3928
  this.forms.set(key, formInstance);
3809
3929
  }
3810
3930
  /**
@@ -3896,8 +4016,7 @@ class FormGroup {
3896
4016
  }) {
3897
4017
  const subs = ids.reduce((acc, formId) => {
3898
4018
  var _a, _b;
3899
- // @TODO add config on debounceTime on this events
3900
- const sub = (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.dataSubject$.pipe(groupBy(payload => `${formId}.${payload.event}`), mergeMap(group$ => group$.pipe(debounceTime(100))), map(({
4019
+ const sub = (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.dataSubject$.pipe(groupBy(payload => `${formId}.${payload.event}`), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), map(({
3901
4020
  key
3902
4021
  }) => {
3903
4022
  var _a;
@@ -3919,6 +4038,31 @@ class FormGroup {
3919
4038
  const sub = combineLatest(subs).subscribe(callback);
3920
4039
  return sub;
3921
4040
  }
4041
+ onValidSubscription({
4042
+ ids,
4043
+ callback
4044
+ }) {
4045
+ const subs = ids.reduce((acc, formId) => {
4046
+ var _a;
4047
+ const sub = (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.formValidNotification$.pipe(groupBy(payload => `${formId}.${payload.fieldTrigger}`), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), startWith({
4048
+ fieldTrigger: null
4049
+ }), map(() => {
4050
+ var _a;
4051
+ return ((_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.isValid) === false ? false : true;
4052
+ }));
4053
+ if (sub) {
4054
+ acc[formId] = sub;
4055
+ } else {
4056
+ console.warn(`failed to register validation subscription form id ${formId}`);
4057
+ }
4058
+ return acc;
4059
+ }, {});
4060
+ const sub = combineLatest(subs).pipe(map(forms => ({
4061
+ groupValid: Object.keys(forms).every(formId => forms[formId]),
4062
+ forms
4063
+ }))).subscribe(callback);
4064
+ return sub;
4065
+ }
3922
4066
  }
3923
4067
 
3924
4068
  export { FormCore, FormField, FormGroup, TMutationEnum };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "0.0.2-beta.05",
3
+ "version": "0.0.2-beta.7",
4
4
  "module": "./index.esm.js",
5
5
  "type": "module",
6
6
  "main": "./index.esm.js",
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { TEMPLATE_AVALIABLE_SCOPES } from '../constants/constants';
3
+ import { TApiResponsePayload } from '../types/schema';
3
4
  import { TSubscribedTemplates } from '../types/template';
4
5
  import { OutgoingHttpHeaders } from 'http2';
5
6
  /**
@@ -16,7 +17,7 @@ import { OutgoingHttpHeaders } from 'http2';
16
17
  * const response = await makeRequest('GET', 'https://api.example.com/data');
17
18
  * ```
18
19
  */
19
- declare function makeRequest(method: string, url: string, headers?: OutgoingHttpHeaders, body?: Record<string, unknown>): Promise<string>;
20
+ declare function makeRequest(method: string, url: string, headers?: OutgoingHttpHeaders, body?: Record<string, unknown>): Promise<TApiResponsePayload>;
20
21
  /**
21
22
  * Extracts keys enclosed in `${}` from a given expression.
22
23
  *
@@ -2,10 +2,11 @@ import { Observable, Subject, Subscription } from 'rxjs';
2
2
  import { TApiConfig, TApiEvent, TApiResponse, TErrorMessages, TFormatters, TMasks, TResetValueMethods, TSchemaFormConfig, TValidations, TVisibility } from '../types/schema';
3
3
  import { IComponentSchema, IComponentSchemaAsFormField } from '../interfaces/schema';
4
4
  import { IState } from '../interfaces/state';
5
- import { TEvents, TFieldEvent, TValueChangeEvent } from '../types/event';
5
+ import { TEvents, TFieldEvent, TFormValidationPayload, TValueChangeEvent } from '../types/event';
6
6
  import { TMapper } from '../types/mapper';
7
7
  import { SafeSubject } from '../helpers/SafeSubject';
8
8
  import { TTemplateEvent } from '../types/template';
9
+ import { TFormValues } from '../types/form';
9
10
  /**
10
11
  * Represents a form field with observables for managing form state, validations, and API requests.
11
12
  */
@@ -55,6 +56,7 @@ declare class FormField {
55
56
  key: string;
56
57
  event: TEvents;
57
58
  }>;
59
+ formValidNotification$: Subject<Pick<TFormValidationPayload, 'fieldTrigger'>>;
58
60
  validateVisibility: (payload: {
59
61
  event: TEvents;
60
62
  key: string;
@@ -63,6 +65,7 @@ declare class FormField {
63
65
  event: TEvents;
64
66
  key: string;
65
67
  }) => void;
68
+ getFormValues: () => TFormValues<unknown>;
66
69
  valueChangeEvent: TValueChangeEvent;
67
70
  /**
68
71
  * Creates an instance of FormField.
@@ -76,8 +79,13 @@ declare class FormField {
76
79
  * @param {Function} options.resetValue - A function to reset the field value.
77
80
  * @param {unknown} [options.initialValue] - The initial value of the form field.
78
81
  * @param {Subject<{ key: string }>} options.templateSubject$ - A subject for template updates.
82
+ * @param {Subject<TFieldEvent>} options.fieldEventSubject$, - Subject for basic event mapped field emissions, except onData to form instance
83
+ * @param {Subject<{ key: string; event: TEvents }>} options.dataSubject$, - Subject to emit onData events to form instance
84
+ * @param {Subject<{ key: string }>} options.formValidNotification$, - Subject to emit field valid change to form instance
85
+ * @param {TMapper<unknown>} options.mapper, - component generic mapper containing render parameters for adapters
86
+ * @param {() => TFormValues<unknown>} options.getFormValues, - form instance function that builds onData parameter payload from fields
79
87
  */
80
- constructor({ schemaComponent, config, path, children, validateVisibility, resetValue, initialValue, templateSubject$, fieldEventSubject$, dataSubject$, mapper, }: {
88
+ constructor({ schemaComponent, config, path, children, validateVisibility, resetValue, initialValue, templateSubject$, fieldEventSubject$, dataSubject$, formValidNotification$, mapper, getFormValues, }: {
81
89
  schemaComponent: IComponentSchema;
82
90
  config?: TSchemaFormConfig;
83
91
  path?: string;
@@ -97,7 +105,9 @@ declare class FormField {
97
105
  key: string;
98
106
  event: TEvents;
99
107
  }>;
108
+ formValidNotification$: Subject<Pick<TFormValidationPayload, 'fieldTrigger'>>;
100
109
  mapper: TMapper<unknown>;
110
+ getFormValues: () => TFormValues<unknown>;
101
111
  });
102
112
  /**
103
113
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
@@ -146,6 +156,10 @@ declare class FormField {
146
156
  * @param {boolean} visible - The new visibility status to be set.
147
157
  */
148
158
  set visibility(visible: boolean);
159
+ /**
160
+ * sets valid field state and notifies form instance via formValidNotification$
161
+ */
162
+ set valid(valid: boolean);
149
163
  /**
150
164
  * Retrieves the validity status of the form field.
151
165
  *
@@ -3,7 +3,7 @@ import { Subject, Subscription } from 'rxjs';
3
3
  import { IComponentSchema, IComponentSchemaAsFormField, IFormSchema } from '../interfaces/schema';
4
4
  import { TSchemaFormConfig } from '../types/schema';
5
5
  import { TSubscribedTemplates, TTemplateEvent } from '../types/template';
6
- import { TEvents, TFieldEvent, TMutationEvents } from '../types/event';
6
+ import { TEvents, TFieldEvent, TFormValidationPayload, TMutationEvents } from '../types/event';
7
7
  import { TFormEntry, TFormValues } from '../types/form';
8
8
  import { TMapper } from '../types/mapper';
9
9
  import { TEMPLATE_AVALIABLE_SCOPES } from '../constants/constants';
@@ -24,11 +24,19 @@ declare class FormCore {
24
24
  key: string;
25
25
  event: TEvents;
26
26
  }>;
27
+ formValidNotification$: Subject<Pick<TFormValidationPayload, 'fieldTrigger'>>;
27
28
  subscribedTemplates: TSubscribedTemplates[];
28
29
  action?: string;
29
30
  method?: string;
30
31
  config: Required<TSchemaFormConfig>;
31
32
  mappers: Map<string, TMapper<unknown>>;
33
+ queuedFieldVisibilityEvents: Map<string, {
34
+ hasError: boolean;
35
+ showOnlyIfTrue?: boolean;
36
+ }>;
37
+ queuedFieldResetValuesEvents: Map<string, {
38
+ value: unknown;
39
+ }>;
32
40
  /**
33
41
  * Creates an instance of FormCore.
34
42
  *
@@ -127,6 +135,12 @@ declare class FormCore {
127
135
  * @param {TMutationEvents} options.event - Internal event descriptor to handle templating.
128
136
  */
129
137
  refreshTemplates({ key, event }: TTemplateEvent): void;
138
+ /**
139
+ * executes events that were stored due to field unavaliability
140
+ *
141
+ * @param {string} field field to check
142
+ */
143
+ checkFieldEventQueues(field: string): void;
130
144
  /**
131
145
  * Validates and collects the names of form fields in the provided schema structure.
132
146
  *
@@ -228,13 +242,18 @@ declare class FormCore {
228
242
  subscribeOnMount<T>(callback: (payload: TFormValues<T>) => void): Subscription;
229
243
  /**
230
244
  *
231
- * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
245
+ * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call onData
232
246
  */
233
247
  subscribeData<T>(callback: (payload: {
234
248
  field: string;
235
249
  data: TFormValues<T>;
236
250
  }) => void): Subscription;
237
251
  subscribeOnSubmit<T>(callback: (payload: TFormValues<T>) => void): Subscription;
252
+ /**
253
+ *
254
+ * @param {(payload: TFormValidationPayload) => void} callback callback function to call onValid
255
+ */
256
+ subscribeFormValidation(callback: (payload: TFormValidationPayload) => void): Subscription;
238
257
  /**
239
258
  * Submits the form by triggering form field events and invoking the onSubmit callback.
240
259
  */