@syncfusion/ej2-form-renderer 34.2.2
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 +60 -0
- package/common.d.ts +4 -0
- package/common.js +4 -0
- package/dist/ej2-form-renderer.umd.min.js +11 -0
- package/dist/ej2-form-renderer.umd.min.js.map +1 -0
- package/dist/es6/ej2-form-renderer.es2015.js +6787 -0
- package/dist/es6/ej2-form-renderer.es2015.js.map +1 -0
- package/dist/es6/ej2-form-renderer.es5.js +7055 -0
- package/dist/es6/ej2-form-renderer.es5.js.map +1 -0
- package/dist/global/ej2-form-renderer.min.js +11 -0
- package/dist/global/ej2-form-renderer.min.js.map +1 -0
- package/dist/global/index.d.ts +14 -0
- package/form-renderer.d.ts +4 -0
- package/form-renderer.js +4 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/license +10 -0
- package/package.json +41 -0
- package/src/common/checkbox-group.d.ts +55 -0
- package/src/common/checkbox-group.js +241 -0
- package/src/common/conditional-rule-engine.d.ts +241 -0
- package/src/common/conditional-rule-engine.js +505 -0
- package/src/common/custom-validation-engine.d.ts +57 -0
- package/src/common/custom-validation-engine.js +152 -0
- package/src/common/datagrid-component.d.ts +156 -0
- package/src/common/datagrid-component.js +410 -0
- package/src/common/expressions/dependency-tracker.d.ts +29 -0
- package/src/common/expressions/dependency-tracker.js +137 -0
- package/src/common/expressions/expression-engine.d.ts +94 -0
- package/src/common/expressions/expression-engine.js +514 -0
- package/src/common/expressions/expression-parser.d.ts +40 -0
- package/src/common/expressions/expression-parser.js +270 -0
- package/src/common/expressions/grid-expressions.d.ts +24 -0
- package/src/common/expressions/grid-expressions.js +92 -0
- package/src/common/expressions/index.d.ts +4 -0
- package/src/common/expressions/index.js +4 -0
- package/src/common/index.d.ts +10 -0
- package/src/common/index.js +10 -0
- package/src/common/json-converter.d.ts +945 -0
- package/src/common/json-converter.js +1110 -0
- package/src/common/minmax-rule-engine.d.ts +105 -0
- package/src/common/minmax-rule-engine.js +380 -0
- package/src/common/option-fetcher.d.ts +30 -0
- package/src/common/option-fetcher.js +139 -0
- package/src/common/parser.d.ts +98 -0
- package/src/common/parser.js +788 -0
- package/src/common/static-html-renderer.d.ts +45 -0
- package/src/common/static-html-renderer.js +77 -0
- package/src/common/utils.d.ts +116 -0
- package/src/common/utils.js +484 -0
- package/src/form-renderer/form-renderer-model.d.ts +65 -0
- package/src/form-renderer/form-renderer.d.ts +425 -0
- package/src/form-renderer/form-renderer.js +2277 -0
- package/src/form-renderer/index.d.ts +6 -0
- package/src/form-renderer/index.js +5 -0
- package/src/form-renderer/render-form-field.d.ts +31 -0
- package/src/form-renderer/render-form-field.js +816 -0
- package/src/form-renderer/types/form-schema.d.ts +452 -0
- package/src/form-renderer/types/form-schema.js +32 -0
- package/src/global.js +1 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +4 -0
- package/styles/bds-lite.css +599 -0
- package/styles/bds-lite.scss +2 -0
- package/styles/bds.css +599 -0
- package/styles/bds.scss +2 -0
- package/styles/bootstrap-dark-lite.css +608 -0
- package/styles/bootstrap-dark-lite.scss +2 -0
- package/styles/bootstrap-dark.css +608 -0
- package/styles/bootstrap-dark.scss +2 -0
- package/styles/bootstrap-lite.css +595 -0
- package/styles/bootstrap-lite.scss +2 -0
- package/styles/bootstrap.css +595 -0
- package/styles/bootstrap.scss +2 -0
- package/styles/bootstrap4-lite.css +595 -0
- package/styles/bootstrap4-lite.scss +2 -0
- package/styles/bootstrap4.css +595 -0
- package/styles/bootstrap4.scss +2 -0
- package/styles/bootstrap5-dark-lite.css +595 -0
- package/styles/bootstrap5-dark-lite.scss +2 -0
- package/styles/bootstrap5-dark.css +595 -0
- package/styles/bootstrap5-dark.scss +2 -0
- package/styles/bootstrap5-lite.css +595 -0
- package/styles/bootstrap5-lite.scss +2 -0
- package/styles/bootstrap5.3-lite.css +679 -0
- package/styles/bootstrap5.3-lite.scss +2 -0
- package/styles/bootstrap5.3.css +679 -0
- package/styles/bootstrap5.3.scss +2 -0
- package/styles/bootstrap5.css +595 -0
- package/styles/bootstrap5.scss +2 -0
- package/styles/fabric-dark-lite.css +608 -0
- package/styles/fabric-dark-lite.scss +2 -0
- package/styles/fabric-dark.css +608 -0
- package/styles/fabric-dark.scss +2 -0
- package/styles/fabric-lite.css +595 -0
- package/styles/fabric-lite.scss +2 -0
- package/styles/fabric.css +595 -0
- package/styles/fabric.scss +2 -0
- package/styles/fluent-dark-lite.css +595 -0
- package/styles/fluent-dark-lite.scss +2 -0
- package/styles/fluent-dark.css +595 -0
- package/styles/fluent-dark.scss +2 -0
- package/styles/fluent-lite.css +595 -0
- package/styles/fluent-lite.scss +2 -0
- package/styles/fluent.css +595 -0
- package/styles/fluent.scss +2 -0
- package/styles/fluent2-lite.css +681 -0
- package/styles/fluent2-lite.scss +2 -0
- package/styles/fluent2.css +681 -0
- package/styles/fluent2.scss +2 -0
- package/styles/form-renderer/_all.scss +2 -0
- package/styles/form-renderer/_bds-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap-dark-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap4-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5-dark-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5.3-definition.scss +8 -0
- package/styles/form-renderer/_fabric-dark-definition.scss +8 -0
- package/styles/form-renderer/_fabric-definition.scss +8 -0
- package/styles/form-renderer/_fluent-dark-definition.scss +8 -0
- package/styles/form-renderer/_fluent-definition.scss +8 -0
- package/styles/form-renderer/_fluent2-definition.scss +8 -0
- package/styles/form-renderer/_highcontrast-definition.scss +8 -0
- package/styles/form-renderer/_highcontrast-light-definition.scss +8 -0
- package/styles/form-renderer/_layout.scss +480 -0
- package/styles/form-renderer/_material-dark-definition.scss +8 -0
- package/styles/form-renderer/_material-definition.scss +8 -0
- package/styles/form-renderer/_material3-dark-definition.scss +8 -0
- package/styles/form-renderer/_material3-definition.scss +8 -0
- package/styles/form-renderer/_mixin.scss +26 -0
- package/styles/form-renderer/_tailwind-dark-definition.scss +8 -0
- package/styles/form-renderer/_tailwind-definition.scss +8 -0
- package/styles/form-renderer/_tailwind3-definition.scss +8 -0
- package/styles/form-renderer/_theme.scss +69 -0
- package/styles/form-renderer/_variables.scss +2 -0
- package/styles/form-renderer/bds.css +599 -0
- package/styles/form-renderer/bds.scss +2 -0
- package/styles/form-renderer/bootstrap-dark.css +608 -0
- package/styles/form-renderer/bootstrap-dark.scss +2 -0
- package/styles/form-renderer/bootstrap.css +595 -0
- package/styles/form-renderer/bootstrap.scss +2 -0
- package/styles/form-renderer/bootstrap4.css +595 -0
- package/styles/form-renderer/bootstrap4.scss +2 -0
- package/styles/form-renderer/bootstrap5-dark.css +595 -0
- package/styles/form-renderer/bootstrap5-dark.scss +2 -0
- package/styles/form-renderer/bootstrap5.3.css +679 -0
- package/styles/form-renderer/bootstrap5.3.scss +2 -0
- package/styles/form-renderer/bootstrap5.css +595 -0
- package/styles/form-renderer/bootstrap5.scss +2 -0
- package/styles/form-renderer/fabric-dark.css +608 -0
- package/styles/form-renderer/fabric-dark.scss +2 -0
- package/styles/form-renderer/fabric.css +595 -0
- package/styles/form-renderer/fabric.scss +2 -0
- package/styles/form-renderer/fluent-dark.css +595 -0
- package/styles/form-renderer/fluent-dark.scss +2 -0
- package/styles/form-renderer/fluent.css +595 -0
- package/styles/form-renderer/fluent.scss +2 -0
- package/styles/form-renderer/fluent2.css +681 -0
- package/styles/form-renderer/fluent2.scss +2 -0
- package/styles/form-renderer/highcontrast-light.css +595 -0
- package/styles/form-renderer/highcontrast-light.scss +2 -0
- package/styles/form-renderer/highcontrast.css +608 -0
- package/styles/form-renderer/highcontrast.scss +2 -0
- package/styles/form-renderer/material-dark.css +608 -0
- package/styles/form-renderer/material-dark.scss +2 -0
- package/styles/form-renderer/material.css +595 -0
- package/styles/form-renderer/material.scss +2 -0
- package/styles/form-renderer/material3-dark.css +677 -0
- package/styles/form-renderer/material3-dark.scss +2 -0
- package/styles/form-renderer/material3.css +679 -0
- package/styles/form-renderer/material3.scss +2 -0
- package/styles/form-renderer/tailwind-dark.css +595 -0
- package/styles/form-renderer/tailwind-dark.scss +2 -0
- package/styles/form-renderer/tailwind.css +595 -0
- package/styles/form-renderer/tailwind.scss +2 -0
- package/styles/form-renderer/tailwind3.css +669 -0
- package/styles/form-renderer/tailwind3.scss +2 -0
- package/styles/highcontrast-light-lite.css +595 -0
- package/styles/highcontrast-light-lite.scss +2 -0
- package/styles/highcontrast-light.css +595 -0
- package/styles/highcontrast-light.scss +2 -0
- package/styles/highcontrast-lite.css +608 -0
- package/styles/highcontrast-lite.scss +2 -0
- package/styles/highcontrast.css +608 -0
- package/styles/highcontrast.scss +2 -0
- package/styles/material-dark-lite.css +608 -0
- package/styles/material-dark-lite.scss +2 -0
- package/styles/material-dark.css +608 -0
- package/styles/material-dark.scss +2 -0
- package/styles/material-lite.css +595 -0
- package/styles/material-lite.scss +2 -0
- package/styles/material.css +595 -0
- package/styles/material.scss +2 -0
- package/styles/material3-dark-lite.css +677 -0
- package/styles/material3-dark-lite.scss +2 -0
- package/styles/material3-dark.css +677 -0
- package/styles/material3-dark.scss +2 -0
- package/styles/material3-lite.css +679 -0
- package/styles/material3-lite.scss +2 -0
- package/styles/material3.css +679 -0
- package/styles/material3.scss +2 -0
- package/styles/tailwind-dark-lite.css +595 -0
- package/styles/tailwind-dark-lite.scss +2 -0
- package/styles/tailwind-dark.css +595 -0
- package/styles/tailwind-dark.scss +2 -0
- package/styles/tailwind-lite.css +595 -0
- package/styles/tailwind-lite.scss +2 -0
- package/styles/tailwind.css +595 -0
- package/styles/tailwind.scss +2 -0
- package/styles/tailwind3-lite.css +669 -0
- package/styles/tailwind3-lite.scss +2 -0
- package/styles/tailwind3.css +669 -0
- package/styles/tailwind3.scss +2 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import { Internationalization } from '@syncfusion/ej2-base';
|
|
2
|
+
/**
|
|
3
|
+
* ConditionalRuleEngine class
|
|
4
|
+
* @private
|
|
5
|
+
*/
|
|
6
|
+
var ConditionalRuleEngine = /** @class */ (function () {
|
|
7
|
+
function ConditionalRuleEngine() {
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Evaluate a conditional rule against form values
|
|
11
|
+
*
|
|
12
|
+
* Handles both simple rules (field operator value) and composite rules (AND/OR).
|
|
13
|
+
* Returns false for malformed rules.
|
|
14
|
+
*
|
|
15
|
+
* @param rule - ConditionalRule or CompositeConditionalRule
|
|
16
|
+
* @param formValues - Current form field values (key = field name, value = field value)
|
|
17
|
+
* @returns true if condition met, false otherwise
|
|
18
|
+
* @private
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const rule = { field: 'age', operator: 'greaterThan', value: 18 };
|
|
22
|
+
* const isAdult = ConditionalRuleEngine.evaluateCondition(rule, { age: 25 });
|
|
23
|
+
* // isAdult = true
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
ConditionalRuleEngine.evaluateCondition = function (rule, formValues) {
|
|
27
|
+
try {
|
|
28
|
+
// Handle simple rule (field operator value)
|
|
29
|
+
if ('field' in rule && 'operator' in rule && 'value' in rule) {
|
|
30
|
+
return this.evaluateSimpleRule(rule, formValues);
|
|
31
|
+
}
|
|
32
|
+
// Handle composite rule (AND/OR)
|
|
33
|
+
if ('condition' in rule && 'rules' in rule) {
|
|
34
|
+
return this.evaluateCompositeRule(rule, formValues);
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return false; // Safe default on error
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Evaluate simple field comparison rule
|
|
44
|
+
*
|
|
45
|
+
* Supports operators: equal, notEqual, greaterThan, lessThan, greaterThanOrEqual,
|
|
46
|
+
* lessThanOrEqual, in, notIn, contains, notContains.
|
|
47
|
+
*
|
|
48
|
+
* Handles both camelCase (greaterThan) and lowercase (greaterthan) operator formats
|
|
49
|
+
* for compatibility with QueryBuilder output.
|
|
50
|
+
*
|
|
51
|
+
* @param rule - Simple conditional rule with field, operator, value
|
|
52
|
+
* @param formValues - Current form field values
|
|
53
|
+
* @returns true if comparison is true, false otherwise
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const rule = { field: 'email', operator: 'contains', value: '@' };
|
|
58
|
+
* const hasAt = evaluateSimpleRule(rule, { email: 'test@example.com' });
|
|
59
|
+
* // hasAt = true
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
ConditionalRuleEngine.evaluateSimpleRule = function (rule, formValues) {
|
|
63
|
+
try {
|
|
64
|
+
var fieldValue_1 = formValues[rule.field];
|
|
65
|
+
var operator = rule.operator.toLowerCase();
|
|
66
|
+
// Allow evaluation for operators that make sense when value is undefined/empty
|
|
67
|
+
if (fieldValue_1 === undefined) {
|
|
68
|
+
var allowedForUndefined = ['equal', 'equals', 'notequal', 'notequals', 'isempty', 'isnotempty'];
|
|
69
|
+
if (allowedForUndefined.indexOf(operator) === -1) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Check if field value is an array (for multiselect, checkboxGroup)
|
|
74
|
+
var isFieldArray = Array.isArray(fieldValue_1);
|
|
75
|
+
var intl = new Internationalization();
|
|
76
|
+
switch (operator) {
|
|
77
|
+
case 'equal':
|
|
78
|
+
case 'equals':
|
|
79
|
+
return fieldValue_1 === rule.value;
|
|
80
|
+
case 'notequal':
|
|
81
|
+
case 'notequals':
|
|
82
|
+
return fieldValue_1 !== rule.value;
|
|
83
|
+
case 'startswith': return fieldValue_1.startsWith(rule.value);
|
|
84
|
+
case 'doesnotstartswith': return !fieldValue_1.startsWith(rule.value);
|
|
85
|
+
case 'greaterthan':
|
|
86
|
+
case 'lessthan':
|
|
87
|
+
case 'greaterthanorequal':
|
|
88
|
+
case 'lessthanorequal': {
|
|
89
|
+
var ln = Number(fieldValue_1);
|
|
90
|
+
var rn = Number(rule.value);
|
|
91
|
+
if (!Number.isNaN(ln) && !Number.isNaN(rn)) {
|
|
92
|
+
if (operator === 'greaterthan')
|
|
93
|
+
return ln > rn;
|
|
94
|
+
if (operator === 'lessthan')
|
|
95
|
+
return ln < rn;
|
|
96
|
+
if (operator === 'greaterthanorequal')
|
|
97
|
+
return ln >= rn;
|
|
98
|
+
return ln <= rn;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
case 'dategreaterthan':
|
|
103
|
+
case 'datelessthan': {
|
|
104
|
+
// try date comparison first
|
|
105
|
+
var leftTs = intl.parseDate(fieldValue_1, { format: 'MM/dd/yyyy' });
|
|
106
|
+
var rightTs = intl.parseDate(rule.value, { format: 'MM/dd/yyyy' });
|
|
107
|
+
if (leftTs !== null && rightTs !== null) {
|
|
108
|
+
if (operator === 'dategreaterthan')
|
|
109
|
+
return leftTs > rightTs;
|
|
110
|
+
if (operator === 'datelessthan')
|
|
111
|
+
return leftTs < rightTs;
|
|
112
|
+
return leftTs <= rightTs;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
case 'timegreaterthan':
|
|
117
|
+
case 'timelessthan': {
|
|
118
|
+
// Parse time strings (format: hh:mm a)
|
|
119
|
+
var leftTime = intl.parseDate(fieldValue_1, { format: 'hh:mm a', type: 'time' });
|
|
120
|
+
var rightTime = intl.parseDate(rule.value, { format: 'hh:mm a', type: 'time' });
|
|
121
|
+
if (leftTime !== null && rightTime !== null) {
|
|
122
|
+
if (operator === 'timegreaterthan')
|
|
123
|
+
return leftTime > rightTime;
|
|
124
|
+
if (operator === 'timelessthan')
|
|
125
|
+
return leftTime < rightTime;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
case 'datetimegreaterthan':
|
|
130
|
+
case 'datetimelessthan': {
|
|
131
|
+
// Parse datetime strings (format: MM/dd/yyyy hh:mm a)
|
|
132
|
+
var leftDateTime = intl.parseDate(fieldValue_1, { format: 'MM/dd/yyyy hh:mm a', type: 'dateTime' });
|
|
133
|
+
var rightDateTime = intl.parseDate(rule.value, { format: 'MM/dd/yyyy hh:mm a', type: 'dateTime' });
|
|
134
|
+
if (leftDateTime !== null && rightDateTime !== null) {
|
|
135
|
+
if (operator === 'datetimegreaterthan')
|
|
136
|
+
return leftDateTime > rightDateTime;
|
|
137
|
+
if (operator === 'datetimelessthan')
|
|
138
|
+
return leftDateTime < rightDateTime;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
case 'contains':
|
|
143
|
+
// If field value is array, check if array contains the specific value(s)
|
|
144
|
+
if (isFieldArray) {
|
|
145
|
+
// Parse comma-separated values in rule.value if it's a string
|
|
146
|
+
if (typeof rule.value === 'string' && rule.value.indexOf(',') !== -1) {
|
|
147
|
+
var searchValues = rule.value.split(',').map(function (v) { return v.trim(); });
|
|
148
|
+
// Check if array contains ANY of the search values
|
|
149
|
+
return searchValues.some(function (searchVal) { return fieldValue_1.indexOf(searchVal) !== -1; });
|
|
150
|
+
}
|
|
151
|
+
return fieldValue_1.indexOf(rule.value) !== -1;
|
|
152
|
+
}
|
|
153
|
+
// Otherwise, check string contains
|
|
154
|
+
return String(fieldValue_1).indexOf(String(rule.value)) !== -1;
|
|
155
|
+
case 'notcontains':
|
|
156
|
+
// If field value is array, check if array does NOT contain the specific value(s)
|
|
157
|
+
if (isFieldArray) {
|
|
158
|
+
// Parse comma-separated values in rule.value if it's a string
|
|
159
|
+
if (typeof rule.value === 'string' && rule.value.indexOf(',') !== -1) {
|
|
160
|
+
var searchValues = rule.value.split(',').map(function (v) { return v.trim(); });
|
|
161
|
+
// Check if array contains NONE of the search values
|
|
162
|
+
return !searchValues.some(function (searchVal) { return fieldValue_1.indexOf(searchVal) !== -1; });
|
|
163
|
+
}
|
|
164
|
+
return fieldValue_1.indexOf(rule.value) === -1;
|
|
165
|
+
}
|
|
166
|
+
// Otherwise, check string does not contain
|
|
167
|
+
return String(fieldValue_1).indexOf(String(rule.value)) === -1;
|
|
168
|
+
case 'isempty':
|
|
169
|
+
// Check if array is empty
|
|
170
|
+
if (isFieldArray) {
|
|
171
|
+
return fieldValue_1.length === 0;
|
|
172
|
+
}
|
|
173
|
+
// For non-arrays, check if falsy or empty string
|
|
174
|
+
return !fieldValue_1 || String(fieldValue_1).length === 0;
|
|
175
|
+
case 'isnotempty':
|
|
176
|
+
// Check if array has items
|
|
177
|
+
if (isFieldArray) {
|
|
178
|
+
return fieldValue_1.length > 0;
|
|
179
|
+
}
|
|
180
|
+
// For non-arrays, check if truthy and not empty string
|
|
181
|
+
return !!fieldValue_1 && String(fieldValue_1).length > 0;
|
|
182
|
+
default:
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Evaluate composite rule with AND/OR logic
|
|
192
|
+
*
|
|
193
|
+
* Recursively evaluates nested rules with AND (all must be true) or OR
|
|
194
|
+
* (at least one must be true) logic.
|
|
195
|
+
*
|
|
196
|
+
* @param rule - Composite rule with condition ('and'|'or') and rules array
|
|
197
|
+
* @param formValues - Current form field values
|
|
198
|
+
* @returns true if composite condition met, false otherwise
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const rule = {
|
|
203
|
+
* condition: 'and',
|
|
204
|
+
* rules: [
|
|
205
|
+
* { field: 'age', operator: 'greaterThan', value: 18 },
|
|
206
|
+
* { field: 'country', operator: 'equal', value: 'US' }
|
|
207
|
+
* ]
|
|
208
|
+
* };
|
|
209
|
+
* const isEligible = evaluateCompositeRule(rule, { age: 25, country: 'US' });
|
|
210
|
+
* // isEligible = true
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
ConditionalRuleEngine.evaluateCompositeRule = function (rule, formValues) {
|
|
214
|
+
var _this = this;
|
|
215
|
+
try {
|
|
216
|
+
if (!Array.isArray(rule.rules) || rule.rules.length === 0) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
var condition = rule.condition.toLowerCase();
|
|
220
|
+
if (condition === 'and') {
|
|
221
|
+
// All rules must be true
|
|
222
|
+
return rule.rules.every(function (childRule) {
|
|
223
|
+
return _this.evaluateCondition(childRule, formValues);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
else if (condition === 'or') {
|
|
227
|
+
// At least one rule must be true
|
|
228
|
+
return rule.rules.some(function (childRule) {
|
|
229
|
+
return _this.evaluateCondition(childRule, formValues);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Check if field should be visible
|
|
242
|
+
*
|
|
243
|
+
* Evaluates visibleWhen condition. Defaults to true (visible) if no condition
|
|
244
|
+
* is defined or on error.
|
|
245
|
+
*
|
|
246
|
+
* @param conditions - FormComponentConditions containing visibleWhen rule
|
|
247
|
+
* @param formValues - Current form field values
|
|
248
|
+
* @returns true if field should be visible, false if hidden
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```typescript
|
|
252
|
+
* const conditions = {
|
|
253
|
+
* visibleWhen: { field: 'showEmail', operator: 'equal', value: true }
|
|
254
|
+
* };
|
|
255
|
+
* const visible = isFieldVisible(conditions, { showEmail: true });
|
|
256
|
+
* // visible = true
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
ConditionalRuleEngine.isFieldVisible = function (conditions, formValues) {
|
|
260
|
+
try {
|
|
261
|
+
if (!conditions || !conditions.visibleWhen) {
|
|
262
|
+
return true; // Default: visible if no condition
|
|
263
|
+
}
|
|
264
|
+
return this.evaluateCondition(conditions.visibleWhen, formValues);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
return true; // Safe default: visible on error
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* Check if field should be read-only
|
|
272
|
+
*
|
|
273
|
+
* Evaluates readOnlyWhen condition. Defaults to false (editable) if no
|
|
274
|
+
* condition is defined or on error.
|
|
275
|
+
*
|
|
276
|
+
* @param conditions - FormComponentConditions containing readOnlyWhen rule
|
|
277
|
+
* @param formValues - Current form field values
|
|
278
|
+
* @returns true if field should be read-only, false if editable
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* const conditions = {
|
|
283
|
+
* readOnlyWhen: { field: 'isVerified', operator: 'equal', value: true }
|
|
284
|
+
* };
|
|
285
|
+
* const readOnly = isFieldReadOnly(conditions, { isVerified: true });
|
|
286
|
+
* // readOnly = true
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
ConditionalRuleEngine.isFieldReadOnly = function (conditions, formValues) {
|
|
290
|
+
try {
|
|
291
|
+
if (!conditions || !conditions.readOnlyWhen) {
|
|
292
|
+
return false; // Default: not read-only if no condition
|
|
293
|
+
}
|
|
294
|
+
return this.evaluateCondition(conditions.readOnlyWhen, formValues);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
return false; // Safe default: not read-only on error
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Check if field should be disabled
|
|
302
|
+
*
|
|
303
|
+
* Evaluates disabledWhen condition. Defaults to false (enabled) if no
|
|
304
|
+
* condition is defined or on error.
|
|
305
|
+
*
|
|
306
|
+
* @param conditions - FormComponentConditions containing disabledWhen rule
|
|
307
|
+
* @param formValues - Current form field values
|
|
308
|
+
* @returns true if field should be disabled, false if enabled
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* const conditions = {
|
|
313
|
+
* disabledWhen: { field: 'orderStatus', operator: 'equal', value: 'completed' }
|
|
314
|
+
* };
|
|
315
|
+
* const disabled = isFieldDisabled(conditions, { orderStatus: 'completed' });
|
|
316
|
+
* // disabled = true
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
ConditionalRuleEngine.isFieldDisabled = function (conditions, formValues) {
|
|
320
|
+
try {
|
|
321
|
+
if (!conditions || !conditions.disabledWhen) {
|
|
322
|
+
return false; // Default: not disabled if no condition
|
|
323
|
+
}
|
|
324
|
+
return this.evaluateCondition(conditions.disabledWhen, formValues);
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
return false; // Safe default: not disabled on error
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Check if field should be hidden
|
|
332
|
+
*
|
|
333
|
+
* Evaluates hideWhen condition. Defaults to false (visible) if no condition
|
|
334
|
+
* is defined or on error.
|
|
335
|
+
*
|
|
336
|
+
* @param conditions - FormComponentConditions containing hideWhen rule
|
|
337
|
+
* @param formValues - Current form field values
|
|
338
|
+
* @returns true if field should be hidden, false if visible
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* const conditions = {
|
|
343
|
+
* hideWhen: { field: 'hideEmail', operator: 'equal', value: true }
|
|
344
|
+
* };
|
|
345
|
+
* const hidden = isFieldHidden(conditions, { hideEmail: true });
|
|
346
|
+
* // hidden = true
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
ConditionalRuleEngine.isFieldHidden = function (conditions, formValues) {
|
|
350
|
+
try {
|
|
351
|
+
if (!conditions || !conditions.hideWhen) {
|
|
352
|
+
return false; // Default: not hidden if no condition
|
|
353
|
+
}
|
|
354
|
+
return this.evaluateCondition(conditions.hideWhen, formValues);
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
return false; // Safe default: not hidden on error
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Check if field should be required
|
|
362
|
+
*
|
|
363
|
+
* Evaluates requiredWhen condition. Defaults to false (not required) if no condition
|
|
364
|
+
* is defined or on error.
|
|
365
|
+
*
|
|
366
|
+
* @param conditions - FormComponentConditions containing requiredWhen rule
|
|
367
|
+
* @param formValues - Current form field values
|
|
368
|
+
* @returns true if field should be required, false if optional
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```typescript
|
|
372
|
+
* const conditions = {
|
|
373
|
+
* requiredWhen: { field: 'requireDetails', operator: 'equal', value: true }
|
|
374
|
+
* };
|
|
375
|
+
* const required = isFieldRequired(conditions, { requireDetails: true });
|
|
376
|
+
* // required = true
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
ConditionalRuleEngine.isFieldRequired = function (conditions, formValues) {
|
|
380
|
+
try {
|
|
381
|
+
if (!conditions || !conditions.requiredWhen) {
|
|
382
|
+
return false; // Default: not required if no condition
|
|
383
|
+
}
|
|
384
|
+
return this.evaluateCondition(conditions.requiredWhen, formValues);
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
return false; // Safe default: not required on error
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
/**
|
|
391
|
+
* Get value to set for field if condition is met
|
|
392
|
+
*
|
|
393
|
+
* Evaluates setValueWhen condition and returns the value to assign if true.
|
|
394
|
+
* Returns undefined if no condition is defined, condition not met, or on error.
|
|
395
|
+
*
|
|
396
|
+
* @param conditions - FormComponentConditions containing setValueWhen rule
|
|
397
|
+
* @param formValues - Current form field values
|
|
398
|
+
* @returns The value to set if condition met, undefined otherwise
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* const conditions = {
|
|
403
|
+
* setValueWhen: {
|
|
404
|
+
* condition: { field: 'country', operator: 'equal', value: 'US' },
|
|
405
|
+
* value: 'United States'
|
|
406
|
+
* }
|
|
407
|
+
* };
|
|
408
|
+
* const value = getConditionalValue(conditions, { country: 'US' });
|
|
409
|
+
* // value = 'United States'
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
ConditionalRuleEngine.getConditionalValue = function (conditions, formValues) {
|
|
413
|
+
try {
|
|
414
|
+
if (!conditions || !conditions.setValueWhen) {
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
417
|
+
var isMet = this.evaluateCondition(conditions.setValueWhen.condition, formValues);
|
|
418
|
+
if (isMet) {
|
|
419
|
+
return conditions.setValueWhen.value;
|
|
420
|
+
}
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
return undefined;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
/**
|
|
428
|
+
* Evaluate conditional data availability
|
|
429
|
+
* Returns true if any conditional data rule's condition is met
|
|
430
|
+
*
|
|
431
|
+
* @param conditionalDataRules - Array of ConditionalDataRule objects
|
|
432
|
+
* @param formValues - Current form field values
|
|
433
|
+
* @returns Index of first matching rule, or -1 if none match
|
|
434
|
+
*/
|
|
435
|
+
ConditionalRuleEngine.evaluateConditionalDataRules = function (conditionalDataRules, formValues) {
|
|
436
|
+
if (!conditionalDataRules || !Array.isArray(conditionalDataRules)) {
|
|
437
|
+
return -1;
|
|
438
|
+
}
|
|
439
|
+
for (var i = 0; i < conditionalDataRules.length; i++) {
|
|
440
|
+
var rule = conditionalDataRules[i];
|
|
441
|
+
if (this.evaluateCondition(rule.condition, formValues)) {
|
|
442
|
+
return i; // Return index of first matching rule
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return -1;
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* Get filtered options for a dependent dropdown based on choice-based field rule
|
|
449
|
+
*
|
|
450
|
+
* If choiceBasedField rule exists and primary field has a value,
|
|
451
|
+
* return only the mapped dependent options for that primary value.
|
|
452
|
+
* Otherwise return all options.
|
|
453
|
+
*
|
|
454
|
+
* @param rule - ChoiceBasedFieldRule configuration
|
|
455
|
+
* @param allDependentOptions - Complete list of dependent field options
|
|
456
|
+
* @param formValues - Current form values
|
|
457
|
+
* @param primaryFieldId - ID of primary field
|
|
458
|
+
* @returns Filtered options array
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```typescript
|
|
462
|
+
* const rule = {
|
|
463
|
+
* primaryFieldId: 'dept-id',
|
|
464
|
+
* choiceMapping: {
|
|
465
|
+
* 'Marketing': ['Markers', 'Sticky Notes'],
|
|
466
|
+
* 'Finance': ['Calculator']
|
|
467
|
+
* }
|
|
468
|
+
* };
|
|
469
|
+
*
|
|
470
|
+
* const filtered = ConditionalRuleEngine.getChoiceBasedOptions(
|
|
471
|
+
* rule,
|
|
472
|
+
* ['Markers', 'Sticky Notes', 'Calculator'],
|
|
473
|
+
* { 'dept-id': 'Marketing' },
|
|
474
|
+
* 'dept-id'
|
|
475
|
+
* );
|
|
476
|
+
* // Returns: ['Markers', 'Sticky Notes']
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
ConditionalRuleEngine.getChoiceBasedOptions = function (rule, allDependentOptions, formValues, primaryFieldId) {
|
|
480
|
+
try {
|
|
481
|
+
// Get primary field value from form
|
|
482
|
+
var primaryValue = formValues && formValues[primaryFieldId];
|
|
483
|
+
if (primaryValue === null || primaryValue === undefined) {
|
|
484
|
+
// No primary value selected - return all or empty based on rule config
|
|
485
|
+
return rule.showAllWhenNotMapped ? allDependentOptions : [];
|
|
486
|
+
}
|
|
487
|
+
// Ensure primary value is treated as string for comparison
|
|
488
|
+
var primaryValueStr = String(primaryValue);
|
|
489
|
+
// Check if this primary value is mapped in the rule
|
|
490
|
+
if (primaryValueStr in rule.choiceMapping) {
|
|
491
|
+
var mappedOptions = rule.choiceMapping[primaryValueStr];
|
|
492
|
+
// Validate mapped options exist in all options (safety check)
|
|
493
|
+
return mappedOptions.filter(function (opt) { return allDependentOptions.indexOf(opt) !== -1; });
|
|
494
|
+
}
|
|
495
|
+
// Primary value not mapped - return all or empty based on rule config
|
|
496
|
+
return rule.showAllWhenNotMapped ? allDependentOptions : [];
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
// Safe default - return all options
|
|
500
|
+
return allDependentOptions;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
return ConditionalRuleEngine;
|
|
504
|
+
}());
|
|
505
|
+
export { ConditionalRuleEngine };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { FormValueType } from './expressions/expression-engine';
|
|
2
|
+
/**
|
|
3
|
+
* Custom Validation Engine
|
|
4
|
+
*
|
|
5
|
+
* Evaluates FormIO-style custom validation expressions:
|
|
6
|
+
* valid = (condition) ? true : 'error message';
|
|
7
|
+
*
|
|
8
|
+
* Reuses ExpressionEngine for dependency extraction and expression parsing
|
|
9
|
+
* @private
|
|
10
|
+
*/
|
|
11
|
+
export declare class CustomValidationEngine {
|
|
12
|
+
private expressionEngine;
|
|
13
|
+
constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Validate field using custom validation expression
|
|
16
|
+
*
|
|
17
|
+
* @param expression - FormIO-style validation expression (generated by UI)
|
|
18
|
+
* @param fieldValue - Current field value (as 'input')
|
|
19
|
+
* @param formValues - All form values for cross-field references
|
|
20
|
+
* @returns true if valid, error message string if invalid
|
|
21
|
+
*/
|
|
22
|
+
validate(expression: string, fieldValue: any, formValues: Record<string, FormValueType>): true | string;
|
|
23
|
+
/**
|
|
24
|
+
* Extract field dependencies from validation expression
|
|
25
|
+
*
|
|
26
|
+
* @param expression - Custom validation expression (or just condition)
|
|
27
|
+
* @returns Array of field names referenced in expression
|
|
28
|
+
*/
|
|
29
|
+
extractDependencies(expression: string): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Validate condition syntax (simplified - no need for "valid =" check)
|
|
32
|
+
*
|
|
33
|
+
* @param condition - Validation condition (without "valid =" prefix)
|
|
34
|
+
* @param availableFields - Available field names for reference validation
|
|
35
|
+
* @returns Array of validation error messages
|
|
36
|
+
*/
|
|
37
|
+
validateConditionSyntax(condition: string, availableFields: string[]): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Build full FormIO expression from condition and error message
|
|
40
|
+
*
|
|
41
|
+
* @param condition - Validation condition (e.g., "(input === {password})")
|
|
42
|
+
* @param errorMessage - Error message to show when validation fails
|
|
43
|
+
* @returns Full FormIO expression
|
|
44
|
+
*/
|
|
45
|
+
buildExpression(condition: string, errorMessage: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Extract condition and error message from full FormIO expression
|
|
48
|
+
* For editing existing rules
|
|
49
|
+
*
|
|
50
|
+
* @param expression - Full FormIO expression
|
|
51
|
+
* @returns { condition, errorMessage } or null if invalid
|
|
52
|
+
*/
|
|
53
|
+
parseExpression(expression: string): {
|
|
54
|
+
condition: string;
|
|
55
|
+
errorMessage: string;
|
|
56
|
+
} | null;
|
|
57
|
+
}
|