@bolttech/form-engine-core 0.0.1-beta.5 → 0.0.1-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 { Subscription, Subject, combineLatest, startWith, filter, groupBy, mergeMap, debounceTime, map } from 'rxjs';
1
+ import { Subscription, Subject, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map } from 'rxjs';
2
2
  import creditCardType from 'credit-card-type';
3
3
  import { isNumber as isNumber$1, isNil, isEqual, get, set } from 'lodash';
4
4
  import { getCurrencySymbol } from '@gaignoux/currency';
@@ -2256,6 +2256,9 @@ const validations = {
2256
2256
  validDate
2257
2257
  };
2258
2258
 
2259
+ const DEFAULT_API_DEBOUNCE_TIME = 1000;
2260
+ const DEFAULT_STATE_REFRESH_TIME = 100;
2261
+
2259
2262
  /**
2260
2263
  * Represents a form field with observables for managing form state, validations, and API requests.
2261
2264
  */
@@ -2288,9 +2291,10 @@ class FormField {
2288
2291
  }) {
2289
2292
  var _a, _b, _c, _d, _e, _f, _g;
2290
2293
  this.fieldStateSubscription$ = new Subscription();
2294
+ this.originalSchema = schemaComponent;
2291
2295
  this.config = {
2292
- defaultAPIdebounceTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) : 1000,
2293
- defaultStateRefreshTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) : 100
2296
+ 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,
2297
+ 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
2294
2298
  };
2295
2299
  this.name = schemaComponent.name;
2296
2300
  this.component = schemaComponent.component;
