@bolttech/form-engine-core 0.0.1-beta.3 → 0.0.1-beta.30
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 +482 -265
- 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 +23 -54
- package/src/managers/form.d.ts +40 -34
- package/src/managers/formGroup.d.ts +13 -5
- 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} value - The date
|
|
1934
|
-
* @param {
|
|
1935
|
-
*
|
|
1936
|
+
* @param {string} value - 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,12 +2090,13 @@ 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
|
+
let originDate = new Date(mappedValue);
|
|
2057
2100
|
let targetDate = new Date();
|
|
2058
2101
|
let target = new Date();
|
|
2059
2102
|
if ((_g = (_f = validations.date) === null || _f === void 0 ? void 0 : _f.target) === null || _g === void 0 ? void 0 : _g.format) {
|
|
@@ -2072,6 +2115,9 @@ const date = (value, validations) => {
|
|
|
2072
2115
|
if (validations.date.onlyValidDate && (!(targetDate instanceof Date && isFinite(targetDate)) || !(targetDate instanceof Date && isFinite(originDate)) || originValue.length < 8)) {
|
|
2073
2116
|
return true;
|
|
2074
2117
|
}
|
|
2118
|
+
if (invalidStringDate(mappedValue, (_l = validations.date) === null || _l === void 0 ? void 0 : _l.origin.format)) {
|
|
2119
|
+
return false;
|
|
2120
|
+
}
|
|
2075
2121
|
const originTimestamp = originDate.getTime();
|
|
2076
2122
|
const targetTimestamp = targetDate.getTime();
|
|
2077
2123
|
const operationsMapper = {
|
|
@@ -2082,7 +2128,7 @@ const date = (value, validations) => {
|
|
|
2082
2128
|
'===': originTimestamp === targetTimestamp,
|
|
2083
2129
|
'!==': originTimestamp !== targetTimestamp
|
|
2084
2130
|
};
|
|
2085
|
-
return operationsMapper[(
|
|
2131
|
+
return operationsMapper[(_m = validations.date) === null || _m === void 0 ? void 0 : _m.operator];
|
|
2086
2132
|
};
|
|
2087
2133
|
/**
|
|
2088
2134
|
* @function validDate
|
|
@@ -2111,11 +2157,74 @@ const validDate = (value, validations) => {
|
|
|
2111
2157
|
const month = parseInt(dateParts[0], 10) - 1; // Month is zero-based
|
|
2112
2158
|
const day = parseInt(dateParts[1], 10);
|
|
2113
2159
|
const date = new Date(year, month, day);
|
|
2160
|
+
/*
|
|
2161
|
+
* Motivation: due to the scenario in which a date field may be 'typeable',
|
|
2162
|
+
* we need to ensure that dates less than a thousand and such years are valid.
|
|
2163
|
+
* E.g.: user types 13-07-199 (without this condition, it becomes a valid date, as 199 is a valid year)
|
|
2164
|
+
*
|
|
2165
|
+
* Ps: 150 is a valid value for now, as we still cannot live more than that, from the
|
|
2166
|
+
* the moment we start to live longer, we can rethink this.
|
|
2167
|
+
*/
|
|
2168
|
+
const today = new Date();
|
|
2169
|
+
today.setFullYear(today.getFullYear() - 150);
|
|
2170
|
+
if (date.getFullYear() < today.getFullYear()) {
|
|
2171
|
+
return true;
|
|
2172
|
+
}
|
|
2114
2173
|
// Check if the date is valid
|
|
2115
2174
|
const isValidDate = date.getFullYear() === year && date.getMonth() === month && date.getDate() === day;
|
|
2116
2175
|
return !isValidDate;
|
|
2117
2176
|
};
|
|
2118
2177
|
|
|
2178
|
+
/**
|
|
2179
|
+
* @internal
|
|
2180
|
+
* Runs a set of validation handlers against a given value.
|
|
2181
|
+
*
|
|
2182
|
+
* @param {unknown} value - The value to be validated.
|
|
2183
|
+
* @param {TValidationMethods} handlers - An object containing validation methods to be applied.
|
|
2184
|
+
* @param {TValidationHandler} validations - An object containing every validation methods to be executed.
|
|
2185
|
+
* @returns {boolean[]} - An array of boolean results for each validation method.
|
|
2186
|
+
*
|
|
2187
|
+
* @example
|
|
2188
|
+
* const handlers = {
|
|
2189
|
+
* max: { max: 10 },
|
|
2190
|
+
* required: true,
|
|
2191
|
+
* email: true
|
|
2192
|
+
* };
|
|
2193
|
+
* const results = run('test@example.com', handlers);
|
|
2194
|
+
* console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
|
|
2195
|
+
*/
|
|
2196
|
+
function run$1(value, handlers, validations) {
|
|
2197
|
+
const runner = [];
|
|
2198
|
+
Object.keys(handlers).forEach(rule => {
|
|
2199
|
+
runner.push(validations[rule](value, {
|
|
2200
|
+
[rule]: handlers[rule]
|
|
2201
|
+
}));
|
|
2202
|
+
});
|
|
2203
|
+
return runner;
|
|
2204
|
+
}
|
|
2205
|
+
/**
|
|
2206
|
+
* Validates a given value based on specified validation methods inside a custom named validation.
|
|
2207
|
+
*
|
|
2208
|
+
* @param {unknown} value - The value to be validated.
|
|
2209
|
+
* @param {TValidationMethods} methods - The validation methods to be applied.
|
|
2210
|
+
* @param {TValidationHandler} validations - An object containing every validation methods to be executed.
|
|
2211
|
+
* @returns {boolean} - Returns true if any of the validation methods pass, otherwise false.
|
|
2212
|
+
*
|
|
2213
|
+
* @example
|
|
2214
|
+
* const value = 'example@example.com';
|
|
2215
|
+
* const methods = {
|
|
2216
|
+
* required: true,
|
|
2217
|
+
* email: true
|
|
2218
|
+
* };
|
|
2219
|
+
*
|
|
2220
|
+
* const isValid = validateValue(value, methods);
|
|
2221
|
+
* console.log(isValid); // Output: true
|
|
2222
|
+
*/
|
|
2223
|
+
var namedRule = ((value, methods, validations) => {
|
|
2224
|
+
if (!methods) return false;
|
|
2225
|
+
return run$1(value, methods, validations).some(validation => validation);
|
|
2226
|
+
});
|
|
2227
|
+
|
|
2119
2228
|
/**
|
|
2120
2229
|
* @internal
|
|
2121
2230
|
* An object mapping validation keys to their respective validation functions.
|
|
@@ -2150,7 +2259,6 @@ const validations$1 = {
|
|
|
2150
2259
|
notEmpty,
|
|
2151
2260
|
bool,
|
|
2152
2261
|
exists,
|
|
2153
|
-
greaterThan: () => true,
|
|
2154
2262
|
isNumber,
|
|
2155
2263
|
conditions,
|
|
2156
2264
|
validDate,
|
|
@@ -2174,20 +2282,26 @@ const validations$1 = {
|
|
|
2174
2282
|
* const results = run('test@example.com', handlers);
|
|
2175
2283
|
* console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
|
|
2176
2284
|
*/
|
|
2177
|
-
|
|
2285
|
+
function run(value, handlers) {
|
|
2178
2286
|
const runner = [];
|
|
2179
2287
|
Object.keys(handlers).forEach(rule => {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2288
|
+
let handler;
|
|
2289
|
+
if (isFunction(validations$1[rule])) {
|
|
2290
|
+
handler = validations$1[rule](value, {
|
|
2291
|
+
[rule]: handlers[rule]
|
|
2292
|
+
});
|
|
2293
|
+
} else {
|
|
2294
|
+
handler = namedRule(value, handlers[rule], validations$1);
|
|
2295
|
+
}
|
|
2296
|
+
runner.push(handler);
|
|
2183
2297
|
});
|
|
2184
2298
|
return runner;
|
|
2185
|
-
}
|
|
2299
|
+
}
|
|
2186
2300
|
/**
|
|
2187
2301
|
* Validates that a value meets multiple validation rules.
|
|
2188
2302
|
*
|
|
2189
2303
|
* @param {number | string | boolean} value - The value to be validated.
|
|
2190
|
-
* @param {TValidationMethods}
|
|
2304
|
+
* @param {TValidationMethods} methods - The validation methods object containing the multipleValidations rule set.
|
|
2191
2305
|
* @returns {boolean} - Returns `true` if the value meets the specified multiple validation rules, otherwise `false`.
|
|
2192
2306
|
*
|
|
2193
2307
|
* @example
|
|
@@ -2212,15 +2326,15 @@ const run = (value, handlers) => {
|
|
|
2212
2326
|
* console.log(result2); // false
|
|
2213
2327
|
* ```
|
|
2214
2328
|
*/
|
|
2215
|
-
const multipleValidations = (value,
|
|
2216
|
-
if (!
|
|
2217
|
-
const runner = run(value,
|
|
2329
|
+
const multipleValidations = (value, methods) => {
|
|
2330
|
+
if (!methods.multipleValidations) return false;
|
|
2331
|
+
const runner = run(value, methods.multipleValidations.validations);
|
|
2218
2332
|
const rulesMapper = {
|
|
2219
2333
|
AND: () => runner.every(validation => validation),
|
|
2220
2334
|
OR: () => runner.some(validation => validation),
|
|
2221
2335
|
NOT: () => !runner.every(validation => validation)
|
|
2222
2336
|
};
|
|
2223
|
-
return rulesMapper[
|
|
2337
|
+
return rulesMapper[methods.multipleValidations.rule]();
|
|
2224
2338
|
};
|
|
2225
2339
|
|
|
2226
2340
|
const validations = {
|
|
@@ -2247,7 +2361,6 @@ const validations = {
|
|
|
2247
2361
|
notEmpty,
|
|
2248
2362
|
bool,
|
|
2249
2363
|
exists,
|
|
2250
|
-
greaterThan: () => true,
|
|
2251
2364
|
isNumber,
|
|
2252
2365
|
conditions,
|
|
2253
2366
|
multipleValidations,
|
|
@@ -2256,6 +2369,53 @@ const validations = {
|
|
|
2256
2369
|
validDate
|
|
2257
2370
|
};
|
|
2258
2371
|
|
|
2372
|
+
/**
|
|
2373
|
+
* Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
|
|
2374
|
+
* that were unmounted due to adapter external handling such as visibility
|
|
2375
|
+
*/
|
|
2376
|
+
class SafeSubject extends Subject {
|
|
2377
|
+
constructor(isMounted) {
|
|
2378
|
+
super();
|
|
2379
|
+
this.isMounted = isMounted;
|
|
2380
|
+
}
|
|
2381
|
+
next(value) {
|
|
2382
|
+
if (this.isMounted()) {
|
|
2383
|
+
super.next(value);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
/**
|
|
2389
|
+
* @internal
|
|
2390
|
+
* Handles the validation of a given value based on specified validation methods and rules.
|
|
2391
|
+
*
|
|
2392
|
+
* @param {string | number | boolean | unknown} value - The value to be validated.
|
|
2393
|
+
* @param {TSchemaValidation} validations - The schema validations to be applied.
|
|
2394
|
+
* @param {TValidationHandler} methods - The validation handler methods.
|
|
2395
|
+
* @param {keyof TValidationMethods} key - The specific key of the validation method to be used.
|
|
2396
|
+
* @returns {boolean} - Returns true if the value passes the validation, otherwise false.
|
|
2397
|
+
*
|
|
2398
|
+
* @example
|
|
2399
|
+
* const value = 'example@example.com';
|
|
2400
|
+
* const validations = {
|
|
2401
|
+
* required: true,
|
|
2402
|
+
* customName: { email: true }
|
|
2403
|
+
* };
|
|
2404
|
+
* const methods = {
|
|
2405
|
+
* email: (value) => /\S+@\S+\.\S+/.test(value)
|
|
2406
|
+
* };
|
|
2407
|
+
* const key = 'required';
|
|
2408
|
+
*
|
|
2409
|
+
* const isValid = handleValidation(value, validations, methods, key);
|
|
2410
|
+
* console.log(isValid); // Output: true
|
|
2411
|
+
*/
|
|
2412
|
+
function handleValidation(value, validations, methods, key) {
|
|
2413
|
+
if (isFunction(methods[key])) {
|
|
2414
|
+
return methods[key](value, validations);
|
|
2415
|
+
}
|
|
2416
|
+
return namedRule(value, validations[key], methods);
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2259
2419
|
/**
|
|
2260
2420
|
* Represents a form field with observables for managing form state, validations, and API requests.
|
|
2261
2421
|
*/
|
|
@@ -2282,40 +2442,41 @@ class FormField {
|
|
|
2282
2442
|
resetValue,
|
|
2283
2443
|
initialValue,
|
|
2284
2444
|
templateSubject$,
|
|
2285
|
-
|
|
2445
|
+
fieldEventSubject$,
|
|
2286
2446
|
dataSubject$,
|
|
2287
2447
|
mapper
|
|
2288
2448
|
}) {
|
|
2289
2449
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
2290
2450
|
this.fieldStateSubscription$ = new Subscription();
|
|
2451
|
+
this.originalSchema = schemaComponent;
|
|
2291
2452
|
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) :
|
|
2453
|
+
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,
|
|
2454
|
+
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
2455
|
};
|
|
2295
2456
|
this.name = schemaComponent.name;
|
|
2457
|
+
this.nameToSubmit = schemaComponent.nameToSubmit;
|
|
2296
2458
|
this.component = schemaComponent.component;
|
|
2297
2459
|
this.path = path;
|
|
2298
2460
|
this.children = children;
|
|
2299
2461
|
this.validations = schemaComponent.validations;
|
|
2300
|
-
this.errorMessages = schemaComponent.
|
|
2462
|
+
this.errorMessages = (_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages;
|
|
2301
2463
|
this.visibilityConditions = schemaComponent.visibilityConditions;
|
|
2302
2464
|
this.resetValues = schemaComponent.resetValues;
|
|
2303
2465
|
this.apiSchema = schemaComponent.api;
|
|
2304
2466
|
this.formatters = schemaComponent.formatters;
|
|
2305
2467
|
this.masks = schemaComponent.masks;
|
|
2306
2468
|
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;
|
|
2469
|
+
if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) this.valuePropName = mapper.events.setValue;
|
|
2309
2470
|
this.mapper = mapper;
|
|
2310
2471
|
this.validateVisibility = validateVisibility;
|
|
2311
2472
|
this.resetValue = resetValue;
|
|
2312
2473
|
this.templateSubject$ = templateSubject$;
|
|
2313
|
-
this.
|
|
2474
|
+
this.fieldEventSubject$ = fieldEventSubject$;
|
|
2314
2475
|
this.dataSubject$ = dataSubject$;
|
|
2315
2476
|
this._props = schemaComponent.props || {};
|
|
2316
|
-
this._value = '';
|
|
2317
|
-
this._stateValue = '';
|
|
2318
2477
|
this._metadata = '';
|
|
2478
|
+
this.errorsString = '';
|
|
2479
|
+
this.errorsList = [];
|
|
2319
2480
|
this.initialValue = initialValue;
|
|
2320
2481
|
this._visibility = true;
|
|
2321
2482
|
this._api = {
|
|
@@ -2331,9 +2492,10 @@ class FormField {
|
|
|
2331
2492
|
}, {})
|
|
2332
2493
|
};
|
|
2333
2494
|
this._errors = {};
|
|
2334
|
-
this._errorsString = '';
|
|
2335
2495
|
this._valid = false;
|
|
2496
|
+
this._mounted = true;
|
|
2336
2497
|
this.initializeObservers();
|
|
2498
|
+
this.value = this.initialValue;
|
|
2337
2499
|
}
|
|
2338
2500
|
/**
|
|
2339
2501
|
* method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
|
|
@@ -2341,53 +2503,38 @@ class FormField {
|
|
|
2341
2503
|
initializeObservers() {
|
|
2342
2504
|
var _a;
|
|
2343
2505
|
if (!this.valueSubject$ || this.valueSubject$.closed) {
|
|
2344
|
-
this.valueSubject$ = new
|
|
2506
|
+
this.valueSubject$ = new SafeSubject(() => this._mounted);
|
|
2345
2507
|
}
|
|
2346
2508
|
if (!this.errorSubject$ || this.errorSubject$.closed) {
|
|
2347
|
-
this.errorSubject$ = new
|
|
2509
|
+
this.errorSubject$ = new SafeSubject(() => this._mounted);
|
|
2348
2510
|
}
|
|
2349
2511
|
if (!this.visibilitySubject$ || this.visibilitySubject$.closed) {
|
|
2350
|
-
this.visibilitySubject$ = new
|
|
2512
|
+
this.visibilitySubject$ = new SafeSubject(() => this._mounted);
|
|
2351
2513
|
}
|
|
2352
2514
|
if (!this.apiSubject$ || this.apiSubject$.closed) {
|
|
2353
|
-
this.apiSubject$ = new
|
|
2515
|
+
this.apiSubject$ = new SafeSubject(() => this._mounted);
|
|
2354
2516
|
}
|
|
2355
2517
|
if (!this.propsSubject$ || this.propsSubject$.closed) {
|
|
2356
|
-
this.propsSubject$ = new
|
|
2518
|
+
this.propsSubject$ = new SafeSubject(() => this._mounted);
|
|
2357
2519
|
}
|
|
2358
2520
|
if (!this.fieldStateSubscription$ || this.fieldStateSubscription$.closed) {
|
|
2359
2521
|
this.fieldStateSubscription$ = new Subscription();
|
|
2360
2522
|
}
|
|
2361
2523
|
if (!this.apiEventQueueSubject$ || this.apiEventQueueSubject$.closed) {
|
|
2362
|
-
this.apiEventQueueSubject$ = new
|
|
2524
|
+
this.apiEventQueueSubject$ = new SafeSubject(() => this._mounted);
|
|
2363
2525
|
}
|
|
2364
2526
|
this.fieldState$ = combineLatest({
|
|
2365
|
-
errors: this.errorSubject$.pipe(startWith([])),
|
|
2366
2527
|
visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
|
|
2367
|
-
|
|
2368
|
-
|
|
2528
|
+
props: this.propsSubject$.pipe(startWith(this._props)),
|
|
2529
|
+
errors: this.errorSubject$.pipe(startWith(Object.assign({}, ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && {
|
|
2530
|
+
[this.mapper.events.setErrorMessage]: this.errorsString
|
|
2531
|
+
})))
|
|
2369
2532
|
});
|
|
2370
|
-
!this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(
|
|
2533
|
+
!this.apiEventQueueSubject$.observed && this.apiEventQueueSubject$.pipe(groupBy(({
|
|
2371
2534
|
event
|
|
2372
|
-
}) => event, (
|
|
2535
|
+
}) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
|
|
2373
2536
|
this.apiRequest(payload);
|
|
2374
2537
|
});
|
|
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
2538
|
}
|
|
2392
2539
|
/**
|
|
2393
2540
|
* Retrieves the properties associated with the form field.
|
|
@@ -2414,7 +2561,7 @@ class FormField {
|
|
|
2414
2561
|
/**
|
|
2415
2562
|
* Retrieves the current state value of the form field.
|
|
2416
2563
|
*
|
|
2417
|
-
* @returns {unknown} - The current state value of the form field.
|
|
2564
|
+
* @returns {Record<string,unknown>} - The current state value of the form field.
|
|
2418
2565
|
*/
|
|
2419
2566
|
get stateValue() {
|
|
2420
2567
|
return this._stateValue;
|
|
@@ -2422,14 +2569,6 @@ class FormField {
|
|
|
2422
2569
|
get metadata() {
|
|
2423
2570
|
return this._metadata;
|
|
2424
2571
|
}
|
|
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
2572
|
/**
|
|
2434
2573
|
* Retrieves the current value of the form field.
|
|
2435
2574
|
*
|
|
@@ -2444,6 +2583,7 @@ class FormField {
|
|
|
2444
2583
|
* @param {unknown} value - The new value to be set.
|
|
2445
2584
|
*/
|
|
2446
2585
|
set value(value) {
|
|
2586
|
+
var _a, _b, _c;
|
|
2447
2587
|
/*
|
|
2448
2588
|
too much unstable, if the valueChangeEvent parses the template event
|
|
2449
2589
|
value, might occur unexpected results
|
|
@@ -2463,22 +2603,19 @@ class FormField {
|
|
|
2463
2603
|
if (typeof val === 'undefined' || val === null) return;
|
|
2464
2604
|
if (typeof val === 'object' && '_value' in val && '_metadata' in val) {
|
|
2465
2605
|
this._value = this.formatValue(val['_value']);
|
|
2466
|
-
this._stateValue =
|
|
2606
|
+
this._stateValue = ((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setValue) ? {
|
|
2607
|
+
[this.mapper.events.setValue]: this.maskValue(this.formatValue(val['_value']))
|
|
2608
|
+
} : {};
|
|
2467
2609
|
this._metadata = val._metadata;
|
|
2468
2610
|
} else {
|
|
2469
2611
|
this._value = this.formatValue(val);
|
|
2470
|
-
this._stateValue =
|
|
2612
|
+
this._stateValue = ((_b = this.mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) ? {
|
|
2613
|
+
[(_c = this.mapper.events) === null || _c === void 0 ? void 0 : _c.setValue]: this.maskValue(this.formatValue(val))
|
|
2614
|
+
} : {};
|
|
2615
|
+
this.maskValue(this.formatValue(val));
|
|
2471
2616
|
this._metadata = val;
|
|
2472
2617
|
}
|
|
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);
|
|
2618
|
+
this.stateValue && this.valueSubject$.next(this.stateValue);
|
|
2482
2619
|
this.templateSubject$.next({
|
|
2483
2620
|
key: this.name,
|
|
2484
2621
|
event: 'ON_VALUE'
|
|
@@ -2500,6 +2637,23 @@ class FormField {
|
|
|
2500
2637
|
set visibility(visible) {
|
|
2501
2638
|
if (typeof visible === 'undefined' || visible === this.visibility) return;
|
|
2502
2639
|
this._visibility = visible;
|
|
2640
|
+
/**
|
|
2641
|
+
* I was sure I would not require to gambiarra, but..
|
|
2642
|
+
* in order to ignore an hidden value on a form submit
|
|
2643
|
+
* or revalidate it when it comes back to visibility
|
|
2644
|
+
* I needed to...
|
|
2645
|
+
* I don't recommend setting private properties like this
|
|
2646
|
+
* this will force the field to be valid when it's hidden
|
|
2647
|
+
* and trigger the validation when it's visible
|
|
2648
|
+
*/
|
|
2649
|
+
if (!this.visibility) {
|
|
2650
|
+
this.value = '';
|
|
2651
|
+
this._valid = true;
|
|
2652
|
+
} else {
|
|
2653
|
+
this.setFieldValidity({
|
|
2654
|
+
event: 'ON_FIELD_MOUNT'
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2503
2657
|
this.visibilitySubject$.next(this.visibility);
|
|
2504
2658
|
this.templateSubject$.next({
|
|
2505
2659
|
key: this.name,
|
|
@@ -2528,10 +2682,17 @@ class FormField {
|
|
|
2528
2682
|
* @param {TErrorMessages} errors - The new error messages to be set.
|
|
2529
2683
|
*/
|
|
2530
2684
|
set errors(errors) {
|
|
2685
|
+
var _a;
|
|
2531
2686
|
if (typeof errors === 'undefined' || isEqual(errors, this.errors)) return;
|
|
2532
2687
|
this._errors = errors;
|
|
2533
|
-
this.
|
|
2534
|
-
this.
|
|
2688
|
+
this.errorsList = Object.values(this.errors);
|
|
2689
|
+
this.errorsString = this.errorsList.join(', ');
|
|
2690
|
+
/**
|
|
2691
|
+
* if any error receives a list of errors, set a prop for it, currently only supporting a single string
|
|
2692
|
+
*/
|
|
2693
|
+
((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && this.errorSubject$.next({
|
|
2694
|
+
[this.mapper.events.setErrorMessage]: this.errorsString
|
|
2695
|
+
});
|
|
2535
2696
|
this.templateSubject$.next({
|
|
2536
2697
|
key: this.name,
|
|
2537
2698
|
event: 'ON_PROPS'
|
|
@@ -2567,11 +2728,8 @@ class FormField {
|
|
|
2567
2728
|
* Mounts the form field by initializing necessary subjects and combining their streams.
|
|
2568
2729
|
*
|
|
2569
2730
|
* @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
2731
|
* @param {(value: unknown) => unknown} prop.valueSubscription - Adapter value change function
|
|
2573
2732
|
* @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
2733
|
* @returns {void}
|
|
2576
2734
|
*/
|
|
2577
2735
|
mountField({
|
|
@@ -2581,6 +2739,7 @@ class FormField {
|
|
|
2581
2739
|
this.initializeObservers();
|
|
2582
2740
|
this.subscribeValue(valueSubscription);
|
|
2583
2741
|
this.subscribeState(propsSubscription);
|
|
2742
|
+
this._mounted = true;
|
|
2584
2743
|
}
|
|
2585
2744
|
/**
|
|
2586
2745
|
* Sets the value of the form field and emits associated events.
|
|
@@ -2622,6 +2781,11 @@ class FormField {
|
|
|
2622
2781
|
this.apiEventQueueSubject$.next({
|
|
2623
2782
|
event
|
|
2624
2783
|
});
|
|
2784
|
+
this.fieldEventSubject$.next({
|
|
2785
|
+
event,
|
|
2786
|
+
fieldName: this.name,
|
|
2787
|
+
fieldInstance: this
|
|
2788
|
+
});
|
|
2625
2789
|
}
|
|
2626
2790
|
/**
|
|
2627
2791
|
* Sets the validity state of the field based on the provided validation rules and triggers error message updates.
|
|
@@ -2632,53 +2796,43 @@ class FormField {
|
|
|
2632
2796
|
setFieldValidity({
|
|
2633
2797
|
event
|
|
2634
2798
|
}) {
|
|
2635
|
-
var _a;
|
|
2636
|
-
if (!this.validations) {
|
|
2799
|
+
var _a, _b, _c, _d;
|
|
2800
|
+
if (!this.validations || !this.visibility) {
|
|
2801
|
+
this.errors = {};
|
|
2637
2802
|
this._valid = true;
|
|
2638
2803
|
return;
|
|
2639
2804
|
}
|
|
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
2805
|
let valid = true;
|
|
2647
|
-
const errors =
|
|
2648
|
-
const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.
|
|
2806
|
+
const errors = {};
|
|
2807
|
+
const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.methods;
|
|
2649
2808
|
schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
|
|
2650
|
-
|
|
2651
|
-
const error = validations[validationKey](this.value, schemaValidations);
|
|
2809
|
+
const error = handleValidation(this.value, schemaValidations, validations, validationKey);
|
|
2652
2810
|
// setting valid flag
|
|
2653
2811
|
valid = !error && valid;
|
|
2654
2812
|
// setting error messages
|
|
2655
|
-
if (
|
|
2656
|
-
if (
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2813
|
+
if (error && this.errorMessages) {
|
|
2814
|
+
if (validationKey in this.errorMessages) {
|
|
2815
|
+
const messages = this.errorMessages;
|
|
2816
|
+
errors[validationKey] = messages[validationKey];
|
|
2817
|
+
} else if ('default' in this.errorMessages) {
|
|
2818
|
+
errors[validationKey] = this.errorMessages.default;
|
|
2660
2819
|
}
|
|
2820
|
+
} else {
|
|
2821
|
+
delete errors[validationKey];
|
|
2661
2822
|
}
|
|
2662
2823
|
});
|
|
2663
2824
|
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;
|
|
2825
|
+
if ((_c = (_b = this.validations) === null || _b === void 0 ? void 0 : _b.eventMessages) === null || _c === void 0 ? void 0 : _c[event]) {
|
|
2826
|
+
const eventMessages = {};
|
|
2827
|
+
(_d = this.validations.eventMessages[event]) === null || _d === void 0 ? void 0 : _d.forEach(method => {
|
|
2828
|
+
if (method in errors) {
|
|
2829
|
+
eventMessages[method] = errors[method];
|
|
2830
|
+
}
|
|
2831
|
+
});
|
|
2832
|
+
this.errors = eventMessages;
|
|
2833
|
+
} else if (event === 'ON_FORM_SUBMIT') {
|
|
2834
|
+
this.errors = errors;
|
|
2835
|
+
}
|
|
2682
2836
|
}
|
|
2683
2837
|
/**
|
|
2684
2838
|
* Formats the field value using the specified formatters, if available.
|
|
@@ -2712,7 +2866,7 @@ class FormField {
|
|
|
2712
2866
|
let valid = true;
|
|
2713
2867
|
const preConditions = config.preConditions;
|
|
2714
2868
|
preConditions && Object.keys(preConditions).forEach(validationKey => {
|
|
2715
|
-
const error =
|
|
2869
|
+
const error = handleValidation(this.value, preConditions, validations, validationKey);
|
|
2716
2870
|
valid = valid && !error;
|
|
2717
2871
|
});
|
|
2718
2872
|
if (config.blockRequestWhenInvalid) {
|
|
@@ -2808,6 +2962,7 @@ class FormField {
|
|
|
2808
2962
|
* @returns {void}
|
|
2809
2963
|
*/
|
|
2810
2964
|
destroyField() {
|
|
2965
|
+
this._mounted = false;
|
|
2811
2966
|
this.valueSubject$.unsubscribe();
|
|
2812
2967
|
this.visibilitySubject$.unsubscribe();
|
|
2813
2968
|
this.fieldStateSubscription$.unsubscribe();
|
|
@@ -2854,26 +3009,29 @@ class FormCore {
|
|
|
2854
3009
|
* @param {string} [entry.action] - The action attribute of the form.
|
|
2855
3010
|
* @param {string} [entry.method] - The method attribute of the form.
|
|
2856
3011
|
* @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
3012
|
* @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
|
|
2859
3013
|
*/
|
|
2860
3014
|
constructor(entry) {
|
|
2861
|
-
var _a, _b, _c, _d;
|
|
3015
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
3016
|
+
this.mappers = new Map();
|
|
2862
3017
|
this.schema = entry.schema;
|
|
2863
3018
|
this.fields = new Map();
|
|
2864
3019
|
this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
|
|
2865
3020
|
this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
|
|
2866
3021
|
this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
|
|
2867
3022
|
this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
|
|
2868
|
-
this.
|
|
2869
|
-
|
|
2870
|
-
|
|
3023
|
+
this.config = {
|
|
3024
|
+
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,
|
|
3025
|
+
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
|
|
3026
|
+
};
|
|
3027
|
+
(_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
|
|
3028
|
+
this.mappers.set(mapper.componentName, mapper);
|
|
3029
|
+
});
|
|
2871
3030
|
this.schema && FormCore.checkIndexes(this.schema.components);
|
|
2872
3031
|
this.templateSubject$ = new Subject();
|
|
2873
3032
|
this.submitSubject$ = new Subject();
|
|
2874
|
-
this.
|
|
3033
|
+
this.fieldEventSubject$ = new Subject();
|
|
2875
3034
|
this.dataSubject$ = new Subject();
|
|
2876
|
-
this.dataCallbackSubscription$ = new Subscription();
|
|
2877
3035
|
this.subscribedTemplates = [];
|
|
2878
3036
|
this.schema && this.serializeStructure(this.schema.components);
|
|
2879
3037
|
this.schema && this.subscribeTemplates();
|
|
@@ -2882,8 +3040,6 @@ class FormCore {
|
|
|
2882
3040
|
key: IVARPROPNAME,
|
|
2883
3041
|
event: 'ON_IVARS'
|
|
2884
3042
|
});
|
|
2885
|
-
this.apiResponseSubject$.subscribe(this.refreshApi.bind(this));
|
|
2886
|
-
entry.onData && this.subscribeData(entry.onData);
|
|
2887
3043
|
/*
|
|
2888
3044
|
mount events needs to occur on form level, only when all the fields are instantiated
|
|
2889
3045
|
is it possible to apply all the side effects that occur globally, same effect occur
|
|
@@ -2934,20 +3090,17 @@ class FormCore {
|
|
|
2934
3090
|
* Subscribes to templates for dynamic updates.
|
|
2935
3091
|
*/
|
|
2936
3092
|
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
|
-
*/
|
|
3093
|
+
this.subscribedTemplates = [];
|
|
2941
3094
|
this.fields.forEach(({
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
3095
|
+
originalSchema: {
|
|
3096
|
+
component,
|
|
3097
|
+
props,
|
|
3098
|
+
name,
|
|
3099
|
+
validations,
|
|
3100
|
+
visibilityConditions,
|
|
3101
|
+
resetValues,
|
|
3102
|
+
api
|
|
3103
|
+
}
|
|
2951
3104
|
}, key) => {
|
|
2952
3105
|
const template = {
|
|
2953
3106
|
component,
|
|
@@ -2956,28 +3109,10 @@ class FormCore {
|
|
|
2956
3109
|
validations,
|
|
2957
3110
|
visibilityConditions,
|
|
2958
3111
|
resetValues,
|
|
2959
|
-
|
|
2960
|
-
apiSchema,
|
|
2961
|
-
metadata
|
|
3112
|
+
apiSchema: api
|
|
2962
3113
|
};
|
|
2963
3114
|
traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
|
|
2964
3115
|
});
|
|
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
3116
|
}
|
|
2982
3117
|
/**
|
|
2983
3118
|
* Gets the value of a property from a field.
|
|
@@ -2997,8 +3132,12 @@ class FormCore {
|
|
|
2997
3132
|
const value = get(this.iVars, [property, ...path]);
|
|
2998
3133
|
return value;
|
|
2999
3134
|
}
|
|
3000
|
-
|
|
3001
|
-
return
|
|
3135
|
+
const field = this.fields.get(key);
|
|
3136
|
+
if (!field) return console.warn(`failed to get value from ${key}`);
|
|
3137
|
+
if (property === 'props' && path[0] === field.valuePropName) {
|
|
3138
|
+
return field.stateValue;
|
|
3139
|
+
}
|
|
3140
|
+
return path.length > 0 ? get(field[property], path) : field[property];
|
|
3002
3141
|
}
|
|
3003
3142
|
/**
|
|
3004
3143
|
* Sets the value of a property in a field.
|
|
@@ -3031,7 +3170,6 @@ class FormCore {
|
|
|
3031
3170
|
now using key !== originKey, check if any recursion error occurs
|
|
3032
3171
|
**/
|
|
3033
3172
|
if (property === 'props' && path[0] === field.valuePropName && key !== originKey) {
|
|
3034
|
-
// field.value = value;
|
|
3035
3173
|
field.emitValue({
|
|
3036
3174
|
event: 'ON_FIELD_CHANGE',
|
|
3037
3175
|
value
|
|
@@ -3062,18 +3200,17 @@ class FormCore {
|
|
|
3062
3200
|
* @returns {string[]} An array of extracted parameters.
|
|
3063
3201
|
*/
|
|
3064
3202
|
extractParams(expression) {
|
|
3065
|
-
const regex =
|
|
3203
|
+
const regex = TEMPLATE_REGEX_DELIMITATOR;
|
|
3066
3204
|
const extractedValues = [];
|
|
3067
3205
|
let match;
|
|
3068
3206
|
while (!isNil(match = regex.exec(expression))) {
|
|
3069
3207
|
extractedValues.push(match[1]);
|
|
3070
3208
|
}
|
|
3071
|
-
const operatorRegex =
|
|
3209
|
+
const operatorRegex = TEMPLATE_REGEX_OPERATOR_SPLITTER;
|
|
3072
3210
|
const splittedString = extractedValues.map(el => el.split(operatorRegex));
|
|
3073
3211
|
const result = splittedString.map(splittedStringVal => {
|
|
3074
|
-
// console.log(splittedStringVal)
|
|
3075
3212
|
return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
|
|
3076
|
-
if (curr.match(
|
|
3213
|
+
if (curr.match(TEMPLATE_REGEX_OPERATOR_MATCHER)) {
|
|
3077
3214
|
return `${acc}${curr}`;
|
|
3078
3215
|
}
|
|
3079
3216
|
let value;
|
|
@@ -3104,6 +3241,11 @@ class FormCore {
|
|
|
3104
3241
|
case 'object':
|
|
3105
3242
|
if (currValue === null) {
|
|
3106
3243
|
value = null;
|
|
3244
|
+
break;
|
|
3245
|
+
}
|
|
3246
|
+
if (currValue instanceof Date) {
|
|
3247
|
+
value = `new Date(\`${currValue}\`)`;
|
|
3248
|
+
break;
|
|
3107
3249
|
}
|
|
3108
3250
|
value = JSON.stringify(currValue);
|
|
3109
3251
|
break;
|
|
@@ -3115,7 +3257,6 @@ class FormCore {
|
|
|
3115
3257
|
});
|
|
3116
3258
|
return result.map(el => {
|
|
3117
3259
|
try {
|
|
3118
|
-
// console.log(el);
|
|
3119
3260
|
return new Function(`return ${el}`)();
|
|
3120
3261
|
} catch (e) {
|
|
3121
3262
|
console.log(e);
|
|
@@ -3131,8 +3272,8 @@ class FormCore {
|
|
|
3131
3272
|
* @returns {string} The expression string with the replacements made.
|
|
3132
3273
|
*/
|
|
3133
3274
|
replaceExpression(expression, values) {
|
|
3134
|
-
const regex =
|
|
3135
|
-
return expression.replace(regex, () => values.shift() || '');
|
|
3275
|
+
const regex = TEMPLATE_REGEX_DELIMITATOR;
|
|
3276
|
+
return expression.replace(regex, () => String(values.shift()) || '');
|
|
3136
3277
|
}
|
|
3137
3278
|
/**
|
|
3138
3279
|
* Checks if an expression string contains string concatenation within a marked expression.
|
|
@@ -3189,32 +3330,19 @@ class FormCore {
|
|
|
3189
3330
|
});
|
|
3190
3331
|
}
|
|
3191
3332
|
/**
|
|
3192
|
-
*
|
|
3333
|
+
* @internal
|
|
3334
|
+
* Update field visibility accordingly.
|
|
3193
3335
|
*
|
|
3194
|
-
* @param {
|
|
3195
|
-
* @param {
|
|
3336
|
+
* @param {string} field - Field name to be updated.
|
|
3337
|
+
* @param {boolean} hasError - Condition to be used as visibility.
|
|
3338
|
+
* @param {boolean|undefined} showOnlyIfTrue - Flag to be considered when update field visibility. If it's true, then considered error, if it's false, always considered the opposite.
|
|
3196
3339
|
*/
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
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
|
-
// });
|
|
3340
|
+
setFieldVisibility(field, hasError, showOnlyIfTrue) {
|
|
3341
|
+
if (!this.fields.has(field)) {
|
|
3342
|
+
console.warn(`failed to update visibility onto field ${field}`);
|
|
3343
|
+
} else {
|
|
3344
|
+
this.fields.get(field).visibility = showOnlyIfTrue ? hasError : !hasError;
|
|
3345
|
+
}
|
|
3218
3346
|
}
|
|
3219
3347
|
/**
|
|
3220
3348
|
* Validates visibility conditions for a given event and updates field visibility accordingly.
|
|
@@ -3229,17 +3357,19 @@ class FormCore {
|
|
|
3229
3357
|
}) {
|
|
3230
3358
|
const field = this.fields.get(key);
|
|
3231
3359
|
const structVisibility = field === null || field === void 0 ? void 0 : field.visibilityConditions;
|
|
3232
|
-
if (!structVisibility || !(structVisibility === null || structVisibility === void 0 ? void 0 : structVisibility.some(config => config.events.includes(event))))
|
|
3360
|
+
if (!structVisibility || !(structVisibility === null || structVisibility === void 0 ? void 0 : structVisibility.some(config => config.events.includes(event)))) {
|
|
3361
|
+
return;
|
|
3362
|
+
}
|
|
3233
3363
|
structVisibility.forEach(structElement => {
|
|
3234
3364
|
if (!structElement.events.includes(event)) return;
|
|
3235
3365
|
Object.keys(structElement.validations).forEach(validationKey => {
|
|
3236
|
-
const error =
|
|
3366
|
+
const error = handleValidation(field.value, structElement.validations, validations, validationKey);
|
|
3237
3367
|
if (Array.isArray(structElement.fields)) {
|
|
3238
3368
|
structElement.fields.forEach(fieldKey => {
|
|
3239
|
-
|
|
3369
|
+
this.setFieldVisibility(fieldKey, error, !!(field.value && structElement.showOnlyIfTrue));
|
|
3240
3370
|
});
|
|
3241
3371
|
} else if (structElement.fields) {
|
|
3242
|
-
|
|
3372
|
+
this.setFieldVisibility(structElement.fields, error, !!(field.value && structElement.showOnlyIfTrue));
|
|
3243
3373
|
}
|
|
3244
3374
|
});
|
|
3245
3375
|
});
|
|
@@ -3261,7 +3391,7 @@ class FormCore {
|
|
|
3261
3391
|
structResetValue.forEach(structElement => {
|
|
3262
3392
|
if (!structElement.events.includes(event)) return;
|
|
3263
3393
|
Object.keys(structElement.validations).forEach(validationKey => {
|
|
3264
|
-
const error =
|
|
3394
|
+
const error = handleValidation(field.value, structElement.validations, validations, validationKey);
|
|
3265
3395
|
if (!error) {
|
|
3266
3396
|
if (Array.isArray(structElement.fields)) {
|
|
3267
3397
|
structElement.fields.forEach((fieldKey, index) => {
|
|
@@ -3285,13 +3415,17 @@ class FormCore {
|
|
|
3285
3415
|
* Adds a field onto the form instance regardless there is a schema or not
|
|
3286
3416
|
*
|
|
3287
3417
|
* @param fieldSchema
|
|
3418
|
+
* @param mapperElement
|
|
3288
3419
|
*/
|
|
3289
|
-
addField(
|
|
3290
|
-
|
|
3420
|
+
addField({
|
|
3421
|
+
fieldSchema,
|
|
3422
|
+
mapperElement
|
|
3423
|
+
}) {
|
|
3424
|
+
var _a, _b, _c;
|
|
3291
3425
|
if (this.fields.has(fieldSchema.name)) {
|
|
3292
3426
|
throw new Error(`field name ${fieldSchema.name} already defined`);
|
|
3293
3427
|
}
|
|
3294
|
-
const mapper = this.mappers
|
|
3428
|
+
const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(fieldSchema.component));
|
|
3295
3429
|
if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
|
|
3296
3430
|
this.fields.set(fieldSchema.name, new FormField({
|
|
3297
3431
|
schemaComponent: fieldSchema,
|
|
@@ -3299,9 +3433,9 @@ class FormCore {
|
|
|
3299
3433
|
children: fieldSchema.children ? fieldSchema.children.map(el => el.name) : [],
|
|
3300
3434
|
validateVisibility: this.validateVisibility.bind(this),
|
|
3301
3435
|
resetValue: this.resetValue.bind(this),
|
|
3302
|
-
initialValue: (
|
|
3436
|
+
initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[fieldSchema.name],
|
|
3303
3437
|
templateSubject$: this.templateSubject$,
|
|
3304
|
-
|
|
3438
|
+
fieldEventSubject$: this.fieldEventSubject$,
|
|
3305
3439
|
dataSubject$: this.dataSubject$,
|
|
3306
3440
|
config: this.config
|
|
3307
3441
|
}));
|
|
@@ -3310,6 +3444,21 @@ class FormCore {
|
|
|
3310
3444
|
event: 'ON_FIELDS',
|
|
3311
3445
|
key: fieldSchema.name
|
|
3312
3446
|
});
|
|
3447
|
+
(_c = this.fields.get(fieldSchema.name)) === null || _c === void 0 ? void 0 : _c.emitEvents({
|
|
3448
|
+
event: 'ON_FIELD_MOUNT'
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
removeField({
|
|
3452
|
+
key
|
|
3453
|
+
}) {
|
|
3454
|
+
var _a;
|
|
3455
|
+
(_a = this.fields.get(key)) === null || _a === void 0 ? void 0 : _a.destroyField();
|
|
3456
|
+
this.fields.delete(key);
|
|
3457
|
+
this.subscribeTemplates();
|
|
3458
|
+
this.templateSubject$.next({
|
|
3459
|
+
key,
|
|
3460
|
+
event: 'ON_FIELDS'
|
|
3461
|
+
});
|
|
3313
3462
|
}
|
|
3314
3463
|
/**
|
|
3315
3464
|
* Serializes the schema structure to create form fields.
|
|
@@ -3320,10 +3469,15 @@ class FormCore {
|
|
|
3320
3469
|
serializeStructure(struct, path) {
|
|
3321
3470
|
if (!struct) return;
|
|
3322
3471
|
struct.forEach(structElement => {
|
|
3323
|
-
var _a, _b;
|
|
3472
|
+
var _a, _b, _c;
|
|
3324
3473
|
const currField = this.fields.get(structElement.name);
|
|
3325
3474
|
if (!currField) {
|
|
3326
|
-
|
|
3475
|
+
let mapper;
|
|
3476
|
+
if (structElement === null || structElement === void 0 ? void 0 : structElement.mapper) {
|
|
3477
|
+
mapper = structElement === null || structElement === void 0 ? void 0 : structElement.mapper;
|
|
3478
|
+
} else {
|
|
3479
|
+
mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(structElement.component);
|
|
3480
|
+
}
|
|
3327
3481
|
if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
|
|
3328
3482
|
this.fields.set(structElement.name, new FormField({
|
|
3329
3483
|
schemaComponent: structElement,
|
|
@@ -3332,19 +3486,20 @@ class FormCore {
|
|
|
3332
3486
|
children: structElement.children ? structElement.children.map(el => el.name) : [],
|
|
3333
3487
|
validateVisibility: this.validateVisibility.bind(this),
|
|
3334
3488
|
resetValue: this.resetValue.bind(this),
|
|
3335
|
-
initialValue: (
|
|
3489
|
+
initialValue: (_b = this.initialValues) === null || _b === void 0 ? void 0 : _b[structElement.name],
|
|
3336
3490
|
templateSubject$: this.templateSubject$,
|
|
3337
|
-
|
|
3491
|
+
fieldEventSubject$: this.fieldEventSubject$,
|
|
3338
3492
|
dataSubject$: this.dataSubject$,
|
|
3339
3493
|
config: this.config
|
|
3340
3494
|
}));
|
|
3341
3495
|
} else {
|
|
3342
|
-
currField.children = ((
|
|
3496
|
+
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) || [];
|
|
3343
3497
|
currField.path = path;
|
|
3498
|
+
currField.originalSchema = structElement;
|
|
3344
3499
|
currField.templateSubject$ = this.templateSubject$;
|
|
3345
3500
|
}
|
|
3346
3501
|
if (structElement.children) {
|
|
3347
|
-
|
|
3502
|
+
this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
|
|
3348
3503
|
}
|
|
3349
3504
|
});
|
|
3350
3505
|
}
|
|
@@ -3401,10 +3556,10 @@ class FormCore {
|
|
|
3401
3556
|
const values = {};
|
|
3402
3557
|
this.fields.forEach((val, key) => {
|
|
3403
3558
|
if (val.value) {
|
|
3404
|
-
values
|
|
3559
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3405
3560
|
}
|
|
3406
3561
|
});
|
|
3407
|
-
console.
|
|
3562
|
+
console.table(values);
|
|
3408
3563
|
}
|
|
3409
3564
|
/**
|
|
3410
3565
|
* Gets the current values of all form fields.
|
|
@@ -3416,7 +3571,7 @@ class FormCore {
|
|
|
3416
3571
|
const erroredFields = [];
|
|
3417
3572
|
this.fields.forEach((val, key) => {
|
|
3418
3573
|
if (val.value) {
|
|
3419
|
-
values
|
|
3574
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3420
3575
|
}
|
|
3421
3576
|
if (!val.valid) {
|
|
3422
3577
|
erroredFields.push(key);
|
|
@@ -3428,6 +3583,35 @@ class FormCore {
|
|
|
3428
3583
|
isValid: this.isValid
|
|
3429
3584
|
};
|
|
3430
3585
|
}
|
|
3586
|
+
subscribeFieldEvent({
|
|
3587
|
+
callback
|
|
3588
|
+
}) {
|
|
3589
|
+
const sub = this.fieldEventSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS)))).subscribe({
|
|
3590
|
+
next: callback
|
|
3591
|
+
});
|
|
3592
|
+
return sub;
|
|
3593
|
+
}
|
|
3594
|
+
/**
|
|
3595
|
+
*
|
|
3596
|
+
* @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
|
|
3597
|
+
*/
|
|
3598
|
+
subscribeData(callback) {
|
|
3599
|
+
const sub = this.dataSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), map(({
|
|
3600
|
+
key
|
|
3601
|
+
}) => ({
|
|
3602
|
+
field: key,
|
|
3603
|
+
data: this.getFormValues()
|
|
3604
|
+
}))).subscribe({
|
|
3605
|
+
next: callback
|
|
3606
|
+
});
|
|
3607
|
+
return sub;
|
|
3608
|
+
}
|
|
3609
|
+
subscribeOnSubmit(callback) {
|
|
3610
|
+
const sub = this.submitSubject$.pipe(map(() => this.getFormValues())).subscribe({
|
|
3611
|
+
next: callback
|
|
3612
|
+
});
|
|
3613
|
+
return sub;
|
|
3614
|
+
}
|
|
3431
3615
|
/**
|
|
3432
3616
|
* Submits the form by triggering form field events and invoking the onSubmit callback.
|
|
3433
3617
|
*/
|
|
@@ -3440,12 +3624,11 @@ class FormCore {
|
|
|
3440
3624
|
if (!this.isValid) return;
|
|
3441
3625
|
const values = this.getFormValues();
|
|
3442
3626
|
this.submitSubject$.next(values);
|
|
3443
|
-
this.onSubmit && this.onSubmit(values);
|
|
3444
3627
|
}
|
|
3445
3628
|
destroy() {
|
|
3446
3629
|
this.submitSubject$.unsubscribe();
|
|
3447
3630
|
this.templateSubject$.unsubscribe();
|
|
3448
|
-
this.
|
|
3631
|
+
this.fieldEventSubject$.unsubscribe();
|
|
3449
3632
|
this.dataSubject$.unsubscribe();
|
|
3450
3633
|
this.fields.forEach(field => field.destroyField());
|
|
3451
3634
|
}
|
|
@@ -3461,15 +3644,22 @@ class FormCore {
|
|
|
3461
3644
|
*/
|
|
3462
3645
|
FormCore.checkIndexes = (struct, indexes = []) => {
|
|
3463
3646
|
if (!struct) return indexes;
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3647
|
+
const helper = (struct, indexes) => {
|
|
3648
|
+
for (let i = 0; i < struct.length; i++) {
|
|
3649
|
+
const structElement = struct[i];
|
|
3650
|
+
if (structElement.name === IVARPROPNAME) {
|
|
3651
|
+
throw new Error(`reserved ${IVARPROPNAME} name for field names`);
|
|
3652
|
+
}
|
|
3653
|
+
indexes.push(structElement.name);
|
|
3654
|
+
if (structElement.children) {
|
|
3655
|
+
helper(structElement.children, indexes);
|
|
3656
|
+
}
|
|
3472
3657
|
}
|
|
3658
|
+
};
|
|
3659
|
+
helper(struct, indexes);
|
|
3660
|
+
const duppedIndexes = indexes.filter((item, index) => indexes.indexOf(item) !== index);
|
|
3661
|
+
if (duppedIndexes.length > 0) {
|
|
3662
|
+
throw new Error(`duplicated indexes found on schema: ${JSON.stringify(duppedIndexes)}`);
|
|
3473
3663
|
}
|
|
3474
3664
|
return indexes;
|
|
3475
3665
|
};
|
|
@@ -3577,29 +3767,56 @@ class FormGroup {
|
|
|
3577
3767
|
* Prints the form group instance to the console.
|
|
3578
3768
|
*/
|
|
3579
3769
|
printFormGroupInstance() {
|
|
3580
|
-
console.
|
|
3770
|
+
console.table(this.forms);
|
|
3581
3771
|
}
|
|
3582
3772
|
/**
|
|
3583
3773
|
* Prototype submit function to multiple forms
|
|
3584
3774
|
* @param {string[]} indexes form indexes to be submitted
|
|
3585
3775
|
* @returns
|
|
3586
3776
|
*/
|
|
3587
|
-
submitMultipleFormsByIndex(indexes) {
|
|
3777
|
+
submitMultipleFormsByIndex(indexes, callback) {
|
|
3588
3778
|
let isValid = true;
|
|
3589
3779
|
let values = {};
|
|
3590
3780
|
let erroredFields = [];
|
|
3591
3781
|
indexes.forEach(index => {
|
|
3592
|
-
var _a;
|
|
3593
|
-
|
|
3782
|
+
var _a, _b;
|
|
3783
|
+
(_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.submit();
|
|
3784
|
+
const res = (_b = this.forms.get(index)) === null || _b === void 0 ? void 0 : _b.getFormValues();
|
|
3594
3785
|
isValid = isValid && ((res === null || res === void 0 ? void 0 : res.isValid) || false);
|
|
3595
3786
|
values = Object.assign(Object.assign({}, values), (res === null || res === void 0 ? void 0 : res.values) || {});
|
|
3596
3787
|
erroredFields = [...erroredFields, ...((res === null || res === void 0 ? void 0 : res.erroredFields) || [])];
|
|
3597
3788
|
});
|
|
3598
|
-
|
|
3789
|
+
isValid && callback && callback({
|
|
3599
3790
|
erroredFields,
|
|
3600
3791
|
isValid,
|
|
3601
3792
|
values
|
|
3602
|
-
};
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
3795
|
+
onDataSubscription({
|
|
3796
|
+
ids,
|
|
3797
|
+
callback
|
|
3798
|
+
}) {
|
|
3799
|
+
const subs = ids.reduce((acc, formId) => {
|
|
3800
|
+
var _a;
|
|
3801
|
+
// @TODO add config on debounceTime on this events
|
|
3802
|
+
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(({
|
|
3803
|
+
key
|
|
3804
|
+
}) => {
|
|
3805
|
+
var _a;
|
|
3806
|
+
return {
|
|
3807
|
+
formField: key,
|
|
3808
|
+
values: (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.getFormValues()
|
|
3809
|
+
};
|
|
3810
|
+
}));
|
|
3811
|
+
if (sub) {
|
|
3812
|
+
acc[formId] = sub;
|
|
3813
|
+
} else {
|
|
3814
|
+
console.warn(`failed to register form id ${formId}`);
|
|
3815
|
+
}
|
|
3816
|
+
return acc;
|
|
3817
|
+
}, {});
|
|
3818
|
+
const sub = combineLatest(subs).subscribe(callback);
|
|
3819
|
+
return sub;
|
|
3603
3820
|
}
|
|
3604
3821
|
}
|
|
3605
3822
|
|