@bolttech/form-engine-core 0.0.1-beta.8 → 0.0.2-beta.1
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 +591 -278
- package/package.json +2 -2
- package/src/constants/constants.d.ts +6 -1
- package/src/helpers/SafeSubject.d.ts +11 -0
- package/src/helpers/helpers.d.ts +3 -1
- package/src/helpers/validation.d.ts +27 -0
- package/src/interfaces/schema.d.ts +30 -8
- package/src/managers/field.d.ts +18 -23
- package/src/managers/form.d.ts +50 -31
- package/src/managers/formGroup.d.ts +10 -2
- package/src/types/event.d.ts +2 -2
- package/src/types/form.d.ts +0 -5
- package/src/types/schema.d.ts +88 -40
- package/src/types/template.d.ts +19 -1
- 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/src/validations/number.d.ts +30 -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, cloneDeep, isEqual, get, isNil, set } from 'lodash';
|
|
4
4
|
import { getCurrencySymbol } from '@gaignoux/currency';
|
|
5
5
|
|
|
6
6
|
var TMutationEnum;
|
|
@@ -44,6 +44,14 @@ 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_STRING_CONCATENATION_DETECTOR = /^\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}$/;
|
|
50
|
+
const TEMPLATE_REGEX_DELIMITATOR = /\$\{((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\}/g;
|
|
51
|
+
const TEMPLATE_REGEX_OPERATOR_SPLITTER = /\s*(\|\||&&|!)\s*/g;
|
|
52
|
+
const TEMPLATE_REGEX_OPERATOR_MATCHER = /^\|\||&&|!$/;
|
|
53
|
+
const TEMPLATE_AVALIABLE_SCOPES = ['fields', 'iVars'];
|
|
54
|
+
|
|
47
55
|
/**
|
|
48
56
|
* Makes an HTTP request using XMLHttpRequest.
|
|
49
57
|
*
|
|
@@ -96,15 +104,18 @@ function makeRequest(method, url, headers, body) {
|
|
|
96
104
|
* ```
|
|
97
105
|
*/
|
|
98
106
|
function extractFieldKeys(expression) {
|
|
99
|
-
const regex =
|
|
107
|
+
const regex = TEMPLATE_REGEX_DELIMITATOR;
|
|
100
108
|
const extractedValues = [];
|
|
101
109
|
let match;
|
|
102
110
|
while ((match = regex.exec(expression)) !== null) {
|
|
103
111
|
extractedValues.push(match[1]);
|
|
104
112
|
}
|
|
113
|
+
const operatorRegex = TEMPLATE_REGEX_OPERATOR_SPLITTER;
|
|
114
|
+
const splittedString = extractedValues.map(el => el.split(operatorRegex).filter(item => !operatorRegex.test(item))).flat().filter(el => el.split('.').length > 1);
|
|
105
115
|
return {
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
originScopeKeys: Array.from(new Set(splittedString.map(el => el.split('.')[0]))),
|
|
117
|
+
originFieldKeys: Array.from(new Set(splittedString.map(el => el.split('.')[1]))),
|
|
118
|
+
originPropertyKeys: Array.from(new Set(splittedString.map(el => el.split('.')[2])))
|
|
108
119
|
};
|
|
109
120
|
}
|
|
110
121
|
/**
|
|
@@ -132,43 +143,35 @@ function extractFieldKeys(expression) {
|
|
|
132
143
|
* // expressions will contain an array of objects with origin expressions, field keys, and destination paths.
|
|
133
144
|
* ```
|
|
134
145
|
*/
|
|
135
|
-
function traverseObject(
|
|
146
|
+
function traverseObject(element, path) {
|
|
136
147
|
const result = [];
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (String(item).includes('$')) {
|
|
146
|
-
// const extractedPath = item.replace(/\$|{|}/g, '').split('.');
|
|
147
|
-
const extractedOriginPath = `${path ? `${path}.` : ``}${key}`.split('.');
|
|
148
|
-
result.push(Object.assign(Object.assign({
|
|
149
|
-
originExpression: item
|
|
150
|
-
}, extractFieldKeys(item)), {
|
|
151
|
-
destinationKey: extractedOriginPath[0],
|
|
152
|
-
destinationProperty: extractedOriginPath[1],
|
|
153
|
-
destinationPath: extractedOriginPath.slice(2)
|
|
154
|
-
}));
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
} else if (typeof value === 'object') {
|
|
148
|
+
if (Array.isArray(element)) {
|
|
149
|
+
element.forEach((item, index) => {
|
|
150
|
+
result.push(...traverseObject(item, `${path ? `${path}.` : ``}${index}`));
|
|
151
|
+
});
|
|
152
|
+
} else if (typeof element === 'object') {
|
|
153
|
+
for (const key in element) {
|
|
154
|
+
if (Object.prototype.hasOwnProperty.call(element, key)) {
|
|
155
|
+
const value = element[key];
|
|
159
156
|
result.push(...traverseObject(value, `${path ? `${path}.` : ``}${key}`));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} else if (typeof element === 'string') {
|
|
160
|
+
if (element.includes('${')) {
|
|
161
|
+
// const extractedPath = value.replace(/\$|{|}/g, '').split('.');
|
|
162
|
+
const destinationPath = (path ? path : '').split('.');
|
|
163
|
+
// if(destinationPath[0])
|
|
164
|
+
const originfieldKeys = extractFieldKeys(element);
|
|
165
|
+
if (!originfieldKeys.originScopeKeys.every(scopeKey => TEMPLATE_AVALIABLE_SCOPES.includes(scopeKey))) {
|
|
166
|
+
console.warn(`scope malformed on this expression: ${element}, ignoring..`);
|
|
167
|
+
} else {
|
|
168
|
+
result.push(Object.assign(Object.assign({
|
|
169
|
+
originExpression: element
|
|
170
|
+
}, extractFieldKeys(element)), {
|
|
171
|
+
destinationKey: destinationPath[0],
|
|
172
|
+
destinationProperty: destinationPath[1],
|
|
173
|
+
destinationPath: destinationPath.slice(2)
|
|
174
|
+
}));
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
177
|
}
|
|
@@ -681,16 +684,9 @@ const currency = (value, masks) => {
|
|
|
681
684
|
if (integerPart === '') {
|
|
682
685
|
integerPart = '0';
|
|
683
686
|
}
|
|
684
|
-
console.log('Before', {
|
|
685
|
-
convertedValue
|
|
686
|
-
});
|
|
687
687
|
if (align === 'right' && String(value).endsWith(' ')) {
|
|
688
688
|
convertedValue = convertedValue.slice(0, -1);
|
|
689
689
|
}
|
|
690
|
-
console.log('After', {
|
|
691
|
-
value,
|
|
692
|
-
convertedValue
|
|
693
|
-
});
|
|
694
690
|
let newRawValue = integerPart;
|
|
695
691
|
let decimalPart = convertedValue.slice(convertedValue.length - precision);
|
|
696
692
|
if (precision > 0) {
|
|
@@ -729,10 +725,11 @@ const custom = (value, masks) => {
|
|
|
729
725
|
if (!masks.custom || !value) return value;
|
|
730
726
|
let mask = '';
|
|
731
727
|
let index = 0;
|
|
728
|
+
const convertedValue = value.replace(/[^\w\s]/gi, '');
|
|
732
729
|
for (let i = 0; i < masks.custom.length; i++) {
|
|
733
730
|
if (masks.custom[i] === '#') {
|
|
734
|
-
if (index <
|
|
735
|
-
mask +=
|
|
731
|
+
if (index < convertedValue.length) {
|
|
732
|
+
mask += convertedValue[index];
|
|
736
733
|
index++;
|
|
737
734
|
} else {
|
|
738
735
|
break;
|
|
@@ -1234,6 +1231,44 @@ const between = (value, validations) => {
|
|
|
1234
1231
|
const num = Number(value);
|
|
1235
1232
|
return !Number.isNaN(num) && !(+num >= validations.between.start && +num <= validations.between.end);
|
|
1236
1233
|
};
|
|
1234
|
+
/**
|
|
1235
|
+
* Checks if a given value is less than a specified threshold.
|
|
1236
|
+
*
|
|
1237
|
+
* @param value - The value to be checked. This value will be converted to a number.
|
|
1238
|
+
* @param validations - An object containing validation methods, specifically the `lessThan` property which holds the threshold value.
|
|
1239
|
+
* @returns Returns `false` if the `lessThan` threshold is not provided or if the value is not a valid number. Otherwise, returns `true` if the value is greater than or equal to the `lessThan` threshold.
|
|
1240
|
+
*
|
|
1241
|
+
* @example
|
|
1242
|
+
* ```typescript
|
|
1243
|
+
* const validations = { lessThan: 10 };
|
|
1244
|
+
* console.log(lessThan(5, validations)); // true
|
|
1245
|
+
* console.log(lessThan(15, validations)); // false
|
|
1246
|
+
* ```
|
|
1247
|
+
*/
|
|
1248
|
+
const lessThan = (value, validations) => {
|
|
1249
|
+
const number = Number(value);
|
|
1250
|
+
if (!validations.lessThan || Number.isNaN(number)) return false;
|
|
1251
|
+
return number >= validations.lessThan;
|
|
1252
|
+
};
|
|
1253
|
+
/**
|
|
1254
|
+
* Checks if a given value is greater than a specified threshold.
|
|
1255
|
+
*
|
|
1256
|
+
* @param value - The value to be checked. This value will be converted to a number.
|
|
1257
|
+
* @param validations - An object containing validation methods, specifically the `greaterThan` property which holds the threshold value.
|
|
1258
|
+
* @returns Returns `false` if the `greaterThan` threshold is not provided or if the value is not a valid number. Otherwise, returns `true` if the value is less than or equal to the `greaterThan` threshold.
|
|
1259
|
+
*
|
|
1260
|
+
* @example
|
|
1261
|
+
* ```typescript
|
|
1262
|
+
* const validations = { greaterThan: 10 };
|
|
1263
|
+
* console.log(greaterThan(15, validations)); // true
|
|
1264
|
+
* console.log(greaterThan(5, validations)); // false
|
|
1265
|
+
* ```
|
|
1266
|
+
*/
|
|
1267
|
+
const greaterThan = (value, validations) => {
|
|
1268
|
+
const number = Number(value);
|
|
1269
|
+
if (!validations.greaterThan || Number.isNaN(number)) return false;
|
|
1270
|
+
return number <= validations.greaterThan;
|
|
1271
|
+
};
|
|
1237
1272
|
/**
|
|
1238
1273
|
* Validates if a value contains sequential numbers.
|
|
1239
1274
|
*
|
|
@@ -1928,41 +1963,44 @@ const conditions = (value, validations) => {
|
|
|
1928
1963
|
};
|
|
1929
1964
|
|
|
1930
1965
|
/**
|
|
1931
|
-
*
|
|
1966
|
+
* @internal
|
|
1967
|
+
* Validates if a date string matches a specific date format.
|
|
1968
|
+
* The function accepts strings with '/' or '-' separators and removes them before validating the format.
|
|
1932
1969
|
*
|
|
1933
|
-
* @param {string} value - The date
|
|
1934
|
-
* @param {
|
|
1935
|
-
*
|
|
1970
|
+
* @param {string} value - The date string to be validated. It can contain '/' or '-' separators.
|
|
1971
|
+
* @param {string} format - The expected date format. It can be one of the following:
|
|
1972
|
+
* 'DDMMYYYY', 'YYYYMMDD', 'YYYYDDMM', 'MMDDYYYY',
|
|
1973
|
+
* 'DMYYYY', 'YYYYMD', 'YYYYDM', 'MDYYYY'.
|
|
1974
|
+
* @returns {boolean} - Returns `false` if the date string matches the specified format, otherwise `true`.
|
|
1936
1975
|
*
|
|
1937
1976
|
* @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
|
-
* };
|
|
1977
|
+
* // Returns false
|
|
1978
|
+
* invalidStringDate("25/07/1997", "DDMMYYYY");
|
|
1945
1979
|
*
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1980
|
+
* @example
|
|
1981
|
+
* // Returns false
|
|
1982
|
+
* invalidStringDate("1997-07-25", "YYYYMMDD");
|
|
1948
1983
|
*
|
|
1949
|
-
*
|
|
1950
|
-
*
|
|
1951
|
-
*
|
|
1984
|
+
* @example
|
|
1985
|
+
* // Returns true, as the format does not match
|
|
1986
|
+
* invalidStringDate("1997/25/07", "MMDDYYYY");
|
|
1952
1987
|
*/
|
|
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
|
-
}
|
|
1988
|
+
const invalidStringDate = (value, format) => {
|
|
1989
|
+
if (!value.includes('/') && !value.includes('-') || !format) {
|
|
1990
|
+
return true;
|
|
1964
1991
|
}
|
|
1965
|
-
|
|
1992
|
+
const valueParts = value.replace(/[-/]/g, '');
|
|
1993
|
+
const dateMapper = {
|
|
1994
|
+
DDMMYYYY: /^(\d{2})(\d{2})(\d{4})$/,
|
|
1995
|
+
YYYYMMDD: /^(\d{4})(\d{2})(\d{2})$/,
|
|
1996
|
+
YYYYDDMM: /^(\d{4})(\d{2})(\d{2})$/,
|
|
1997
|
+
MMDDYYYY: /^(\d{2})(\d{2})(\d{4})$/,
|
|
1998
|
+
DMYYYY: /^(\d{1,2})(\d{1,2})(\d{4})$/,
|
|
1999
|
+
YYYYMD: /^(\d{4})(\d{1,2})(\d{1,2})$/,
|
|
2000
|
+
YYYYDM: /^(\d{4})(\d{1,2})(\d{1,2})$/,
|
|
2001
|
+
MDYYYY: /^(\d{1,2})(\d{1,2})(\d{4})$/
|
|
2002
|
+
};
|
|
2003
|
+
return !dateMapper[format].test(valueParts);
|
|
1966
2004
|
};
|
|
1967
2005
|
/**
|
|
1968
2006
|
* @internal
|
|
@@ -2015,6 +2053,44 @@ const dateRearrangeMapper = {
|
|
|
2015
2053
|
MMDDYYYY: value => value,
|
|
2016
2054
|
timestamp: value => new Date(value).toString()
|
|
2017
2055
|
};
|
|
2056
|
+
/**
|
|
2057
|
+
* @function betweenDates
|
|
2058
|
+
* Validates if a date value falls between two specified dates.
|
|
2059
|
+
*
|
|
2060
|
+
* @param {string} value - The date value to be validated in string format.
|
|
2061
|
+
* @param {TValidationMethods} validations - The validation methods object containing the betweenDates validation rules.
|
|
2062
|
+
* @returns {boolean} - Returns `true` if the date value fails the betweenDates validation, otherwise `false`.
|
|
2063
|
+
*
|
|
2064
|
+
* @example
|
|
2065
|
+
* ```typescript
|
|
2066
|
+
* const validations = {
|
|
2067
|
+
* betweenDates: [
|
|
2068
|
+
* { origin: { value: '2023-01-01', format: 'yyyy-MM-dd' }, operator: '>=' },
|
|
2069
|
+
* { origin: { value: '2023-12-31', format: 'yyyy-MM-dd' }, operator: '<=' }
|
|
2070
|
+
* ]
|
|
2071
|
+
* };
|
|
2072
|
+
*
|
|
2073
|
+
* const result1 = betweenDates('2023-06-01', validations);
|
|
2074
|
+
* console.log(result1); // false (date is within the range)
|
|
2075
|
+
*
|
|
2076
|
+
* const result2 = betweenDates('2024-01-01', validations);
|
|
2077
|
+
* console.log(result2); // true (date is outside the range)
|
|
2078
|
+
* ```
|
|
2079
|
+
*/
|
|
2080
|
+
const betweenDates = (value, validations) => {
|
|
2081
|
+
var _a;
|
|
2082
|
+
let fail = false;
|
|
2083
|
+
if (((_a = validations.betweenDates) === null || _a === void 0 ? void 0 : _a.length) != 2) return false;
|
|
2084
|
+
for (const validation of validations.betweenDates) {
|
|
2085
|
+
if (date(value, {
|
|
2086
|
+
date: validation
|
|
2087
|
+
})) {
|
|
2088
|
+
fail = true;
|
|
2089
|
+
break;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
return fail;
|
|
2093
|
+
};
|
|
2018
2094
|
/**
|
|
2019
2095
|
* @function date
|
|
2020
2096
|
* Validates a date value based on various date conditions and intervals.
|
|
@@ -2048,12 +2124,13 @@ const dateRearrangeMapper = {
|
|
|
2048
2124
|
* ```
|
|
2049
2125
|
*/
|
|
2050
2126
|
const date = (value, validations) => {
|
|
2051
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
2127
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
2052
2128
|
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
2129
|
return false;
|
|
2054
2130
|
}
|
|
2055
2131
|
const originValue = validations.date.origin.value || value;
|
|
2056
|
-
|
|
2132
|
+
const mappedValue = dateRearrangeMapper[(_e = validations.date) === null || _e === void 0 ? void 0 : _e.origin.format](originValue).toString();
|
|
2133
|
+
let originDate = new Date(mappedValue);
|
|
2057
2134
|
let targetDate = new Date();
|
|
2058
2135
|
let target = new Date();
|
|
2059
2136
|
if ((_g = (_f = validations.date) === null || _f === void 0 ? void 0 : _f.target) === null || _g === void 0 ? void 0 : _g.format) {
|
|
@@ -2072,6 +2149,9 @@ const date = (value, validations) => {
|
|
|
2072
2149
|
if (validations.date.onlyValidDate && (!(targetDate instanceof Date && isFinite(targetDate)) || !(targetDate instanceof Date && isFinite(originDate)) || originValue.length < 8)) {
|
|
2073
2150
|
return true;
|
|
2074
2151
|
}
|
|
2152
|
+
if (invalidStringDate(mappedValue, (_l = validations.date) === null || _l === void 0 ? void 0 : _l.origin.format)) {
|
|
2153
|
+
return false;
|
|
2154
|
+
}
|
|
2075
2155
|
const originTimestamp = originDate.getTime();
|
|
2076
2156
|
const targetTimestamp = targetDate.getTime();
|
|
2077
2157
|
const operationsMapper = {
|
|
@@ -2082,7 +2162,7 @@ const date = (value, validations) => {
|
|
|
2082
2162
|
'===': originTimestamp === targetTimestamp,
|
|
2083
2163
|
'!==': originTimestamp !== targetTimestamp
|
|
2084
2164
|
};
|
|
2085
|
-
return operationsMapper[(
|
|
2165
|
+
return operationsMapper[(_m = validations.date) === null || _m === void 0 ? void 0 : _m.operator];
|
|
2086
2166
|
};
|
|
2087
2167
|
/**
|
|
2088
2168
|
* @function validDate
|
|
@@ -2111,11 +2191,74 @@ const validDate = (value, validations) => {
|
|
|
2111
2191
|
const month = parseInt(dateParts[0], 10) - 1; // Month is zero-based
|
|
2112
2192
|
const day = parseInt(dateParts[1], 10);
|
|
2113
2193
|
const date = new Date(year, month, day);
|
|
2194
|
+
/*
|
|
2195
|
+
* Motivation: due to the scenario in which a date field may be 'typeable',
|
|
2196
|
+
* we need to ensure that dates less than a thousand and such years are valid.
|
|
2197
|
+
* E.g.: user types 13-07-199 (without this condition, it becomes a valid date, as 199 is a valid year)
|
|
2198
|
+
*
|
|
2199
|
+
* Ps: 150 is a valid value for now, as we still cannot live more than that, from the
|
|
2200
|
+
* the moment we start to live longer, we can rethink this.
|
|
2201
|
+
*/
|
|
2202
|
+
const today = new Date();
|
|
2203
|
+
today.setFullYear(today.getFullYear() - 150);
|
|
2204
|
+
if (date.getFullYear() < today.getFullYear()) {
|
|
2205
|
+
return true;
|
|
2206
|
+
}
|
|
2114
2207
|
// Check if the date is valid
|
|
2115
2208
|
const isValidDate = date.getFullYear() === year && date.getMonth() === month && date.getDate() === day;
|
|
2116
2209
|
return !isValidDate;
|
|
2117
2210
|
};
|
|
2118
2211
|
|
|
2212
|
+
/**
|
|
2213
|
+
* @internal
|
|
2214
|
+
* Runs a set of validation handlers against a given value.
|
|
2215
|
+
*
|
|
2216
|
+
* @param {unknown} value - The value to be validated.
|
|
2217
|
+
* @param {TValidationMethods} handlers - An object containing validation methods to be applied.
|
|
2218
|
+
* @param {TValidationHandler} validations - An object containing every validation methods to be executed.
|
|
2219
|
+
* @returns {boolean[]} - An array of boolean results for each validation method.
|
|
2220
|
+
*
|
|
2221
|
+
* @example
|
|
2222
|
+
* const handlers = {
|
|
2223
|
+
* max: { max: 10 },
|
|
2224
|
+
* required: true,
|
|
2225
|
+
* email: true
|
|
2226
|
+
* };
|
|
2227
|
+
* const results = run('test@example.com', handlers);
|
|
2228
|
+
* console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
|
|
2229
|
+
*/
|
|
2230
|
+
function run$1(value, handlers, validations) {
|
|
2231
|
+
const runner = [];
|
|
2232
|
+
Object.keys(handlers).forEach(rule => {
|
|
2233
|
+
runner.push(validations[rule](value, {
|
|
2234
|
+
[rule]: handlers[rule]
|
|
2235
|
+
}));
|
|
2236
|
+
});
|
|
2237
|
+
return runner;
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Validates a given value based on specified validation methods inside a custom named validation.
|
|
2241
|
+
*
|
|
2242
|
+
* @param {unknown} value - The value to be validated.
|
|
2243
|
+
* @param {TValidationMethods} methods - The validation methods to be applied.
|
|
2244
|
+
* @param {TValidationHandler} validations - An object containing every validation methods to be executed.
|
|
2245
|
+
* @returns {boolean} - Returns true if any of the validation methods pass, otherwise false.
|
|
2246
|
+
*
|
|
2247
|
+
* @example
|
|
2248
|
+
* const value = 'example@example.com';
|
|
2249
|
+
* const methods = {
|
|
2250
|
+
* required: true,
|
|
2251
|
+
* email: true
|
|
2252
|
+
* };
|
|
2253
|
+
*
|
|
2254
|
+
* const isValid = validateValue(value, methods);
|
|
2255
|
+
* console.log(isValid); // Output: true
|
|
2256
|
+
*/
|
|
2257
|
+
var namedRule = ((value, methods, validations) => {
|
|
2258
|
+
if (!methods) return false;
|
|
2259
|
+
return run$1(value, methods, validations).some(validation => validation);
|
|
2260
|
+
});
|
|
2261
|
+
|
|
2119
2262
|
/**
|
|
2120
2263
|
* @internal
|
|
2121
2264
|
* An object mapping validation keys to their respective validation functions.
|
|
@@ -2129,6 +2272,8 @@ const validDate = (value, validations) => {
|
|
|
2129
2272
|
const validations$1 = {
|
|
2130
2273
|
max,
|
|
2131
2274
|
min,
|
|
2275
|
+
lessThan,
|
|
2276
|
+
greaterThan,
|
|
2132
2277
|
length,
|
|
2133
2278
|
regex,
|
|
2134
2279
|
url,
|
|
@@ -2150,7 +2295,6 @@ const validations$1 = {
|
|
|
2150
2295
|
notEmpty,
|
|
2151
2296
|
bool,
|
|
2152
2297
|
exists,
|
|
2153
|
-
greaterThan: () => true,
|
|
2154
2298
|
isNumber,
|
|
2155
2299
|
conditions,
|
|
2156
2300
|
validDate,
|
|
@@ -2174,20 +2318,26 @@ const validations$1 = {
|
|
|
2174
2318
|
* const results = run('test@example.com', handlers);
|
|
2175
2319
|
* console.log(results); // [false, false, true] (value fails 'max', passes 'required', passes 'email')
|
|
2176
2320
|
*/
|
|
2177
|
-
|
|
2321
|
+
function run(value, handlers) {
|
|
2178
2322
|
const runner = [];
|
|
2179
2323
|
Object.keys(handlers).forEach(rule => {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2324
|
+
let handler;
|
|
2325
|
+
if (isFunction(validations$1[rule])) {
|
|
2326
|
+
handler = validations$1[rule](value, {
|
|
2327
|
+
[rule]: handlers[rule]
|
|
2328
|
+
});
|
|
2329
|
+
} else {
|
|
2330
|
+
handler = namedRule(value, handlers[rule], validations$1);
|
|
2331
|
+
}
|
|
2332
|
+
runner.push(handler);
|
|
2183
2333
|
});
|
|
2184
2334
|
return runner;
|
|
2185
|
-
}
|
|
2335
|
+
}
|
|
2186
2336
|
/**
|
|
2187
2337
|
* Validates that a value meets multiple validation rules.
|
|
2188
2338
|
*
|
|
2189
2339
|
* @param {number | string | boolean} value - The value to be validated.
|
|
2190
|
-
* @param {TValidationMethods}
|
|
2340
|
+
* @param {TValidationMethods} methods - The validation methods object containing the multipleValidations rule set.
|
|
2191
2341
|
* @returns {boolean} - Returns `true` if the value meets the specified multiple validation rules, otherwise `false`.
|
|
2192
2342
|
*
|
|
2193
2343
|
* @example
|
|
@@ -2212,20 +2362,22 @@ const run = (value, handlers) => {
|
|
|
2212
2362
|
* console.log(result2); // false
|
|
2213
2363
|
* ```
|
|
2214
2364
|
*/
|
|
2215
|
-
const multipleValidations = (value,
|
|
2216
|
-
if (!
|
|
2217
|
-
const runner = run(value,
|
|
2365
|
+
const multipleValidations = (value, methods) => {
|
|
2366
|
+
if (!methods.multipleValidations) return false;
|
|
2367
|
+
const runner = run(value, methods.multipleValidations.validations);
|
|
2218
2368
|
const rulesMapper = {
|
|
2219
2369
|
AND: () => runner.every(validation => validation),
|
|
2220
2370
|
OR: () => runner.some(validation => validation),
|
|
2221
2371
|
NOT: () => !runner.every(validation => validation)
|
|
2222
2372
|
};
|
|
2223
|
-
return rulesMapper[
|
|
2373
|
+
return rulesMapper[methods.multipleValidations.rule]();
|
|
2224
2374
|
};
|
|
2225
2375
|
|
|
2226
2376
|
const validations = {
|
|
2227
2377
|
max,
|
|
2228
2378
|
min,
|
|
2379
|
+
lessThan,
|
|
2380
|
+
greaterThan,
|
|
2229
2381
|
length,
|
|
2230
2382
|
regex,
|
|
2231
2383
|
url,
|
|
@@ -2247,7 +2399,6 @@ const validations = {
|
|
|
2247
2399
|
notEmpty,
|
|
2248
2400
|
bool,
|
|
2249
2401
|
exists,
|
|
2250
|
-
greaterThan: () => true,
|
|
2251
2402
|
isNumber,
|
|
2252
2403
|
conditions,
|
|
2253
2404
|
multipleValidations,
|
|
@@ -2256,8 +2407,52 @@ const validations = {
|
|
|
2256
2407
|
validDate
|
|
2257
2408
|
};
|
|
2258
2409
|
|
|
2259
|
-
|
|
2260
|
-
|
|
2410
|
+
/**
|
|
2411
|
+
* Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
|
|
2412
|
+
* that were unmounted due to adapter external handling such as visibility
|
|
2413
|
+
*/
|
|
2414
|
+
class SafeSubject extends Subject {
|
|
2415
|
+
constructor(isMounted) {
|
|
2416
|
+
super();
|
|
2417
|
+
this.isMounted = isMounted;
|
|
2418
|
+
}
|
|
2419
|
+
next(value) {
|
|
2420
|
+
if (this.isMounted()) {
|
|
2421
|
+
super.next(value);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
/**
|
|
2427
|
+
* @internal
|
|
2428
|
+
* Handles the validation of a given value based on specified validation methods and rules.
|
|
2429
|
+
*
|
|
2430
|
+
* @param {string | number | boolean | unknown} value - The value to be validated.
|
|
2431
|
+
* @param {TSchemaValidation} validations - The schema validations to be applied.
|
|
2432
|
+
* @param {TValidationHandler} methods - The validation handler methods.
|
|
2433
|
+
* @param {keyof TValidationMethods} key - The specific key of the validation method to be used.
|
|
2434
|
+
* @returns {boolean} - Returns true if the value passes the validation, otherwise false.
|
|
2435
|
+
*
|
|
2436
|
+
* @example
|
|
2437
|
+
* const value = 'example@example.com';
|
|
2438
|
+
* const validations = {
|
|
2439
|
+
* required: true,
|
|
2440
|
+
* customName: { email: true }
|
|
2441
|
+
* };
|
|
2442
|
+
* const methods = {
|
|
2443
|
+
* email: (value) => /\S+@\S+\.\S+/.test(value)
|
|
2444
|
+
* };
|
|
2445
|
+
* const key = 'required';
|
|
2446
|
+
*
|
|
2447
|
+
* const isValid = handleValidation(value, validations, methods, key);
|
|
2448
|
+
* console.log(isValid); // Output: true
|
|
2449
|
+
*/
|
|
2450
|
+
function handleValidation(value, validations, methods, key) {
|
|
2451
|
+
if (isFunction(methods[key])) {
|
|
2452
|
+
return methods[key](value, validations);
|
|
2453
|
+
}
|
|
2454
|
+
return namedRule(value, validations[key], methods);
|
|
2455
|
+
}
|
|
2261
2456
|
|
|
2262
2457
|
/**
|
|
2263
2458
|
* Represents a form field with observables for managing form state, validations, and API requests.
|
|
@@ -2290,25 +2485,27 @@ class FormField {
|
|
|
2290
2485
|
mapper
|
|
2291
2486
|
}) {
|
|
2292
2487
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
2488
|
+
this.valueSubscription$ = new Subscription();
|
|
2293
2489
|
this.fieldStateSubscription$ = new Subscription();
|
|
2294
|
-
this.originalSchema = schemaComponent;
|
|
2490
|
+
this.originalSchema = cloneDeep(schemaComponent);
|
|
2295
2491
|
this.config = {
|
|
2296
2492
|
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,
|
|
2297
2493
|
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
|
|
2298
2494
|
};
|
|
2299
2495
|
this.name = schemaComponent.name;
|
|
2496
|
+
this.nameToSubmit = schemaComponent.nameToSubmit;
|
|
2300
2497
|
this.component = schemaComponent.component;
|
|
2301
2498
|
this.path = path;
|
|
2302
2499
|
this.children = children;
|
|
2303
2500
|
this.validations = schemaComponent.validations;
|
|
2304
|
-
this.errorMessages = schemaComponent.
|
|
2501
|
+
this.errorMessages = (_a = schemaComponent.validations) === null || _a === void 0 ? void 0 : _a.messages;
|
|
2305
2502
|
this.visibilityConditions = schemaComponent.visibilityConditions;
|
|
2306
2503
|
this.resetValues = schemaComponent.resetValues;
|
|
2307
2504
|
this.apiSchema = schemaComponent.api;
|
|
2308
2505
|
this.formatters = schemaComponent.formatters;
|
|
2309
2506
|
this.masks = schemaComponent.masks;
|
|
2310
2507
|
if (mapper.valueChangeEvent) this.valueChangeEvent = mapper.valueChangeEvent;
|
|
2311
|
-
if ((
|
|
2508
|
+
if ((_b = mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) this.valuePropName = mapper.events.setValue;
|
|
2312
2509
|
this.mapper = mapper;
|
|
2313
2510
|
this.validateVisibility = validateVisibility;
|
|
2314
2511
|
this.resetValue = resetValue;
|
|
@@ -2316,10 +2513,6 @@ class FormField {
|
|
|
2316
2513
|
this.fieldEventSubject$ = fieldEventSubject$;
|
|
2317
2514
|
this.dataSubject$ = dataSubject$;
|
|
2318
2515
|
this._props = schemaComponent.props || {};
|
|
2319
|
-
this._value = '';
|
|
2320
|
-
this._stateValue = ((_b = this.mapper.events) === null || _b === void 0 ? void 0 : _b.setValue) ? {
|
|
2321
|
-
[this.mapper.events.setValue]: ''
|
|
2322
|
-
} : {};
|
|
2323
2516
|
this._metadata = '';
|
|
2324
2517
|
this.errorsString = '';
|
|
2325
2518
|
this.errorsList = [];
|
|
@@ -2339,7 +2532,9 @@ class FormField {
|
|
|
2339
2532
|
};
|
|
2340
2533
|
this._errors = {};
|
|
2341
2534
|
this._valid = false;
|
|
2535
|
+
this._mounted = true;
|
|
2342
2536
|
this.initializeObservers();
|
|
2537
|
+
this.value = this.initialValue || '';
|
|
2343
2538
|
}
|
|
2344
2539
|
/**
|
|
2345
2540
|
* method to initialize all recycled Subjects and initialize Observers on field instance creation or rerender
|
|
@@ -2347,25 +2542,25 @@ class FormField {
|
|
|
2347
2542
|
initializeObservers() {
|
|
2348
2543
|
var _a;
|
|
2349
2544
|
if (!this.valueSubject$ || this.valueSubject$.closed) {
|
|
2350
|
-
this.valueSubject$ = new
|
|
2545
|
+
this.valueSubject$ = new SafeSubject(() => this._mounted);
|
|
2351
2546
|
}
|
|
2352
2547
|
if (!this.errorSubject$ || this.errorSubject$.closed) {
|
|
2353
|
-
this.errorSubject$ = new
|
|
2548
|
+
this.errorSubject$ = new SafeSubject(() => this._mounted);
|
|
2354
2549
|
}
|
|
2355
2550
|
if (!this.visibilitySubject$ || this.visibilitySubject$.closed) {
|
|
2356
|
-
this.visibilitySubject$ = new
|
|
2551
|
+
this.visibilitySubject$ = new SafeSubject(() => this._mounted);
|
|
2357
2552
|
}
|
|
2358
2553
|
if (!this.apiSubject$ || this.apiSubject$.closed) {
|
|
2359
|
-
this.apiSubject$ = new
|
|
2554
|
+
this.apiSubject$ = new SafeSubject(() => this._mounted);
|
|
2360
2555
|
}
|
|
2361
2556
|
if (!this.propsSubject$ || this.propsSubject$.closed) {
|
|
2362
|
-
this.propsSubject$ = new
|
|
2557
|
+
this.propsSubject$ = new SafeSubject(() => this._mounted);
|
|
2363
2558
|
}
|
|
2364
2559
|
if (!this.fieldStateSubscription$ || this.fieldStateSubscription$.closed) {
|
|
2365
2560
|
this.fieldStateSubscription$ = new Subscription();
|
|
2366
2561
|
}
|
|
2367
2562
|
if (!this.apiEventQueueSubject$ || this.apiEventQueueSubject$.closed) {
|
|
2368
|
-
this.apiEventQueueSubject$ = new
|
|
2563
|
+
this.apiEventQueueSubject$ = new SafeSubject(() => this._mounted);
|
|
2369
2564
|
}
|
|
2370
2565
|
this.fieldState$ = combineLatest({
|
|
2371
2566
|
visibility: this.visibilitySubject$.pipe(startWith(this._visibility)),
|
|
@@ -2379,9 +2574,6 @@ class FormField {
|
|
|
2379
2574
|
}) => event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultAPIdebounceTimeMS))), filter(() => this.apiSubject$ && !this.apiSubject$.closed)).subscribe(payload => {
|
|
2380
2575
|
this.apiRequest(payload);
|
|
2381
2576
|
});
|
|
2382
|
-
if (!isNil(this.initialValue)) {
|
|
2383
|
-
this.value = this.initialValue;
|
|
2384
|
-
}
|
|
2385
2577
|
}
|
|
2386
2578
|
/**
|
|
2387
2579
|
* Retrieves the properties associated with the form field.
|
|
@@ -2401,6 +2593,7 @@ class FormField {
|
|
|
2401
2593
|
this._props = props;
|
|
2402
2594
|
this.propsSubject$.next(this.props);
|
|
2403
2595
|
this.templateSubject$.next({
|
|
2596
|
+
scope: 'fields',
|
|
2404
2597
|
key: this.name,
|
|
2405
2598
|
event: 'ON_PROPS'
|
|
2406
2599
|
});
|
|
@@ -2464,6 +2657,7 @@ class FormField {
|
|
|
2464
2657
|
}
|
|
2465
2658
|
this.stateValue && this.valueSubject$.next(this.stateValue);
|
|
2466
2659
|
this.templateSubject$.next({
|
|
2660
|
+
scope: 'fields',
|
|
2467
2661
|
key: this.name,
|
|
2468
2662
|
event: 'ON_VALUE'
|
|
2469
2663
|
});
|
|
@@ -2484,8 +2678,26 @@ class FormField {
|
|
|
2484
2678
|
set visibility(visible) {
|
|
2485
2679
|
if (typeof visible === 'undefined' || visible === this.visibility) return;
|
|
2486
2680
|
this._visibility = visible;
|
|
2681
|
+
/**
|
|
2682
|
+
* I was sure I would not require to gambiarra, but..
|
|
2683
|
+
* in order to ignore an hidden value on a form submit
|
|
2684
|
+
* or revalidate it when it comes back to visibility
|
|
2685
|
+
* I needed to...
|
|
2686
|
+
* I don't recommend setting private properties like this
|
|
2687
|
+
* this will force the field to be valid when it's hidden
|
|
2688
|
+
* and trigger the validation when it's visible
|
|
2689
|
+
*/
|
|
2690
|
+
if (!this.visibility) {
|
|
2691
|
+
this.value = '';
|
|
2692
|
+
this._valid = true;
|
|
2693
|
+
} else {
|
|
2694
|
+
this.setFieldValidity({
|
|
2695
|
+
event: 'ON_FIELD_MOUNT'
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2487
2698
|
this.visibilitySubject$.next(this.visibility);
|
|
2488
2699
|
this.templateSubject$.next({
|
|
2700
|
+
scope: 'fields',
|
|
2489
2701
|
key: this.name,
|
|
2490
2702
|
event: 'ON_VISIBILITY'
|
|
2491
2703
|
});
|
|
@@ -2518,12 +2730,13 @@ class FormField {
|
|
|
2518
2730
|
this.errorsList = Object.values(this.errors);
|
|
2519
2731
|
this.errorsString = this.errorsList.join(', ');
|
|
2520
2732
|
/**
|
|
2521
|
-
* if any error
|
|
2733
|
+
* if any error receives a list of errors, set a prop for it, currently only supporting a single string
|
|
2522
2734
|
*/
|
|
2523
2735
|
((_a = this.mapper.events) === null || _a === void 0 ? void 0 : _a.setErrorMessage) && this.errorSubject$.next({
|
|
2524
2736
|
[this.mapper.events.setErrorMessage]: this.errorsString
|
|
2525
2737
|
});
|
|
2526
2738
|
this.templateSubject$.next({
|
|
2739
|
+
scope: 'fields',
|
|
2527
2740
|
key: this.name,
|
|
2528
2741
|
event: 'ON_PROPS'
|
|
2529
2742
|
});
|
|
@@ -2546,6 +2759,7 @@ class FormField {
|
|
|
2546
2759
|
this._api = response;
|
|
2547
2760
|
this.apiSubject$.next(this.api);
|
|
2548
2761
|
this.templateSubject$.next({
|
|
2762
|
+
scope: 'fields',
|
|
2549
2763
|
key: this.name,
|
|
2550
2764
|
event: 'ON_API'
|
|
2551
2765
|
});
|
|
@@ -2569,6 +2783,7 @@ class FormField {
|
|
|
2569
2783
|
this.initializeObservers();
|
|
2570
2784
|
this.subscribeValue(valueSubscription);
|
|
2571
2785
|
this.subscribeState(propsSubscription);
|
|
2786
|
+
this._mounted = true;
|
|
2572
2787
|
}
|
|
2573
2788
|
/**
|
|
2574
2789
|
* Sets the value of the form field and emits associated events.
|
|
@@ -2625,49 +2840,43 @@ class FormField {
|
|
|
2625
2840
|
setFieldValidity({
|
|
2626
2841
|
event
|
|
2627
2842
|
}) {
|
|
2628
|
-
var _a;
|
|
2629
|
-
if (!this.validations) {
|
|
2843
|
+
var _a, _b, _c, _d;
|
|
2844
|
+
if (!this.validations || !this.visibility) {
|
|
2845
|
+
this.errors = {};
|
|
2630
2846
|
this._valid = true;
|
|
2631
2847
|
return;
|
|
2632
2848
|
}
|
|
2633
|
-
/*
|
|
2634
|
-
@TODO evaluate if _valid flag needs to be updated on all events, this condition saves resources,
|
|
2635
|
-
currently form submition needs to be controlled with form instance submit property function that
|
|
2636
|
-
will evaluate if all fields are valid regardless the events that triggers error messages
|
|
2637
|
-
*/
|
|
2638
|
-
if (!this.validations.events.includes(event) && event !== 'ON_FORM_SUBMIT') return;
|
|
2639
2849
|
let valid = true;
|
|
2640
|
-
const errors =
|
|
2641
|
-
const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.
|
|
2850
|
+
const errors = {};
|
|
2851
|
+
const schemaValidations = (_a = this.validations) === null || _a === void 0 ? void 0 : _a.methods;
|
|
2642
2852
|
schemaValidations && Object.keys(schemaValidations).forEach(validationKey => {
|
|
2643
|
-
|
|
2644
|
-
const error = validations[validationKey](this.value, schemaValidations);
|
|
2853
|
+
const error = handleValidation(this.value, schemaValidations, validations, validationKey);
|
|
2645
2854
|
// setting valid flag
|
|
2646
2855
|
valid = !error && valid;
|
|
2647
2856
|
// setting error messages
|
|
2648
|
-
if (
|
|
2649
|
-
if (
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2857
|
+
if (error && this.errorMessages) {
|
|
2858
|
+
if (validationKey in this.errorMessages) {
|
|
2859
|
+
const messages = this.errorMessages;
|
|
2860
|
+
errors[validationKey] = messages[validationKey];
|
|
2861
|
+
} else if ('default' in this.errorMessages) {
|
|
2862
|
+
errors[validationKey] = this.errorMessages.default;
|
|
2653
2863
|
}
|
|
2864
|
+
} else {
|
|
2865
|
+
delete errors[validationKey];
|
|
2654
2866
|
}
|
|
2655
2867
|
});
|
|
2656
2868
|
this._valid = valid;
|
|
2657
|
-
this.
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
valid = !error && valid;
|
|
2669
|
-
});
|
|
2670
|
-
this._valid = valid;
|
|
2869
|
+
if ((_c = (_b = this.validations) === null || _b === void 0 ? void 0 : _b.eventMessages) === null || _c === void 0 ? void 0 : _c[event]) {
|
|
2870
|
+
const eventMessages = {};
|
|
2871
|
+
(_d = this.validations.eventMessages[event]) === null || _d === void 0 ? void 0 : _d.forEach(method => {
|
|
2872
|
+
if (method in errors) {
|
|
2873
|
+
eventMessages[method] = errors[method];
|
|
2874
|
+
}
|
|
2875
|
+
});
|
|
2876
|
+
this.errors = eventMessages;
|
|
2877
|
+
} else if (event === 'ON_FORM_SUBMIT') {
|
|
2878
|
+
this.errors = errors;
|
|
2879
|
+
}
|
|
2671
2880
|
}
|
|
2672
2881
|
/**
|
|
2673
2882
|
* Formats the field value using the specified formatters, if available.
|
|
@@ -2701,7 +2910,7 @@ class FormField {
|
|
|
2701
2910
|
let valid = true;
|
|
2702
2911
|
const preConditions = config.preConditions;
|
|
2703
2912
|
preConditions && Object.keys(preConditions).forEach(validationKey => {
|
|
2704
|
-
const error =
|
|
2913
|
+
const error = handleValidation(this.value, preConditions, validations, validationKey);
|
|
2705
2914
|
valid = valid && !error;
|
|
2706
2915
|
});
|
|
2707
2916
|
if (config.blockRequestWhenInvalid) {
|
|
@@ -2720,6 +2929,17 @@ class FormField {
|
|
|
2720
2929
|
}) {
|
|
2721
2930
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
2722
2931
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2932
|
+
const configRequest = config => __awaiter(this, void 0, void 0, function* () {
|
|
2933
|
+
try {
|
|
2934
|
+
const responseData = yield makeRequest(config.method, config.url, config.headers, config.body);
|
|
2935
|
+
const apiResponseData = JSON.parse(String(responseData));
|
|
2936
|
+
const response = config.resultPath ? get(apiResponseData, config.resultPath) : apiResponseData;
|
|
2937
|
+
// this.apiResponseData = { response };
|
|
2938
|
+
return response;
|
|
2939
|
+
} catch (e) {
|
|
2940
|
+
return !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error';
|
|
2941
|
+
}
|
|
2942
|
+
});
|
|
2723
2943
|
if (!((_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.defaultConfig) === null || _b === void 0 ? void 0 : _b.events.includes(event)) && !(((_c = this.apiSchema) === null || _c === void 0 ? void 0 : _c.configs) && Object.keys((_d = this.apiSchema) === null || _d === void 0 ? void 0 : _d.configs).some(key => {
|
|
2724
2944
|
var _a, _b;
|
|
2725
2945
|
return (_b = (_a = this.apiSchema) === null || _a === void 0 ? void 0 : _a.configs) === null || _b === void 0 ? void 0 : _b[key].events.includes(event);
|
|
@@ -2730,19 +2950,10 @@ class FormField {
|
|
|
2730
2950
|
};
|
|
2731
2951
|
const config = (_e = this.apiSchema.defaultConfig) === null || _e === void 0 ? void 0 : _e.config;
|
|
2732
2952
|
if (config && ((_g = (_f = this.apiSchema) === null || _f === void 0 ? void 0 : _f.defaultConfig) === null || _g === void 0 ? void 0 : _g.events.includes(event)) && this.checkApiRequestValidations(config)) {
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
// this.apiResponseData = { response };
|
|
2738
|
-
responses.default = {
|
|
2739
|
-
response
|
|
2740
|
-
};
|
|
2741
|
-
} catch (e) {
|
|
2742
|
-
responses.default = {
|
|
2743
|
-
response: !isNil(config === null || config === void 0 ? void 0 : config.fallbackValue) ? config.fallbackValue : 'error'
|
|
2744
|
-
};
|
|
2745
|
-
}
|
|
2953
|
+
const response = yield configRequest(config);
|
|
2954
|
+
responses.default = {
|
|
2955
|
+
response
|
|
2956
|
+
};
|
|
2746
2957
|
}
|
|
2747
2958
|
if (((_h = this.apiSchema) === null || _h === void 0 ? void 0 : _h.configs) && Object.keys((_j = this.apiSchema) === null || _j === void 0 ? void 0 : _j.configs).some(key => {
|
|
2748
2959
|
var _a, _b;
|
|
@@ -2753,38 +2964,21 @@ class FormField {
|
|
|
2753
2964
|
@TODO handle promises with error
|
|
2754
2965
|
*/
|
|
2755
2966
|
const result = yield Promise.all(Object.keys(this.apiSchema.configs).map(configKey => __awaiter(this, void 0, void 0, function* () {
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
res({
|
|
2765
|
-
name: configKey,
|
|
2766
|
-
result: {
|
|
2767
|
-
response
|
|
2768
|
-
}
|
|
2769
|
-
});
|
|
2770
|
-
// responses.named![configKey] = { response };
|
|
2771
|
-
});
|
|
2772
|
-
} catch (e) {
|
|
2773
|
-
res({
|
|
2774
|
-
name: configKey,
|
|
2775
|
-
result: {
|
|
2776
|
-
response: !isNil(config.fallbackValue) ? config.fallbackValue : 'error'
|
|
2777
|
-
}
|
|
2778
|
-
});
|
|
2967
|
+
var _k, _l, _m, _o;
|
|
2968
|
+
const config = (_l = (_k = this.apiSchema) === null || _k === void 0 ? void 0 : _k.configs) === null || _l === void 0 ? void 0 : _l[configKey].config;
|
|
2969
|
+
if (config && ((_o = (_m = this.apiSchema) === null || _m === void 0 ? void 0 : _m.configs) === null || _o === void 0 ? void 0 : _o[configKey].events.includes(event)) && this.checkApiRequestValidations(config)) {
|
|
2970
|
+
const response = yield configRequest(config);
|
|
2971
|
+
return {
|
|
2972
|
+
name: configKey,
|
|
2973
|
+
result: {
|
|
2974
|
+
response
|
|
2779
2975
|
}
|
|
2780
|
-
}
|
|
2781
|
-
}
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
return null;
|
|
2782
2979
|
})));
|
|
2783
|
-
result.forEach(
|
|
2784
|
-
name
|
|
2785
|
-
result
|
|
2786
|
-
}) => {
|
|
2787
|
-
if (responses.named) responses.named[name] = result;
|
|
2980
|
+
result.forEach(payload => {
|
|
2981
|
+
if (payload && responses.named) responses.named[payload.name] = payload.result;
|
|
2788
2982
|
});
|
|
2789
2983
|
}
|
|
2790
2984
|
}
|
|
@@ -2797,7 +2991,8 @@ class FormField {
|
|
|
2797
2991
|
* @returns {void}
|
|
2798
2992
|
*/
|
|
2799
2993
|
destroyField() {
|
|
2800
|
-
this.
|
|
2994
|
+
this._mounted = false;
|
|
2995
|
+
this.valueSubscription$.unsubscribe();
|
|
2801
2996
|
this.visibilitySubject$.unsubscribe();
|
|
2802
2997
|
this.fieldStateSubscription$.unsubscribe();
|
|
2803
2998
|
this.propsSubject$.unsubscribe();
|
|
@@ -2823,13 +3018,12 @@ class FormField {
|
|
|
2823
3018
|
* @returns {void}
|
|
2824
3019
|
*/
|
|
2825
3020
|
subscribeValue(callback) {
|
|
2826
|
-
this.valueSubject$.subscribe({
|
|
3021
|
+
this.valueSubscription$ = this.valueSubject$.subscribe({
|
|
2827
3022
|
next: callback
|
|
2828
3023
|
});
|
|
2829
3024
|
}
|
|
2830
3025
|
}
|
|
2831
3026
|
|
|
2832
|
-
const IVARPROPNAME = 'iVars';
|
|
2833
3027
|
/**
|
|
2834
3028
|
* Represents the core logic for managing a form, including field management, validation, and submission.
|
|
2835
3029
|
*/
|
|
@@ -2843,38 +3037,39 @@ class FormCore {
|
|
|
2843
3037
|
* @param {string} [entry.action] - The action attribute of the form.
|
|
2844
3038
|
* @param {string} [entry.method] - The method attribute of the form.
|
|
2845
3039
|
* @param {IFormSchema.iVars} [entry.iVars] - The internal variables of the form.
|
|
2846
|
-
* @param {(data: TFormValues) => void} [entry.onSubmit] - A callback function to handle form submission.
|
|
2847
3040
|
* @param {((payload: {field: string;data: TFormValues;}) => void) | undefined} [entry.onData] - A callback function to handle data emission.
|
|
2848
3041
|
*/
|
|
2849
3042
|
constructor(entry) {
|
|
2850
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3043
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
3044
|
+
this.templateSubscription$ = new Subscription();
|
|
3045
|
+
this.mappers = new Map();
|
|
2851
3046
|
this.schema = entry.schema;
|
|
2852
3047
|
this.fields = new Map();
|
|
2853
3048
|
this.initialValues = entry.initialValues || ((_a = entry.schema) === null || _a === void 0 ? void 0 : _a.initialValues);
|
|
2854
3049
|
this.action = entry.action || ((_b = entry.schema) === null || _b === void 0 ? void 0 : _b.action);
|
|
2855
3050
|
this.method = entry.method || ((_c = entry.schema) === null || _c === void 0 ? void 0 : _c.method);
|
|
2856
3051
|
this._iVars = entry.iVars || ((_d = entry.schema) === null || _d === void 0 ? void 0 : _d.iVars) || {};
|
|
2857
|
-
this.onSubmit = entry.onSubmit;
|
|
2858
3052
|
this.config = {
|
|
2859
3053
|
defaultAPIdebounceTimeMS: Number((_e = entry.config) === null || _e === void 0 ? void 0 : _e.defaultAPIdebounceTimeMS) ? Number((_f = entry.config) === null || _f === void 0 ? void 0 : _f.defaultAPIdebounceTimeMS) : DEFAULT_API_DEBOUNCE_TIME,
|
|
2860
3054
|
defaultStateRefreshTimeMS: Number((_g = entry.config) === null || _g === void 0 ? void 0 : _g.defaultStateRefreshTimeMS) ? Number((_h = entry.config) === null || _h === void 0 ? void 0 : _h.defaultStateRefreshTimeMS) : DEFAULT_STATE_REFRESH_TIME
|
|
2861
3055
|
};
|
|
2862
|
-
|
|
3056
|
+
(_j = entry.mappers) === null || _j === void 0 ? void 0 : _j.map(mapper => {
|
|
3057
|
+
this.mappers.set(mapper.componentName, mapper);
|
|
3058
|
+
});
|
|
2863
3059
|
this.schema && FormCore.checkIndexes(this.schema.components);
|
|
2864
3060
|
this.templateSubject$ = new Subject();
|
|
2865
3061
|
this.submitSubject$ = new Subject();
|
|
2866
3062
|
this.fieldEventSubject$ = new Subject();
|
|
2867
3063
|
this.dataSubject$ = new Subject();
|
|
2868
|
-
this.
|
|
3064
|
+
this.mountSubject$ = new Subject();
|
|
2869
3065
|
this.subscribedTemplates = [];
|
|
2870
3066
|
this.schema && this.serializeStructure(this.schema.components);
|
|
2871
3067
|
this.schema && this.subscribeTemplates();
|
|
2872
|
-
this.templateSubject$.subscribe(this.refreshTemplates.bind(this));
|
|
3068
|
+
this.templateSubscription$ = this.templateSubject$.subscribe(this.refreshTemplates.bind(this));
|
|
2873
3069
|
this.templateSubject$.next({
|
|
2874
|
-
|
|
3070
|
+
scope: 'iVars',
|
|
2875
3071
|
event: 'ON_IVARS'
|
|
2876
3072
|
});
|
|
2877
|
-
entry.onData && this.subscribeData(entry.onData);
|
|
2878
3073
|
/*
|
|
2879
3074
|
mount events needs to occur on form level, only when all the fields are instantiated
|
|
2880
3075
|
is it possible to apply all the side effects that occur globally, same effect occur
|
|
@@ -2885,6 +3080,7 @@ class FormCore {
|
|
|
2885
3080
|
event: 'ON_FIELD_MOUNT'
|
|
2886
3081
|
});
|
|
2887
3082
|
this.refreshTemplates({
|
|
3083
|
+
scope: 'fields',
|
|
2888
3084
|
key,
|
|
2889
3085
|
event: 'ON_FIELDS'
|
|
2890
3086
|
});
|
|
@@ -2906,7 +3102,7 @@ class FormCore {
|
|
|
2906
3102
|
set iVars(payload) {
|
|
2907
3103
|
this._iVars = payload;
|
|
2908
3104
|
this.templateSubject$.next({
|
|
2909
|
-
|
|
3105
|
+
scope: 'iVars',
|
|
2910
3106
|
event: 'ON_IVARS'
|
|
2911
3107
|
});
|
|
2912
3108
|
}
|
|
@@ -2934,7 +3130,6 @@ class FormCore {
|
|
|
2934
3130
|
validations,
|
|
2935
3131
|
visibilityConditions,
|
|
2936
3132
|
resetValues,
|
|
2937
|
-
errorMessages,
|
|
2938
3133
|
api
|
|
2939
3134
|
}
|
|
2940
3135
|
}, key) => {
|
|
@@ -2945,26 +3140,11 @@ class FormCore {
|
|
|
2945
3140
|
validations,
|
|
2946
3141
|
visibilityConditions,
|
|
2947
3142
|
resetValues,
|
|
2948
|
-
errorMessages,
|
|
2949
3143
|
apiSchema: api
|
|
2950
3144
|
};
|
|
2951
3145
|
traverseObject(template, key).forEach(element => this.subscribedTemplates.push(element));
|
|
2952
3146
|
});
|
|
2953
3147
|
}
|
|
2954
|
-
/**
|
|
2955
|
-
*
|
|
2956
|
-
* @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
|
|
2957
|
-
*/
|
|
2958
|
-
subscribeData(callback) {
|
|
2959
|
-
this.dataCallbackSubscription$ = this.dataSubject$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS), map(({
|
|
2960
|
-
key
|
|
2961
|
-
}) => ({
|
|
2962
|
-
field: key,
|
|
2963
|
-
data: this.getFormValues()
|
|
2964
|
-
}))).subscribe({
|
|
2965
|
-
next: callback
|
|
2966
|
-
});
|
|
2967
|
-
}
|
|
2968
3148
|
/**
|
|
2969
3149
|
* Gets the value of a property from a field.
|
|
2970
3150
|
*
|
|
@@ -2975,20 +3155,27 @@ class FormCore {
|
|
|
2975
3155
|
* @returns {unknown | undefined} The value of the property, or undefined if the field doesn't exist.
|
|
2976
3156
|
*/
|
|
2977
3157
|
getValue({
|
|
3158
|
+
scope,
|
|
2978
3159
|
key,
|
|
2979
3160
|
property,
|
|
2980
3161
|
path
|
|
2981
3162
|
}) {
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3163
|
+
switch (scope) {
|
|
3164
|
+
case 'iVars':
|
|
3165
|
+
{
|
|
3166
|
+
const value = get(this.iVars, [key, ...(property ? [property] : []), ...path]);
|
|
3167
|
+
return value;
|
|
3168
|
+
}
|
|
3169
|
+
case 'fields':
|
|
3170
|
+
{
|
|
3171
|
+
const field = this.fields.get(key);
|
|
3172
|
+
if (!field) return console.warn(`failed to get value from ${key}`);
|
|
3173
|
+
if (property === 'props' && path[0] === field.valuePropName) {
|
|
3174
|
+
return field.stateValue;
|
|
3175
|
+
}
|
|
3176
|
+
return path.length > 0 ? get(field[property], path) : field[property];
|
|
3177
|
+
}
|
|
2990
3178
|
}
|
|
2991
|
-
return path.length > 0 ? get(field[property], path) : field[property];
|
|
2992
3179
|
}
|
|
2993
3180
|
/**
|
|
2994
3181
|
* Sets the value of a property in a field.
|
|
@@ -3051,27 +3238,26 @@ class FormCore {
|
|
|
3051
3238
|
* @returns {string[]} An array of extracted parameters.
|
|
3052
3239
|
*/
|
|
3053
3240
|
extractParams(expression) {
|
|
3054
|
-
const regex = /\${(.*?)}/g;
|
|
3055
3241
|
const extractedValues = [];
|
|
3056
3242
|
let match;
|
|
3057
|
-
while (!isNil(match =
|
|
3243
|
+
while (!isNil(match = TEMPLATE_REGEX_DELIMITATOR.exec(expression))) {
|
|
3058
3244
|
extractedValues.push(match[1]);
|
|
3059
3245
|
}
|
|
3060
|
-
const
|
|
3061
|
-
const splittedString = extractedValues.map(el => el.split(operatorRegex));
|
|
3246
|
+
const splittedString = extractedValues.map(el => el.split(TEMPLATE_REGEX_OPERATOR_SPLITTER));
|
|
3062
3247
|
const result = splittedString.map(splittedStringVal => {
|
|
3063
3248
|
return splittedStringVal.filter(Boolean).reduce((acc, curr) => {
|
|
3064
|
-
if (curr.match(
|
|
3249
|
+
if (curr.match(TEMPLATE_REGEX_OPERATOR_MATCHER)) {
|
|
3065
3250
|
return `${acc}${curr}`;
|
|
3066
3251
|
}
|
|
3067
3252
|
let value;
|
|
3068
3253
|
// check if element is in dot notation to get from field instances
|
|
3069
3254
|
const element = curr.split('.');
|
|
3070
3255
|
const currElementContent = element.length > 1 ? this.getValue({
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3256
|
+
scope: element[0],
|
|
3257
|
+
key: element[1],
|
|
3258
|
+
property: element[2],
|
|
3259
|
+
path: element.slice(3)
|
|
3260
|
+
}) : curr;
|
|
3075
3261
|
let currValue;
|
|
3076
3262
|
// if any parsable content was passed to the conditions, parse them
|
|
3077
3263
|
// required to be able to apply conditions
|
|
@@ -3092,6 +3278,11 @@ class FormCore {
|
|
|
3092
3278
|
case 'object':
|
|
3093
3279
|
if (currValue === null) {
|
|
3094
3280
|
value = null;
|
|
3281
|
+
break;
|
|
3282
|
+
}
|
|
3283
|
+
if (currValue instanceof Date) {
|
|
3284
|
+
value = `new Date(\`${currValue}\`)`;
|
|
3285
|
+
break;
|
|
3095
3286
|
}
|
|
3096
3287
|
value = JSON.stringify(currValue);
|
|
3097
3288
|
break;
|
|
@@ -3118,8 +3309,8 @@ class FormCore {
|
|
|
3118
3309
|
* @returns {string} The expression string with the replacements made.
|
|
3119
3310
|
*/
|
|
3120
3311
|
replaceExpression(expression, values) {
|
|
3121
|
-
const regex =
|
|
3122
|
-
return expression.replace(regex, () => values.shift() || '');
|
|
3312
|
+
const regex = TEMPLATE_REGEX_DELIMITATOR;
|
|
3313
|
+
return expression.replace(regex, () => String(values.shift()) || '');
|
|
3123
3314
|
}
|
|
3124
3315
|
/**
|
|
3125
3316
|
* Checks if an expression string contains string concatenation within a marked expression.
|
|
@@ -3128,8 +3319,7 @@ class FormCore {
|
|
|
3128
3319
|
* @returns {boolean} True if the expression contains string concatenation, otherwise false.
|
|
3129
3320
|
*/
|
|
3130
3321
|
hasStringConcatenation(expression) {
|
|
3131
|
-
|
|
3132
|
-
return !regex.test(expression);
|
|
3322
|
+
return !TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR.test(expression);
|
|
3133
3323
|
}
|
|
3134
3324
|
/**
|
|
3135
3325
|
* Refreshes templates with updated values.
|
|
@@ -3149,7 +3339,7 @@ class FormCore {
|
|
|
3149
3339
|
originExpression,
|
|
3150
3340
|
originFieldKeys
|
|
3151
3341
|
}) => {
|
|
3152
|
-
if (originFieldKeys.includes(key)) {
|
|
3342
|
+
if (!key || originFieldKeys.includes(key)) {
|
|
3153
3343
|
const originExpressions = this.extractParams(originExpression);
|
|
3154
3344
|
let originValue;
|
|
3155
3345
|
if (this.hasStringConcatenation(originExpression)) {
|
|
@@ -3158,6 +3348,7 @@ class FormCore {
|
|
|
3158
3348
|
originValue = originExpressions === null || originExpressions === void 0 ? void 0 : originExpressions[0];
|
|
3159
3349
|
}
|
|
3160
3350
|
const destinationValue = this.getValue({
|
|
3351
|
+
scope: 'fields',
|
|
3161
3352
|
key: destinationKey,
|
|
3162
3353
|
property: destinationProperty,
|
|
3163
3354
|
path: destinationPath
|
|
@@ -3175,6 +3366,26 @@ class FormCore {
|
|
|
3175
3366
|
}
|
|
3176
3367
|
});
|
|
3177
3368
|
}
|
|
3369
|
+
/**
|
|
3370
|
+
* @internal
|
|
3371
|
+
* Update field visibility accordingly.
|
|
3372
|
+
*
|
|
3373
|
+
* @param {string} field - Field name to be updated.
|
|
3374
|
+
* @param {boolean} hasError - Condition to be used as visibility.
|
|
3375
|
+
* @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.
|
|
3376
|
+
*/
|
|
3377
|
+
setFieldVisibility(field, hasError, showOnlyIfTrue) {
|
|
3378
|
+
if (!this.fields.has(field)) {
|
|
3379
|
+
/*
|
|
3380
|
+
* check with João the need for a global mount event for the form
|
|
3381
|
+
* to avoid not displaying this message in some cases (AsFormField)
|
|
3382
|
+
* suggestion: create subscriber to targetField.ON_FIELD_MOUNT
|
|
3383
|
+
*/
|
|
3384
|
+
console.warn(`failed to update visibility onto field ${field}`);
|
|
3385
|
+
} else {
|
|
3386
|
+
this.fields.get(field).visibility = showOnlyIfTrue ? hasError : !hasError;
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3178
3389
|
/**
|
|
3179
3390
|
* Validates visibility conditions for a given event and updates field visibility accordingly.
|
|
3180
3391
|
*
|
|
@@ -3188,21 +3399,44 @@ class FormCore {
|
|
|
3188
3399
|
}) {
|
|
3189
3400
|
const field = this.fields.get(key);
|
|
3190
3401
|
const structVisibility = field === null || field === void 0 ? void 0 : field.visibilityConditions;
|
|
3191
|
-
if (!structVisibility || !(structVisibility === null || structVisibility === void 0 ? void 0 : structVisibility.some(config => config.events.includes(event))))
|
|
3402
|
+
if (!structVisibility || !(structVisibility === null || structVisibility === void 0 ? void 0 : structVisibility.some(config => config.events.includes(event)))) {
|
|
3403
|
+
return;
|
|
3404
|
+
}
|
|
3192
3405
|
structVisibility.forEach(structElement => {
|
|
3193
3406
|
if (!structElement.events.includes(event)) return;
|
|
3194
3407
|
Object.keys(structElement.validations).forEach(validationKey => {
|
|
3195
|
-
const error =
|
|
3408
|
+
const error = handleValidation(field.value, structElement.validations, validations, validationKey);
|
|
3196
3409
|
if (Array.isArray(structElement.fields)) {
|
|
3197
3410
|
structElement.fields.forEach(fieldKey => {
|
|
3198
|
-
|
|
3411
|
+
this.setFieldVisibility(fieldKey, error, !!(field.value && structElement.showOnlyIfTrue));
|
|
3199
3412
|
});
|
|
3200
3413
|
} else if (structElement.fields) {
|
|
3201
|
-
|
|
3414
|
+
this.setFieldVisibility(structElement.fields, error, !!(field.value && structElement.showOnlyIfTrue));
|
|
3202
3415
|
}
|
|
3203
3416
|
});
|
|
3204
3417
|
});
|
|
3205
3418
|
}
|
|
3419
|
+
/**
|
|
3420
|
+
* @internal
|
|
3421
|
+
* Update field value and emit change and cleared event.
|
|
3422
|
+
*
|
|
3423
|
+
* @param {string} key - Field name to be updated.
|
|
3424
|
+
* @param {unknown} value - Value to be inserted into field.
|
|
3425
|
+
*/
|
|
3426
|
+
setResetFieldValue(key, value) {
|
|
3427
|
+
if (!this.fields.has(key)) {
|
|
3428
|
+
console.warn(`failed to reset value onto field ${key}`);
|
|
3429
|
+
} else {
|
|
3430
|
+
const field = this.fields.get(key);
|
|
3431
|
+
field.emitValue({
|
|
3432
|
+
value: value,
|
|
3433
|
+
event: 'ON_FIELD_CHANGE'
|
|
3434
|
+
});
|
|
3435
|
+
field.emitEvents({
|
|
3436
|
+
event: 'ON_FIELD_CLEARED'
|
|
3437
|
+
});
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3206
3440
|
/**
|
|
3207
3441
|
* Resets field values based on reset conditions defined in the schema.
|
|
3208
3442
|
*
|
|
@@ -3219,23 +3453,23 @@ class FormCore {
|
|
|
3219
3453
|
if (!structResetValue || !(structResetValue === null || structResetValue === void 0 ? void 0 : structResetValue.some(config => config.events.includes(event)))) return;
|
|
3220
3454
|
structResetValue.forEach(structElement => {
|
|
3221
3455
|
if (!structElement.events.includes(event)) return;
|
|
3456
|
+
const handleFieldRestoration = () => {
|
|
3457
|
+
if (Array.isArray(structElement.fields)) {
|
|
3458
|
+
structElement.fields.forEach((fieldKey, index) => {
|
|
3459
|
+
const resettledValue = Array.isArray(structElement.resettledValue) ? structElement.resettledValue[index] : structElement.resettledValue;
|
|
3460
|
+
this.setResetFieldValue(fieldKey, resettledValue);
|
|
3461
|
+
});
|
|
3462
|
+
} else if (structElement.fields) {
|
|
3463
|
+
this.setResetFieldValue(structElement.fields, structElement.resettledValue);
|
|
3464
|
+
}
|
|
3465
|
+
};
|
|
3466
|
+
if (!structElement.validations) {
|
|
3467
|
+
return handleFieldRestoration();
|
|
3468
|
+
}
|
|
3222
3469
|
Object.keys(structElement.validations).forEach(validationKey => {
|
|
3223
|
-
const error =
|
|
3470
|
+
const error = handleValidation(field.value, structElement.validations, validations, validationKey);
|
|
3224
3471
|
if (!error) {
|
|
3225
|
-
|
|
3226
|
-
structElement.fields.forEach((fieldKey, index) => {
|
|
3227
|
-
const resettledValue = Array.isArray(structElement.resettledValue) ? structElement.resettledValue[index] : structElement.resettledValue;
|
|
3228
|
-
if (!this.fields.has(fieldKey)) console.warn(`failed to reset value onto field ${fieldKey}`);else this.fields.get(fieldKey).emitValue({
|
|
3229
|
-
value: resettledValue,
|
|
3230
|
-
event: 'ON_FIELD_CHANGE'
|
|
3231
|
-
});
|
|
3232
|
-
});
|
|
3233
|
-
} else if (structElement.fields) {
|
|
3234
|
-
if (!this.fields.has(structElement.fields)) console.warn(`failed to reset value onto field ${structElement.fields}`);else this.fields.get(structElement.fields).emitValue({
|
|
3235
|
-
value: structElement.resettledValue,
|
|
3236
|
-
event: 'ON_FIELD_CHANGE'
|
|
3237
|
-
});
|
|
3238
|
-
}
|
|
3472
|
+
handleFieldRestoration();
|
|
3239
3473
|
}
|
|
3240
3474
|
});
|
|
3241
3475
|
});
|
|
@@ -3244,6 +3478,7 @@ class FormCore {
|
|
|
3244
3478
|
* Adds a field onto the form instance regardless there is a schema or not
|
|
3245
3479
|
*
|
|
3246
3480
|
* @param fieldSchema
|
|
3481
|
+
* @param mapperElement
|
|
3247
3482
|
*/
|
|
3248
3483
|
addField({
|
|
3249
3484
|
fieldSchema,
|
|
@@ -3253,7 +3488,7 @@ class FormCore {
|
|
|
3253
3488
|
if (this.fields.has(fieldSchema.name)) {
|
|
3254
3489
|
throw new Error(`field name ${fieldSchema.name} already defined`);
|
|
3255
3490
|
}
|
|
3256
|
-
const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.
|
|
3491
|
+
const mapper = mapperElement || ((_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(fieldSchema.component));
|
|
3257
3492
|
if (!mapper) throw new Error(`mapper not found for ${fieldSchema.component}, add it to the mappers configuration`);
|
|
3258
3493
|
this.fields.set(fieldSchema.name, new FormField({
|
|
3259
3494
|
schemaComponent: fieldSchema,
|
|
@@ -3269,6 +3504,7 @@ class FormCore {
|
|
|
3269
3504
|
}));
|
|
3270
3505
|
this.subscribeTemplates();
|
|
3271
3506
|
this.refreshTemplates({
|
|
3507
|
+
scope: 'fields',
|
|
3272
3508
|
event: 'ON_FIELDS',
|
|
3273
3509
|
key: fieldSchema.name
|
|
3274
3510
|
});
|
|
@@ -3284,6 +3520,7 @@ class FormCore {
|
|
|
3284
3520
|
this.fields.delete(key);
|
|
3285
3521
|
this.subscribeTemplates();
|
|
3286
3522
|
this.templateSubject$.next({
|
|
3523
|
+
scope: 'fields',
|
|
3287
3524
|
key,
|
|
3288
3525
|
event: 'ON_FIELDS'
|
|
3289
3526
|
});
|
|
@@ -3300,7 +3537,12 @@ class FormCore {
|
|
|
3300
3537
|
var _a, _b, _c;
|
|
3301
3538
|
const currField = this.fields.get(structElement.name);
|
|
3302
3539
|
if (!currField) {
|
|
3303
|
-
|
|
3540
|
+
let mapper;
|
|
3541
|
+
if (structElement === null || structElement === void 0 ? void 0 : structElement.mapper) {
|
|
3542
|
+
mapper = structElement === null || structElement === void 0 ? void 0 : structElement.mapper;
|
|
3543
|
+
} else {
|
|
3544
|
+
mapper = (_a = this.mappers) === null || _a === void 0 ? void 0 : _a.get(structElement.component);
|
|
3545
|
+
}
|
|
3304
3546
|
if (!mapper) throw new Error(`mapper not found for ${structElement.component}, add it to the mappers configuration`);
|
|
3305
3547
|
this.fields.set(structElement.name, new FormField({
|
|
3306
3548
|
schemaComponent: structElement,
|
|
@@ -3318,10 +3560,11 @@ class FormCore {
|
|
|
3318
3560
|
} else {
|
|
3319
3561
|
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) || [];
|
|
3320
3562
|
currField.path = path;
|
|
3563
|
+
currField.originalSchema = structElement;
|
|
3321
3564
|
currField.templateSubject$ = this.templateSubject$;
|
|
3322
3565
|
}
|
|
3323
3566
|
if (structElement.children) {
|
|
3324
|
-
|
|
3567
|
+
this.serializeStructure(structElement.children, `${path ? `${path}.` : ``}${structElement.name}`);
|
|
3325
3568
|
}
|
|
3326
3569
|
});
|
|
3327
3570
|
}
|
|
@@ -3353,6 +3596,7 @@ class FormCore {
|
|
|
3353
3596
|
this.subscribedTemplates.forEach(el => {
|
|
3354
3597
|
el.originFieldKeys.forEach(field => {
|
|
3355
3598
|
this.templateSubject$.next({
|
|
3599
|
+
scope: 'fields',
|
|
3356
3600
|
key: field,
|
|
3357
3601
|
event: 'ON_FIELDS'
|
|
3358
3602
|
});
|
|
@@ -3378,10 +3622,10 @@ class FormCore {
|
|
|
3378
3622
|
const values = {};
|
|
3379
3623
|
this.fields.forEach((val, key) => {
|
|
3380
3624
|
if (val.value) {
|
|
3381
|
-
values
|
|
3625
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3382
3626
|
}
|
|
3383
3627
|
});
|
|
3384
|
-
console.
|
|
3628
|
+
console.table(values);
|
|
3385
3629
|
}
|
|
3386
3630
|
/**
|
|
3387
3631
|
* Gets the current values of all form fields.
|
|
@@ -3393,7 +3637,7 @@ class FormCore {
|
|
|
3393
3637
|
const erroredFields = [];
|
|
3394
3638
|
this.fields.forEach((val, key) => {
|
|
3395
3639
|
if (val.value) {
|
|
3396
|
-
values
|
|
3640
|
+
set(values, val.nameToSubmit || key, val.value);
|
|
3397
3641
|
}
|
|
3398
3642
|
if (!val.valid) {
|
|
3399
3643
|
erroredFields.push(key);
|
|
@@ -3413,6 +3657,45 @@ class FormCore {
|
|
|
3413
3657
|
});
|
|
3414
3658
|
return sub;
|
|
3415
3659
|
}
|
|
3660
|
+
subscribeOnMount(callback) {
|
|
3661
|
+
const sub = this.mountSubject$.pipe(map(() => this.getFormValues())).subscribe({
|
|
3662
|
+
next: callback
|
|
3663
|
+
});
|
|
3664
|
+
return sub;
|
|
3665
|
+
}
|
|
3666
|
+
/**
|
|
3667
|
+
*
|
|
3668
|
+
* @param {(payload: { field: string; data: TFormValues }) => void} callback callback function to call on data
|
|
3669
|
+
*/
|
|
3670
|
+
subscribeData(callback) {
|
|
3671
|
+
const sub = this.dataSubject$.pipe(groupBy(payload => payload.event), mergeMap(group$ => group$.pipe(debounceTime(this.config.defaultStateRefreshTimeMS))), map(({
|
|
3672
|
+
key
|
|
3673
|
+
}) => ({
|
|
3674
|
+
field: key,
|
|
3675
|
+
data: this.getFormValues()
|
|
3676
|
+
}))).subscribe({
|
|
3677
|
+
next: callback
|
|
3678
|
+
});
|
|
3679
|
+
return sub;
|
|
3680
|
+
}
|
|
3681
|
+
subscribeOnSubmit(callback) {
|
|
3682
|
+
const sub = this.submitSubject$.pipe(map(() => this.getFormValues())).subscribe({
|
|
3683
|
+
next: callback
|
|
3684
|
+
});
|
|
3685
|
+
return sub;
|
|
3686
|
+
}
|
|
3687
|
+
/**
|
|
3688
|
+
* Submits the form by triggering form field events and invoking the onSubmit callback.
|
|
3689
|
+
*/
|
|
3690
|
+
mounted() {
|
|
3691
|
+
this.fields.forEach(field => {
|
|
3692
|
+
field.emitEvents({
|
|
3693
|
+
event: 'ON_FORM_MOUNT'
|
|
3694
|
+
});
|
|
3695
|
+
});
|
|
3696
|
+
const values = this.getFormValues();
|
|
3697
|
+
this.mountSubject$.next(values);
|
|
3698
|
+
}
|
|
3416
3699
|
/**
|
|
3417
3700
|
* Submits the form by triggering form field events and invoking the onSubmit callback.
|
|
3418
3701
|
*/
|
|
@@ -3425,11 +3708,10 @@ class FormCore {
|
|
|
3425
3708
|
if (!this.isValid) return;
|
|
3426
3709
|
const values = this.getFormValues();
|
|
3427
3710
|
this.submitSubject$.next(values);
|
|
3428
|
-
this.onSubmit && this.onSubmit(values);
|
|
3429
3711
|
}
|
|
3430
3712
|
destroy() {
|
|
3431
3713
|
this.submitSubject$.unsubscribe();
|
|
3432
|
-
this.
|
|
3714
|
+
this.templateSubscription$.unsubscribe();
|
|
3433
3715
|
this.fieldEventSubject$.unsubscribe();
|
|
3434
3716
|
this.dataSubject$.unsubscribe();
|
|
3435
3717
|
this.fields.forEach(field => field.destroyField());
|
|
@@ -3446,15 +3728,19 @@ class FormCore {
|
|
|
3446
3728
|
*/
|
|
3447
3729
|
FormCore.checkIndexes = (struct, indexes = []) => {
|
|
3448
3730
|
if (!struct) return indexes;
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
return FormCore.checkIndexes(structElement.children, indexes);
|
|
3731
|
+
const helper = (struct, indexes) => {
|
|
3732
|
+
for (let i = 0; i < struct.length; i++) {
|
|
3733
|
+
const structElement = struct[i];
|
|
3734
|
+
indexes.push(structElement.name);
|
|
3735
|
+
if (structElement.children) {
|
|
3736
|
+
helper(structElement.children, indexes);
|
|
3737
|
+
}
|
|
3457
3738
|
}
|
|
3739
|
+
};
|
|
3740
|
+
helper(struct, indexes);
|
|
3741
|
+
const duppedIndexes = indexes.filter((item, index) => indexes.indexOf(item) !== index);
|
|
3742
|
+
if (duppedIndexes.length > 0) {
|
|
3743
|
+
throw new Error(`duplicated indexes found on schema: ${JSON.stringify(duppedIndexes)}`);
|
|
3458
3744
|
}
|
|
3459
3745
|
return indexes;
|
|
3460
3746
|
};
|
|
@@ -3562,29 +3848,56 @@ class FormGroup {
|
|
|
3562
3848
|
* Prints the form group instance to the console.
|
|
3563
3849
|
*/
|
|
3564
3850
|
printFormGroupInstance() {
|
|
3565
|
-
console.
|
|
3851
|
+
console.table(this.forms);
|
|
3566
3852
|
}
|
|
3567
3853
|
/**
|
|
3568
3854
|
* Prototype submit function to multiple forms
|
|
3569
3855
|
* @param {string[]} indexes form indexes to be submitted
|
|
3570
3856
|
* @returns
|
|
3571
3857
|
*/
|
|
3572
|
-
submitMultipleFormsByIndex(indexes) {
|
|
3858
|
+
submitMultipleFormsByIndex(indexes, callback) {
|
|
3573
3859
|
let isValid = true;
|
|
3574
3860
|
let values = {};
|
|
3575
3861
|
let erroredFields = [];
|
|
3576
3862
|
indexes.forEach(index => {
|
|
3577
|
-
var _a;
|
|
3578
|
-
|
|
3863
|
+
var _a, _b;
|
|
3864
|
+
(_a = this.forms.get(index)) === null || _a === void 0 ? void 0 : _a.submit();
|
|
3865
|
+
const res = (_b = this.forms.get(index)) === null || _b === void 0 ? void 0 : _b.getFormValues();
|
|
3579
3866
|
isValid = isValid && ((res === null || res === void 0 ? void 0 : res.isValid) || false);
|
|
3580
3867
|
values = Object.assign(Object.assign({}, values), (res === null || res === void 0 ? void 0 : res.values) || {});
|
|
3581
3868
|
erroredFields = [...erroredFields, ...((res === null || res === void 0 ? void 0 : res.erroredFields) || [])];
|
|
3582
3869
|
});
|
|
3583
|
-
|
|
3870
|
+
isValid && callback && callback({
|
|
3584
3871
|
erroredFields,
|
|
3585
3872
|
isValid,
|
|
3586
3873
|
values
|
|
3587
|
-
};
|
|
3874
|
+
});
|
|
3875
|
+
}
|
|
3876
|
+
onDataSubscription({
|
|
3877
|
+
ids,
|
|
3878
|
+
callback
|
|
3879
|
+
}) {
|
|
3880
|
+
const subs = ids.reduce((acc, formId) => {
|
|
3881
|
+
var _a;
|
|
3882
|
+
// @TODO add config on debounceTime on this events
|
|
3883
|
+
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(({
|
|
3884
|
+
key
|
|
3885
|
+
}) => {
|
|
3886
|
+
var _a;
|
|
3887
|
+
return {
|
|
3888
|
+
formField: key,
|
|
3889
|
+
values: (_a = this.forms.get(formId)) === null || _a === void 0 ? void 0 : _a.getFormValues()
|
|
3890
|
+
};
|
|
3891
|
+
}));
|
|
3892
|
+
if (sub) {
|
|
3893
|
+
acc[formId] = sub;
|
|
3894
|
+
} else {
|
|
3895
|
+
console.warn(`failed to register form id ${formId}`);
|
|
3896
|
+
}
|
|
3897
|
+
return acc;
|
|
3898
|
+
}, {});
|
|
3899
|
+
const sub = combineLatest(subs).subscribe(callback);
|
|
3900
|
+
return sub;
|
|
3588
3901
|
}
|
|
3589
3902
|
}
|
|
3590
3903
|
|