@bolttech/form-engine-core 0.0.1-beta.15 → 0.0.1-beta.17

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,6 +1,6 @@
1
1
  import { Subject, Subscription, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map } from 'rxjs';
2
2
  import creditCardType from 'credit-card-type';
3
- import { isNumber as isNumber$1, isNil, isEqual, get, set } from 'lodash';
3
+ import { isNumber as isNumber$1, isEqual, get, isNil, set } from 'lodash';
4
4
  import { getCurrencySymbol } from '@gaignoux/currency';
5
5
 
6
6
  var TMutationEnum;
@@ -44,6 +44,12 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
44
44
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
45
45
  };
46
46
 
47
+ const DEFAULT_API_DEBOUNCE_TIME = 1000;
48
+ const DEFAULT_STATE_REFRESH_TIME = 100;
49
+ const TEMPLATE_REGEX_DELIMITATOR = /\${(.*?)}/g;
50
+ const TEMPLATE_REGEX_OPERATOR_SPLITTER = /\s*(\|\||&&|!)\s*/g;
51
+ const TEMPLATE_REGEX_OPERATOR_MATCHER = /^\|\||&&|!$/;
52
+
47
53
  /**
48
54
  * Makes an HTTP request using XMLHttpRequest.
49
55
  *
@@ -96,15 +102,17 @@ function makeRequest(method, url, headers, body) {
96
102
  * ```
97
103
  */
98
104
  function extractFieldKeys(expression) {
99
- const regex = /\${(.*?)}/g;
105
+ const regex = TEMPLATE_REGEX_DELIMITATOR;
100
106
  const extractedValues = [];
101
107
  let match;
102
108
  while ((match = regex.exec(expression)) !== null) {
103
109
  extractedValues.push(match[1]);
104
110
  }
111
+ const operatorRegex = TEMPLATE_REGEX_OPERATOR_SPLITTER;
112
+ const splittedString = extractedValues.map(el => el.split(operatorRegex).filter(item => !operatorRegex.test(item))).flat().filter(el => el.split('.').length > 1);
105
113
  return {
106
- originFieldKeys: Array.from(new Set(extractedValues.map(el => el.split('.')[0]))),
107
- originPropertyKeys: Array.from(new Set(extractedValues.map(el => el.split('.')[1])))
114
+ originFieldKeys: Array.from(new Set(splittedString.map(el => el.split('.')[0]))),
115
+ originPropertyKeys: Array.from(new Set(splittedString.map(el => el.split('.')[1])))
108
116
  };
109
117
  }