@@ -2339,7 +2343,6 @@ class FormField {
2339
2343
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
2340
2344
  */
2341
2345
  initializeObservers() {
2342
- var _a;
2343
2346
  if (!this.valueSubject$ || this.valueSubject$.closed) {
2344
2347
  this.valueSubject$ = new Subject();
2345
2348
  }
@@ -2367,28 +2370,15 @@ class FormField {
2367
2370
  apiResponse: this.apiSubject$.pipe(startWith(this._api)),
2368
2371
  props: this.propsSubject$.pipe(startWith(this._props))
2369
2372
  });
2370
- !this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(this.debounceDistinct(({
2373
+ !this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(groupBy(({
2371
2374
  event
2372
- }) => event, (_a = this.config) === null || _a === void 0 ? void 0 : _a.defaultAPIdebounceTimeMS), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
2375
+ }) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
2373
2376
  this.apiRequest(payload);
2374
2377
  });
2375
2378
  if (!isNil(this.initialValue)) {
2376
2379
  this.value = this.initialValue;
2377
2380
  }
2378
2381
  }
2379
- /**
2380
- * Observable function to emit api events debounced and distinct for each event type,
2381
- * avoiding previous events being cancelled by new events if they occur inside the debounce time interval
2382
- *
2383
- * @param {(event: { event: TEvents }) => TEvents} keyExtractor function that will pass the event key to the groupBy operator
2384
- * @param {number} debounceTimeMs time to wait for each individual event emmited
2385
- * @returns
2386
- */
2387
- debounceDistinct(keyExtractor, debounceTimeMs) {
2388
- return source$ => source$.pipe(groupBy(keyExtractor), mergeMap(group$ => group$.pipe(debounceTime(debounceTimeMs), map(() => ({
2389
- event: group$.key
2390
- })))));
2391
- }
2392
2382
  /**
2393
2383
  * Retrieves the properties associated with the form field.
2394
2384
  *
@@ -2470,14 +2460,6 @@ class FormField {
2470
2460
  this._stateValue = this.maskValue(this.formatValue(val));
2471
2461
  this._metadata = val;
2472
2462
  }
2473
- /*
2474
- update prop value attribute to sync with templating
2475
- currently doesn't need prop Subject emission since it's synced with value
2476
- to avoid excessive prop subject emissions on each keystroke
2477
- */
2478
- if (this.valuePropName) this._props = Object.assign(Object.assign({}, this.props), {
2479
- [this.valuePropName]: this.value
2480
- });
2481
2463
  this.valueSubject$.next(this._stateValue);
2482
2464
  this.templateSubject$.next({
2483
2465
  key: this.name,
@@ -2863,7 +2845,7 @@ class FormCore {
2863
2845
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
2864
2846
  */
2865
2847
  constructor(entry) {
2866
- var _a, _b, _c, _d;
2848
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2867
2849
  this.schema = entry.schema;
2868
2850
  this.fields = new Map();
2869
2851
  this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
@@ -2871,7 +2853,10 @@ class FormCore {
2871
2853
  this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
2872
2854
  this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
2873
2855
  this.onSubmit = entry.onSubmit;
2874
- this.config = entry.config;
2856
+ this.config = {
2857
+ 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,
2858
+ 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
2859
+ };
2875
2860
  this.mappers = entry.mappers;
2876
2861
  this.schema && FormCore.checkIndexes(this.schema.components);
2877
2862
  this.templateSubject$ = new Subject();
@@ -2887,7 +2872,6 @@ class FormCore {
2887
2872
  key: IVARPROPNAME,
2888
2873
  event: 'ON_IVARS'
2889
2874
  });
2890
- // this.fieldEventsSubject$.subscribe(this.refreshApi.bind(this));
2891
2875
  entry.onData && this.subscribeData(entry.onData);
2892
2876
  /*
2893
2877
  mount events needs to occur on form level, only when all the fields are instantiated
@@ -2939,20 +2923,18 @@ class FormCore {
2939
2923
  * Subscribes to templates for dynamic updates.
2940
2924
  */
2941
2925
  subscribeTemplates() {
2942
- /*
2943
- @TODO fix removal of templates of removed fields, they are kept
2944
- tried: this.subscribedTemplates = [] and only stores the last one..
2945
- */
2926
+ this.subscribedTemplates = [];
2946
2927
  this.fields.forEach(({
2947
- component,
2948
- props,
2949
- name,
2950
- validations,
2951
- visibilityConditions,
2952
- resetValues,
2953
- errorMessages,
2954
- apiSchema,
2955
- metadata
2928
+ originalSchema: {
2929
+ component,
2930
+ props,
2931
+ name,
2932
+ validations,
2933
+ visibilityConditions,
2934
+ resetValues,
2935
+ errorMessages,
2936
+ api
2937
+ }
2956
2938
  }, key) => {
2957
2939
  const template = {
2958
2940
  component,
@@ -2962,20 +2944,17 @@ class FormCore {
2962
2944
  visibilityConditions,
2963
2945
  resetValues,
2964
2946
  errorMessages,
2965
- apiSchema,
2966
- metadata
2947
+ apiSchema: api
2967
2948
  };
2968
2949
  traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
2969
2950
  });
2970
- // console.log(subscribedProps);
2971
2951
  }
2972
2952
  /**
2973
2953
  *
2974
2954
  * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
2975
2955
  */
2976
2956
  subscribeData(callback) {
2977
- var _a, _b;
2978
- this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(Number((_a = this.config) === null || _a === void 0 ? void 0 : _a.defaultStateRefreshTimeMS) ? Number((_b = this.config) === null || _b === void 0 ? void 0 : _b.defaultStateRefreshTimeMS) : 100), map(({
2957
+ this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
2979
2958
  key
2980
2959
  }) => ({
2981
2960
  field: key,
@@ -3002,8 +2981,12 @@ class FormCore {
3002
2981
  const value = get(this.iVars, [property, ...path]);
3003
2982
  return value;
3004
2983
  }
3005
- if (!this.fields.has(key)) return console.warn(`failed to get value from ${key}`);
3006
- return path.length > 0 ? get(this.fields.get(key)[property], path) : this.fields.get(key)[property];
2984
+ const field = this.fields.get(key);
2985
+ if (!field) return console.warn(`failed to get value from ${key}`);
2986
+ if (property === 'props' && path[0] === field.valuePropName) {
2987
+ return field.stateValue;
2988
+ }
2989
+ return path.length > 0 ? get(field[property], path) : field[property];
3007
2990
  }
3008
2991
  /**
3009
2992
  * Sets the value of a property in a field.
@@ -3036,7 +3019,6 @@ class FormCore {
3036
3019
  now using key !== originKey, check if any recursion error occurs
3037
3020
  **/
3038
3021
  if (property === 'props' && path[0] === field.valuePropName && key !== originKey) {
3039
- // field.value = value;
3040
3022
  field.emitValue({
3041
3023
  event: 'ON_FIELD_CHANGE',
3042
3024
  value
@@ -3076,7 +3058,6 @@ class FormCore {
3076
3058
  const operatorRegex = /\s*(\|\||&&|!)\s*/g;
3077
3059
  const splittedString = extractedValues.map(el => el.split(operatorRegex));
3078
3060
  const result = splittedString.map(splittedStringVal => {
3079
- // console.log(splittedStringVal)
3080
3061
  return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
3081
3062
  if (curr.match(/^\|\||&&|!$/)) {
3082
3063
  return `${acc}${curr}`;
@@ -3120,7 +3101,6 @@ class FormCore {
3120
3101
  });
3121
3102
  return result.map(el => {
3122
3103
  try {
3123
- // console.log(el);
3124
3104
  return new Function(`return ${el}`)();
3125
3105
  } catch (e) {
3126
3106
  console.log(e);
@@ -3193,34 +3173,6 @@ class FormCore {
3193
3173
  }
3194
3174
  });
3195
3175
  }
3196
- /**
3197
- * Refreshes api observed fields.
3198
- *
3199
- * @param {object} options - Options for refreshing api.
3200
- * @param {string} options.key - The key of the field triggering the update.
3201
- */
3202
- refreshApi({
3203
- key
3204
- }) {
3205
- /*
3206
- global api notifications needs to have field dependency array
3207
- in order to be reliable, disabled for now
3208
- */
3209
- return key;
3210
- // const emmittedFields: string[] = [];
3211
- // this.subscribedTemplates.forEach((template) => {
3212
- // if (
3213
- // template.originFieldKeys.includes(key) &&
3214
- // template.originPropertyKeys.includes('api') &&
3215
- // !emmittedFields.includes(template.destinationKey)
3216
- // ) {
3217
- // emmittedFields.push(template.destinationKey);
3218
- // this.fields
3219
- // .get(template.destinationKey)
3220
- // ?.emitEvents({ event: 'ON_API_RESPONSE' });
3221
- // }
3222
- // });
3223
- }
3224
3176
  /**
3225
3177
  * Validates visibility conditions for a given event and updates field visibility accordingly.
3226
3178
  *
@@ -3291,20 +3243,23 @@ class FormCore {
3291
3243
  *
3292
3244
  * @param fieldSchema
3293
3245
  */
3294
- addField(fieldSchema, mapper) {
3295
- var _a;
3246
+ addField({
3247
+ fieldSchema,
3248
+ mapperElement
3249
+ }) {
3250
+ var _a, _b, _c;
3296
3251
  if (this.fields.has(fieldSchema.name)) {
3297
3252
  throw new Error(`field name ${fieldSchema.name} already defined`);
3298
3253
  }
3299
- const mapperConfig = mapper || this.mappers.find(mapEl => mapEl.componentName === fieldSchema.component);
3300
- if (!mapperConfig) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
3254
+ const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.find(mapEl => mapEl.componentName === fieldSchema.component));
3255
+ if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
3301
3256
  this.fields.set(fieldSchema.name, new FormField({
3302
3257
  schemaComponent: fieldSchema,
3303
- mapper: mapperConfig,
3258
+ mapper,
3304
3259
  children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
3305
3260
  validateVisibility: this.validateVisibility.bind(this),
3306
3261
  resetValue: this.resetValue.bind(this),
3307
- initialValue: (_a = this.initialValues) === null || _a === void 0 ? void 0 : _a[fieldSchema.name],
3262
+ initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
3308
3263
  templateSubject$: this.templateSubject$,
3309
3264
  fieldEventSubject$: this.fieldEventSubject$,
3310
3265
  dataSubject$: this.dataSubject$,
@@ -3315,6 +3270,21 @@ class FormCore {
3315
3270
  event: 'ON_FIELDS',
3316
3271
  key: fieldSchema.name
3317
3272
  });
3273
+ (_c = this.fields.get(fieldSchema.name)) === null || _c === void 0 ? void 0 : _c.emitEvents({
3274
+ event: 'ON_FIELD_MOUNT'
3275
+ });
3276
+ }
3277
+ removeField({
3278
+ key
3279
+ }) {
3280
+ var _a;
3281
+ (_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.destroyField();
3282
+ this.fields.delete(key);
3283
+ this.subscribeTemplates();
3284
+ this.templateSubject$.next({
3285
+ key,
3286
+ event: 'ON_FIELDS'
3287
+ });
3318
3288
  }
3319
3289
  /**
3320
3290
  * Serializes the schema structure to create form fields.
@@ -3325,10 +3295,10 @@ class FormCore {
3325
3295
  serializeStructure(struct, path) {
3326
3296
  if (!struct) return;
3327
3297
  struct.forEach(structElement => {
3328
- var _a, _b;
3298
+ var _a, _b, _c;
3329
3299
  const currField = this.fields.get(structElement.name);
3330
3300
  if (!currField) {
3331
- const mapper = this.mappers.find(mapEl => mapEl.componentName === structElement.component);
3301
+ const mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.find(mapEl => mapEl.componentName === structElement.component);
3332
3302
  if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
3333
3303
  this.fields.set(structElement.name, new FormField({
3334
3304
  schemaComponent: structElement,
@@ -3337,14 +3307,14 @@ class FormCore {
3337
3307
  children: structElement.children ? structElement.children.map(el => el.name) : [],
3338
3308
  validateVisibility: this.validateVisibility.bind(this),
3339
3309
  resetValue: this.resetValue.bind(this),
3340
- initialValue: (_a = this.initialValues) === null || _a === void 0 ? void 0 : _a[structElement.name],
3310
+ initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[structElement.name],
3341
3311
  templateSubject$: this.templateSubject$,
3342
3312
  fieldEventSubject$: this.fieldEventSubject$,
3343
3313
  dataSubject$: this.dataSubject$,
3344
3314
  config: this.config
3345
3315
  }));
3346
3316
  } else {
3347
- 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) || [];
3317
+ 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) || [];
3348
3318
  currField.path = path;
3349
3319
  currField.templateSubject$ = this.templateSubject$;
3350
3320
  }
@@ -3434,10 +3404,9 @@ class FormCore {
3434
3404
  };
3435
3405
  }
3436
3406
  subscribeFieldEvent({
3437
- event,
3438
3407
  callback
3439
3408
  }) {
3440
- const sub = this.fieldEventSubject$.pipe(filter(payload => payload.event === event)).subscribe({
3409
+ const sub = this.fieldEventSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS)))).subscribe({
3441
3410
  next: callback
3442
3411
  });
3443
3412
  return sub;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "0.0.1-beta.5",
3
+ "version": "0.0.1-beta.7",
4
4
  "module": "./index.esm.js",
5
5
  "type": "module",
6
6
  "main": "./index.esm.js",
@@ -0,0 +1,3 @@
1
+ declare const DEFAULT_API_DEBOUNCE_TIME = 1000;
2
+ declare const DEFAULT_STATE_REFRESH_TIME = 100;
3
+ export { DEFAULT_API_DEBOUNCE_TIME, DEFAULT_STATE_REFRESH_TIME };
@@ -12,6 +12,7 @@ declare class FormField {
12
12
  component: string;
13
13
  path?: string;
14
14
  children?: string[];
15
+ originalSchema: IComponentSchema;
15
16
  validations?: TEvent<TValidationMethods>;
16
17
  visibilityConditions?: TVisibility[];
17
18
  resetValues?: TResetValueMethods[];
@@ -103,21 +104,6 @@ declare class FormField {
103
104
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
104
105
  */
105
106
  initializeObservers(): void;
106
- /**
107
- * Observable function to emit api events debounced and distinct for each event type,
108
- * avoiding previous events being cancelled by new events if they occur inside the debounce time interval
109
- *
110
- * @param {(event: { event: TEvents }) => TEvents} keyExtractor function that will pass the event key to the groupBy operator
111
- * @param {number} debounceTimeMs time to wait for each individual event emmited
112
- * @returns
113
- */
114
- debounceDistinct(keyExtractor: (event: {
115
- event: TEvents;
116
- }) => TEvents, debounceTimeMs: number): (source$: Observable<{
117
- event: TEvents;
118
- }>) => Observable<{
119
- event: TEvents;
120
- }>;
121
107
  /**
122
108
  * Retrieves the properties associated with the form field.
123
109
  *
@@ -28,8 +28,8 @@ declare class FormCore {
28
28
  subscribedTemplates: TSubscribedTemplates[];
29
29
  action?: string;
30
30
  method?: string;
31
- config?: TSchemaFormConfig;
32
- mappers: TMapper<unknown>[];
31
+ config: Required<TSchemaFormConfig>;
32
+ mappers?: TMapper<unknown>[];
33
33
  onSubmit?: (data: TFormValues) => void;
34
34
  /**
35
35
  * Creates an instance of FormCore.
@@ -140,15 +140,6 @@ declare class FormCore {
140
140
  key: string;
141
141
  event: TMutationEvents;
142
142
  }): void;
143
- /**
144
- * Refreshes api observed fields.
145
- *
146
- * @param {object} options - Options for refreshing api.
147
- * @param {string} options.key - The key of the field triggering the update.
148
- */
149
- refreshApi({ key }: {
150
- key: string;
151
- }): string;
152
143
  /**
153
144
  * Validates and collects the names of form fields in the provided schema structure.
154
145
  *
@@ -186,7 +177,13 @@ declare class FormCore {
186
177
  *
187
178
  * @param fieldSchema
188
179
  */
189
- addField(fieldSchema: IComponentSchema, mapper?: TMapper<unknown>): void;
180
+ addField({ fieldSchema, mapperElement, }: {
181
+ fieldSchema: IComponentSchema;
182
+ mapperElement?: TMapper<unknown>;
183
+ }): void;
184
+ removeField({ key }: {
185
+ key: string;
186
+ }): void;
190
187
  /**
191
188
  * Serializes the schema structure to create form fields.
192
189
  *
@@ -220,8 +217,7 @@ declare class FormCore {
220
217
  * @returns {TFormValues} The current form values.
221
218
  */
222
219
  getFormValues(): TFormValues;
223
- subscribeFieldEvent({ event, callback, }: {
224
- event: TEvents;
220
+ subscribeFieldEvent({ callback, }: {
225
221
  callback: (payload: TFieldEvent) => void;
226
222
  }): Subscription;
227
223
  /**
@@ -18,7 +18,7 @@ declare class FormGroup {
18
18
  */
19
19
  createFormWithIndex({ index, mappers, }: {
20
20
  index: string;
21
- mappers: TMapper<unknown>[];
21
+ mappers?: TMapper<unknown>[];
22
22
  }): void;
23
23
  /**
24
24
  * Adds a form instance to the form group.
@@ -46,6 +46,6 @@ type TFormEntry = Omit<IFormSchema, 'components'> & {
46
46
  field: string;
47
47
  data: TFormValues;
48
48
  }) => void;
49
- mappers: TMapper<unknown>[];
49
+ mappers?: TMapper<unknown>[];
50
50
  };
51
51
  export { TFormValues, TFormEntry };