@bolttech/form-engine-core 0.0.1-beta.10 → 0.0.1-beta.12
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 +61 -21
- package/package.json +2 -2
- package/src/helpers/SafeSubject.d.ts +11 -0
- package/src/interfaces/schema.d.ts +7 -1
- package/src/managers/field.d.ts +10 -8
- package/src/managers/form.d.ts +6 -6
package/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Subject, Subscription, 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';
|
|
@@ -2252,6 +2252,22 @@ const validations = {
|
|
|
2252
2252
|
const DEFAULT_API_DEBOUNCE_TIME = 1000;
|
|
2253
2253
|
const DEFAULT_STATE_REFRESH_TIME = 100;
|
|
2254
2254
|
|
|
2255
|
+
/**
|
|
2256
|
+
* Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
|
|
2257
|
+
* that were unmounted due to adapter external handling such as visibility
|
|
2258
|
+
*/
|
|
2259
|
+
class SafeSubject extends Subject {
|
|
2260
|
+
constructor(isMounted) {
|
|
2261
|
+
super();
|
|
2262
|
+
this.isMounted = isMounted;
|
|
2263
|
+
}
|
|
2264
|
+
next(value) {
|
|
2265
|
+
if (this.isMounted()) {
|
|
2266
|
+
super.next(value);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2255
2271
|
/**
|
|
2256
2272
|
* Represents a form field with observables for managing form state, validations, and API requests.
|
|
2257
2273
|
*/
|
|
@@ -2333,6 +2349,7 @@ class FormField {
|
|
|
2333
2349
|
};
|
|
2334
2350
|
this._errors = {};
|
|
2335
2351
|
this._valid = false;
|
|
2352
|
+
this._mounted = true;
|
|
2336
2353
|
this.initializeObservers();
|
|
2337
2354
|
}
|
|
2338
2355
|
/**
|
|
@@ -2341,25 +2358,25 @@ class FormField {
|
|
|
2341
2358
|
initializeObservers() {
|
|
2342
2359
|
var _a;
|
|
2343
2360
|
if (!this.valueSubject$ || this.valueSubject$.closed) {
|
|
2344
|
-
this.valueSubject$ = new
|
|
2361
|
+
this.valueSubject$ = new SafeSubject(() => this._mounted);
|
|
2345
2362
|
}
|
|
2346
2363
|
if (!this.errorSubject$ || this.errorSubject$.closed) {
|
|
2347
|
-
this.errorSubject$ = new
|
|
2364
|
+
this.errorSubject$ = new SafeSubject(() => this._mounted);
|
|
2348
2365
|
}
|
|
2349
2366
|
if (!this.visibilitySubject$ || this.visibilitySubject$.closed) {
|
|
2350
|
-
this.visibilitySubject$ = new
|
|
2367
|
+
this.visibilitySubject$ = new SafeSubject(() => this._mounted);
|
|
2351
2368
|
}
|
|
2352
2369
|
if (!this.apiSubject$ || this.apiSubject$.closed) {
|
|
2353
|
-
this.apiSubject$ = new
|
|
2370
|
+
this.apiSubject$ = new SafeSubject(() => this._mounted);
|
|
2354
2371
|
}
|
|
2355
2372
|
if (!this.propsSubject$ || this.propsSubject$.closed) {
|
|
2356
|
-
this.propsSubject$ = new
|
|
2373
|
+
this.propsSubject$ = new SafeSubject(() => this._mounted);
|
|
2357
2374
|
}
|
|
2358
2375
|
if (!this.fieldStateSubscription$ || this.fieldStateSubscription$.closed) {
|
|
2359
2376
|
this.fieldStateSubscription$ = new Subscription();
|
|
2360
2377
|
}
|
|
2361
2378
|
if (!this.apiEventQueueSubject$ || this.apiEventQueueSubject$.closed) {
|
|
2362
|
-
this.apiEventQueueSubject$ = new
|
|
2379
|
+
this.apiEventQueueSubject$ = new SafeSubject(() => this._mounted);
|
|
2363
2380
|
}
|
|
2364
2381
|
this.fieldState$ = combineLatest({
|
|
2365
2382
|
visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
|
|
@@ -2563,6 +2580,7 @@ class FormField {
|
|
|
2563
2580
|
this.initializeObservers();
|
|
2564
2581
|
this.subscribeValue(valueSubscription);
|
|
2565
2582
|
this.subscribeState(propsSubscription);
|
|
2583
|
+
this._mounted = true;
|
|
2566
2584
|
}
|
|
2567
2585
|
/**
|
|
2568
2586
|
* Sets the value of the form field and emits associated events.
|
|
@@ -2796,6 +2814,7 @@ class FormField {
|
|
|
2796
2814
|
* @returns {void}
|
|
2797
2815
|
*/
|
|
2798
2816
|
destroyField() {
|
|
2817
|
+
this._mounted = false;
|
|
2799
2818
|
this.valueSubject$.unsubscribe();
|
|
2800
2819
|
this.visibilitySubject$.unsubscribe();
|
|
2801
2820
|
this.fieldStateSubscription$.unsubscribe();
|
|
@@ -2846,7 +2865,8 @@ class FormCore {
|
|
|
2846
2865
|
* @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
|
|
2847
2866
|
*/
|
|
2848
2867
|
constructor(entry) {
|
|
2849
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2868
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
2869
|
+
this.mappers = new Map();
|
|
2850
2870
|
this.schema = entry.schema;
|
|
2851
2871
|
this.fields = new Map();
|
|
2852
2872
|
this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
|
|
@@ -2858,7 +2878,9 @@ class FormCore {
|
|
|
2858
2878
|
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
2879
|
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
2880
|
};
|
|
2861
|
-
|
|
2881
|
+
(_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
|
|
2882
|
+
this.mappers.set(mapper.componentName, mapper);
|
|
2883
|
+
});
|
|
2862
2884
|
this.schema && FormCore.checkIndexes(this.schema.components);
|
|
2863
2885
|
this.templateSubject$ = new Subject();
|
|
2864
2886
|
this.submitSubject$ = new Subject();
|
|
@@ -3091,6 +3113,11 @@ class FormCore {
|
|
|
3091
3113
|
case 'object':
|
|
3092
3114
|
if (currValue === null) {
|
|
3093
3115
|
value = null;
|
|
3116
|
+
break;
|
|
3117
|
+
}
|
|
3118
|
+
if (currValue instanceof Date) {
|
|
3119
|
+
value = `new Date(\`${currValue}\`)`;
|
|
3120
|
+
break;
|
|
3094
3121
|
}
|
|
3095
3122
|
value = JSON.stringify(currValue);
|
|
3096
3123
|
break;
|
|
@@ -3118,7 +3145,7 @@ class FormCore {
|
|
|
3118
3145
|
*/
|
|
3119
3146
|
replaceExpression(expression, values) {
|
|
3120
3147
|
const regex = /\${(.*?)}/g;
|
|
3121
|
-
return expression.replace(regex, () => values.shift() || '');
|
|
3148
|
+
return expression.replace(regex, () => String(values.shift()) || '');
|
|
3122
3149
|
}
|
|
3123
3150
|
/**
|
|
3124
3151
|
* Checks if an expression string contains string concatenation within a marked expression.
|
|
@@ -3252,7 +3279,7 @@ class FormCore {
|
|
|
3252
3279
|
if (this.fields.has(fieldSchema.name)) {
|
|
3253
3280
|
throw new Error(`field name ${fieldSchema.name} already defined`);
|
|
3254
3281
|
}
|
|
3255
|
-
const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.
|
|
3282
|
+
const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(fieldSchema.component));
|
|
3256
3283
|
if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
|
|
3257
3284
|
this.fields.set(fieldSchema.name, new FormField({
|
|
3258
3285
|
schemaComponent: fieldSchema,
|
|
@@ -3299,7 +3326,12 @@ class FormCore {
|
|
|
3299
3326
|
var _a, _b, _c;
|
|
3300
3327
|
const currField = this.fields.get(structElement.name);
|
|
3301
3328
|
if (!currField) {
|
|
3302
|
-
|
|
3329
|
+
let mapper;
|
|
3330
|
+
if (structElement === null || structElement === void 0 ? void 0 : structElement.mapper) {
|
|
3331
|
+
mapper = structElement === null || structElement === void 0 ? void 0 : structElement.mapper;
|
|
3332
|
+
} else {
|
|
3333
|
+
mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(structElement.component);
|
|
3334
|
+
}
|
|
3303
3335
|
if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
|
|
3304
3336
|
this.fields.set(structElement.name, new FormField({
|
|
3305
3337
|
schemaComponent: structElement,
|
|
@@ -3317,10 +3349,11 @@ class FormCore {
|
|
|
3317
3349
|
} else {
|
|
3318
3350
|
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) || [];
|
|
3319
3351
|
currField.path = path;
|
|
3352
|
+
currField.originalSchema = structElement;
|
|
3320
3353
|
currField.templateSubject$ = this.templateSubject$;
|
|
3321
3354
|
}
|
|
3322
3355
|
if (structElement.children) {
|
|
3323
|
-
|
|
3356
|
+
this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
|
|
3324
3357
|
}
|
|
3325
3358
|
});
|
|
3326
3359
|
}
|
|
@@ -3444,15 +3477,22 @@ class FormCore {
|
|
|
3444
3477
|
*/
|
|
3445
3478
|
FormCore.checkIndexes = (struct, indexes = []) => {
|
|
3446
3479
|
if (!struct) return indexes;
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3480
|
+
const helper = (struct, indexes) => {
|
|
3481
|
+
for (let i = 0; i < struct.length; i++) {
|
|
3482
|
+
const structElement = struct[i];
|
|
3483
|
+
if (structElement.name === IVARPROPNAME) {
|
|
3484
|
+
throw new Error(`reserved ${IVARPROPNAME} name for field names`);
|
|
3485
|
+
}
|
|
3486
|
+
indexes.push(structElement.name);
|
|
3487
|
+
if (structElement.children) {
|
|
3488
|
+
helper(structElement.children, indexes);
|
|
3489
|
+
}
|
|
3455
3490
|
}
|
|
3491
|
+
};
|
|
3492
|
+
helper(struct, indexes);
|
|
3493
|
+
const duppedIndexes = indexes.filter((item, index) => indexes.indexOf(item) !== index);
|
|
3494
|
+
if (duppedIndexes.length > 0) {
|
|
3495
|
+
throw new Error(`duplicated indexes found on schema: ${JSON.stringify(duppedIndexes)}`);
|
|
3456
3496
|
}
|
|
3457
3497
|
return indexes;
|
|
3458
3498
|
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bolttech/form-engine-core",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.12",
|
|
4
4
|
"module": "./index.esm.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.esm.js",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@gaignoux/currency": "1.1.0",
|
|
9
|
-
"credit-card-type": "10.0.
|
|
9
|
+
"credit-card-type": "10.0.1",
|
|
10
10
|
"lodash": "4.17.21",
|
|
11
11
|
"rxjs": "7.8.1"
|
|
12
12
|
},
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Subject } from 'rxjs';
|
|
2
|
+
/**
|
|
3
|
+
* Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
|
|
4
|
+
* that were unmounted due to adapter external handling such as visibility
|
|
5
|
+
*/
|
|
6
|
+
declare class SafeSubject<T> extends Subject<T> {
|
|
7
|
+
private isMounted;
|
|
8
|
+
constructor(isMounted: () => boolean);
|
|
9
|
+
next(value: T): void;
|
|
10
|
+
}
|
|
11
|
+
export { SafeSubject };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TMapper } from '../types/mapper';
|
|
1
2
|
import { TApiEvent, TErrorMessages, TEvent, TFormatters, TMasks, TProps, TResetValueMethods, TSchemaFormConfig, TValidationMethods, TVisibility } from '../types/schema';
|
|
2
3
|
/**
|
|
3
4
|
* @interface IComponentSchema
|
|
@@ -48,6 +49,11 @@ interface IComponentSchema {
|
|
|
48
49
|
masks?: TMasks;
|
|
49
50
|
children?: IComponentSchema[];
|
|
50
51
|
}
|
|
52
|
+
interface IComponentSchemaAsFormField<T> extends IComponentSchema {
|
|
53
|
+
mapper?: TMapper<T>;
|
|
54
|
+
order?: number;
|
|
55
|
+
children?: IComponentSchemaAsFormField<T>[];
|
|
56
|
+
}
|
|
51
57
|
/**
|
|
52
58
|
* @interface IFormSchema
|
|
53
59
|
* Represents the schema for a form.
|
|
@@ -83,4 +89,4 @@ interface IFormSchema {
|
|
|
83
89
|
iVars?: Record<string, unknown>;
|
|
84
90
|
components?: IComponentSchema[];
|
|
85
91
|
}
|
|
86
|
-
export { IFormSchema, IComponentSchema };
|
|
92
|
+
export { IFormSchema, IComponentSchema, IComponentSchemaAsFormField };
|
package/src/managers/field.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Observable, Subject, Subscription } from 'rxjs';
|
|
2
2
|
import { TApiConfig, TApiEvent, TApiResponse, TErrorMessages, TEvent, TFormatters, TMasks, TResetValueMethods, TSchemaFormConfig, TValidationMethods, TVisibility } from '../types/schema';
|
|
3
|
-
import { IComponentSchema } from '../interfaces/schema';
|
|
3
|
+
import { IComponentSchema, IComponentSchemaAsFormField } from '../interfaces/schema';
|
|
4
4
|
import { IState } from '../interfaces/state';
|
|
5
5
|
import { TEvents, TFieldEvent, TMutationEvents, TValueChangeEvent } from '../types/event';
|
|
6
6
|
import { TMapper } from '../types/mapper';
|
|
7
|
+
import { SafeSubject } from '../helpers/SafeSubject';
|
|
7
8
|
/**
|
|
8
9
|
* Represents a form field with observables for managing form state, validations, and API requests.
|
|
9
10
|
*/
|
|
@@ -13,7 +14,7 @@ declare class FormField {
|
|
|
13
14
|
component: string;
|
|
14
15
|
path?: string;
|
|
15
16
|
children?: string[];
|
|
16
|
-
originalSchema:
|
|
17
|
+
originalSchema: IComponentSchemaAsFormField<unknown>;
|
|
17
18
|
validations?: TEvent<TValidationMethods>;
|
|
18
19
|
visibilityConditions?: TVisibility[];
|
|
19
20
|
resetValues?: TResetValueMethods[];
|
|
@@ -35,13 +36,14 @@ declare class FormField {
|
|
|
35
36
|
private _errors;
|
|
36
37
|
private _api;
|
|
37
38
|
private _valid;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
private _mounted;
|
|
40
|
+
propsSubject$: SafeSubject<Record<string, unknown>>;
|
|
41
|
+
errorSubject$: SafeSubject<Record<string, unknown>>;
|
|
42
|
+
valueSubject$: SafeSubject<Record<string, unknown>>;
|
|
43
|
+
visibilitySubject$: SafeSubject<boolean>;
|
|
44
|
+
apiSubject$: SafeSubject<TApiResponse>;
|
|
43
45
|
fieldEventSubject$: Subject<TFieldEvent>;
|
|
44
|
-
apiEventQueueSubject$:
|
|
46
|
+
apiEventQueueSubject$: SafeSubject<{
|
|
45
47
|
event: TEvents;
|
|
46
48
|
}>;
|
|
47
49
|
fieldState$: Observable<IState>;
|
package/src/managers/form.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IFormField } from './field';
|
|
2
2
|
import { Subject, Subscription } from 'rxjs';
|
|
3
|
-
import { IComponentSchema, IFormSchema } from '../interfaces/schema';
|
|
3
|
+
import { IComponentSchema, IComponentSchemaAsFormField, IFormSchema } from '../interfaces/schema';
|
|
4
4
|
import { TSchemaFormConfig } from '../types/schema';
|
|
5
5
|
import { TSubscribedTemplates } from '../types/template';
|
|
6
6
|
import { TEvents, TFieldEvent, TMutationEvents } from '../types/event';
|
|
@@ -29,7 +29,7 @@ declare class FormCore {
|
|
|
29
29
|
action?: string;
|
|
30
30
|
method?: string;
|
|
31
31
|
config: Required<TSchemaFormConfig>;
|
|
32
|
-
mappers
|
|
32
|
+
mappers: Map<string, TMapper<unknown>>;
|
|
33
33
|
onSubmit?: (data: TFormValues<Record<string, unknown>>) => void;
|
|
34
34
|
/**
|
|
35
35
|
* Creates an instance of FormCore.
|
|
@@ -113,7 +113,7 @@ declare class FormCore {
|
|
|
113
113
|
* @param {string} expression - The expression containing parameters.
|
|
114
114
|
* @returns {string[]} An array of extracted parameters.
|
|
115
115
|
*/
|
|
116
|
-
extractParams(expression: string):
|
|
116
|
+
extractParams(expression: string): unknown[];
|
|
117
117
|
/**
|
|
118
118
|
* Replaces expressions marked by ${...} in the expression string with the provided values.
|
|
119
119
|
*
|
|
@@ -121,7 +121,7 @@ declare class FormCore {
|
|
|
121
121
|
* @param {string[]} values - The values to be inserted into the marked expressions.
|
|
122
122
|
* @returns {string} The expression string with the replacements made.
|
|
123
123
|
*/
|
|
124
|
-
replaceExpression(expression: string, values:
|
|
124
|
+
replaceExpression(expression: string, values: unknown[]): string;
|
|
125
125
|
/**
|
|
126
126
|
* Checks if an expression string contains string concatenation within a marked expression.
|
|
127
127
|
*
|
|
@@ -190,13 +190,13 @@ declare class FormCore {
|
|
|
190
190
|
* @param {IComponentSchema[]} [struct] - The schema structure to serialize.
|
|
191
191
|
* @param {string} [path] - The path of the parent component.
|
|
192
192
|
*/
|
|
193
|
-
serializeStructure(struct?:
|
|
193
|
+
serializeStructure(struct?: IComponentSchemaAsFormField<unknown>[], path?: string): void;
|
|
194
194
|
/**
|
|
195
195
|
* Refreshes form fields based on changes in the schema structure.
|
|
196
196
|
*
|
|
197
197
|
* @param {IComponentSchema[]} struct - The updated schema structure.
|
|
198
198
|
*/
|
|
199
|
-
refreshFields(struct:
|
|
199
|
+
refreshFields(struct: IComponentSchemaAsFormField<unknown>[]): void;
|
|
200
200
|
/**
|
|
201
201
|
* Gets a form field by its key.
|
|
202
202
|
*
|