110
118
  /**
@@ -142,7 +150,7 @@ function traverseObject(obj, path) {
142
150
  if (typeof item === 'object') {
143
151
  result.push(...traverseObject(item, `${path ? `${path}.` : ``}${key}.${index}`));
144
152
  } else if (typeof item === 'string') {
145
- if (String(item).includes('$')) {
153
+ if (String(item).includes('${')) {
146
154
  // const extractedPath = item.replace(/\$|{|}/g, '').split('.');
147
155
  const extractedOriginPath = `${path ? `${path}.` : ``}${key}`.split('.');
148
156
  result.push(Object.assign(Object.assign({
@@ -158,7 +166,7 @@ function traverseObject(obj, path) {
158
166
  } else if (typeof value === 'object') {
159
167
  result.push(...traverseObject(value, `${path ? `${path}.` : ``}${key}`));
160
168
  } else if (typeof value === 'string') {
161
- if (value.includes('$')) {
169
+ if (value.includes('${')) {
162
170
  // const extractedPath = value.replace(/\$|{|}/g, '').split('.');
163
171
  const destinationPath = `${path ? `${path}.` : ``}${key}`.split('.');
164
172
  result.push(Object.assign(Object.assign({
@@ -2249,9 +2257,6 @@ const validations = {
2249
2257
  validDate
2250
2258
  };
2251
2259
 
2252
- const DEFAULT_API_DEBOUNCE_TIME = 1000;
2253
- const DEFAULT_STATE_REFRESH_TIME = 100;
2254
-
2255
2260
  /**
2256
2261
  * Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
2257
2262
  * that were unmounted due to adapter external handling such as visibility
@@ -2298,7 +2303,7 @@ class FormField {
2298
2303
  dataSubject$,
2299
2304
  mapper
2300
2305
  }) {
2301
- var _a, _b, _c, _d, _e, _f, _g, _h;
2306
+ var _a, _b, _c, _d, _e, _f, _g;
2302
2307
  this.fieldStateSubscription$ = new Subscription();
2303
2308
  this.originalSchema = schemaComponent;
2304
2309
  this.config = {
@@ -2326,10 +2331,6 @@ class FormField {
2326
2331
  this.fieldEventSubject$ = fieldEventSubject$;
2327
2332
  this.dataSubject$ = dataSubject$;
2328
2333
  this._props = schemaComponent.props || {};
2329
- this._value = '';
2330
- this._stateValue = ((_c = this.mapper.events) === null || _c === void 0 ? void 0 : _c.setValue) ? {
2331
- [this.mapper.events.setValue]: ''
2332
- } : {};
2333
2334
  this._metadata = '';
2334
2335
  this.errorsString = '';
2335
2336
  this.errorsList = [];
@@ -2337,9 +2338,9 @@ class FormField {
2337
2338
  this._visibility = true;
2338
2339
  this._api = {
2339
2340
  default: {
2340
- response: ((_f = (_e = (_d = this.apiSchema) === null || _d === void 0 ? void 0 : _d.defaultConfig) === null || _e === void 0 ? void 0 : _e.config) === null || _f === void 0 ? void 0 : _f.fallbackValue) || ''
2341
+ 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) || ''
2341
2342
  },
2342
- named: ((_g = this.apiSchema) === null || _g === void 0 ? void 0 : _g.configs) && Object.keys((_h = this.apiSchema) === null || _h === void 0 ? void 0 : _h.configs).reduce((acc, curr) => {
2343
+ 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) => {
2343
2344
  var _a, _b;
2344
2345
  acc[curr] = {
2345
2346
  response: ((_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[curr].config.fallbackValue) || ''
@@ -2351,6 +2352,7 @@ class FormField {
2351
2352
  this._valid = false;
2352
2353
  this._mounted = true;
2353
2354
  this.initializeObservers();
2355
+ this.value = this.initialValue || '';
2354
2356
  }
2355
2357
  /**
2356
2358
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
@@ -2390,12 +2392,6 @@ class FormField {
2390
2392
  }) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
2391
2393
  this.apiRequest(payload);
2392
2394
  });
2393
- if (!isNil(this.initialValue)) {
2394
- this.value = this.initialValue;
2395
- this.setFieldValidity({
2396
- event: 'ON_FORM_SUBMIT'
2397
- });
2398
- }
2399
2395
  }
2400
2396
  /**
2401
2397
  * Retrieves the properties associated with the form field.
@@ -2498,6 +2494,23 @@ class FormField {
2498
2494
  set visibility(visible) {
2499
2495
  if (typeof visible === 'undefined' || visible === this.visibility) return;
2500
2496
  this._visibility = visible;
2497
+ /**
2498
+ * I was sure I would not require to gambiarra, but..
2499
+ * in order to ignore an hidden value on a form submit
2500
+ * or revalidate it when it comes back to visibility
2501
+ * I needed to...
2502
+ * I don't recommend setting private properties like this
2503
+ * this will force the field to be valid when it's hidden
2504
+ * and trigger the validation when it's visible
2505
+ */
2506
+ if (!this.visibility) {
2507
+ this.value = '';
2508
+ this._valid = true;
2509
+ } else {
2510
+ this.setFieldValidity({
2511
+ event: 'ON_FIELD_MOUNT'
2512
+ });
2513
+ }
2501
2514
  this.visibilitySubject$.next(this.visibility);
