@bolttech/form-engine-core 0.0.1-beta.1 → 0.0.1-beta.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.
package/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Subscription, Subject, combineLatest, startWith, 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';
@@ -681,16 +681,9 @@ const currency = (value, masks) => {
681
681
  if (integerPart === '') {
682
682
  integerPart = '0';
683
683
  }
684
- console.log('Before', {
685
- convertedValue
686
- });
687
684
  if (align === 'right' && String(value).endsWith(' ')) {
688
685
  convertedValue = convertedValue.slice(0, -1);
689
686
  }
690
- console.log('After', {
691
- value,
692
- convertedValue
693
- });
694
687
  let newRawValue = integerPart;
695
688
  let decimalPart = convertedValue.slice(convertedValue.length - precision);
696
689
  if (precision > 0) {
@@ -2256,6 +2249,9 @@ const validations = {
2256
2249
  validDate
2257
2250
  };
2258
2251
 
2252
+ const DEFAULT_API_DEBOUNCE_TIME = 1000;
2253
+ const DEFAULT_STATE_REFRESH_TIME = 100;
2254
+
2259
2255
  /**
2260
2256
  * Represents a form field with observables for managing form state, validations, and API requests.
2261
2257
  */
@@ -2265,6 +2261,7 @@ class FormField {
2265
2261
  *
2266
2262
  * @param {object} options - Configuration options for the form field.
2267
2263
  * @param {IComponentSchema} options.schemaComponent - The schema definition for the form field.
2264
+ * @param {TSchemaFormConfig} options.config - The schema default configuration for debounced actions.
2268
2265
  * @param {string} [options.path] - The path within the form field (used internally during recursion).
2269
2266
  * @param {string[]} options.children - An array of children fields names.
2270
2267
  * @param {Function} options.validateVisibility - A function to validate the visibility of the field.
@@ -2274,19 +2271,26 @@ class FormField {
2274
2271
  */
2275
2272
  constructor({
2276
2273
  schemaComponent,
2274
+ config,
2277
2275
  path,
2278
2276
  children,
2279
2277
  validateVisibility,
2280
2278
  resetValue,
2281
2279
  initialValue,
2282
2280
  templateSubject$,
2283
- apiResponseSubject$,
2281
+ fieldEventSubject$,
2284
2282
  dataSubject$,
2285
2283
  mapper
2286
2284
  }) {
2287
2285
  var _a, _b, _c, _d, _e, _f, _g;
2288
2286
  this.fieldStateSubscription$ = new Subscription();
2287
+ this.originalSchema = schemaComponent;
2288
+ this.config = {
2289
+ 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,
2290
+ 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
2291
+ };
2289
2292
  this.name = schemaComponent.name;
2293
+ this.nameToSubmit = schemaComponent.nameToSubmit;
2290
2294
  this.component = schemaComponent.component;
2291
2295
  this.path = path;
2292
2296
  this.children = children;
@@ -2299,17 +2303,20 @@ class FormField {
2299
2303
  this.masks = schemaComponent.masks;
2300
2304
  if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
2301
2305
  if ((_a = mapper.events) === null || _a === void 0 ? void 0 : _a.setValue) this.valuePropName = mapper.events.setValue;
2302
- if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setErrorMessage) this.errorMessagePropName = mapper.events.setErrorMessage;
2303
2306
  this.mapper = mapper;
2304
2307
  this.validateVisibility = validateVisibility;
2305
2308
  this.resetValue = resetValue;
2306
2309
  this.templateSubject$ = templateSubject$;
2307
- this.apiResponseSubject$ = apiResponseSubject$;
2310
+ this.fieldEventSubject$ = fieldEventSubject$;
2308
2311
  this.dataSubject$ = dataSubject$;
2309
2312
  this._props = schemaComponent.props || {};
2310
2313
  this._value = '';
2311
- this._stateValue = '';
2314
+ this._stateValue = ((_b = this.mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) ? {
2315
+ [this.mapper.events.setValue]: ''
2316
+ } : {};
2312
2317
  this._metadata = '';
2318
+ this.errorsString = '';
2319
+ this.errorsList = [];
2313
2320
  this.initialValue = initialValue;
2314
2321
  this._visibility = true;
2315
2322
  this._api = {
@@ -2324,7 +2331,7 @@ class FormField {
2324
2331
  return acc;
2325
2332
  }, {})
2326
2333
  };
2327
- this._errorsString = '';
2334
+ this._errors = {};
2328
2335
  this._valid = false;
2329
2336
  this.initializeObservers();
2330
2337
  }
@@ -2332,6 +2339,7 @@ class FormField {
2332
2339
  * method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
2333
2340
  */
2334
2341
  initializeObservers() {
2342
+ var _a;
2335
2343
  if (!this.valueSubject$ || this.valueSubject$.closed) {
2336
2344
  this.valueSubject$ = new Subject();
2337
2345
  }
@@ -2354,33 +2362,21 @@ class FormField {
2354
2362
  this.apiEventQueueSubject$ = new Subject();
2355
2363
  }
2356
2364
  this.fieldState$ = combineLatest({
2357
- errors: this.errorSubject$.pipe(startWith([])),
2358
2365
  visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
2359
- apiResponse: this.apiSubject$.pipe(startWith(this._api)),
2360
- props: this.propsSubject$.pipe(startWith(this._props))
2366
+ props: this.propsSubject$.pipe(startWith(this._props)),
2367
+ errors: this.errorSubject$.pipe(startWith(Object.assign({}, ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && {
2368
+ [this.mapper.events.setErrorMessage]: this.errorsString
2369
+ })))
2361
2370
  });
2362
- !this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(this.debounceDistinct(({
2371
+ !this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(groupBy(({
2363
2372
  event
2364
- }) => event, 1000)).subscribe(payload => {
2373
+ }) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
2365
2374
  this.apiRequest(payload);
2366
2375
  });
2367
2376
  if (!isNil(this.initialValue)) {
2368
2377
  this.value = this.initialValue;
2369
2378
  }
2370
2379
  }
2371
- /**
2372
- * Observable function to emit api events debounced and distinct for each event type,
2373
- * avoiding previous events being cancelled by new events if they occur inside the debounce time interval
2374
- *
2375
- * @param {(event: { event: TEvents }) => TEvents} keyExtractor function that will pass the event key to the groupBy operator
2376
- * @param {number} debounceTimeMs time to wait for each individual event emmited
2377
- * @returns
2378
- */
2379
- debounceDistinct(keyExtractor, debounceTimeMs) {
2380
- return source$ => source$.pipe(groupBy(keyExtractor), mergeMap(group$ => group$.pipe(debounceTime(debounceTimeMs), map(() => ({
2381
- event: group$.key
2382
- })))));
2383
- }
2384
2380
  /**
2385
2381
  * Retrieves the properties associated with the form field.
2386
2382
  *
@@ -2406,7 +2402,7 @@ class FormField {
2406
2402
  /**
2407
2403
  * Retrieves the current state value of the form field.
2408
2404
  *
2409
- * @returns {unknown} - The current state value of the form field.
2405
+ * @returns {Record<string,unknown>} - The current state value of the form field.
2410
2406
  */
2411
2407
  get stateValue() {
2412
2408
  return this._stateValue;
@@ -2414,14 +2410,6 @@ class FormField {
2414
2410
  get metadata() {
2415
2411
  return this._metadata;
2416
2412
  }
2417
- /**
2418
- * Retrieves the concatenated string of errors associated with the form field.
2419
- *
2420
- * @returns {string} - The concatenated string of errors.
2421
- */
2422
- get errorsString() {
2423
- return this._errorsString;
2424
- }
2425
2413
  /**
2426
2414
  * Retrieves the current value of the form field.
2427
2415
  *
@@ -2436,6 +2424,7 @@ class FormField {
2436
2424
  * @param {unknown} value - The new value to be set.
2437
2425
  */
2438
2426
  set value(value) {
2427
+ var _a, _b, _c;
2439
2428
  /*
2440
2429
  too much unstable, if the valueChangeEvent parses the template event
2441
2430
  value, might occur unexpected results
@@ -2455,22 +2444,19 @@ class FormField {
2455
2444
  if (typeof val === 'undefined' || val === null) return;
2456
2445
  if (typeof val === 'object' && '_value' in val && '_metadata' in val) {
2457
2446
  this._value = this.formatValue(val['_value']);
2458
- this._stateValue = this.maskValue(this.formatValue(val['_value']));
2447
+ this._stateValue = ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setValue) ? {
2448
+ [this.mapper.events.setValue]: this.maskValue(this.formatValue(val['_value']))
2449
+ } : {};
2459
2450
  this._metadata = val._metadata;
2460
2451
  } else {
2461
2452
  this._value = this.formatValue(val);
2462
- this._stateValue = this.maskValue(this.formatValue(val));
2453
+ this._stateValue = ((_b = this.mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) ? {
2454
+ [(_c = this.mapper.events) === null || _c === void 0 ? void 0 : _c.setValue]: this.maskValue(this.formatValue(val))
2455
+ } : {};
2456
+ this.maskValue(this.formatValue(val));
2463
2457
  this._metadata = val;
2464
2458
  }
2465
- /*
2466
- update prop value attribute to sync with templating
2467
- currently doesn't need prop Subject emission since it's synced with value
2468
- to avoid excessive prop subject emissions on each keystroke
2469
- */
2470
- if (this.valuePropName) this._props = Object.assign(Object.assign({}, this.props), {
2471
- [this.valuePropName]: this.value
2472
- });
2473
- this.valueSubject$.next(this._stateValue);
2459
+ this.stateValue && this.valueSubject$.next(this.stateValue);
2474
2460
  this.templateSubject$.next({
2475
2461
  key: this.name,
2476
2462
  event: 'ON_VALUE'
@@ -2520,10 +2506,17 @@ class FormField {
2520
2506
  * @param {TErrorMessages} errors - The new error messages to be set.
2521
2507
  */
2522
2508
  set errors(errors) {
2509
+ var _a;
2523
2510
  if (typeof errors === 'undefined' || isEqual(errors, this.errors)) return;
2524
2511
  this._errors = errors;
2525
- this._errorsString = Object.values(this.errors).join(', ');
2526
- this.errorSubject$.next(Object.values(this.errors));
2512
+ this.errorsList = Object.values(this.errors);
2513
+ this.errorsString = this.errorsList.join(', ');
2514
+ /**
2515
+ * if any error receieves a list of errors, set a prop for it, currently only supporting a single string
2516
+ */
2517
+ ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && this.errorSubject$.next({
2518
+ [this.mapper.events.setErrorMessage]: this.errorsString
2519
+ });
2527
2520
  this.templateSubject$.next({
2528
2521
  key: this.name,
2529
2522
  event: 'ON_PROPS'
@@ -2559,11 +2552,8 @@ class FormField {
2559
2552
  * Mounts the form field by initializing necessary subjects and combining their streams.
2560
2553
  *
2561
2554
  * @param {object} mountOpts - Adapter mount options.
2562
- * @param {string} prop.valuePropName - Adapter value property name.
2563
- * @param {(event: unknown) => unknown} prop.valueChangeEvent - Adapter change event handler function
2564
2555
  * @param {(value: unknown) => unknown} prop.valueSubscription - Adapter value change function
2565
2556
  * @param {(payload: Partial<IState>) => unknown} prop.propsSubscription - Adapter prop change function
2566
- * @param {string} prop.errorMessagePropName - error message property name to set errors onto component
2567
2557
  * @returns {void}
2568
2558
  */
2569
2559
  mountField({
@@ -2614,6 +2604,11 @@ class FormField {
2614
2604
  this.apiEventQueueSubject$.next({
2615
2605
  event
2616
2606
  });
2607
+ this.fieldEventSubject$.next({
2608
+ event,
2609
+ fieldName: this.name,
2610
+ fieldInstance: this
2611
+ });
2617
2612
  }
2618
2613
  /**
2619
2614
  * Sets the validity state of the field based on the provided validation rules and triggers error message updates.
@@ -2639,14 +2634,19 @@ class FormField {
2639
2634
  const errors = Object.assign({}, this.errors);
2640
2635
  const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.config;
2641
2636
  schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
2642
- var _a, _b;
2637
+ var _a;
2643
2638
  const error = validations[validationKey](this.value, schemaValidations);
2644
2639
  // setting valid flag
2645
2640
  valid = !error && valid;
2646
2641
  // setting error messages
2647
2642
  if (((_a = this.validations) === null || _a === void 0 ? void 0 : _a.events.includes(event)) || event === 'ON_FORM_SUBMIT') {
2648
- if (error && ((_b = this.errorMessages) === null || _b === void 0 ? void 0 : _b[validationKey])) {
2649
- errors[validationKey] = this.errorMessages[validationKey];
2643
+ if (error && this.errorMessages) {
2644
+ if (validationKey in this.errorMessages) {
2645
+ const messages = this.errorMessages;
2646
+ errors[validationKey] = messages[validationKey];
2647
+ } else if ('default' in this.errorMessages) {
2648
+ errors[validationKey] = this.errorMessages.default;
2649
+ }
2650
2650
  } else {
2651
2651
  delete errors[validationKey];
2652
2652
  }
@@ -2654,10 +2654,19 @@ class FormField {
2654
2654
  });
2655
2655
  this._valid = valid;
2656
2656
  this.errors = errors;
2657
- // remove later
2658
- if (this.errorMessagePropName) this.props = Object.assign(Object.assign({}, this.props), {
2659
- [this.errorMessagePropName]: this.errorsString
2657
+ }
2658
+ /**
2659
+ * WIP expensive function to get updated field validity on each event
2660
+ */
2661
+ updateValidityFlag() {
2662
+ var _a;
2663
+ let valid = true;
2664
+ const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.config;
2665
+ schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
2666
+ const error = validations[validationKey](this.value, schemaValidations);
2667
+ valid = !error && valid;
2660
2668
  });
2669
+ this._valid = valid;
2661
2670
  }
2662
2671
  /**
2663
2672
  * Formats the field value using the specified formatters, if available.
@@ -2802,7 +2811,7 @@ class FormField {
2802
2811
  * @returns {void}
2803
2812
  */
2804
2813
  subscribeState(callback) {
2805
- this.fieldStateSubscription$ = this.fieldState$.pipe(debounceTime(100)).subscribe({
2814
+ this.fieldStateSubscription$ = this.fieldState$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS)).subscribe({
2806
2815
  next: callback
2807
2816
  });
2808
2817
  }
@@ -2837,7 +2846,7 @@ class FormCore {
2837
2846
  * @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
2838
2847
  */
2839
2848
  constructor(entry) {
2840
- var _a, _b, _c, _d;
2849
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2841
2850
  this.schema = entry.schema;
2842
2851
  this.fields = new Map();
2843
2852
  this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
@@ -2845,11 +2854,15 @@ class FormCore {
2845
2854
  this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
2846
2855
  this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
2847
2856
  this.onSubmit = entry.onSubmit;
2857
+ this.config = {
2858
+ 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,
2859
+ 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
2860
+ };
2848
2861
  this.mappers = entry.mappers;
2849
2862
  this.schema && FormCore.checkIndexes(this.schema.components);
2850
2863
  this.templateSubject$ = new Subject();
2851
2864
  this.submitSubject$ = new Subject();
2852
- this.apiResponseSubject$ = new Subject();
2865
+ this.fieldEventSubject$ = new Subject();
2853
2866
  this.dataSubject$ = new Subject();
2854
2867
  this.dataCallbackSubscription$ = new Subscription();
2855
2868
  this.subscribedTemplates = [];
@@ -2860,7 +2873,6 @@ class FormCore {
2860
2873
  key: IVARPROPNAME,
2861
2874
  event: 'ON_IVARS'
2862
2875
  });
2863
- this.apiResponseSubject$.subscribe(this.refreshApi.bind(this));
2864
2876
  entry.onData && this.subscribeData(entry.onData);
2865
2877
  /*
2866
2878
  mount events needs to occur on form level, only when all the fields are instantiated
@@ -2912,20 +2924,18 @@ class FormCore {
2912
2924
  * Subscribes to templates for dynamic updates.
2913
2925
  */
2914
2926
  subscribeTemplates() {
2915
- /*
2916
- @TODO fix removal of templates of removed fields, they are kept
2917
- tried: this.subscribedTemplates = [] and only stores the last one..
2918
- */
2927
+ this.subscribedTemplates = [];
2919
2928
  this.fields.forEach(({
2920
- component,
2921
- props,
2922
- name,
2923
- validations,
2924
- visibilityConditions,
2925
- resetValues,
2926
- errorMessages,
2927
- apiSchema,
2928
- metadata
2929
+ originalSchema: {
2930
+ component,
2931
+ props,
2932
+ name,
2933
+ validations,
2934
+ visibilityConditions,
2935
+ resetValues,
2936
+ errorMessages,
2937
+ api
2938
+ }
2929
2939
  }, key) => {
2930
2940
  const template = {
2931
2941
  component,
@@ -2935,19 +2945,17 @@ class FormCore {
2935
2945
  visibilityConditions,
2936
2946
  resetValues,
2937
2947
  errorMessages,
2938
- apiSchema,
2939
- metadata
2948
+ apiSchema: api
2940
2949
  };
2941
2950
  traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
2942
2951
  });
2943
- // console.log(subscribedProps);
2944
2952
  }
2945
2953
  /**
2946
2954
  *
2947
2955
  * @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
2948
2956
  */
2949
2957
  subscribeData(callback) {
2950
- this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(100), map(({
2958
+ this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
2951
2959
  key
2952
2960
  }) => ({
2953
2961
  field: key,
@@ -2974,8 +2982,12 @@ class FormCore {
2974
2982
  const value = get(this.iVars, [property, ...path]);
2975
2983
  return value;
2976
2984
  }
2977
- if (!this.fields.has(key)) return console.warn(`failed to get value from ${key}`);
2978
- return path.length > 0 ? get(this.fields.get(key)[property], path) : this.fields.get(key)[property];
2985
+ const field = this.fields.get(key);
2986
+ if (!field) return console.warn(`failed to get value from ${key}`);
2987
+ if (property === 'props' && path[0] === field.valuePropName) {
2988
+ return field.stateValue;
2989
+ }
2990
+ return path.length > 0 ? get(field[property], path) : field[property];
2979
2991
  }
2980
2992
  /**
2981
2993
  * Sets the value of a property in a field.
@@ -3008,7 +3020,6 @@ class FormCore {
3008
3020
  now using key !== originKey, check if any recursion error occurs
3009
3021
  **/
3010
3022
  if (property === 'props' && path[0] === field.valuePropName && key !== originKey) {
3011
- // field.value = value;
3012
3023
  field.emitValue({
3013
3024
  event: 'ON_FIELD_CHANGE',
3014
3025
  value
@@ -3048,7 +3059,6 @@ class FormCore {
3048
3059
  const operatorRegex = /\s*(\|\||&&|!)\s*/g;
3049
3060
  const splittedString = extractedValues.map(el => el.split(operatorRegex));
3050
3061
  const result = splittedString.map(splittedStringVal => {
3051
- // console.log(splittedStringVal)
3052
3062
  return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
3053
3063
  if (curr.match(/^\|\||&&|!$/)) {
3054
3064
  return `${acc}${curr}`;
@@ -3092,7 +3102,6 @@ class FormCore {
3092
3102
  });
3093
3103
  return result.map(el => {
3094
3104
  try {
3095
- // console.log(el);
3096
3105
  return new Function(`return ${el}`)();
3097
3106
  } catch (e) {
3098
3107
  console.log(e);
@@ -3165,34 +3174,6 @@ class FormCore {
3165
3174
  }
3166
3175
  });
3167
3176
  }
3168
- /**
3169
- * Refreshes api observed fields.
3170
- *
3171
- * @param {object} options - Options for refreshing api.
3172
- * @param {string} options.key - The key of the field triggering the update.
3173
- */
3174
- refreshApi({
3175
- key
3176
- }) {
3177
- /*
3178
- global api notifications needs to have field dependency array
3179
- in order to be reliable, disabled for now
3180
- */
3181
- return key;
3182
- // const emmittedFields: string[] = [];
3183
- // this.subscribedTemplates.forEach((template) => {
3184
- // if (
3185
- // template.originFieldKeys.includes(key) &&
3186
- // template.originPropertyKeys.includes('api') &&
3187
- // !emmittedFields.includes(template.destinationKey)
3188
- // ) {
3189
- // emmittedFields.push(template.destinationKey);
3190
- // this.fields
3191
- // .get(template.destinationKey)
3192
- // ?.emitEvents({ event: 'ON_API_RESPONSE' });
3193
- // }
3194
- // });
3195
- }
3196
3177
  /**
3197
3178
  * Validates visibility conditions for a given event and updates field visibility accordingly.
3198
3179
  *
@@ -3258,6 +3239,54 @@ class FormCore {
3258
3239
  });
3259
3240
  });
3260
3241
  }
3242
+ /**
3243
+ * Adds a field onto the form instance regardless there is a schema or not
3244
+ *
3245
+ * @param fieldSchema
3246
+ */
3247
+ addField({
3248
+ fieldSchema,
3249
+ mapperElement
3250
+ }) {
3251
+ var _a, _b, _c;
3252
+ if (this.fields.has(fieldSchema.name)) {
3253
+ throw new Error(`field name ${fieldSchema.name} already defined`);
3254
+ }
3255
+ const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.find(mapEl => mapEl.componentName === fieldSchema.component));
3256
+ if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
3257
+ this.fields.set(fieldSchema.name, new FormField({
3258
+ schemaComponent: fieldSchema,
3259
+ mapper,
3260
+ children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
3261
+ validateVisibility: this.validateVisibility.bind(this),
3262
+ resetValue: this.resetValue.bind(this),
3263
+ initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
3264
+ templateSubject$: this.templateSubject$,
3265
+ fieldEventSubject$: this.fieldEventSubject$,
3266
+ dataSubject$: this.dataSubject$,
3267
+ config: this.config
3268
+ }));
3269
+ this.subscribeTemplates();
3270
+ this.refreshTemplates({
3271
+ event: 'ON_FIELDS',
3272
+ key: fieldSchema.name
3273
+ });
3274
+ (_c = this.fields.get(fieldSchema.name)) === null || _c === void 0 ? void 0 : _c.emitEvents({
3275
+ event: 'ON_FIELD_MOUNT'
3276
+ });
3277
+ }
3278
+ removeField({
3279
+ key
3280
+ }) {
3281
+ var _a;
3282
+ (_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.destroyField();
3283
+ this.fields.delete(key);
3284
+ this.subscribeTemplates();
3285
+ this.templateSubject$.next({
3286
+ key,
3287
+ event: 'ON_FIELDS'
3288
+ });
3289
+ }
3261
3290
  /**
3262
3291
  * Serializes the schema structure to create form fields.
3263
3292
  *
@@ -3267,10 +3296,10 @@ class FormCore {
3267
3296
  serializeStructure(struct, path) {
3268
3297
  if (!struct) return;
3269
3298
  struct.forEach(structElement => {
3270
- var _a, _b;
3299
+ var _a, _b, _c;
3271
3300
  const currField = this.fields.get(structElement.name);
3272
3301
  if (!currField) {
3273
- const mapper = this.mappers.find(mapEl => mapEl.componentName === structElement.component);
3302
+ const mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.find(mapEl => mapEl.componentName === structElement.component);
3274
3303
  if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
3275
3304
  this.fields.set(structElement.name, new FormField({
3276
3305
  schemaComponent: structElement,
@@ -3279,13 +3308,14 @@ class FormCore {
3279
3308
  children: structElement.children ? structElement.children.map(el => el.name) : [],
3280
3309
  validateVisibility: this.validateVisibility.bind(this),
3281
3310
  resetValue: this.resetValue.bind(this),
3282
- initialValue: (_a = this.initialValues) === null || _a === void 0 ? void 0 : _a[structElement.name],
3311
+ initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[structElement.name],
3283
3312
  templateSubject$: this.templateSubject$,
3284
- apiResponseSubject$: this.apiResponseSubject$,
3285
- dataSubject$: this.dataSubject$
3313
+ fieldEventSubject$: this.fieldEventSubject$,
3314
+ dataSubject$: this.dataSubject$,
3315
+ config: this.config
3286
3316
  }));
3287
3317
  } else {
3288
- 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) || [];
3318
+ 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) || [];
3289
3319
  currField.path = path;
3290
3320
  currField.templateSubject$ = this.templateSubject$;
3291
3321
  }
@@ -3347,10 +3377,9 @@ class FormCore {
3347
3377
  const values = {};
3348
3378
  this.fields.forEach((val, key) => {
3349
3379
  if (val.value) {
3350
- values[key] = val.value;
3380
+ set(values, val.nameToSubmit || key, val.value);
3351
3381
  }
3352
3382
  });
3353
- console.log(values);
3354
3383
  }
3355
3384
  /**
3356
3385
  * Gets the current values of all form fields.
@@ -3362,7 +3391,7 @@ class FormCore {
3362
3391
  const erroredFields = [];
3363
3392
  this.fields.forEach((val, key) => {
3364
3393
  if (val.value) {
3365
- values[key] = val.value;
3394
+ set(values, val.nameToSubmit || key, val.value);
3366
3395
  }
3367
3396
  if (!val.valid) {
3368
3397
  erroredFields.push(key);
@@ -3374,6 +3403,14 @@ class FormCore {
3374
3403
  isValid: this.isValid
3375
3404
  };
3376
3405
  }
3406
+ subscribeFieldEvent({
3407
+ callback
3408
+ }) {
3409
+ const sub = this.fieldEventSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS)))).subscribe({
3410
+ next: callback
3411
+ });
3412
+ return sub;
3413
+ }
3377
3414
  /**
3378
3415
  * Submits the form by triggering form field events and invoking the onSubmit callback.
3379
3416
  */
@@ -3391,8 +3428,9 @@ class FormCore {
3391
3428
  destroy() {
3392
3429
  this.submitSubject$.unsubscribe();
3393
3430
  this.templateSubject$.unsubscribe();
3394
- this.apiResponseSubject$.unsubscribe();
3431
+ this.fieldEventSubject$.unsubscribe();
3395
3432
  this.dataSubject$.unsubscribe();
3433
+ this.fields.forEach(field => field.destroyField());
3396
3434
  }
3397
3435
  }
3398
3436
  /**
@@ -3429,6 +3467,25 @@ class FormGroup {
3429
3467
  constructor() {
3430
3468
  this.forms = new Map();
3431
3469
  }
3470
+ /**
3471
+ * Creates an empty form with given index
3472
+ *
3473
+ * @param {string} options.index
3474
+ * @param {TMapper<unknown>} options.mappers
3475
+ */
3476
+ createFormWithIndex({
3477
+ index,
3478
+ mappers
3479
+ }) {
3480
+ const formInstance = new FormCore({
3481
+ index,
3482
+ mappers
3483
+ });
3484
+ this.addForm({
3485
+ key: index,
3486
+ formInstance
3487
+ });
3488
+ }
3432
3489
  /**
3433
3490
  * Adds a form instance to the form group.
3434
3491
  *
@@ -3471,6 +3528,20 @@ class FormGroup {
3471
3528
  (_a = this.forms.get(key)) === null || _a === void 0 ? void 0 : _a.destroy();
3472
3529
  this.forms.delete(key);
3473
3530
  }
3531
+ /**
3532
+ * removes a field given a form and field index
3533
+ *
3534
+ * @param {string} options.formIndex
3535
+ * @param {string} options.fieldIndex
3536
+ */
3537
+ removeField({
3538
+ formIndex,
3539
+ fieldIndex
3540
+ }) {
3541
+ var _a, _b, _c;
3542
+ (_b = (_a = this.forms.get(formIndex)) === null || _a === void 0 ? void 0 : _a.fields.get(fieldIndex)) === null || _b === void 0 ? void 0 : _b.destroyField();
3543
+ (_c = this.forms.get(formIndex)) === null || _c === void 0 ? void 0 : _c.fields.delete(fieldIndex);
3544
+ }
3474
3545
  /**
3475
3546
  * Checks if the specified key already exists in the form group.
3476
3547
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "0.0.1-beta.1",
3
+ "version": "0.0.1-beta.10",
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 };