@bolttech/form-engine-core 0.0.1-beta.1 → 0.0.1-beta.11
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/README.md +1616 -0
- package/index.esm.js +229 -137
- package/package.json +2 -2
- package/src/constants/constants.d.ts +3 -0
- package/src/interfaces/schema.d.ts +12 -2
- package/src/interfaces/state.d.ts +1 -6
- package/src/managers/field.d.ts +25 -47
- package/src/managers/form.d.ts +29 -23
- package/src/managers/formGroup.d.ts +23 -2
- package/src/managers/index.d.ts +3 -0
- package/src/types/event.d.ts +7 -1
- package/src/types/form.d.ts +9 -9
- package/src/types/schema.d.ts +13 -2
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
2360
|
-
|
|
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(
|
|
2371
|
+
!this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(groupBy(({
|
|
2363
2372
|
event
|
|
2364
|
-
}) => event,
|
|
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 =
|
|
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 =
|
|
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.
|
|
2526
|
-
this.
|
|
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
|
|
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 &&
|
|
2649
|
-
|
|
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
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
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(
|
|
2814
|
+
this.fieldStateSubscription$ = this.fieldState$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS)).subscribe({
|
|
2806
2815
|
next: callback
|
|
2807
2816
|
});
|
|
2808
2817
|
}
|
|
@@ -2837,7 +2846,8 @@ 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, _j;
|
|
2850
|
+
this.mappers = new Map();
|
|
2841
2851
|
this.schema = entry.schema;
|
|
2842
2852
|
this.fields = new Map();
|
|
2843
2853
|
this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
|
|
@@ -2845,11 +2855,17 @@ class FormCore {
|
|
|
2845
2855
|
this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
|
|
2846
2856
|
this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
|
|
2847
2857
|
this.onSubmit = entry.onSubmit;
|
|
2848
|
-
this.
|
|
2858
|
+
this.config = {
|
|
2859
|
+
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,
|
|
2860
|
+
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
|
|
2861
|
+
};
|
|
2862
|
+
(_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
|
|
2863
|
+
this.mappers.set(mapper.componentName, mapper);
|
|
2864
|
+
});
|
|
2849
2865
|
this.schema && FormCore.checkIndexes(this.schema.components);
|
|
2850
2866
|
this.templateSubject$ = new Subject();
|
|
2851
2867
|
this.submitSubject$ = new Subject();
|
|
2852
|
-
this.
|
|
2868
|
+
this.fieldEventSubject$ = new Subject();
|
|
2853
2869
|
this.dataSubject$ = new Subject();
|
|
2854
2870
|
this.dataCallbackSubscription$ = new Subscription();
|
|
2855
2871
|
this.subscribedTemplates = [];
|
|
@@ -2860,7 +2876,6 @@ class FormCore {
|
|
|
2860
2876
|
key: IVARPROPNAME,
|
|
2861
2877
|
event: 'ON_IVARS'
|
|
2862
2878
|
});
|
|
2863
|
-
this.apiResponseSubject$.subscribe(this.refreshApi.bind(this));
|
|
2864
2879
|
entry.onData && this.subscribeData(entry.onData);
|
|
2865
2880
|
/*
|
|
2866
2881
|
mount events needs to occur on form level, only when all the fields are instantiated
|
|
@@ -2912,20 +2927,18 @@ class FormCore {
|
|
|
2912
2927
|
* Subscribes to templates for dynamic updates.
|
|
2913
2928
|
*/
|
|
2914
2929
|
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
|
-
*/
|
|
2930
|
+
this.subscribedTemplates = [];
|
|
2919
2931
|
this.fields.forEach(({
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2932
|
+
originalSchema: {
|
|
2933
|
+
component,
|
|
2934
|
+
props,
|
|
2935
|
+
name,
|
|
2936
|
+
validations,
|
|
2937
|
+
visibilityConditions,
|
|
2938
|
+
resetValues,
|
|
2939
|
+
errorMessages,
|
|
2940
|
+
api
|
|
2941
|
+
}
|
|
2929
2942
|
}, key) => {
|
|
2930
2943
|
const template = {
|
|
2931
2944
|
component,
|
|
@@ -2935,19 +2948,17 @@ class FormCore {
|
|
|
2935
2948
|
visibilityConditions,
|
|
2936
2949
|
resetValues,
|
|
2937
2950
|
errorMessages,
|
|
2938
|
-
apiSchema
|
|
2939
|
-
metadata
|
|
2951
|
+
apiSchema: api
|
|
2940
2952
|
};
|
|
2941
2953
|
traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
|
|
2942
2954
|
});
|
|
2943
|
-
// console.log(subscribedProps);
|
|
2944
2955
|
}
|
|
2945
2956
|
/**
|
|
2946
2957
|
*
|
|
2947
2958
|
* @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
|
|
2948
2959
|
*/
|
|
2949
2960
|
subscribeData(callback) {
|
|
2950
|
-
this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(
|
|
2961
|
+
this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
|
|
2951
2962
|
key
|
|
2952
2963
|
}) => ({
|
|
2953
2964
|
field: key,
|
|
@@ -2974,8 +2985,12 @@ class FormCore {
|
|
|
2974
2985
|
const value = get(this.iVars, [property, ...path]);
|
|
2975
2986
|
return value;
|
|
2976
2987
|
}
|
|
2977
|
-
|
|
2978
|
-
return
|
|
2988
|
+
const field = this.fields.get(key);
|
|
2989
|
+
if (!field) return console.warn(`failed to get value from ${key}`);
|
|
2990
|
+
if (property === 'props' && path[0] === field.valuePropName) {
|
|
2991
|
+
return field.stateValue;
|
|
2992
|
+
}
|
|
2993
|
+
return path.length > 0 ? get(field[property], path) : field[property];
|
|
2979
2994
|
}
|
|
2980
2995
|
/**
|
|
2981
2996
|
* Sets the value of a property in a field.
|
|
@@ -3008,7 +3023,6 @@ class FormCore {
|
|
|
3008
3023
|
now using key !== originKey, check if any recursion error occurs
|
|
3009
3024
|
**/
|
|
3010
3025
|
if (property === 'props' && path[0] === field.valuePropName && key !== originKey) {
|
|
3011
|
-
// field.value = value;
|
|
3012
3026
|
field.emitValue({
|
|
3013
3027
|
event: 'ON_FIELD_CHANGE',
|
|
3014
3028
|
value
|
|
@@ -3048,7 +3062,6 @@ class FormCore {
|
|
|
3048
3062
|
const operatorRegex = /\s*(\|\||&&|!)\s*/g;
|
|
3049
3063
|
const splittedString = extractedValues.map(el => el.split(operatorRegex));
|
|
3050
3064
|
const result = splittedString.map(splittedStringVal => {
|
|
3051
|
-
// console.log(splittedStringVal)
|
|
3052
3065
|
return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
|
|
3053
3066
|
if (curr.match(/^\|\||&&|!$/)) {
|
|
3054
3067
|
return `${acc}${curr}`;
|
|
@@ -3081,6 +3094,11 @@ class FormCore {
|
|
|
3081
3094
|
case 'object':
|
|
3082
3095
|
if (currValue === null) {
|
|
3083
3096
|
value = null;
|
|
3097
|
+
break;
|
|
3098
|
+
}
|
|
3099
|
+
if (currValue instanceof Date) {
|
|
3100
|
+
value = `new Date(\`${currValue}\`)`;
|
|
3101
|
+
break;
|
|
3084
3102
|
}
|
|
3085
3103
|
value = JSON.stringify(currValue);
|
|
3086
3104
|
break;
|
|
@@ -3092,7 +3110,6 @@ class FormCore {
|
|
|
3092
3110
|
});
|
|
3093
3111
|
return result.map(el => {
|
|
3094
3112
|
try {
|
|
3095
|
-
// console.log(el);
|
|
3096
3113
|
return new Function(`return ${el}`)();
|
|
3097
3114
|
} catch (e) {
|
|
3098
3115
|
console.log(e);
|
|
@@ -3109,7 +3126,7 @@ class FormCore {
|
|
|
3109
3126
|
*/
|
|
3110
3127
|
replaceExpression(expression, values) {
|
|
3111
3128
|
const regex = /\${(.*?)}/g;
|
|
3112
|
-
return expression.replace(regex, () => values.shift() || '');
|
|
3129
|
+
return expression.replace(regex, () => String(values.shift()) || '');
|
|
3113
3130
|
}
|
|
3114
3131
|
/**
|
|
3115
3132
|
* Checks if an expression string contains string concatenation within a marked expression.
|
|
@@ -3165,34 +3182,6 @@ class FormCore {
|
|
|
3165
3182
|
}
|
|
3166
3183
|
});
|
|
3167
3184
|
}
|
|
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
3185
|
/**
|
|
3197
3186
|
* Validates visibility conditions for a given event and updates field visibility accordingly.
|
|
3198
3187
|
*
|
|
@@ -3258,6 +3247,54 @@ class FormCore {
|
|
|
3258
3247
|
});
|
|
3259
3248
|
});
|
|
3260
3249
|
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Adds a field onto the form instance regardless there is a schema or not
|
|
3252
|
+
*
|
|
3253
|
+
* @param fieldSchema
|
|
3254
|
+
*/
|
|
3255
|
+
addField({
|
|
3256
|
+
fieldSchema,
|
|
3257
|
+
mapperElement
|
|
3258
|
+
}) {
|
|
3259
|
+
var _a, _b, _c;
|
|
3260
|
+
if (this.fields.has(fieldSchema.name)) {
|
|
3261
|
+
throw new Error(`field name ${fieldSchema.name} already defined`);
|
|
3262
|
+
}
|
|
3263
|
+
const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(fieldSchema.component));
|
|
3264
|
+
if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
|
|
3265
|
+
this.fields.set(fieldSchema.name, new FormField({
|
|
3266
|
+
schemaComponent: fieldSchema,
|
|
3267
|
+
mapper,
|
|
3268
|
+
children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
|
|
3269
|
+
validateVisibility: this.validateVisibility.bind(this),
|
|
3270
|
+
resetValue: this.resetValue.bind(this),
|
|
3271
|
+
initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
|
|
3272
|
+
templateSubject$: this.templateSubject$,
|
|
3273
|
+
fieldEventSubject$: this.fieldEventSubject$,
|
|
3274
|
+
dataSubject$: this.dataSubject$,
|
|
3275
|
+
config: this.config
|
|
3276
|
+
}));
|
|
3277
|
+
this.subscribeTemplates();
|
|
3278
|
+
this.refreshTemplates({
|
|
3279
|
+
event: 'ON_FIELDS',
|
|
3280
|
+
key: fieldSchema.name
|
|
3281
|
+
});
|
|
3282
|
+
(_c = this.fields.get(fieldSchema.name)) === null || _c === void 0 ? void 0 : _c.emitEvents({
|
|
3283
|
+
event: 'ON_FIELD_MOUNT'
|
|
3284
|
+
});
|
|
3285
|
+
}
|
|
3286
|
+
removeField({
|
|
3287
|
+
key
|
|
3288
|
+
}) {
|
|
3289
|
+
var _a;
|
|
3290
|
+
(_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.destroyField();
|
|
3291
|
+
this.fields.delete(key);
|
|
3292
|
+
this.subscribeTemplates();
|
|
3293
|
+
this.templateSubject$.next({
|
|
3294
|
+
key,
|
|
3295
|
+
event: 'ON_FIELDS'
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3261
3298
|
/**
|
|
3262
3299
|
* Serializes the schema structure to create form fields.
|
|
3263
3300
|
*
|
|
@@ -3267,10 +3304,15 @@ class FormCore {
|
|
|
3267
3304
|
serializeStructure(struct, path) {
|
|
3268
3305
|
if (!struct) return;
|
|
3269
3306
|
struct.forEach(structElement => {
|
|
3270
|
-
var _a, _b;
|
|
3307
|
+
var _a, _b, _c;
|
|
3271
3308
|
const currField = this.fields.get(structElement.name);
|
|
3272
3309
|
if (!currField) {
|
|
3273
|
-
|
|
3310
|
+
let mapper;
|
|
3311
|
+
if (structElement === null || structElement === void 0 ? void 0 : structElement.mapper) {
|
|
3312
|
+
mapper = structElement === null || structElement === void 0 ? void 0 : structElement.mapper;
|
|
3313
|
+
} else {
|
|
3314
|
+
mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(structElement.component);
|
|
3315
|
+
}
|
|
3274
3316
|
if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
|
|
3275
3317
|
this.fields.set(structElement.name, new FormField({
|
|
3276
3318
|
schemaComponent: structElement,
|
|
@@ -3279,18 +3321,20 @@ class FormCore {
|
|
|
3279
3321
|
children: structElement.children ? structElement.children.map(el => el.name) : [],
|
|
3280
3322
|
validateVisibility: this.validateVisibility.bind(this),
|
|
3281
3323
|
resetValue: this.resetValue.bind(this),
|
|
3282
|
-
initialValue: (
|
|
3324
|
+
initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[structElement.name],
|
|
3283
3325
|
templateSubject$: this.templateSubject$,
|
|
3284
|
-
|
|
3285
|
-
dataSubject$: this.dataSubject
|
|
3326
|
+
fieldEventSubject$: this.fieldEventSubject$,
|
|
3327
|
+
dataSubject$: this.dataSubject$,
|
|
3328
|
+
config: this.config
|
|
3286
3329
|
}));
|
|
3287
3330
|
} else {
|
|
3288
|
-
currField.children = ((
|
|
3331
|
+
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
3332
|
currField.path = path;
|
|
3333
|
+
currField.originalSchema = structElement;
|
|
3290
3334
|
currField.templateSubject$ = this.templateSubject$;
|
|
3291
3335
|
}
|
|
3292
3336
|
if (structElement.children) {
|
|
3293
|
-
|
|
3337
|
+
this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
|
|
3294
3338
|
}
|
|
3295
3339
|
});
|
|
3296
3340
|
}
|
|
@@ -3347,10 +3391,9 @@ class FormCore {
|
|
|
3347
3391
|
const values = {};
|
|
3348
3392
|
this.fields.forEach((val, key) => {
|
|
3349
3393
|
if (val.value) {
|
|
3350
|
-
values
|
|
3394
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3351
3395
|
}
|
|
3352
3396
|
});
|
|
3353
|
-
console.log(values);
|
|
3354
3397
|
}
|
|
3355
3398
|
/**
|
|
3356
3399
|
* Gets the current values of all form fields.
|
|
@@ -3362,7 +3405,7 @@ class FormCore {
|
|
|
3362
3405
|
const erroredFields = [];
|
|
3363
3406
|
this.fields.forEach((val, key) => {
|
|
3364
3407
|
if (val.value) {
|
|
3365
|
-
values
|
|
3408
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3366
3409
|
}
|
|
3367
3410
|
if (!val.valid) {
|
|
3368
3411
|
erroredFields.push(key);
|
|
@@ -3374,6 +3417,14 @@ class FormCore {
|
|
|
3374
3417
|
isValid: this.isValid
|
|
3375
3418
|
};
|
|
3376
3419
|
}
|
|
3420
|
+
subscribeFieldEvent({
|
|
3421
|
+
callback
|
|
3422
|
+
}) {
|
|
3423
|
+
const sub = this.fieldEventSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS)))).subscribe({
|
|
3424
|
+
next: callback
|
|
3425
|
+
});
|
|
3426
|
+
return sub;
|
|
3427
|
+
}
|
|
3377
3428
|
/**
|
|
3378
3429
|
* Submits the form by triggering form field events and invoking the onSubmit callback.
|
|
3379
3430
|
*/
|
|
@@ -3391,8 +3442,9 @@ class FormCore {
|
|
|
3391
3442
|
destroy() {
|
|
3392
3443
|
this.submitSubject$.unsubscribe();
|
|
3393
3444
|
this.templateSubject$.unsubscribe();
|
|
3394
|
-
this.
|
|
3445
|
+
this.fieldEventSubject$.unsubscribe();
|
|
3395
3446
|
this.dataSubject$.unsubscribe();
|
|
3447
|
+
this.fields.forEach(field => field.destroyField());
|
|
3396
3448
|
}
|
|
3397
3449
|
}
|
|
3398
3450
|
/**
|
|
@@ -3406,15 +3458,22 @@ class FormCore {
|
|
|
3406
3458
|
*/
|
|
3407
3459
|
FormCore.checkIndexes = (struct, indexes = []) => {
|
|
3408
3460
|
if (!struct) return indexes;
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3461
|
+
const helper = (struct, indexes) => {
|
|
3462
|
+
for (let i = 0; i < struct.length; i++) {
|
|
3463
|
+
const structElement = struct[i];
|
|
3464
|
+
if (structElement.name === IVARPROPNAME) {
|
|
3465
|
+
throw new Error(`reserved ${IVARPROPNAME} name for field names`);
|
|
3466
|
+
}
|
|
3467
|
+
indexes.push(structElement.name);
|
|
3468
|
+
if (structElement.children) {
|
|
3469
|
+
helper(structElement.children, indexes);
|
|
3470
|
+
}
|
|
3417
3471
|
}
|
|
3472
|
+
};
|
|
3473
|
+
helper(struct, indexes);
|
|
3474
|
+
const duppedIndexes = indexes.filter((item, index) => indexes.indexOf(item) !== index);
|
|
3475
|
+
if (duppedIndexes.length > 0) {
|
|
3476
|
+
throw new Error(`duplicated indexes found on schema: ${JSON.stringify(duppedIndexes)}`);
|
|
3418
3477
|
}
|
|
3419
3478
|
return indexes;
|
|
3420
3479
|
};
|
|
@@ -3429,6 +3488,25 @@ class FormGroup {
|
|
|
3429
3488
|
constructor() {
|
|
3430
3489
|
this.forms = new Map();
|
|
3431
3490
|
}
|
|
3491
|
+
/**
|
|
3492
|
+
* Creates an empty form with given index
|
|
3493
|
+
*
|
|
3494
|
+
* @param {string} options.index
|
|
3495
|
+
* @param {TMapper<unknown>} options.mappers
|
|
3496
|
+
*/
|
|
3497
|
+
createFormWithIndex({
|
|
3498
|
+
index,
|
|
3499
|
+
mappers
|
|
3500
|
+
}) {
|
|
3501
|
+
const formInstance = new FormCore({
|
|
3502
|
+
index,
|
|
3503
|
+
mappers
|
|
3504
|
+
});
|
|
3505
|
+
this.addForm({
|
|
3506
|
+
key: index,
|
|
3507
|
+
formInstance
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3432
3510
|
/**
|
|
3433
3511
|
* Adds a form instance to the form group.
|
|
3434
3512
|
*
|
|
@@ -3471,6 +3549,20 @@ class FormGroup {
|
|
|
3471
3549
|
(_a = this.forms.get(key)) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
3472
3550
|
this.forms.delete(key);
|
|
3473
3551
|
}
|
|
3552
|
+
/**
|
|
3553
|
+
* removes a field given a form and field index
|
|
3554
|
+
*
|
|
3555
|
+
* @param {string} options.formIndex
|
|
3556
|
+
* @param {string} options.fieldIndex
|
|
3557
|
+
*/
|
|
3558
|
+
removeField({
|
|
3559
|
+
formIndex,
|
|
3560
|
+
fieldIndex
|
|
3561
|
+
}) {
|
|
3562
|
+
var _a, _b, _c;
|
|
3563
|
+
(_b = (_a = this.forms.get(formIndex)) === null || _a === void 0 ? void 0 : _a.fields.get(fieldIndex)) === null || _b === void 0 ? void 0 : _b.destroyField();
|
|
3564
|
+
(_c = this.forms.get(formIndex)) === null || _c === void 0 ? void 0 : _c.fields.delete(fieldIndex);
|
|
3565
|
+
}
|
|
3474
3566
|
/**
|
|
3475
3567
|
* Checks if the specified key already exists in the form group.
|
|
3476
3568
|
*
|