2502
2515
  this.templateSubject$.next({
2503
2516
  key: this.name,
@@ -2853,7 +2866,6 @@ class FormCore {
2853
2866
  * @param {string} [entry.action] - The action attribute of the form.
2854
2867
  * @param {string} [entry.method] - The method attribute of the form.
2855
2868
  * @param {IFormSchema.iVars} [entry.iVars] - The internal variables of the form.
2856
- * @param {(data: TFormValues) => void} [entry.onSubmit] - A callback function to handle form submission.
2857
2869
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
2858
2870
  */
2859
2871
  constructor(entry) {
@@ -2865,7 +2877,6 @@ class FormCore {
2865
2877
  this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
2866
2878
  this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
2867
2879
  this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
2868
- this.onSubmit = entry.onSubmit;
2869
2880
  this.config = {
2870
2881
  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,
2871
2882
  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
@@ -2878,7 +2889,6 @@ class FormCore {
2878
2889
  this.submitSubject$ = new Subject();
2879
2890
  this.fieldEventSubject$ = new Subject();
2880
2891
  this.dataSubject$ = new Subject();
2881
- this.dataCallbackSubscription$ = new Subscription();
2882
2892
  this.subscribedTemplates = [];
2883
2893
  this.schema && this.serializeStructure(this.schema.components);
2884
2894
  this.schema && this.subscribeTemplates();
@@ -2887,7 +2897,6 @@ class FormCore {
2887
2897
  key: IVARPROPNAME,
2888
2898
  event: 'ON_IVARS'
2889
2899
  });
2890
- entry.onData && this.subscribeData(entry.onData);
2891
2900
  /*
2892
2901
  mount events needs to occur on form level, only when all the fields are instantiated
2893
2902
  is it possible to apply all the side effects that occur globally, same effect occur
@@ -2962,20 +2971,6 @@ class FormCore {
2962
2971
  traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
2963
2972
  });
2964
2973
  }
2965
- /**
2966
- *
2967
- * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
2968
- */
2969
- subscribeData(callback) {
2970
- this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
2971
- key
2972
- }) => ({
2973
- field: key,
2974
- data: this.getFormValues()
2975
- }))).subscribe({
2976
- next: callback
2977
- });
2978
- }
2979
2974
  /**
2980
2975
  * Gets the value of a property from a field.
2981
2976
  *
@@ -3062,17 +3057,17 @@ class FormCore {
3062
3057
  * @returns {string[]} An array of extracted parameters.
3063
3058
  */
3064
3059
  extractParams(expression) {
3065
- const regex = /\${(.*?)}/g;
3060
+ const regex = TEMPLATE_REGEX_DELIMITATOR;
3066
3061
  const extractedValues = [];
3067
3062
  let match;
3068
3063
  while (!isNil(match = regex.exec(expression))) {
3069
3064
  extractedValues.push(match[1]);
3070
3065
  }
3071
- const operatorRegex = /\s*(\|\||&&|!)\s*/g;
3066
+ const operatorRegex = TEMPLATE_REGEX_OPERATOR_SPLITTER;
3072
3067
  const splittedString = extractedValues.map(el => el.split(operatorRegex));
3073
3068
  const result = splittedString.map(splittedStringVal => {
3074
3069
  return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
3075
- if (curr.match(/^\|\||&&|!$/)) {
3070
+ if (curr.match(TEMPLATE_REGEX_OPERATOR_MATCHER)) {
3076
3071
  return `${acc}${curr}`;
3077
3072
  }
3078
3073
  let value;
@@ -3134,7 +3129,7 @@ class FormCore {
3134
3129
  * @returns {string} The expression string with the replacements made.
3135
3130
  */
3136
3131
  replaceExpression(expression, values) {
3137
- const regex = /\${(.*?)}/g;
3132
+ const regex = TEMPLATE_REGEX_DELIMITATOR;
3138
3133
  return expression.replace(regex, () => String(values.shift()) || '');
3139
3134
  }
3140
3135
  /**
@@ -3211,16 +3206,10 @@ class FormCore {
3211
3206
  const error = validations[validationKey](field.value, structElement.validations);
3212
3207
  if (Array.isArray(structElement.fields)) {
3213
3208
  structElement.fields.forEach(fieldKey => {
3214
- if (!this.fields.has(fieldKey)) console.warn(`failed to update visibility onto field ${fieldKey}`);else {
3215
- this.fields.get(fieldKey).visibility = !error;
3216
- if (error) this.fields.get(fieldKey).value = '';
3217
- }
3209
+ if (!this.fields.has(fieldKey)) console.warn(`failed to update visibility onto field ${fieldKey}`);else this.fields.get(fieldKey).visibility = !error;
3218
3210
  });
3219
3211
  } else if (structElement.fields) {
3220
- if (!this.fields.has(structElement.fields)) console.warn(`failed to update visibility onto field ${structElement.fields}`);else {
3221
- this.fields.get(structElement.fields).visibility = !error;
3222
- if (error) this.fields.get(structElement.fields).value = '';
3223
- }
3212
+ if (!this.fields.has(structElement.fields)) console.warn(`failed to update visibility onto field ${structElement.fields}`);else this.fields.get(structElement.fields).visibility = !error;
3224
3213
  }
3225
3214
  });
3226
3215
  });
@@ -3441,6 +3430,27 @@ class FormCore {
3441
3430
  });
3442
3431
  return sub;
3443
3432
  }
3433
+ /**
3434
+ *
3435
+ * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
3436
+ */
3437
+ subscribeData(callback) {
3438
+ const sub = this.dataSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), map(({
3439
+ key
3440
+ }) => ({
3441
+ field: key,
3442
+ data: this.getFormValues()
3443
+ }))).subscribe({
3444
+ next: callback
3445
+ });
3446
+ return sub;
3447
+ }
3448
+ subscribeOnSubmit(callback) {
3449
+ const sub = this.submitSubject$.pipe(map(() => this.getFormValues())).subscribe({
3450
+ next: callback
3451
+ });
3452
+ return sub;
3453
+ }
3444
3454
  /**
3445
3455
  * Submits the form by triggering form field events and invoking the onSubmit callback.
3446
3456
  */
@@ -3453,7 +3463,6 @@ class FormCore {
3453
3463
  if (!this.isValid) return;
3454
3464
  const values = this.getFormValues();
3455
3465
  this.submitSubject$.next(values);
3456
- this.onSubmit && this.onSubmit(values);
3457
3466
  }
3458
3467
  destroy() {
3459
3468
  this.submitSubject$.unsubscribe();
@@ -3604,22 +3613,49 @@ class FormGroup {
3604
3613
  * @param {string[]} indexes form indexes to be submitted
3605
3614
  * @returns
3606
3615
  */
3607
- submitMultipleFormsByIndex(indexes) {
3616
+ submitMultipleFormsByIndex(indexes, callback) {
3608
3617
  let isValid = true;
3609
3618
  let values = {};
3610
3619
  let erroredFields = [];
3611
3620
  indexes.forEach(index => {
3612
- var _a;
3613
- const res = (_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.getFormValues();
3621
+ var _a, _b;
3622
+ (_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.submit();
3623
+ const res = (_b = this.forms.get(index)) === null || _b === void 0 ? void 0 : _b.getFormValues();
3614
3624
  isValid = isValid && ((res === null || res === void 0 ? void 0 : res.isValid) || false);
3615
3625
  values = Object.assign(Object.assign({}, values), (res === null || res === void 0 ? void 0 : res.values) || {});
3616
3626
  erroredFields = [...erroredFields, ...((res === null || res === void 0 ? void 0 : res.erroredFields) || [])];
3617
3627
  });
3618
- return {
3628
+ isValid && callback && callback({
3619
3629
  erroredFields,
3620
3630
  isValid,
3621
3631
  values
3622
- };
3632
+ });
3633
+ }
3634
+ onDataSubscription({
3635
+ ids,
3636
+ callback
3637
+ }) {
3638
+ const subs = ids.reduce((acc, formId) => {
3639
+ var _a;
3640
+ // @TODO add config on debounceTime on this events
3641
+ 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(({
3642
+ key
3643
+ }) => {
3644
+ var _a;
3645
+ return {
3646
+ formField: key,
3647
+ values: (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.getFormValues()
3648
+ };
3649
+ }));
3650
+ if (sub) {
3651
+ acc[formId] = sub;
3652
+ } else {
3653
+ console.warn(`failed to register form id ${formId}`);
3654
+ }
3655
+ return acc;
3656
+ }, {});
3657
+ const sub = combineLatest(subs).subscribe(callback);
3658
+ return sub;
3623
3659
  }
3624
3660
  }
3625
3661
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "0.0.1-beta.15",
3
+ "version": "0.0.1-beta.17",
4
4
  "module": "./index.esm.js",
5
5
  "type": "module",
6
6
  "main": "./index.esm.js",
@@ -1,3 +1,6 @@
1
1
  declare const DEFAULT_API_DEBOUNCE_TIME = 1000;
2
2
  declare const DEFAULT_STATE_REFRESH_TIME = 100;
3
- export { DEFAULT_API_DEBOUNCE_TIME, DEFAULT_STATE_REFRESH_TIME };
3
+ declare const TEMPLATE_REGEX_DELIMITATOR: RegExp;
4
+ declare const TEMPLATE_REGEX_OPERATOR_SPLITTER: RegExp;
5
+ declare const TEMPLATE_REGEX_OPERATOR_MATCHER: RegExp;
6
+ export { DEFAULT_API_DEBOUNCE_TIME, DEFAULT_STATE_REFRESH_TIME, TEMPLATE_REGEX_DELIMITATOR, TEMPLATE_REGEX_OPERATOR_SPLITTER, TEMPLATE_REGEX_OPERATOR_MATCHER };
@@ -24,13 +24,11 @@ declare class FormCore {
24
24
  key: string;
25
25
  event: TEvents;
26
26
  }>;
27
- dataCallbackSubscription$: Subscription;
28
27
  subscribedTemplates: TSubscribedTemplates[];
29
28
  action?: string;
30
29
  method?: string;
31
30
  config: Required<TSchemaFormConfig>;
32
31
  mappers: Map<string, TMapper<unknown>>;
33
- onSubmit?: (data: TFormValues<Record<string, unknown>>) => void;
34
32
  /**
35
33
  * Creates an instance of FormCore.
36
34
  *
@@ -40,7 +38,6 @@ declare class FormCore {
40
38
  * @param {string} [entry.action] - The action attribute of the form.
41
39
  * @param {string} [entry.method] - The method attribute of the form.
42
40
  * @param {IFormSchema.iVars} [entry.iVars] - The internal variables of the form.
43
- * @param {(data: TFormValues) => void} [entry.onSubmit] - A callback function to handle form submission.
44
41
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
45
42
  */
46
43
  constructor(entry: TFormEntry & Omit<IFormSchema, 'components'>);
@@ -66,14 +63,6 @@ declare class FormCore {
66
63
  * Subscribes to templates for dynamic updates.
67
64
  */
68
65
  subscribeTemplates(): void;
69
- /**
70
- *
71
- * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
72
- */
73
- subscribeData(callback: (payload: {
74
- field: string;
75
- data: TFormValues<Record<string, unknown>>;
76
- }) => void): void;
77
66
  /**
78
67
  * Gets the value of a property from a field.
79
68
  *
@@ -220,6 +209,15 @@ declare class FormCore {
220
209
  subscribeFieldEvent({ callback, }: {
221
210
  callback: (payload: TFieldEvent) => void;
222
211
  }): Subscription;
212
+ /**
213
+ *
214
+ * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
215
+ */
216
+ subscribeData(callback: (payload: {
217
+ field: string;
218
+ data: TFormValues<Record<string, unknown>>;
219
+ }) => void): Subscription;
220
+ subscribeOnSubmit(callback: (payload: TFormValues<Record<string, unknown>>) => void): Subscription;
223
221
  /**
224
222
  * Submits the form by triggering form field events and invoking the onSubmit callback.
225
223
  */
@@ -79,7 +79,15 @@ declare class FormGroup {
79
79
  * @param {string[]} indexes form indexes to be submitted
80
80
  * @returns
81
81
  */
82
- submitMultipleFormsByIndex<T>(indexes: string[]): TFormValues<T>;
82
+ submitMultipleFormsByIndex<T>(indexes: string[], callback?: (payload: TFormValues<T>) => void): void;
83
+ onDataSubscription({ ids, callback, }: {
84
+ ids: string[];
85
+ callback: (payload: Record<string, {
86
+ formId: string;
87
+ formField: string;
88
+ values?: TFormValues<Record<string, unknown>>;
89
+ }>) => void;
90
+ }): import("rxjs").Subscription;
83
91
  }
84
92
  type TFormGroup = FormGroup;
85
93
  export { TFormGroup, FormGroup };
@@ -41,11 +41,6 @@ type TFormValues<T> = {
41
41
  */
42
42
  type TFormEntry = Omit<IFormSchema, 'components'> & {
43
43
  schema?: IFormSchema;
44
- onSubmit?: <T>(data: TFormValues<T>) => void;
45
- onData?: <T>(payload: {
46
- field: string;
47
- data: TFormValues<T>;
48
- }) => void;
49
44
  mappers?: TMapper<unknown>[];
50
45
  };
51
46
  export { TFormValues, TFormEntry };