@bolttech/form-engine-core 0.0.1-beta.2 → 0.0.1-beta.20
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 +40 -36
- package/index.esm.js +511 -264
- package/package.json +2 -2
- package/src/constants/constants.d.ts +6 -0
- package/src/helpers/SafeSubject.d.ts +11 -0
- package/src/helpers/validation.d.ts +27 -0
- package/src/interfaces/schema.d.ts +30 -8
- package/src/interfaces/state.d.ts +1 -6
- package/src/managers/field.d.ts +25 -56
- package/src/managers/form.d.ts +35 -33
- package/src/managers/formGroup.d.ts +31 -2
- package/src/types/event.d.ts +7 -1
- package/src/types/form.d.ts +6 -11
- package/src/types/schema.d.ts +80 -39
- package/src/types/utility.d.ts +2 -0
- package/src/validations/date.d.ts +1 -0
- package/src/validations/handler.d.ts +2 -2
- package/src/validations/multiple.d.ts +2 -2
- package/src/validations/namedRule.d.ts +22 -0
package/index.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Subject, Subscription, combineLatest, startWith, groupBy, mergeMap, debounceTime, filter, map } from 'rxjs';
|
|
2
2
|
import creditCardType from 'credit-card-type';
|
|
3
|
-
import { isNumber as isNumber$1,
|
|
3
|
+
import { isNumber as isNumber$1, isFunction, isEqual, get, isNil, set } from 'lodash';
|
|
4
4
|
import { getCurrencySymbol } from '@gaignoux/currency';
|
|
5
5
|
|
|
6
6
|
var TMutationEnum;
|
|
@@ -44,6 +44,12 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
44
44
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
const DEFAULT_API_DEBOUNCE_TIME = 1000;
|
|
48
|
+
const DEFAULT_STATE_REFRESH_TIME = 100;
|
|
49
|
+
const TEMPLATE_REGEX_DELIMITATOR = /\${(.*?)}/g;
|
|
50
|
+
const TEMPLATE_REGEX_OPERATOR_SPLITTER = /\s*(\|\||&&|!)\s*/g;
|
|
51
|
+
const TEMPLATE_REGEX_OPERATOR_MATCHER = /^\|\||&&|!$/;
|
|
52
|
+
|
|
47
53
|
/**
|
|
48
54
|
* Makes an HTTP request using XMLHttpRequest.
|
|
49
55
|
*
|
|
@@ -96,15 +102,17 @@ function makeRequest(method, url, headers, body) {
|
|
|
96
102
|
* ```
|
|
97
103
|
*/
|
|
98
104
|
function extractFieldKeys(expression) {
|
|
99
|
-
const regex =
|
|
105
|
+
const regex = TEMPLATE_REGEX_DELIMITATOR;
|
|
100
106
|
const extractedValues = [];
|
|
101
107
|
let match;
|
|
102
108
|
while ((match = regex.exec(expression)) !== null) {
|
|
103
109
|
extractedValues.push(match[1]);
|
|
104
110
|
}
|
|
111
|
+
const operatorRegex = TEMPLATE_REGEX_OPERATOR_SPLITTER;
|
|
112
|
+
const splittedString = extractedValues.map(el => el.split(operatorRegex).filter(item => !operatorRegex.test(item))).flat().filter(el => el.split('.').length > 1);
|
|
105
113
|
return {
|
|
106
|
-
originFieldKeys: Array.from(new Set(
|
|
107
|
-
originPropertyKeys: Array.from(new Set(
|
|
114
|
+
originFieldKeys: Array.from(new Set(splittedString.map(el => el.split('.')[0]))),
|
|
115
|
+
originPropertyKeys: Array.from(new Set(splittedString.map(el => el.split('.')[1])))
|
|
108
116
|
};
|
|
109
117
|
}
|
|
110
118
|
/**
|
|
@@ -142,7 +150,7 @@ function traverseObject(obj, path) {
|
|
|
142
150
|
if (typeof item === 'object') {
|
|
143
151
|
result.push(...traverseObject(item, `${path ? `${path}.` : ``}${key}.${index}`));
|
|
144
152
|
} else if (typeof item === 'string') {
|
|
145
|
-
if (String(item).includes('$')) {
|
|
153
|
+
if (String(item).includes('${')) {
|
|
146
154
|
// const extractedPath = item.replace(/\$|{|}/g, '').split('.');
|
|
147
155
|
const extractedOriginPath = `${path ? `${path}.` : ``}${key}`.split('.');
|
|
148
156
|
result.push(Object.assign(Object.assign({
|
|
@@ -158,7 +166,7 @@ function traverseObject(obj, path) {
|
|
|
158
166
|
} else if (typeof value === 'object') {
|
|
159
167
|
result.push(...traverseObject(value, `${path ? `${path}.` : ``}${key}`));
|
|
160
168
|
} else if (typeof value === 'string') {
|
|
161
|
-
if (value.includes('$')) {
|
|
169
|
+
if (value.includes('${')) {
|
|
162
170
|
// const extractedPath = value.replace(/\$|{|}/g, '').split('.');
|
|
163
171
|
const destinationPath = `${path ? `${path}.` : ``}${key}`.split('.');
|
|
164
172
|
result.push(Object.assign(Object.assign({
|
|
@@ -681,16 +689,9 @@ const currency = (value, masks) => {
|
|
|
681
689
|
if (integerPart === '') {
|
|
682
690
|
integerPart = '0';
|
|
683
691
|
}
|
|
684
|
-
console.log('Before', {
|
|
685
|
-
convertedValue
|
|
686
|
-
});
|
|
687
692
|
if (align === 'right' && String(value).endsWith(' ')) {
|
|
688
693
|
convertedValue = convertedValue.slice(0, -1);
|
|
689
694
|
}
|
|
690
|
-
console.log('After', {
|
|
691
|
-
value,
|
|
692
|
-
convertedValue
|
|
693
|
-
});
|
|
694
695
|
let newRawValue = integerPart;
|
|
695
696
|
let decimalPart = convertedValue.slice(convertedValue.length - precision);
|
|
696
697
|
if (precision > 0) {
|
|
@@ -1928,41 +1929,44 @@ const conditions = (value, validations) => {
|
|
|
1928
1929
|
};
|
|
1929
1930
|
|
|
1930
1931
|
/**
|
|
1931
|
-
*
|
|
1932
|
+
* @internal
|
|
1933
|
+
* Validates if a date string matches a specific date format.
|
|
1934
|
+
* The function accepts strings with '/' or '-' separators and removes them before validating the format.
|
|
1932
1935
|
*
|
|
1933
|
-
* @param {string}
|
|
1934
|
-
* @param {
|
|
1935
|
-
*
|
|
1936
|
+
* @param {string} string - The date string to be validated. It can contain '/' or '-' separators.
|
|
1937
|
+
* @param {string} format - The expected date format. It can be one of the following:
|
|
1938
|
+
* 'DDMMYYYY', 'YYYYMMDD', 'YYYYDDMM', 'MMDDYYYY',
|
|
1939
|
+
* 'DMYYYY', 'YYYYMD', 'YYYYDM', 'MDYYYY'.
|
|
1940
|
+
* @returns {boolean} - Returns `false` if the date string matches the specified format, otherwise `true`.
|
|
1936
1941
|
*
|
|
1937
1942
|
* @example
|
|
1938
|
-
*
|
|
1939
|
-
*
|
|
1940
|
-
* betweenDates: [
|
|
1941
|
-
* { origin: { value: '2023-01-01', format: 'yyyy-MM-dd' }, operator: '>=' },
|
|
1942
|
-
* { origin: { value: '2023-12-31', format: 'yyyy-MM-dd' }, operator: '<=' }
|
|
1943
|
-
* ]
|
|
1944
|
-
* };
|
|
1943
|
+
* // Returns false
|
|
1944
|
+
* invalidStringDate("25/07/1997", "DDMMYYYY");
|
|
1945
1945
|
*
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1946
|
+
* @example
|
|
1947
|
+
* // Returns false
|
|
1948
|
+
* invalidStringDate("1997-07-25", "YYYYMMDD");
|
|
1948
1949
|
*
|
|
1949
|
-
*
|
|
1950
|
-
*
|
|
1951
|
-
*
|
|
1950
|
+
* @example
|
|
1951
|
+
* // Returns true, as the format does not match
|
|
1952
|
+
* invalidStringDate("1997/25/07", "MMDDYYYY");
|
|
1952
1953
|
*/
|
|
1953
|
-
const
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
if (((_a = validations.betweenDates) === null || _a === void 0 ? void 0 : _a.length) != 2) return false;
|
|
1957
|
-
for (const validation of validations.betweenDates) {
|
|
1958
|
-
if (date(value, {
|
|
1959
|
-
date: validation
|
|
1960
|
-
})) {
|
|
1961
|
-
fail = true;
|
|
1962
|
-
break;
|
|
1963
|
-
}
|
|
1954
|
+
const invalidStringDate = (value, format) => {
|
|
1955
|
+
if (!value.includes('/') && !value.includes('-') || !format) {
|
|
1956
|
+
return true;
|
|
1964
1957
|
}
|
|
1965
|
-
|
|
1958
|
+
const valueParts = value.replace(/[-/]/g, '');
|
|
1959
|
+
const dateMapper = {
|
|
1960
|
+
DDMMYYYY: /^(\d{2})(\d{2})(\d{4})$/,
|
|
1961
|
+
YYYYMMDD: /^(\d{4})(\d{2})(\d{2})$/,
|
|
1962
|
+
YYYYDDMM: /^(\d{4})(\d{2})(\d{2})$/,
|
|
1963
|
+
MMDDYYYY: /^(\d{2})(\d{2})(\d{4})$/,
|
|
1964
|
+
DMYYYY: /^(\d{1,2})(\d{1,2})(\d{4})$/,
|
|
1965
|
+
YYYYMD: /^(\d{4})(\d{1,2})(\d{1,2})$/,
|
|
1966
|
+
YYYYDM: /^(\d{4})(\d{1,2})(\d{1,2})$/,
|
|
1967
|
+
MDYYYY: /^(\d{1,2})(\d{1,2})(\d{4})$/
|
|
1968
|
+
};
|
|
1969
|
+
return !dateMapper[format].test(valueParts);
|
|
1966
1970
|
};
|
|
1967
1971
|
/**
|
|
1968
1972
|
* @internal
|
|
@@ -2015,6 +2019,44 @@ const dateRearrangeMapper = {
|
|
|
2015
2019
|
MMDDYYYY: value => value,
|
|
2016
2020
|
timestamp: value => new Date(value).toString()
|
|
2017
2021
|
};
|
|
2022
|
+
/**
|
|
2023
|
+
* @function betweenDates
|
|
2024
|
+
* Validates if a date value falls between two specified dates.
|
|
2025
|
+
*
|
|
2026
|
+
* @param {string} value - The date value to be validated in string format.
|
|
2027
|
+
* @param {TValidationMethods} validations - The validation methods object containing the betweenDates validation rules.
|
|
2028
|
+
* @returns {boolean} - Returns `true` if the date value fails the betweenDates validation, otherwise `false`.
|
|
2029
|
+
*
|
|
2030
|
+
* @example
|
|
2031
|
+
* ```typescript
|
|
2032
|
+
* const validations = {
|
|
2033
|
+
* betweenDates: [
|
|
2034
|
+
* { origin: { value: '2023-01-01', format: 'yyyy-MM-dd' }, operator: '>=' },
|
|
2035
|
+
* { origin: { value: '2023-12-31', format: 'yyyy-MM-dd' }, operator: '<=' }
|
|
2036
|
+
* ]
|
|
2037
|
+
* };
|
|
2038
|
+
*
|
|
2039
|
+
* const result1 = betweenDates('2023-06-01', validations);
|
|
2040
|
+
* console.log(result1); // false (date is within the range)
|
|
2041
|
+
*
|
|
2042
|
+
* const result2 = betweenDates('2024-01-01', validations);
|
|
2043
|
+
* console.log(result2); // true (date is outside the range)
|
|
2044
|
+
* ```
|
|
2045
|
+
*/
|
|
2046
|
+
const betweenDates = (value, validations) => {
|
|
2047
|
+
var _a;
|
|
2048
|
+
let fail = false;
|
|
2049
|
+
if (((_a = validations.betweenDates) === null || _a === void 0 ? void 0 : _a.length) != 2) return false;
|
|
2050
|
+
for (const validation of validations.betweenDates) {
|
|
2051
|
+
if (date(value, {
|
|
2052
|
+
date: validation
|
|
2053
|
+
})) {
|
|
2054
|
+
fail = true;
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
return fail;
|
|
2059
|
+
};
|
|
2018
2060
|
/**
|
|
2019
2061
|
* @function date
|
|
2020
2062
|
* Validates a date value based on various date conditions and intervals.
|
|
@@ -2048,22 +2090,24 @@ const dateRearrangeMapper = {
|
|
|
2048
2090
|
* ```
|
|
2049
2091
|
*/
|
|
2050
2092
|
const date = (value, validations) => {
|
|
2051
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
2093
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
2052
2094
|
if (!((_b = (_a = validations.date) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.value) && !((_d = (_c = validations.date) === null || _c === void 0 ? void 0 : _c.origin) === null || _d === void 0 ? void 0 : _d.intervals)) {
|
|
2053
2095
|
return false;
|
|
2054
2096
|
}
|
|
2055
2097
|
const originValue = validations.date.origin.value || value;
|
|
2056
|
-
|
|
2098
|
+
const mappedValue = dateRearrangeMapper[(_e = validations.date) === null || _e === void 0 ? void 0 : _e.origin.format](originValue).toString();
|
|
2099
|
+
if (invalidStringDate(mappedValue, (_f = validations.date) === null || _f === void 0 ? void 0 : _f.origin.format)) return false;
|
|
2100
|
+
let originDate = new Date(mappedValue);
|
|
2057
2101
|
let targetDate = new Date();
|
|
2058
2102
|
let target = new Date();
|
|
2059
|
-
if ((
|
|
2060
|
-
target = new Date(dateRearrangeMapper[(
|
|
2103
|
+
if ((_h = (_g = validations.date) === null || _g === void 0 ? void 0 : _g.target) === null || _h === void 0 ? void 0 : _h.format) {
|
|
2104
|
+
target = new Date(dateRearrangeMapper[(_k = (_j = validations.date) === null || _j === void 0 ? void 0 : _j.target) === null || _k === void 0 ? void 0 : _k.format](validations.date.target.value).toString());
|
|
2061
2105
|
targetDate = target;
|
|
2062
2106
|
}
|
|
2063
2107
|
if (validations.date.origin.intervals) {
|
|
2064
2108
|
targetDate = getIntervalsDate(originDate, validations.date.origin.intervals);
|
|
2065
2109
|
const date = new Date();
|
|
2066
|
-
if (((
|
|
2110
|
+
if (((_l = validations.date.target) === null || _l === void 0 ? void 0 : _l.value) && target) {
|
|
2067
2111
|
date.setDate(target.getDate());
|
|
2068
2112
|
date.setMonth(target.getMonth());
|
|
2069
2113
|
}
|
|
@@ -2082,7 +2126,7 @@ const date = (value, validations) => {
|
|
|
2082
2126
|
'===': originTimestamp === targetTimestamp,
|
|
2083
2127
|
'!==': originTimestamp !== targetTimestamp
|
|
2084
2128
|
};
|
|
2085
|
-
return operationsMapper[(
|
|
2129
|
+
return operationsMapper[(_m = validations.date) === null || _m === void 0 ? void 0 : _m.operator];
|
|
2086
2130
|
};
|
|
2087
2131
|
/**
|
|
2088
2132
|
* @function validDate
|
|
@@ -2116,6 +2160,56 @@ const validDate = (value, validations) => {
|
|
|
2116
2160
|
return !isValidDate;
|
|
2117
2161
|
};
|
|
2118
2162
|
|
|
2163
|
+
/**
|
|
2164
|
+
* @internal
|
|
2165
|
+
* Runs a set of validation handlers against a given value.
|
|
2166
|
+
*
|
|
2167
|
+
* @param {unknown} value - The value to be validated.
|
|
2168
|
+
* @param {TValidationMethods} handlers - An object containing validation methods to be applied.
|
|
2169
|
+
* @param {TValidationHandler} validations - An object containing every validation methods to be executed.
|
|
2170
|
+
* @returns {boolean[]} - An array of boolean results for each validation method.
|
|
2171
|
+
*
|
|
2172
|
+
* @example
|
|
2173
|
+
* const handlers = {
|
|
2174
|
+
* max: { max: 10 },
|
|
2175
|
+
* required: true,
|
|
2176
|
+
* email: true
|
|
2177
|
+
* };
|
|
2178
|
+
* const results = run('test@example.com', handlers);
|
|
2179
|
+
* console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
|
|
2180
|
+
*/
|
|
2181
|
+
function run$1(value, handlers, validations) {
|
|
2182
|
+
const runner = [];
|
|
2183
|
+
Object.keys(handlers).forEach(rule => {
|
|
2184
|
+
runner.push(validations[rule](value, {
|
|
2185
|
+
[rule]: handlers[rule]
|
|
2186
|
+
}));
|
|
2187
|
+
});
|
|
2188
|
+
return runner;
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Validates a given value based on specified validation methods inside a custom named validation.
|
|
2192
|
+
*
|
|
2193
|
+
* @param {unknown} value - The value to be validated.
|
|
2194
|
+
* @param {TValidationMethods} methods - The validation methods to be applied.
|
|
2195
|
+
* @param {TValidationHandler} validations - An object containing every validation methods to be executed.
|
|
2196
|
+
* @returns {boolean} - Returns true if any of the validation methods pass, otherwise false.
|
|
2197
|
+
*
|
|
2198
|
+
* @example
|
|
2199
|
+
* const value = 'example@example.com';
|
|
2200
|
+
* const methods = {
|
|
2201
|
+
* required: true,
|
|
2202
|
+
* email: true
|
|
2203
|
+
* };
|
|
2204
|
+
*
|
|
2205
|
+
* const isValid = validateValue(value, methods);
|
|
2206
|
+
* console.log(isValid); // Output: true
|
|
2207
|
+
*/
|
|
2208
|
+
var namedRule = ((value, methods, validations) => {
|
|
2209
|
+
if (!methods) return false;
|
|
2210
|
+
return run$1(value, methods, validations).some(validation => validation);
|
|
2211
|
+
});
|
|
2212
|
+
|
|
2119
2213
|
/**
|
|
2120
2214
|
* @internal
|
|
2121
2215
|
* An object mapping validation keys to their respective validation functions.
|
|
@@ -2150,7 +2244,6 @@ const validations$1 = {
|
|
|
2150
2244
|
notEmpty,
|
|
2151
2245
|
bool,
|
|
2152
2246
|
exists,
|
|
2153
|
-
greaterThan: () => true,
|
|
2154
2247
|
isNumber,
|
|
2155
2248
|
conditions,
|
|
2156
2249
|
validDate,
|
|
@@ -2174,20 +2267,26 @@ const validations$1 = {
|
|
|
2174
2267
|
* const results = run('test@example.com', handlers);
|
|
2175
2268
|
* console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
|
|
2176
2269
|
*/
|
|
2177
|
-
|
|
2270
|
+
function run(value, handlers) {
|
|
2178
2271
|
const runner = [];
|
|
2179
2272
|
Object.keys(handlers).forEach(rule => {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2273
|
+
let handler;
|
|
2274
|
+
if (isFunction(validations$1[rule])) {
|
|
2275
|
+
handler = validations$1[rule](value, {
|
|
2276
|
+
[rule]: handlers[rule]
|
|
2277
|
+
});
|
|
2278
|
+
} else {
|
|
2279
|
+
handler = namedRule(value, handlers[rule], validations$1);
|
|
2280
|
+
}
|
|
2281
|
+
runner.push(handler);
|
|
2183
2282
|
});
|
|
2184
2283
|
return runner;
|
|
2185
|
-
}
|
|
2284
|
+
}
|
|
2186
2285
|
/**
|
|
2187
2286
|
* Validates that a value meets multiple validation rules.
|
|
2188
2287
|
*
|
|
2189
2288
|
* @param {number | string | boolean} value - The value to be validated.
|
|
2190
|
-
* @param {TValidationMethods}
|
|
2289
|
+
* @param {TValidationMethods} methods - The validation methods object containing the multipleValidations rule set.
|
|
2191
2290
|
* @returns {boolean} - Returns `true` if the value meets the specified multiple validation rules, otherwise `false`.
|
|
2192
2291
|
*
|
|
2193
2292
|
* @example
|
|
@@ -2212,15 +2311,15 @@ const run = (value, handlers) => {
|
|
|
2212
2311
|
* console.log(result2); // false
|
|
2213
2312
|
* ```
|
|
2214
2313
|
*/
|
|
2215
|
-
const multipleValidations = (value,
|
|
2216
|
-
if (!
|
|
2217
|
-
const runner = run(value,
|
|
2314
|
+
const multipleValidations = (value, methods) => {
|
|
2315
|
+
if (!methods.multipleValidations) return false;
|
|
2316
|
+
const runner = run(value, methods.multipleValidations.validations);
|
|
2218
2317
|
const rulesMapper = {
|
|
2219
2318
|
AND: () => runner.every(validation => validation),
|
|
2220
2319
|
OR: () => runner.some(validation => validation),
|
|
2221
2320
|
NOT: () => !runner.every(validation => validation)
|
|
2222
2321
|
};
|
|
2223
|
-
return rulesMapper[
|
|
2322
|
+
return rulesMapper[methods.multipleValidations.rule]();
|
|
2224
2323
|
};
|
|
2225
2324
|
|
|
2226
2325
|
const validations = {
|
|
@@ -2247,7 +2346,6 @@ const validations = {
|
|
|
2247
2346
|
notEmpty,
|
|
2248
2347
|
bool,
|
|
2249
2348
|
exists,
|
|
2250
|
-
greaterThan: () => true,
|
|
2251
2349
|
isNumber,
|
|
2252
2350
|
conditions,
|
|
2253
2351
|
multipleValidations,
|
|
@@ -2256,6 +2354,53 @@ const validations = {
|
|
|
2256
2354
|
validDate
|
|
2257
2355
|
};
|
|
2258
2356
|
|
|
2357
|
+
/**
|
|
2358
|
+
* Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
|
|
2359
|
+
* that were unmounted due to adapter external handling such as visibility
|
|
2360
|
+
*/
|
|
2361
|
+
class SafeSubject extends Subject {
|
|
2362
|
+
constructor(isMounted) {
|
|
2363
|
+
super();
|
|
2364
|
+
this.isMounted = isMounted;
|
|
2365
|
+
}
|
|
2366
|
+
next(value) {
|
|
2367
|
+
if (this.isMounted()) {
|
|
2368
|
+
super.next(value);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
/**
|
|
2374
|
+
* @internal
|
|
2375
|
+
* Handles the validation of a given value based on specified validation methods and rules.
|
|
2376
|
+
*
|
|
2377
|
+
* @param {string | number | boolean | unknown} value - The value to be validated.
|
|
2378
|
+
* @param {TSchemaValidation} validations - The schema validations to be applied.
|
|
2379
|
+
* @param {TValidationHandler} methods - The validation handler methods.
|
|
2380
|
+
* @param {keyof TValidationMethods} key - The specific key of the validation method to be used.
|
|
2381
|
+
* @returns {boolean} - Returns true if the value passes the validation, otherwise false.
|
|
2382
|
+
*
|
|
2383
|
+
* @example
|
|
2384
|
+
* const value = 'example@example.com';
|
|
2385
|
+
* const validations = {
|
|
2386
|
+
* required: true,
|
|
2387
|
+
* customName: { email: true }
|
|
2388
|
+
* };
|
|
2389
|
+
* const methods = {
|
|
2390
|
+
* email: (value) => /\S+@\S+\.\S+/.test(value)
|
|
2391
|
+
* };
|
|
2392
|
+
* const key = 'required';
|
|
2393
|
+
*
|
|
2394
|
+
* const isValid = handleValidation(value, validations, methods, key);
|
|
2395
|
+
* console.log(isValid); // Output: true
|
|
2396
|
+
*/
|
|
2397
|
+
function handleValidation(value, validations, methods, key) {
|
|
2398
|
+
if (isFunction(methods[key])) {
|
|
2399
|
+
return methods[key](value, validations);
|
|
2400
|
+
}
|
|
2401
|
+
return namedRule(value, validations[key], methods);
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2259
2404
|
/**
|
|
2260
2405
|
* Represents a form field with observables for managing form state, validations, and API requests.
|
|
2261
2406
|
*/
|
|
@@ -2282,40 +2427,41 @@ class FormField {
|
|
|
2282
2427
|
resetValue,
|
|
2283
2428
|
initialValue,
|
|
2284
2429
|
templateSubject$,
|
|
2285
|
-
|
|
2430
|
+
fieldEventSubject$,
|
|
2286
2431
|
dataSubject$,
|
|
2287
2432
|
mapper
|
|
2288
2433
|
}) {
|
|
2289
2434
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
2290
2435
|
this.fieldStateSubscription$ = new Subscription();
|
|
2436
|
+
this.originalSchema = schemaComponent;
|
|
2291
2437
|
this.config = {
|
|
2292
|
-
defaultAPIdebounceTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultAPIdebounceTimeMS) :
|
|
2293
|
-
defaultStateRefreshTimeMS: Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) ? Number(config === null || config === void 0 ? void 0 : config.defaultStateRefreshTimeMS) :
|
|
2438
|
+
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,
|
|
2439
|
+
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
2440
|
};
|
|
2295
2441
|
this.name = schemaComponent.name;
|
|
2442
|
+
this.nameToSubmit = schemaComponent.nameToSubmit;
|
|
2296
2443
|
this.component = schemaComponent.component;
|
|
2297
2444
|
this.path = path;
|
|
2298
2445
|
this.children = children;
|
|
2299
2446
|
this.validations = schemaComponent.validations;
|
|
2300
|
-
this.errorMessages = schemaComponent.
|
|
2447
|
+
this.errorMessages = (_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages;
|
|
2301
2448
|
this.visibilityConditions = schemaComponent.visibilityConditions;
|
|
2302
2449
|
this.resetValues = schemaComponent.resetValues;
|
|
2303
2450
|
this.apiSchema = schemaComponent.api;
|
|
2304
2451
|
this.formatters = schemaComponent.formatters;
|
|
2305
2452
|
this.masks = schemaComponent.masks;
|
|
2306
2453
|
if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
|
|
2307
|
-
if ((
|
|
2308
|
-
if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setErrorMessage) this.errorMessagePropName = mapper.events.setErrorMessage;
|
|
2454
|
+
if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) this.valuePropName = mapper.events.setValue;
|
|
2309
2455
|
this.mapper = mapper;
|
|
2310
2456
|
this.validateVisibility = validateVisibility;
|
|
2311
2457
|
this.resetValue = resetValue;
|
|
2312
2458
|
this.templateSubject$ = templateSubject$;
|
|
2313
|
-
this.
|
|
2459
|
+
this.fieldEventSubject$ = fieldEventSubject$;
|
|
2314
2460
|
this.dataSubject$ = dataSubject$;
|
|
2315
2461
|
this._props = schemaComponent.props || {};
|
|
2316
|
-
this._value = '';
|
|
2317
|
-
this._stateValue = '';
|
|
2318
2462
|
this._metadata = '';
|
|
2463
|
+
this.errorsString = '';
|
|
2464
|
+
this.errorsList = [];
|
|
2319
2465
|
this.initialValue = initialValue;
|
|
2320
2466
|
this._visibility = true;
|
|
2321
2467
|
this._api = {
|
|
@@ -2331,9 +2477,10 @@ class FormField {
|
|
|
2331
2477
|
}, {})
|
|
2332
2478
|
};
|
|
2333
2479
|
this._errors = {};
|
|
2334
|
-
this._errorsString = '';
|
|
2335
2480
|
this._valid = false;
|
|
2481
|
+
this._mounted = true;
|
|
2336
2482
|
this.initializeObservers();
|
|
2483
|
+
this.value = this.initialValue || '';
|
|
2337
2484
|
}
|
|
2338
2485
|
/**
|
|
2339
2486
|
* method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
|
|
@@ -2341,53 +2488,38 @@ class FormField {
|
|
|
2341
2488
|
initializeObservers() {
|
|
2342
2489
|
var _a;
|
|
2343
2490
|
if (!this.valueSubject$ || this.valueSubject$.closed) {
|
|
2344
|
-
this.valueSubject$ = new
|
|
2491
|
+
this.valueSubject$ = new SafeSubject(() => this._mounted);
|
|
2345
2492
|
}
|
|
2346
2493
|
if (!this.errorSubject$ || this.errorSubject$.closed) {
|
|
2347
|
-
this.errorSubject$ = new
|
|
2494
|
+
this.errorSubject$ = new SafeSubject(() => this._mounted);
|
|
2348
2495
|
}
|
|
2349
2496
|
if (!this.visibilitySubject$ || this.visibilitySubject$.closed) {
|
|
2350
|
-
this.visibilitySubject$ = new
|
|
2497
|
+
this.visibilitySubject$ = new SafeSubject(() => this._mounted);
|
|
2351
2498
|
}
|
|
2352
2499
|
if (!this.apiSubject$ || this.apiSubject$.closed) {
|
|
2353
|
-
this.apiSubject$ = new
|
|
2500
|
+
this.apiSubject$ = new SafeSubject(() => this._mounted);
|
|
2354
2501
|
}
|
|
2355
2502
|
if (!this.propsSubject$ || this.propsSubject$.closed) {
|
|
2356
|
-
this.propsSubject$ = new
|
|
2503
|
+
this.propsSubject$ = new SafeSubject(() => this._mounted);
|
|
2357
2504
|
}
|
|
2358
2505
|
if (!this.fieldStateSubscription$ || this.fieldStateSubscription$.closed) {
|
|
2359
2506
|
this.fieldStateSubscription$ = new Subscription();
|
|
2360
2507
|
}
|
|
2361
2508
|
if (!this.apiEventQueueSubject$ || this.apiEventQueueSubject$.closed) {
|
|
2362
|
-
this.apiEventQueueSubject$ = new
|
|
2509
|
+
this.apiEventQueueSubject$ = new SafeSubject(() => this._mounted);
|
|
2363
2510
|
}
|
|
2364
2511
|
this.fieldState$ = combineLatest({
|
|
2365
|
-
errors: this.errorSubject$.pipe(startWith([])),
|
|
2366
2512
|
visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
|
|
2367
|
-
|
|
2368
|
-
|
|
2513
|
+
props: this.propsSubject$.pipe(startWith(this._props)),
|
|
2514
|
+
errors: this.errorSubject$.pipe(startWith(Object.assign({}, ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && {
|
|
2515
|
+
[this.mapper.events.setErrorMessage]: this.errorsString
|
|
2516
|
+
})))
|
|
2369
2517
|
});
|
|
2370
|
-
!this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(
|
|
2518
|
+
!this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(groupBy(({
|
|
2371
2519
|
event
|
|
2372
|
-
}) => event, (
|
|
2520
|
+
}) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
|
|
2373
2521
|
this.apiRequest(payload);
|
|
2374
2522
|
});
|
|
2375
|
-
if (!isNil(this.initialValue)) {
|
|
2376
|
-
this.value = this.initialValue;
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
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
2523
|
}
|
|
2392
2524
|
/**
|
|
2393
2525
|
* Retrieves the properties associated with the form field.
|
|
@@ -2414,7 +2546,7 @@ class FormField {
|
|
|
2414
2546
|
/**
|
|
2415
2547
|
* Retrieves the current state value of the form field.
|
|
2416
2548
|
*
|
|
2417
|
-
* @returns {unknown} - The current state value of the form field.
|
|
2549
|
+
* @returns {Record<string,unknown>} - The current state value of the form field.
|
|
2418
2550
|
*/
|
|
2419
2551
|
get stateValue() {
|
|
2420
2552
|
return this._stateValue;
|
|
@@ -2422,14 +2554,6 @@ class FormField {
|
|
|
2422
2554
|
get metadata() {
|
|
2423
2555
|
return this._metadata;
|
|
2424
2556
|
}
|
|
2425
|
-
/**
|
|
2426
|
-
* Retrieves the concatenated string of errors associated with the form field.
|
|
2427
|
-
*
|
|
2428
|
-
* @returns {string} - The concatenated string of errors.
|
|
2429
|
-
*/
|
|
2430
|
-
get errorsString() {
|
|
2431
|
-
return this._errorsString;
|
|
2432
|
-
}
|
|
2433
2557
|
/**
|
|
2434
2558
|
* Retrieves the current value of the form field.
|
|
2435
2559
|
*
|
|
@@ -2444,6 +2568,7 @@ class FormField {
|
|
|
2444
2568
|
* @param {unknown} value - The new value to be set.
|
|
2445
2569
|
*/
|
|
2446
2570
|
set value(value) {
|
|
2571
|
+
var _a, _b, _c;
|
|
2447
2572
|
/*
|
|
2448
2573
|
too much unstable, if the valueChangeEvent parses the template event
|
|
2449
2574
|
value, might occur unexpected results
|
|
@@ -2463,22 +2588,19 @@ class FormField {
|
|
|
2463
2588
|
if (typeof val === 'undefined' || val === null) return;
|
|
2464
2589
|
if (typeof val === 'object' && '_value' in val && '_metadata' in val) {
|
|
2465
2590
|
this._value = this.formatValue(val['_value']);
|
|
2466
|
-
this._stateValue =
|
|
2591
|
+
this._stateValue = ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setValue) ? {
|
|
2592
|
+
[this.mapper.events.setValue]: this.maskValue(this.formatValue(val['_value']))
|
|
2593
|
+
} : {};
|
|
2467
2594
|
this._metadata = val._metadata;
|
|
2468
2595
|
} else {
|
|
2469
2596
|
this._value = this.formatValue(val);
|
|
2470
|
-
this._stateValue =
|
|
2597
|
+
this._stateValue = ((_b = this.mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) ? {
|
|
2598
|
+
[(_c = this.mapper.events) === null || _c === void 0 ? void 0 : _c.setValue]: this.maskValue(this.formatValue(val))
|
|
2599
|
+
} : {};
|
|
2600
|
+
this.maskValue(this.formatValue(val));
|
|
2471
2601
|
this._metadata = val;
|
|
2472
2602
|
}
|
|
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
|
-
this.valueSubject$.next(this._stateValue);
|
|
2603
|
+
this.stateValue && this.valueSubject$.next(this.stateValue);
|
|
2482
2604
|
this.templateSubject$.next({
|
|
2483
2605
|
key: this.name,
|
|
2484
2606
|
event: 'ON_VALUE'
|
|
@@ -2500,6 +2622,23 @@ class FormField {
|
|
|
2500
2622
|
set visibility(visible) {
|
|
2501
2623
|
if (typeof visible === 'undefined' || visible === this.visibility) return;
|
|
2502
2624
|
this._visibility = visible;
|
|
2625
|
+
/**
|
|
2626
|
+
* I was sure I would not require to gambiarra, but..
|
|
2627
|
+
* in order to ignore an hidden value on a form submit
|
|
2628
|
+
* or revalidate it when it comes back to visibility
|
|
2629
|
+
* I needed to...
|
|
2630
|
+
* I don't recommend setting private properties like this
|
|
2631
|
+
* this will force the field to be valid when it's hidden
|
|
2632
|
+
* and trigger the validation when it's visible
|
|
2633
|
+
*/
|
|
2634
|
+
if (!this.visibility) {
|
|
2635
|
+
this.value = '';
|
|
2636
|
+
this._valid = true;
|
|
2637
|
+
} else {
|
|
2638
|
+
this.setFieldValidity({
|
|
2639
|
+
event: 'ON_FIELD_MOUNT'
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2503
2642
|
this.visibilitySubject$.next(this.visibility);
|
|
2504
2643
|
this.templateSubject$.next({
|
|
2505
2644
|
key: this.name,
|
|
@@ -2528,10 +2667,17 @@ class FormField {
|
|
|
2528
2667
|
* @param {TErrorMessages} errors - The new error messages to be set.
|
|
2529
2668
|
*/
|
|
2530
2669
|
set errors(errors) {
|
|
2670
|
+
var _a;
|
|
2531
2671
|
if (typeof errors === 'undefined' || isEqual(errors, this.errors)) return;
|
|
2532
2672
|
this._errors = errors;
|
|
2533
|
-
this.
|
|
2534
|
-
this.
|
|
2673
|
+
this.errorsList = Object.values(this.errors);
|
|
2674
|
+
this.errorsString = this.errorsList.join(', ');
|
|
2675
|
+
/**
|
|
2676
|
+
* if any error receives a list of errors, set a prop for it, currently only supporting a single string
|
|
2677
|
+
*/
|
|
2678
|
+
((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && this.errorSubject$.next({
|
|
2679
|
+
[this.mapper.events.setErrorMessage]: this.errorsString
|
|
2680
|
+
});
|
|
2535
2681
|
this.templateSubject$.next({
|
|
2536
2682
|
key: this.name,
|
|
2537
2683
|
event: 'ON_PROPS'
|
|
@@ -2567,11 +2713,8 @@ class FormField {
|
|
|
2567
2713
|
* Mounts the form field by initializing necessary subjects and combining their streams.
|
|
2568
2714
|
*
|
|
2569
2715
|
* @param {object} mountOpts - Adapter mount options.
|
|
2570
|
-
* @param {string} prop.valuePropName - Adapter value property name.
|
|
2571
|
-
* @param {(event: unknown) => unknown} prop.valueChangeEvent - Adapter change event handler function
|
|
2572
2716
|
* @param {(value: unknown) => unknown} prop.valueSubscription - Adapter value change function
|
|
2573
2717
|
* @param {(payload: Partial<IState>) => unknown} prop.propsSubscription - Adapter prop change function
|
|
2574
|
-
* @param {string} prop.errorMessagePropName - error message property name to set errors onto component
|
|
2575
2718
|
* @returns {void}
|
|
2576
2719
|
*/
|
|
2577
2720
|
mountField({
|
|
@@ -2581,6 +2724,7 @@ class FormField {
|
|
|
2581
2724
|
this.initializeObservers();
|
|
2582
2725
|
this.subscribeValue(valueSubscription);
|
|
2583
2726
|
this.subscribeState(propsSubscription);
|
|
2727
|
+
this._mounted = true;
|
|
2584
2728
|
}
|
|
2585
2729
|
/**
|
|
2586
2730
|
* Sets the value of the form field and emits associated events.
|
|
@@ -2622,6 +2766,11 @@ class FormField {
|
|
|
2622
2766
|
this.apiEventQueueSubject$.next({
|
|
2623
2767
|
event
|
|
2624
2768
|
});
|
|
2769
|
+
this.fieldEventSubject$.next({
|
|
2770
|
+
event,
|
|
2771
|
+
fieldName: this.name,
|
|
2772
|
+
fieldInstance: this
|
|
2773
|
+
});
|
|
2625
2774
|
}
|
|
2626
2775
|
/**
|
|
2627
2776
|
* Sets the validity state of the field based on the provided validation rules and triggers error message updates.
|
|
@@ -2632,53 +2781,43 @@ class FormField {
|
|
|
2632
2781
|
setFieldValidity({
|
|
2633
2782
|
event
|
|
2634
2783
|
}) {
|
|
2635
|
-
var _a;
|
|
2636
|
-
if (!this.validations) {
|
|
2784
|
+
var _a, _b, _c, _d;
|
|
2785
|
+
if (!this.validations || !this.visibility) {
|
|
2786
|
+
this.errors = {};
|
|
2637
2787
|
this._valid = true;
|
|
2638
2788
|
return;
|
|
2639
2789
|
}
|
|
2640
|
-
/*
|
|
2641
|
-
@TODO evaluate if _valid flag needs to be updated on all events, this condition saves resources,
|
|
2642
|
-
currently form submition needs to be controlled with form instance submit property function that
|
|
2643
|
-
will evaluate if all fields are valid regardless the events that triggers error messages
|
|
2644
|
-
*/
|
|
2645
|
-
if (!this.validations.events.includes(event) && event !== 'ON_FORM_SUBMIT') return;
|
|
2646
2790
|
let valid = true;
|
|
2647
|
-
const errors =
|
|
2648
|
-
const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.
|
|
2791
|
+
const errors = {};
|
|
2792
|
+
const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.methods;
|
|
2649
2793
|
schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
|
|
2650
|
-
|
|
2651
|
-
const error = validations[validationKey](this.value, schemaValidations);
|
|
2794
|
+
const error = handleValidation(this.value, schemaValidations, validations, validationKey);
|
|
2652
2795
|
// setting valid flag
|
|
2653
2796
|
valid = !error && valid;
|
|
2654
2797
|
// setting error messages
|
|
2655
|
-
if (
|
|
2656
|
-
if (
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2798
|
+
if (error && this.errorMessages) {
|
|
2799
|
+
if (validationKey in this.errorMessages) {
|
|
2800
|
+
const messages = this.errorMessages;
|
|
2801
|
+
errors[validationKey] = messages[validationKey];
|
|
2802
|
+
} else if ('default' in this.errorMessages) {
|
|
2803
|
+
errors[validationKey] = this.errorMessages.default;
|
|
2660
2804
|
}
|
|
2805
|
+
} else {
|
|
2806
|
+
delete errors[validationKey];
|
|
2661
2807
|
}
|
|
2662
2808
|
});
|
|
2663
2809
|
this._valid = valid;
|
|
2664
|
-
this.
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
let valid = true;
|
|
2676
|
-
const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.config;
|
|
2677
|
-
schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
|
|
2678
|
-
const error = validations[validationKey](this.value, schemaValidations);
|
|
2679
|
-
valid = !error && valid;
|
|
2680
|
-
});
|
|
2681
|
-
this._valid = valid;
|
|
2810
|
+
if ((_c = (_b = this.validations) === null || _b === void 0 ? void 0 : _b.eventMessages) === null || _c === void 0 ? void 0 : _c[event]) {
|
|
2811
|
+
const eventMessages = {};
|
|
2812
|
+
(_d = this.validations.eventMessages[event]) === null || _d === void 0 ? void 0 : _d.forEach(method => {
|
|
2813
|
+
if (method in errors) {
|
|
2814
|
+
eventMessages[method] = errors[method];
|
|
2815
|
+
}
|
|
2816
|
+
});
|
|
2817
|
+
this.errors = eventMessages;
|
|
2818
|
+
} else if (event === 'ON_FORM_SUBMIT') {
|
|
2819
|
+
this.errors = errors;
|
|
2820
|
+
}
|
|
2682
2821
|
}
|
|
2683
2822
|
/**
|
|
2684
2823
|
* Formats the field value using the specified formatters, if available.
|
|
@@ -2712,7 +2851,7 @@ class FormField {
|
|
|
2712
2851
|
let valid = true;
|
|
2713
2852
|
const preConditions = config.preConditions;
|
|
2714
2853
|
preConditions && Object.keys(preConditions).forEach(validationKey => {
|
|
2715
|
-
const error =
|
|
2854
|
+
const error = handleValidation(this.value, preConditions, validations, validationKey);
|
|
2716
2855
|
valid = valid && !error;
|
|
2717
2856
|
});
|
|
2718
2857
|
if (config.blockRequestWhenInvalid) {
|
|
@@ -2808,6 +2947,7 @@ class FormField {
|
|
|
2808
2947
|
* @returns {void}
|
|
2809
2948
|
*/
|
|
2810
2949
|
destroyField() {
|
|
2950
|
+
this._mounted = false;
|
|
2811
2951
|
this.valueSubject$.unsubscribe();
|
|
2812
2952
|
this.visibilitySubject$.unsubscribe();
|
|
2813
2953
|
this.fieldStateSubscription$.unsubscribe();
|
|
@@ -2854,26 +2994,29 @@ class FormCore {
|
|
|
2854
2994
|
* @param {string} [entry.action] - The action attribute of the form.
|
|
2855
2995
|
* @param {string} [entry.method] - The method attribute of the form.
|
|
2856
2996
|
* @param {IFormSchema.iVars} [entry.iVars] - The internal variables of the form.
|
|
2857
|
-
* @param {(data: TFormValues) => void} [entry.onSubmit] - A callback function to handle form submission.
|
|
2858
2997
|
* @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
|
|
2859
2998
|
*/
|
|
2860
2999
|
constructor(entry) {
|
|
2861
|
-
var _a, _b, _c, _d;
|
|
3000
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
3001
|
+
this.mappers = new Map();
|
|
2862
3002
|
this.schema = entry.schema;
|
|
2863
3003
|
this.fields = new Map();
|
|
2864
3004
|
this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
|
|
2865
3005
|
this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
|
|
2866
3006
|
this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
|
|
2867
3007
|
this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
|
|
2868
|
-
this.
|
|
2869
|
-
|
|
2870
|
-
|
|
3008
|
+
this.config = {
|
|
3009
|
+
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,
|
|
3010
|
+
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
|
|
3011
|
+
};
|
|
3012
|
+
(_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
|
|
3013
|
+
this.mappers.set(mapper.componentName, mapper);
|
|
3014
|
+
});
|
|
2871
3015
|
this.schema && FormCore.checkIndexes(this.schema.components);
|
|
2872
3016
|
this.templateSubject$ = new Subject();
|
|
2873
3017
|
this.submitSubject$ = new Subject();
|
|
2874
|
-
this.
|
|
3018
|
+
this.fieldEventSubject$ = new Subject();
|
|
2875
3019
|
this.dataSubject$ = new Subject();
|
|
2876
|
-
this.dataCallbackSubscription$ = new Subscription();
|
|
2877
3020
|
this.subscribedTemplates = [];
|
|
2878
3021
|
this.schema && this.serializeStructure(this.schema.components);
|
|
2879
3022
|
this.schema && this.subscribeTemplates();
|
|
@@ -2882,8 +3025,6 @@ class FormCore {
|
|
|
2882
3025
|
key: IVARPROPNAME,
|
|
2883
3026
|
event: 'ON_IVARS'
|
|
2884
3027
|
});
|
|
2885
|
-
this.apiResponseSubject$.subscribe(this.refreshApi.bind(this));
|
|
2886
|
-
entry.onData && this.subscribeData(entry.onData);
|
|
2887
3028
|
/*
|
|
2888
3029
|
mount events needs to occur on form level, only when all the fields are instantiated
|
|
2889
3030
|
is it possible to apply all the side effects that occur globally, same effect occur
|
|
@@ -2934,20 +3075,17 @@ class FormCore {
|
|
|
2934
3075
|
* Subscribes to templates for dynamic updates.
|
|
2935
3076
|
*/
|
|
2936
3077
|
subscribeTemplates() {
|
|
2937
|
-
|
|
2938
|
-
@TODO fix removal of templates of removed fields, they are kept
|
|
2939
|
-
tried: this.subscribedTemplates = [] and only stores the last one..
|
|
2940
|
-
*/
|
|
3078
|
+
this.subscribedTemplates = [];
|
|
2941
3079
|
this.fields.forEach(({
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
3080
|
+
originalSchema: {
|
|
3081
|
+
component,
|
|
3082
|
+
props,
|
|
3083
|
+
name,
|
|
3084
|
+
validations,
|
|
3085
|
+
visibilityConditions,
|
|
3086
|
+
resetValues,
|
|
3087
|
+
api
|
|
3088
|
+
}
|
|
2951
3089
|
}, key) => {
|
|
2952
3090
|
const template = {
|
|
2953
3091
|
component,
|
|
@@ -2956,28 +3094,10 @@ class FormCore {
|
|
|
2956
3094
|
validations,
|
|
2957
3095
|
visibilityConditions,
|
|
2958
3096
|
resetValues,
|
|
2959
|
-
|
|
2960
|
-
apiSchema,
|
|
2961
|
-
metadata
|
|
3097
|
+
apiSchema: api
|
|
2962
3098
|
};
|
|
2963
3099
|
traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
|
|
2964
3100
|
});
|
|
2965
|
-
// console.log(subscribedProps);
|
|
2966
|
-
}
|
|
2967
|
-
/**
|
|
2968
|
-
*
|
|
2969
|
-
* @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
|
|
2970
|
-
*/
|
|
2971
|
-
subscribeData(callback) {
|
|
2972
|
-
var _a, _b;
|
|
2973
|
-
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(({
|
|
2974
|
-
key
|
|
2975
|
-
}) => ({
|
|
2976
|
-
field: key,
|
|
2977
|
-
data: this.getFormValues()
|
|
2978
|
-
}))).subscribe({
|
|
2979
|
-
next: callback
|
|
2980
|
-
});
|
|
2981
3101
|
}
|
|
2982
3102
|
/**
|
|
2983
3103
|
* Gets the value of a property from a field.
|
|
@@ -2997,8 +3117,12 @@ class FormCore {
|
|
|
2997
3117
|
const value = get(this.iVars, [property, ...path]);
|
|
2998
3118
|
return value;
|
|
2999
3119
|
}
|
|
3000
|
-
|
|
3001
|
-
return
|
|
3120
|
+
const field = this.fields.get(key);
|
|
3121
|
+
if (!field) return console.warn(`failed to get value from ${key}`);
|
|
3122
|
+
if (property === 'props' && path[0] === field.valuePropName) {
|
|
3123
|
+
return field.stateValue;
|
|
3124
|
+
}
|
|
3125
|
+
return path.length > 0 ? get(field[property], path) : field[property];
|
|
3002
3126
|
}
|
|
3003
3127
|
/**
|
|
3004
3128
|
* Sets the value of a property in a field.
|
|
@@ -3031,7 +3155,6 @@ class FormCore {
|
|
|
3031
3155
|
now using key !== originKey, check if any recursion error occurs
|
|
3032
3156
|
**/
|
|
3033
3157
|
if (property === 'props' && path[0] === field.valuePropName && key !== originKey) {
|
|
3034
|
-
// field.value = value;
|
|
3035
3158
|
field.emitValue({
|
|
3036
3159
|
event: 'ON_FIELD_CHANGE',
|
|
3037
3160
|
value
|
|
@@ -3062,18 +3185,17 @@ class FormCore {
|
|
|
3062
3185
|
* @returns {string[]} An array of extracted parameters.
|
|
3063
3186
|
*/
|
|
3064
3187
|
extractParams(expression) {
|
|
3065
|
-
const regex =
|
|
3188
|
+
const regex = TEMPLATE_REGEX_DELIMITATOR;
|
|
3066
3189
|
const extractedValues = [];
|
|
3067
3190
|
let match;
|
|
3068
3191
|
while (!isNil(match = regex.exec(expression))) {
|
|
3069
3192
|
extractedValues.push(match[1]);
|
|
3070
3193
|
}
|
|
3071
|
-
const operatorRegex =
|
|
3194
|
+
const operatorRegex = TEMPLATE_REGEX_OPERATOR_SPLITTER;
|
|
3072
3195
|
const splittedString = extractedValues.map(el => el.split(operatorRegex));
|
|
3073
3196
|
const result = splittedString.map(splittedStringVal => {
|
|
3074
|
-
// console.log(splittedStringVal)
|
|
3075
3197
|
return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
|
|
3076
|
-
if (curr.match(
|
|
3198
|
+
if (curr.match(TEMPLATE_REGEX_OPERATOR_MATCHER)) {
|
|
3077
3199
|
return `${acc}${curr}`;
|
|
3078
3200
|
}
|
|
3079
3201
|
let value;
|
|
@@ -3104,6 +3226,11 @@ class FormCore {
|
|
|
3104
3226
|
case 'object':
|
|
3105
3227
|
if (currValue === null) {
|
|
3106
3228
|
value = null;
|
|
3229
|
+
break;
|
|
3230
|
+
}
|
|
3231
|
+
if (currValue instanceof Date) {
|
|
3232
|
+
value = `new Date(\`${currValue}\`)`;
|
|
3233
|
+
break;
|
|
3107
3234
|
}
|
|
3108
3235
|
value = JSON.stringify(currValue);
|
|
3109
3236
|
break;
|
|
@@ -3115,7 +3242,6 @@ class FormCore {
|
|
|
3115
3242
|
});
|
|
3116
3243
|
return result.map(el => {
|
|
3117
3244
|
try {
|
|
3118
|
-
// console.log(el);
|
|
3119
3245
|
return new Function(`return ${el}`)();
|
|
3120
3246
|
} catch (e) {
|
|
3121
3247
|
console.log(e);
|
|
@@ -3131,8 +3257,8 @@ class FormCore {
|
|
|
3131
3257
|
* @returns {string} The expression string with the replacements made.
|
|
3132
3258
|
*/
|
|
3133
3259
|
replaceExpression(expression, values) {
|
|
3134
|
-
const regex =
|
|
3135
|
-
return expression.replace(regex, () => values.shift() || '');
|
|
3260
|
+
const regex = TEMPLATE_REGEX_DELIMITATOR;
|
|
3261
|
+
return expression.replace(regex, () => String(values.shift()) || '');
|
|
3136
3262
|
}
|
|
3137
3263
|
/**
|
|
3138
3264
|
* Checks if an expression string contains string concatenation within a marked expression.
|
|
@@ -3188,34 +3314,6 @@ class FormCore {
|
|
|
3188
3314
|
}
|
|
3189
3315
|
});
|
|
3190
3316
|
}
|
|
3191
|
-
/**
|
|
3192
|
-
* Refreshes api observed fields.
|
|
3193
|
-
*
|
|
3194
|
-
* @param {object} options - Options for refreshing api.
|
|
3195
|
-
* @param {string} options.key - The key of the field triggering the update.
|
|
3196
|
-
*/
|
|
3197
|
-
refreshApi({
|
|
3198
|
-
key
|
|
3199
|
-
}) {
|
|
3200
|
-
/*
|
|
3201
|
-
global api notifications needs to have field dependency array
|
|
3202
|
-
in order to be reliable, disabled for now
|
|
3203
|
-
*/
|
|
3204
|
-
return key;
|
|
3205
|
-
// const emmittedFields: string[] = [];
|
|
3206
|
-
// this.subscribedTemplates.forEach((template) => {
|
|
3207
|
-
// if (
|
|
3208
|
-
// template.originFieldKeys.includes(key) &&
|
|
3209
|
-
// template.originPropertyKeys.includes('api') &&
|
|
3210
|
-
// !emmittedFields.includes(template.destinationKey)
|
|
3211
|
-
// ) {
|
|
3212
|
-
// emmittedFields.push(template.destinationKey);
|
|
3213
|
-
// this.fields
|
|
3214
|
-
// .get(template.destinationKey)
|
|
3215
|
-
// ?.emitEvents({ event: 'ON_API_RESPONSE' });
|
|
3216
|
-
// }
|
|
3217
|
-
// });
|
|
3218
|
-
}
|
|
3219
3317
|
/**
|
|
3220
3318
|
* Validates visibility conditions for a given event and updates field visibility accordingly.
|
|
3221
3319
|
*
|
|
@@ -3233,13 +3331,13 @@ class FormCore {
|
|
|
3233
3331
|
structVisibility.forEach(structElement => {
|
|
3234
3332
|
if (!structElement.events.includes(event)) return;
|
|
3235
3333
|
Object.keys(structElement.validations).forEach(validationKey => {
|
|
3236
|
-
const error =
|
|
3334
|
+
const error = handleValidation(field.value, structElement.validations, validations, validationKey);
|
|
3237
3335
|
if (Array.isArray(structElement.fields)) {
|
|
3238
3336
|
structElement.fields.forEach(fieldKey => {
|
|
3239
|
-
if (!this.fields.has(fieldKey)) console.warn(`failed to update visibility onto field ${fieldKey}`);else this.fields.get(fieldKey).visibility = error;
|
|
3337
|
+
if (!this.fields.has(fieldKey)) console.warn(`failed to update visibility onto field ${fieldKey}`);else this.fields.get(fieldKey).visibility = structElement.showOnlyIfTrue ? error : !error;
|
|
3240
3338
|
});
|
|
3241
3339
|
} else if (structElement.fields) {
|
|
3242
|
-
if (!this.fields.has(structElement.fields)) console.warn(`failed to update visibility onto field ${structElement.fields}`);else this.fields.get(structElement.fields).visibility = error;
|
|
3340
|
+
if (!this.fields.has(structElement.fields)) console.warn(`failed to update visibility onto field ${structElement.fields}`);else this.fields.get(structElement.fields).visibility = structElement.showOnlyIfTrue ? error : !error;
|
|
3243
3341
|
}
|
|
3244
3342
|
});
|
|
3245
3343
|
});
|
|
@@ -3261,7 +3359,7 @@ class FormCore {
|
|
|
3261
3359
|
structResetValue.forEach(structElement => {
|
|
3262
3360
|
if (!structElement.events.includes(event)) return;
|
|
3263
3361
|
Object.keys(structElement.validations).forEach(validationKey => {
|
|
3264
|
-
const error =
|
|
3362
|
+
const error = handleValidation(field.value, structElement.validations, validations, validationKey);
|
|
3265
3363
|
if (!error) {
|
|
3266
3364
|
if (Array.isArray(structElement.fields)) {
|
|
3267
3365
|
structElement.fields.forEach((fieldKey, index) => {
|
|
@@ -3281,6 +3379,54 @@ class FormCore {
|
|
|
3281
3379
|
});
|
|
3282
3380
|
});
|
|
3283
3381
|
}
|
|
3382
|
+
/**
|
|
3383
|
+
* Adds a field onto the form instance regardless there is a schema or not
|
|
3384
|
+
*
|
|
3385
|
+
* @param fieldSchema
|
|
3386
|
+
*/
|
|
3387
|
+
addField({
|
|
3388
|
+
fieldSchema,
|
|
3389
|
+
mapperElement
|
|
3390
|
+
}) {
|
|
3391
|
+
var _a, _b, _c;
|
|
3392
|
+
if (this.fields.has(fieldSchema.name)) {
|
|
3393
|
+
throw new Error(`field name ${fieldSchema.name} already defined`);
|
|
3394
|
+
}
|
|
3395
|
+
const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(fieldSchema.component));
|
|
3396
|
+
if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
|
|
3397
|
+
this.fields.set(fieldSchema.name, new FormField({
|
|
3398
|
+
schemaComponent: fieldSchema,
|
|
3399
|
+
mapper,
|
|
3400
|
+
children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
|
|
3401
|
+
validateVisibility: this.validateVisibility.bind(this),
|
|
3402
|
+
resetValue: this.resetValue.bind(this),
|
|
3403
|
+
initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
|
|
3404
|
+
templateSubject$: this.templateSubject$,
|
|
3405
|
+
fieldEventSubject$: this.fieldEventSubject$,
|
|
3406
|
+
dataSubject$: this.dataSubject$,
|
|
3407
|
+
config: this.config
|
|
3408
|
+
}));
|
|
3409
|
+
this.subscribeTemplates();
|
|
3410
|
+
this.refreshTemplates({
|
|
3411
|
+
event: 'ON_FIELDS',
|
|
3412
|
+
key: fieldSchema.name
|
|
3413
|
+
});
|
|
3414
|
+
(_c = this.fields.get(fieldSchema.name)) === null || _c === void 0 ? void 0 : _c.emitEvents({
|
|
3415
|
+
event: 'ON_FIELD_MOUNT'
|
|
3416
|
+
});
|
|
3417
|
+
}
|
|
3418
|
+
removeField({
|
|
3419
|
+
key
|
|
3420
|
+
}) {
|
|
3421
|
+
var _a;
|
|
3422
|
+
(_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.destroyField();
|
|
3423
|
+
this.fields.delete(key);
|
|
3424
|
+
this.subscribeTemplates();
|
|
3425
|
+
this.templateSubject$.next({
|
|
3426
|
+
key,
|
|
3427
|
+
event: 'ON_FIELDS'
|
|
3428
|
+
});
|
|
3429
|
+
}
|
|
3284
3430
|
/**
|
|
3285
3431
|
* Serializes the schema structure to create form fields.
|
|
3286
3432
|
*
|
|
@@ -3290,10 +3436,15 @@ class FormCore {
|
|
|
3290
3436
|
serializeStructure(struct, path) {
|
|
3291
3437
|
if (!struct) return;
|
|
3292
3438
|
struct.forEach(structElement => {
|
|
3293
|
-
var _a, _b;
|
|
3439
|
+
var _a, _b, _c;
|
|
3294
3440
|
const currField = this.fields.get(structElement.name);
|
|
3295
3441
|
if (!currField) {
|
|
3296
|
-
|
|
3442
|
+
let mapper;
|
|
3443
|
+
if (structElement === null || structElement === void 0 ? void 0 : structElement.mapper) {
|
|
3444
|
+
mapper = structElement === null || structElement === void 0 ? void 0 : structElement.mapper;
|
|
3445
|
+
} else {
|
|
3446
|
+
mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(structElement.component);
|
|
3447
|
+
}
|
|
3297
3448
|
if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
|
|
3298
3449
|
this.fields.set(structElement.name, new FormField({
|
|
3299
3450
|
schemaComponent: structElement,
|
|
@@ -3302,19 +3453,20 @@ class FormCore {
|
|
|
3302
3453
|
children: structElement.children ? structElement.children.map(el => el.name) : [],
|
|
3303
3454
|
validateVisibility: this.validateVisibility.bind(this),
|
|
3304
3455
|
resetValue: this.resetValue.bind(this),
|
|
3305
|
-
initialValue: (
|
|
3456
|
+
initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[structElement.name],
|
|
3306
3457
|
templateSubject$: this.templateSubject$,
|
|
3307
|
-
|
|
3458
|
+
fieldEventSubject$: this.fieldEventSubject$,
|
|
3308
3459
|
dataSubject$: this.dataSubject$,
|
|
3309
3460
|
config: this.config
|
|
3310
3461
|
}));
|
|
3311
3462
|
} else {
|
|
3312
|
-
currField.children = ((
|
|
3463
|
+
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) || [];
|
|
3313
3464
|
currField.path = path;
|
|
3465
|
+
currField.originalSchema = structElement;
|
|
3314
3466
|
currField.templateSubject$ = this.templateSubject$;
|
|
3315
3467
|
}
|
|
3316
3468
|
if (structElement.children) {
|
|
3317
|
-
|
|
3469
|
+
this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
|
|
3318
3470
|
}
|
|
3319
3471
|
});
|
|
3320
3472
|
}
|
|
@@ -3371,7 +3523,7 @@ class FormCore {
|
|
|
3371
3523
|
const values = {};
|
|
3372
3524
|
this.fields.forEach((val, key) => {
|
|
3373
3525
|
if (val.value) {
|
|
3374
|
-
values
|
|
3526
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3375
3527
|
}
|
|
3376
3528
|
});
|
|
3377
3529
|
console.log(values);
|
|
@@ -3386,7 +3538,7 @@ class FormCore {
|
|
|
3386
3538
|
const erroredFields = [];
|
|
3387
3539
|
this.fields.forEach((val, key) => {
|
|
3388
3540
|
if (val.value) {
|
|
3389
|
-
values
|
|
3541
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3390
3542
|
}
|
|
3391
3543
|
if (!val.valid) {
|
|
3392
3544
|
erroredFields.push(key);
|
|
@@ -3398,6 +3550,35 @@ class FormCore {
|
|
|
3398
3550
|
isValid: this.isValid
|
|
3399
3551
|
};
|
|
3400
3552
|
}
|
|
3553
|
+
subscribeFieldEvent({
|
|
3554
|
+
callback
|
|
3555
|
+
}) {
|
|
3556
|
+
const sub = this.fieldEventSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS)))).subscribe({
|
|
3557
|
+
next: callback
|
|
3558
|
+
});
|
|
3559
|
+
return sub;
|
|
3560
|
+
}
|
|
3561
|
+
/**
|
|
3562
|
+
*
|
|
3563
|
+
* @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
|
|
3564
|
+
*/
|
|
3565
|
+
subscribeData(callback) {
|
|
3566
|
+
const sub = this.dataSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), map(({
|
|
3567
|
+
key
|
|
3568
|
+
}) => ({
|
|
3569
|
+
field: key,
|
|
3570
|
+
data: this.getFormValues()
|
|
3571
|
+
}))).subscribe({
|
|
3572
|
+
next: callback
|
|
3573
|
+
});
|
|
3574
|
+
return sub;
|
|
3575
|
+
}
|
|
3576
|
+
subscribeOnSubmit(callback) {
|
|
3577
|
+
const sub = this.submitSubject$.pipe(map(() => this.getFormValues())).subscribe({
|
|
3578
|
+
next: callback
|
|
3579
|
+
});
|
|
3580
|
+
return sub;
|
|
3581
|
+
}
|
|
3401
3582
|
/**
|
|
3402
3583
|
* Submits the form by triggering form field events and invoking the onSubmit callback.
|
|
3403
3584
|
*/
|
|
@@ -3410,12 +3591,11 @@ class FormCore {
|
|
|
3410
3591
|
if (!this.isValid) return;
|
|
3411
3592
|
const values = this.getFormValues();
|
|
3412
3593
|
this.submitSubject$.next(values);
|
|
3413
|
-
this.onSubmit && this.onSubmit(values);
|
|
3414
3594
|
}
|
|
3415
3595
|
destroy() {
|
|
3416
3596
|
this.submitSubject$.unsubscribe();
|
|
3417
3597
|
this.templateSubject$.unsubscribe();
|
|
3418
|
-
this.
|
|
3598
|
+
this.fieldEventSubject$.unsubscribe();
|
|
3419
3599
|
this.dataSubject$.unsubscribe();
|
|
3420
3600
|
this.fields.forEach(field => field.destroyField());
|
|
3421
3601
|
}
|
|
@@ -3431,15 +3611,22 @@ class FormCore {
|
|
|
3431
3611
|
*/
|
|
3432
3612
|
FormCore.checkIndexes = (struct, indexes = []) => {
|
|
3433
3613
|
if (!struct) return indexes;
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3614
|
+
const helper = (struct, indexes) => {
|
|
3615
|
+
for (let i = 0; i < struct.length; i++) {
|
|
3616
|
+
const structElement = struct[i];
|
|
3617
|
+
if (structElement.name === IVARPROPNAME) {
|
|
3618
|
+
throw new Error(`reserved ${IVARPROPNAME} name for field names`);
|
|
3619
|
+
}
|
|
3620
|
+
indexes.push(structElement.name);
|
|
3621
|
+
if (structElement.children) {
|
|
3622
|
+
helper(structElement.children, indexes);
|
|
3623
|
+
}
|
|
3442
3624
|
}
|
|
3625
|
+
};
|
|
3626
|
+
helper(struct, indexes);
|
|
3627
|
+
const duppedIndexes = indexes.filter((item, index) => indexes.indexOf(item) !== index);
|
|
3628
|
+
if (duppedIndexes.length > 0) {
|
|
3629
|
+
throw new Error(`duplicated indexes found on schema: ${JSON.stringify(duppedIndexes)}`);
|
|
3443
3630
|
}
|
|
3444
3631
|
return indexes;
|
|
3445
3632
|
};
|
|
@@ -3454,6 +3641,25 @@ class FormGroup {
|
|
|
3454
3641
|
constructor() {
|
|
3455
3642
|
this.forms = new Map();
|
|
3456
3643
|
}
|
|
3644
|
+
/**
|
|
3645
|
+
* Creates an empty form with given index
|
|
3646
|
+
*
|
|
3647
|
+
* @param {string} options.index
|
|
3648
|
+
* @param {TMapper<unknown>} options.mappers
|
|
3649
|
+
*/
|
|
3650
|
+
createFormWithIndex({
|
|
3651
|
+
index,
|
|
3652
|
+
mappers
|
|
3653
|
+
}) {
|
|
3654
|
+
const formInstance = new FormCore({
|
|
3655
|
+
index,
|
|
3656
|
+
mappers
|
|
3657
|
+
});
|
|
3658
|
+
this.addForm({
|
|
3659
|
+
key: index,
|
|
3660
|
+
formInstance
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3457
3663
|
/**
|
|
3458
3664
|
* Adds a form instance to the form group.
|
|
3459
3665
|
*
|
|
@@ -3496,6 +3702,20 @@ class FormGroup {
|
|
|
3496
3702
|
(_a = this.forms.get(key)) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
3497
3703
|
this.forms.delete(key);
|
|
3498
3704
|
}
|
|
3705
|
+
/**
|
|
3706
|
+
* removes a field given a form and field index
|
|
3707
|
+
*
|
|
3708
|
+
* @param {string} options.formIndex
|
|
3709
|
+
* @param {string} options.fieldIndex
|
|
3710
|
+
*/
|
|
3711
|
+
removeField({
|
|
3712
|
+
formIndex,
|
|
3713
|
+
fieldIndex
|
|
3714
|
+
}) {
|
|
3715
|
+
var _a, _b, _c;
|
|
3716
|
+
(_b = (_a = this.forms.get(formIndex)) === null || _a === void 0 ? void 0 : _a.fields.get(fieldIndex)) === null || _b === void 0 ? void 0 : _b.destroyField();
|
|
3717
|
+
(_c = this.forms.get(formIndex)) === null || _c === void 0 ? void 0 : _c.fields.delete(fieldIndex);
|
|
3718
|
+
}
|
|
3499
3719
|
/**
|
|
3500
3720
|
* Checks if the specified key already exists in the form group.
|
|
3501
3721
|
*
|
|
@@ -3521,22 +3741,49 @@ class FormGroup {
|
|
|
3521
3741
|
* @param {string[]} indexes form indexes to be submitted
|
|
3522
3742
|
* @returns
|
|
3523
3743
|
*/
|
|
3524
|
-
submitMultipleFormsByIndex(indexes) {
|
|
3744
|
+
submitMultipleFormsByIndex(indexes, callback) {
|
|
3525
3745
|
let isValid = true;
|
|
3526
3746
|
let values = {};
|
|
3527
3747
|
let erroredFields = [];
|
|
3528
3748
|
indexes.forEach(index => {
|
|
3529
|
-
var _a;
|
|
3530
|
-
|
|
3749
|
+
var _a, _b;
|
|
3750
|
+
(_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.submit();
|
|
3751
|
+
const res = (_b = this.forms.get(index)) === null || _b === void 0 ? void 0 : _b.getFormValues();
|
|
3531
3752
|
isValid = isValid && ((res === null || res === void 0 ? void 0 : res.isValid) || false);
|
|
3532
3753
|
values = Object.assign(Object.assign({}, values), (res === null || res === void 0 ? void 0 : res.values) || {});
|
|
3533
3754
|
erroredFields = [...erroredFields, ...((res === null || res === void 0 ? void 0 : res.erroredFields) || [])];
|
|
3534
3755
|
});
|
|
3535
|
-
|
|
3756
|
+
isValid && callback && callback({
|
|
3536
3757
|
erroredFields,
|
|
3537
3758
|
isValid,
|
|
3538
3759
|
values
|
|
3539
|
-
};
|
|
3760
|
+
});
|
|
3761
|
+
}
|
|
3762
|
+
onDataSubscription({
|
|
3763
|
+
ids,
|
|
3764
|
+
callback
|
|
3765
|
+
}) {
|
|
3766
|
+
const subs = ids.reduce((acc, formId) => {
|
|
3767
|
+
var _a;
|
|
3768
|
+
// @TODO add config on debounceTime on this events
|
|
3769
|
+
const sub = (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.dataSubject$.pipe(groupBy(payload => `${formId}.${payload.event}`), mergeMap(group$ => group$.pipe(debounceTime(100))), map(({
|
|
3770
|
+
key
|
|
3771
|
+
}) => {
|
|
3772
|
+
var _a;
|
|
3773
|
+
return {
|
|
3774
|
+
formField: key,
|
|
3775
|
+
values: (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.getFormValues()
|
|
3776
|
+
};
|
|
3777
|
+
}));
|
|
3778
|
+
if (sub) {
|
|
3779
|
+
acc[formId] = sub;
|
|
3780
|
+
} else {
|
|
3781
|
+
console.warn(`failed to register form id ${formId}`);
|
|
3782
|
+
}
|
|
3783
|
+
return acc;
|
|
3784
|
+
}, {});
|
|
3785
|
+
const sub = combineLatest(subs).subscribe(callback);
|
|
3786
|
+
return sub;
|
|
3540
3787
|
}
|
|
3541
3788
|
}
|
|
3542
3789
|
|