@bolttech/form-engine-core 1.0.8 → 1.0.10

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.
Files changed (3) hide show
  1. package/index.d.ts +14 -3
  2. package/index.esm.js +66 -10
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as rxjs from 'rxjs';
2
- import { Subject, Subscription, BehaviorSubject } from 'rxjs';
2
+ import { Subject, BehaviorSubject, Subscription } from 'rxjs';
3
3
  import { TCurrencyLocalCode, TCurrencyCode } from '@gaignoux/currency';
4
4
  import { OutgoingHttpHeaders } from 'http2';
5
5
 
@@ -330,7 +330,7 @@ type TLengthValidation = {
330
330
  * const callbackValidation: TCallbackValidation = (value) => typeof value === 'string';
331
331
  * ```
332
332
  */
333
- type TCallbackValidation = (value: unknown, formValues: TFormValues<unknown>) => boolean;
333
+ type TCallbackValidation = (value: unknown, formValues: Pick<TFormValues<unknown>, 'values' | 'metadata'>) => boolean;
334
334
  /**
335
335
  * @type TBetweenValidation
336
336
  * Represents validation rules that check if a value is between a range.
@@ -1314,6 +1314,16 @@ declare class SafeSubject<T> extends Subject<T> {
1314
1314
  constructor(isMounted: () => boolean);
1315
1315
  next(value: T): void;
1316
1316
  }
1317
+ /**
1318
+ * Custom RXJS BehaviourSubject to gracefully handle errors on unsubscribed Subjects
1319
+ * since its fire and forget, no mount status needed to check if its available or not
1320
+ */
1321
+ declare class SafeBehaviourSubject<T> extends BehaviorSubject<T> {
1322
+ defaultValue: T;
1323
+ constructor(value: T);
1324
+ next(value: T): void;
1325
+ get value(): T;
1326
+ }
1317
1327
 
1318
1328
  type TTemplateAvaliableScopes = (typeof TEMPLATE_AVALIABLE_SCOPES)[number];
1319
1329
  /**
@@ -1727,7 +1737,7 @@ declare class FormCore {
1727
1737
  dataSubject$: Subject<TFormDataPayload>;
1728
1738
  formValidSubject$: Subject<TFormValidationPayload>;
1729
1739
  fieldValidNotification$: Subject<TFieldValidationPayload>;
1730
- formValuesStateSubject$: BehaviorSubject<TFormValues<unknown>>;
1740
+ formValuesStateSubject$: SafeBehaviourSubject<TFormValues<unknown>>;
1731
1741
  subscribedTemplates: TSubscribedTemplates[];
1732
1742
  action?: string;
1733
1743
  method?: string;
@@ -1750,6 +1760,7 @@ declare class FormCore {
1750
1760
  stopEventsOnSubmit: boolean;
1751
1761
  submitted: boolean;
1752
1762
  getFormValues: () => TFormValues<unknown>;
1763
+ isolatedFormInstance: boolean;
1753
1764
  /**
1754
1765
  * Creates an instance of FormCore.
1755
1766
  *
package/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Subject, Subscription, groupBy, mergeMap, debounceTime, filter, combineLatest, startWith, BehaviorSubject, map, distinctUntilKeyChanged } from 'rxjs';
1
+ import { BehaviorSubject, Subject, Subscription, groupBy, mergeMap, debounceTime, filter, combineLatest, startWith, map, distinctUntilKeyChanged } from 'rxjs';
2
2
  import creditCardType from 'credit-card-type';
3
3
  import isNumber$1 from 'lodash/isNumber';
4
4
  import { getCurrencySymbol } from '@gaignoux/currency';
@@ -1594,6 +1594,7 @@ const repeated = (value, validations) => {
1594
1594
  *
1595
1595
  * @param value - The value to be validated.
1596
1596
  * @param validations - An object containing validation methods, including a custom callback function.
1597
+ * @param formValues - An object containing the form state, NOTE: validations might be dirty here
1597
1598
  * @returns `true` if the custom callback validation function returns `true`, otherwise `false`.
1598
1599
  *
1599
1600
  * @example
@@ -1612,9 +1613,20 @@ const repeated = (value, validations) => {
1612
1613
  * };
1613
1614
  * ```
1614
1615
  */
1615
- const callback = (value, validations, formValues) => {
1616
+ const callback = (value, validations, {
1617
+ values = [],
1618
+ metadata = []
1619
+ } = {
1620
+ values: [],
1621
+ metadata: [],
1622
+ erroredFields: [],
1623
+ isValid: true
1624
+ }) => {
1616
1625
  if (!validations.callback || !((validations === null || validations === void 0 ? void 0 : validations.callback) instanceof Function)) return false;
1617
- return validations.callback(value, formValues);
1626
+ return validations.callback(value, {
1627
+ values,
1628
+ metadata
1629
+ });
1618
1630
  };
1619
1631
 
1620
1632
  /**
@@ -2450,6 +2462,28 @@ class SafeSubject extends Subject {
2450
2462
  }
2451
2463
  }
2452
2464
  }
2465
+ /**
2466
+ * Custom RXJS BehaviourSubject to gracefully handle errors on unsubscribed Subjects
2467
+ * since its fire and forget, no mount status needed to check if its available or not
2468
+ */
2469
+ class SafeBehaviourSubject extends BehaviorSubject {
2470
+ constructor(value) {
2471
+ super(value);
2472
+ this.defaultValue = value;
2473
+ }
2474
+ next(value) {
2475
+ if (!this.closed) {
2476
+ super.next(value);
2477
+ }
2478
+ }
2479
+ get value() {
2480
+ if (!this.closed) {
2481
+ return super.value;
2482
+ } else {
2483
+ return this.defaultValue;
2484
+ }
2485
+ }
2486
+ }
2453
2487
 
2454
2488
  /**
2455
2489
  * @internal
@@ -2913,14 +2947,20 @@ class FormField {
2913
2947
  valueSubscription,
2914
2948
  propsSubscription
2915
2949
  }) {
2950
+ /*
2951
+ NOTE: using emitEvents here will make ON_FIELD_MOUNT emit twice
2952
+ mount logic is managed on form.ts mountActions on first render
2953
+ */
2916
2954
  this.mounted = true;
2917
2955
  this.subscribeValue(valueSubscription);
2918
2956
  this.subscribeState(propsSubscription);
2919
2957
  this.valueSubject$.next(this.stateValue);
2920
2958
  this.propsSubject$.next(this.props);
2921
2959
  this.visibilitySubject$.next(this.visibility);
2922
- this.emitEvents({
2923
- event: 'ON_FIELD_MOUNT'
2960
+ this.fieldEventSubject$.next({
2961
+ event: 'ON_FIELD_MOUNT',
2962
+ fieldName: this.name,
2963
+ fieldInstance: this
2924
2964
  });
2925
2965
  }
2926
2966
  /**
@@ -3255,6 +3295,7 @@ class FormCore {
3255
3295
  this._valid = false;
3256
3296
  this.stopEventsOnSubmit = false;
3257
3297
  this.submitted = false;
3298
+ this.isolatedFormInstance = false;
3258
3299
  this.index = entry.index;
3259
3300
  this.schema = entry.schema;
3260
3301
  this.fields = new Map();
@@ -3268,7 +3309,10 @@ class FormCore {
3268
3309
  (_h = entry.mappers) === null || _h === void 0 ? void 0 : _h.map(mapper => {
3269
3310
  this.mappers.set(mapper.componentName, mapper);
3270
3311
  });
3271
- if ((!entry.submitSubject$ || !entry.dataSubject$ || !entry.formValidSubject$) && this.config.defaultLogVerbose) console.warn(`some formGroup events are not properly instanciated, any onData, onValid, onSubmit events managed by formGroup won't trigger on form: ${this.index}`);
3312
+ if (!entry.submitSubject$ || !entry.dataSubject$ || !entry.formValidSubject$) {
3313
+ this.isolatedFormInstance = true;
3314
+ if (this.config.defaultLogVerbose) console.warn(`some formGroup events are not properly instanciated, any onData, onValid, onSubmit events managed by formGroup won't trigger on form: ${this.index}`);
3315
+ }
3272
3316
  if (this.schema) {
3273
3317
  FormCore.checkIndexes(this.schema.components);
3274
3318
  }
@@ -3279,7 +3323,7 @@ class FormCore {
3279
3323
  this.submitSubject$ = entry.submitSubject$ ? entry.submitSubject$ : new Subject();
3280
3324
  this.dataSubject$ = entry.dataSubject$ ? entry.dataSubject$ : new Subject();
3281
3325
  this.formValidSubject$ = entry.formValidSubject$ ? entry.formValidSubject$ : new Subject();
3282
- this.formValuesStateSubject$ = new BehaviorSubject({
3326
+ this.formValuesStateSubject$ = new SafeBehaviourSubject({
3283
3327
  erroredFields: [],
3284
3328
  isValid: true,
3285
3329
  metadata: [],
@@ -3291,9 +3335,19 @@ class FormCore {
3291
3335
  this.fieldValidNotification$.subscribe(() => {
3292
3336
  this.validateForm();
3293
3337
  });
3338
+ /*
3339
+ @TODO check if this emissions can be merged
3340
+ every value update needs to occur after and before validations on field emitValue change
3341
+ so both callback validation and onData values are synced and fields and validations are correct
3342
+
3343
+ check field.ts emitValue and emitEvents
3344
+ */
3294
3345
  this.fieldEventSubject$.subscribe(() => {
3295
3346
  this.formValuesStateSubject$.next(this.getFormState());
3296
3347
  });
3348
+ this.dataSubject$.subscribe(() => {
3349
+ this.formValuesStateSubject$.next(this.getFormState());
3350
+ });
3297
3351
  this.mountSubject$.subscribe(this.mountActions.bind(this));
3298
3352
  this.initialValues = entry.initialValues || ((_j = entry.schema) === null || _j === void 0 ? void 0 : _j.initialValues);
3299
3353
  this.iVars = entry.iVars || ((_k = entry.schema) === null || _k === void 0 ? void 0 : _k.iVars) || {};
@@ -4241,13 +4295,15 @@ class FormCore {
4241
4295
  destroy() {
4242
4296
  this.templateSubject$.unsubscribe();
4243
4297
  this.templateSubscription$.unsubscribe();
4244
- this.submitSubject$.unsubscribe();
4245
4298
  this.mountSubject$.unsubscribe();
4246
4299
  this.fieldEventSubject$.unsubscribe();
4247
- this.dataSubject$.unsubscribe();
4248
- this.formValidSubject$.unsubscribe();
4249
4300
  this.fieldValidNotification$.unsubscribe();
4250
4301
  this.formValuesStateSubject$.unsubscribe();
4302
+ if (this.isolatedFormInstance) {
4303
+ this.submitSubject$.unsubscribe();
4304
+ this.dataSubject$.unsubscribe();
4305
+ this.formValidSubject$.unsubscribe();
4306
+ }
4251
4307
  this.fields.forEach(field => field.destroyField());
4252
4308
  }
4253
4309
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "dependencies": {
5
5
  "@gaignoux/currency": "^1.1.0",
6
6
  "credit-card-type": "^10.0.0",