@syncfusion/ej2-react-form-renderer 0.1.2-beta → 34.2.3
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 +88 -105
- package/diConfig.json +85 -0
- package/dist/ej2-react-form-renderer.min.js +10 -0
- package/dist/ej2-react-form-renderer.umd.min.js +11 -0
- package/dist/ej2-react-form-renderer.umd.min.js.map +1 -0
- package/dist/es6/ej2-react-form-renderer.es2015.js +37 -0
- package/dist/es6/ej2-react-form-renderer.es2015.js.map +1 -0
- package/form-renderer.d.ts +4 -0
- package/form-renderer.js +4 -0
- package/index.d.ts +2 -4
- package/index.js +2 -4
- package/license +6 -13
- package/package.json +19 -38
- package/src/form-renderer/formrenderer.component.d.ts +34 -0
- package/src/form-renderer/formrenderer.component.js +49 -0
- package/src/form-renderer/index.d.ts +1 -0
- package/src/form-renderer/index.js +1 -0
- package/src/global.js +1 -0
- package/src/index.d.ts +2 -6
- package/src/index.js +2 -6
- package/styles/bds-lite.css +1 -0
- package/styles/bds-lite.scss +1 -0
- package/styles/bds.css +1 -0
- package/styles/bds.scss +1 -0
- package/styles/bootstrap-dark-lite.css +1 -0
- package/styles/bootstrap-dark-lite.scss +1 -0
- package/styles/bootstrap-dark.css +1 -26
- package/styles/bootstrap-dark.scss +1 -0
- package/styles/bootstrap-lite.css +1 -0
- package/styles/bootstrap-lite.scss +1 -0
- package/styles/bootstrap.css +1 -26
- package/styles/bootstrap.scss +1 -0
- package/styles/bootstrap4-lite.css +1 -0
- package/styles/bootstrap4-lite.scss +1 -0
- package/styles/bootstrap4.css +1 -26
- package/styles/bootstrap4.scss +1 -0
- package/styles/bootstrap5-dark-lite.css +1 -0
- package/styles/bootstrap5-dark-lite.scss +1 -0
- package/styles/bootstrap5-dark.css +1 -26
- package/styles/bootstrap5-dark.scss +1 -0
- package/styles/bootstrap5-lite.css +1 -0
- package/styles/bootstrap5-lite.scss +1 -0
- package/styles/bootstrap5.3-lite.css +1 -0
- package/styles/bootstrap5.3-lite.scss +1 -0
- package/styles/bootstrap5.3.css +1 -26
- package/styles/bootstrap5.3.scss +1 -0
- package/styles/bootstrap5.css +1 -26
- package/styles/bootstrap5.scss +1 -0
- package/styles/fabric-dark-lite.css +1 -0
- package/styles/fabric-dark-lite.scss +1 -0
- package/styles/fabric-dark.css +1 -0
- package/styles/fabric-dark.scss +1 -0
- package/styles/fabric-lite.css +1 -0
- package/styles/fabric-lite.scss +1 -0
- package/styles/fabric.css +1 -0
- package/styles/fabric.scss +1 -0
- package/styles/fluent-dark-lite.css +1 -0
- package/styles/fluent-dark-lite.scss +1 -0
- package/styles/fluent-dark.css +1 -26
- package/styles/fluent-dark.scss +1 -0
- package/styles/fluent-lite.css +1 -0
- package/styles/fluent-lite.scss +1 -0
- package/styles/fluent.css +1 -26
- package/styles/fluent.scss +1 -0
- package/styles/fluent2-lite.css +1 -0
- package/styles/fluent2-lite.scss +1 -0
- package/styles/fluent2.css +1 -26
- package/styles/fluent2.scss +1 -0
- package/styles/form-renderer/bds.css +1 -0
- package/styles/form-renderer/bds.scss +1 -0
- package/styles/form-renderer/bootstrap-dark.css +1 -0
- package/styles/form-renderer/bootstrap-dark.scss +1 -0
- package/styles/form-renderer/bootstrap.css +1 -0
- package/styles/form-renderer/bootstrap.scss +1 -0
- package/styles/form-renderer/bootstrap4.css +1 -0
- package/styles/form-renderer/bootstrap4.scss +1 -0
- package/styles/form-renderer/bootstrap5-dark.css +1 -0
- package/styles/form-renderer/bootstrap5-dark.scss +1 -0
- package/styles/form-renderer/bootstrap5.3.css +1 -0
- package/styles/form-renderer/bootstrap5.3.scss +1 -0
- package/styles/form-renderer/bootstrap5.css +1 -0
- package/styles/form-renderer/bootstrap5.scss +1 -0
- package/styles/form-renderer/fabric-dark.css +1 -0
- package/styles/form-renderer/fabric-dark.scss +1 -0
- package/styles/form-renderer/fabric.css +1 -0
- package/styles/form-renderer/fabric.scss +1 -0
- package/styles/form-renderer/fluent-dark.css +1 -0
- package/styles/form-renderer/fluent-dark.scss +1 -0
- package/styles/form-renderer/fluent.css +1 -0
- package/styles/form-renderer/fluent.scss +1 -0
- package/styles/form-renderer/fluent2.css +1 -0
- package/styles/form-renderer/fluent2.scss +1 -0
- package/styles/form-renderer/highcontrast-light.css +1 -0
- package/styles/form-renderer/highcontrast-light.scss +1 -0
- package/styles/form-renderer/highcontrast.css +1 -0
- package/styles/form-renderer/highcontrast.scss +1 -0
- package/styles/form-renderer/material-dark.css +1 -0
- package/styles/form-renderer/material-dark.scss +1 -0
- package/styles/form-renderer/material.css +1 -0
- package/styles/form-renderer/material.scss +1 -0
- package/styles/form-renderer/material3-dark.css +1 -0
- package/styles/form-renderer/material3-dark.scss +1 -0
- package/styles/form-renderer/material3.css +1 -0
- package/styles/form-renderer/material3.scss +1 -0
- package/styles/form-renderer/tailwind-dark.css +1 -0
- package/styles/form-renderer/tailwind-dark.scss +1 -0
- package/styles/form-renderer/tailwind.css +1 -0
- package/styles/form-renderer/tailwind.scss +1 -0
- package/styles/form-renderer/tailwind3.css +1 -0
- package/styles/form-renderer/tailwind3.scss +1 -0
- package/styles/highcontrast-light-lite.css +1 -0
- package/styles/highcontrast-light-lite.scss +1 -0
- package/styles/highcontrast-light.css +1 -0
- package/styles/highcontrast-light.scss +1 -0
- package/styles/highcontrast-lite.css +1 -0
- package/styles/highcontrast-lite.scss +1 -0
- package/styles/highcontrast.css +1 -0
- package/styles/highcontrast.scss +1 -0
- package/styles/material-dark-lite.css +1 -0
- package/styles/material-dark-lite.scss +1 -0
- package/styles/material-dark.css +1 -26
- package/styles/material-dark.scss +1 -0
- package/styles/material-lite.css +1 -0
- package/styles/material-lite.scss +1 -0
- package/styles/material.css +1 -26
- package/styles/material.scss +1 -0
- package/styles/material3-dark-lite.css +1 -0
- package/styles/material3-dark-lite.scss +1 -0
- package/styles/material3-dark.css +1 -26
- package/styles/material3-dark.scss +1 -0
- package/styles/material3-lite.css +1 -0
- package/styles/material3-lite.scss +1 -0
- package/styles/material3.css +1 -26
- package/styles/material3.scss +1 -0
- package/styles/tailwind-dark-lite.css +1 -0
- package/styles/tailwind-dark-lite.scss +1 -0
- package/styles/tailwind-dark.css +1 -26
- package/styles/tailwind-dark.scss +1 -0
- package/styles/tailwind-lite.css +1 -0
- package/styles/tailwind-lite.scss +1 -0
- package/styles/tailwind.css +1 -26
- package/styles/tailwind.scss +1 -0
- package/styles/tailwind3-lite.css +1 -0
- package/styles/tailwind3-lite.scss +1 -0
- package/styles/tailwind3.css +1 -26
- package/styles/tailwind3.scss +1 -0
- package/src/CheckboxGroup.d.ts +0 -12
- package/src/CheckboxGroup.js +0 -49
- package/src/ConditionalRuleEngine.d.ts +0 -295
- package/src/ConditionalRuleEngine.js +0 -566
- package/src/CustomTabComponent.d.ts +0 -13
- package/src/CustomTabComponent.js +0 -35
- package/src/DataGridComponent.d.ts +0 -28
- package/src/DataGridComponent.js +0 -147
- package/src/FormRenderer.d.ts +0 -84
- package/src/FormRenderer.js +0 -707
- package/src/FormValidator.d.ts +0 -479
- package/src/FormValidator.js +0 -579
- package/src/MinMaxRuleEngine.d.ts +0 -95
- package/src/MinMaxRuleEngine.js +0 -361
- package/src/StaticHtmlRenderer.d.ts +0 -8
- package/src/StaticHtmlRenderer.js +0 -21
- package/src/expressions/DependencyTracker.d.ts +0 -30
- package/src/expressions/DependencyTracker.js +0 -128
- package/src/expressions/ExpressionEngine.d.ts +0 -81
- package/src/expressions/ExpressionEngine.js +0 -503
- package/src/expressions/ExpressionParser.d.ts +0 -39
- package/src/expressions/ExpressionParser.js +0 -270
- package/src/json-converter.d.ts +0 -171
- package/src/json-converter.js +0 -1209
- package/src/parser.d.ts +0 -99
- package/src/parser.js +0 -753
- package/src/renderFormField.d.ts +0 -21
- package/src/renderFormField.js +0 -226
- package/src/types/FormSchema.d.ts +0 -211
- package/src/types/FormSchema.js +0 -1
- package/src/utils.js +0 -408
- package/styles/bootstrap5.3-dark.css +0 -26
- package/styles/fluent2-dark.css +0 -26
- package/styles/form-renderer.base.css +0 -499
- package/styles/tailwind3-dark.css +0 -10
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ExpressionParser - Parse and compile expressions
|
|
3
|
-
*
|
|
4
|
-
* Converts string expressions into safe, compiled functions
|
|
5
|
-
* Supports field references, operators, functions, and conditionals
|
|
6
|
-
*
|
|
7
|
-
* @spec Expression_Engine.md
|
|
8
|
-
*/
|
|
9
|
-
export class ExpressionParser {
|
|
10
|
-
supportedFunctions = [
|
|
11
|
-
'SUM', 'AVG', 'MIN', 'MAX', 'COUNT',
|
|
12
|
-
'ROUND', 'FLOOR', 'CEIL', 'ABS', 'SQRT',
|
|
13
|
-
'UPPER', 'LOWER', 'CONCAT', 'LENGTH', 'TRIM', 'SUBSTRING',
|
|
14
|
-
'TODAY', 'AGE', 'DATEFORMAT', 'DATEDIFF', 'ADDDAYS', 'SUBDAYS'
|
|
15
|
-
];
|
|
16
|
-
availableFields = new Map();
|
|
17
|
-
/**
|
|
18
|
-
* Initialize parser with available fields for type validation
|
|
19
|
-
*/
|
|
20
|
-
setAvailableFields(fields) {
|
|
21
|
-
this.availableFields.clear();
|
|
22
|
-
fields.forEach(field => {
|
|
23
|
-
this.availableFields.set(field.name, field);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
validateFieldOperations(expression) {
|
|
27
|
-
const errors = [];
|
|
28
|
-
// Skip if no fields available
|
|
29
|
-
if (this.availableFields.size === 0) {
|
|
30
|
-
return errors;
|
|
31
|
-
}
|
|
32
|
-
// Invalid operators for string fields (allow only + for concatenation)
|
|
33
|
-
const stringInvalidOps = ['-', '*', '/', '%', '^'];
|
|
34
|
-
// All operators invalid for date fields
|
|
35
|
-
const dateInvalidOps = ['-', '*', '/', '%', '^', '+'];
|
|
36
|
-
// Track reported operator positions to avoid duplicates
|
|
37
|
-
const reportedOperators = new Set();
|
|
38
|
-
// Extract all field references and their surrounding operators
|
|
39
|
-
const fieldRefPattern = /(\{[^}]+\})/g;
|
|
40
|
-
let match;
|
|
41
|
-
while ((match = fieldRefPattern.exec(expression)) !== null) {
|
|
42
|
-
const fieldRef = match[0];
|
|
43
|
-
const fieldName = fieldRef.slice(1, -1); // Remove { }
|
|
44
|
-
const field = this.availableFields.get(fieldName);
|
|
45
|
-
if (!field)
|
|
46
|
-
continue; // Field not found in available fields, skip validation
|
|
47
|
-
const fieldIndex = match.index;
|
|
48
|
-
const fieldEndIndex = fieldIndex + fieldRef.length;
|
|
49
|
-
// Get context around field reference (including spaces)
|
|
50
|
-
const textBefore = expression.substring(Math.max(0, fieldIndex - 10), fieldIndex);
|
|
51
|
-
const textAfter = expression.substring(fieldEndIndex, Math.min(expression.length, fieldEndIndex + 10));
|
|
52
|
-
// Extract the operator if present (skip spaces)
|
|
53
|
-
const operatorBefore = textBefore.trimEnd().slice(-1);
|
|
54
|
-
const operatorAfter = textAfter.trimStart().charAt(0);
|
|
55
|
-
// Calculate actual position of operators in the original expression
|
|
56
|
-
const operatorBeforePos = fieldIndex - (textBefore.length - textBefore.trimEnd().length) - 1;
|
|
57
|
-
const operatorAfterPos = fieldEndIndex + (textAfter.length - textAfter.trimStart().length);
|
|
58
|
-
// For STRING fields: reject all operators except +
|
|
59
|
-
if (field.type === 'string' && !field.format) {
|
|
60
|
-
if (operatorBefore && stringInvalidOps.includes(operatorBefore)) {
|
|
61
|
-
const key = `${operatorBeforePos}:${operatorBefore}`;
|
|
62
|
-
if (!reportedOperators.has(key)) {
|
|
63
|
-
errors.push(`Cannot perform '${operatorBefore}' operation on string field '{${fieldName}}'`);
|
|
64
|
-
reportedOperators.add(key);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (operatorAfter && stringInvalidOps.includes(operatorAfter)) {
|
|
68
|
-
const key = `${operatorAfterPos}:${operatorAfter}`;
|
|
69
|
-
if (!reportedOperators.has(key)) {
|
|
70
|
-
errors.push(`Cannot perform '${operatorAfter}' operation on string field '{${fieldName}}'`);
|
|
71
|
-
reportedOperators.add(key);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// For DATE fields: reject ALL operators including +
|
|
76
|
-
if (field.format === 'date' || field.format === 'date-time' || field.format === 'time') {
|
|
77
|
-
if (operatorBefore && dateInvalidOps.includes(operatorBefore)) {
|
|
78
|
-
const key = `${operatorBeforePos}:${operatorBefore}`;
|
|
79
|
-
if (!reportedOperators.has(key)) {
|
|
80
|
-
errors.push(`Cannot perform '${operatorBefore}' operation on date field '{${fieldName}}'`);
|
|
81
|
-
reportedOperators.add(key);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (operatorAfter && dateInvalidOps.includes(operatorAfter)) {
|
|
85
|
-
const key = `${operatorAfterPos}:${operatorAfter}`;
|
|
86
|
-
if (!reportedOperators.has(key)) {
|
|
87
|
-
errors.push(`Cannot perform '${operatorAfter}' operation on date field '{${fieldName}}'`);
|
|
88
|
-
reportedOperators.add(key);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return errors;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Validate expression syntax
|
|
97
|
-
*/
|
|
98
|
-
validate(expression) {
|
|
99
|
-
const errors = [];
|
|
100
|
-
if (!expression || expression.trim() === '') {
|
|
101
|
-
return { valid: true, errors: [] };
|
|
102
|
-
}
|
|
103
|
-
// Check for unbalanced braces
|
|
104
|
-
const openBraces = (expression.match(/\{/g) || []).length;
|
|
105
|
-
const closeBraces = (expression.match(/\}/g) || []).length;
|
|
106
|
-
if (openBraces !== closeBraces) {
|
|
107
|
-
errors.push('Unbalanced braces in expression');
|
|
108
|
-
}
|
|
109
|
-
// Check for unbalanced parentheses
|
|
110
|
-
const openParens = (expression.match(/\(/g) || []).length;
|
|
111
|
-
const closeParens = (expression.match(/\)/g) || []).length;
|
|
112
|
-
if (openParens !== closeParens) {
|
|
113
|
-
errors.push('Unbalanced parentheses in expression');
|
|
114
|
-
}
|
|
115
|
-
// Check for invalid function calls
|
|
116
|
-
const funcPattern = /(\w+)\s*\(/g;
|
|
117
|
-
let match;
|
|
118
|
-
while ((match = funcPattern.exec(expression)) !== null) {
|
|
119
|
-
const funcName = match[1].toUpperCase();
|
|
120
|
-
// Only validate uppercase-named functions
|
|
121
|
-
if (funcName === funcName.toUpperCase() && !this.supportedFunctions.includes(funcName)) {
|
|
122
|
-
errors.push(`Unknown function: ${funcName}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Validate field operations based on field types
|
|
126
|
-
const fieldOpErrors = this.validateFieldOperations(expression);
|
|
127
|
-
errors.push(...fieldOpErrors);
|
|
128
|
-
return {
|
|
129
|
-
valid: errors.length === 0,
|
|
130
|
-
errors
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Compile expression to executable function
|
|
135
|
-
*/
|
|
136
|
-
compile(expression, options = {}) {
|
|
137
|
-
try {
|
|
138
|
-
// Set available fields for validation if provided
|
|
139
|
-
if (options.availableFields && Array.isArray(options.availableFields)) {
|
|
140
|
-
this.setAvailableFields(options.availableFields);
|
|
141
|
-
}
|
|
142
|
-
// Validate expression before compiling
|
|
143
|
-
const validation = this.validate(expression);
|
|
144
|
-
if (!validation.valid) {
|
|
145
|
-
console.error('Expression validation errors:', validation.errors, { expression });
|
|
146
|
-
return () => null;
|
|
147
|
-
}
|
|
148
|
-
// Replace field references with context lookups
|
|
149
|
-
let compiled = expression;
|
|
150
|
-
// Handle field references FIRST (this includes refs inside ${...})
|
|
151
|
-
compiled = this.compileFieldReferences(compiled);
|
|
152
|
-
// Handle bare function calls SECOND (e.g., SUM([...]) without braces)
|
|
153
|
-
compiled = this.compileBareFunctionCalls(compiled);
|
|
154
|
-
// Handle math expressions THIRD (now ${} and {} contain already-replaced refs)
|
|
155
|
-
compiled = this.compileMatrixExpressions(compiled);
|
|
156
|
-
// Create function that safely evaluates the expression
|
|
157
|
-
return (context) => {
|
|
158
|
-
try {
|
|
159
|
-
// This uses Function constructor with controlled context binding
|
|
160
|
-
// which is safer than eval() but still allows expressions
|
|
161
|
-
// Note: compiled expression already uses context['field'] syntax
|
|
162
|
-
const func = new Function('context', `
|
|
163
|
-
try {
|
|
164
|
-
return ${compiled};
|
|
165
|
-
} catch (e) {
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
`);
|
|
169
|
-
return func(context);
|
|
170
|
-
}
|
|
171
|
-
catch (e) {
|
|
172
|
-
console.error('Expression execution error:', e, { expression, compiled });
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
console.error('Expression compilation error:', error, { expression });
|
|
179
|
-
return () => null;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
// Private helpers
|
|
183
|
-
compileMatrixExpressions(expression) {
|
|
184
|
-
let compiled = expression;
|
|
185
|
-
const keywords = ['and', 'or', 'in', 'new', 'typeof', 'instanceof', 'delete', 'void', 'this', 'super', 'async', 'await'];
|
|
186
|
-
const constants = ['PI', 'TRUE', 'FALSE', 'NULL', 'UNDEFINED', 'Math'];
|
|
187
|
-
const functions = [
|
|
188
|
-
'SUM', 'AVG', 'MIN', 'MAX', 'COUNT',
|
|
189
|
-
'ROUND', 'FLOOR', 'CEIL', 'ABS', 'SQRT',
|
|
190
|
-
'UPPER', 'LOWER', 'CONCAT', 'LENGTH', 'TRIM', 'SUBSTRING',
|
|
191
|
-
'TODAY', 'AGE', 'DATEFORMAT', 'DATEDIFF', 'ADDDAYS', 'SUBDAYS'
|
|
192
|
-
];
|
|
193
|
-
// Create fresh regex each time to avoid lastIndex issues with g flag
|
|
194
|
-
const mathExprPattern = /\$?\{([^}]+)\}/g;
|
|
195
|
-
// Process ${...} and {...} blocks: replace bare identifiers with context lookups
|
|
196
|
-
compiled = compiled.replace(mathExprPattern, (_match, content) => {
|
|
197
|
-
let transformed = content;
|
|
198
|
-
// STEP 1: Handle function calls first
|
|
199
|
-
// Match function names followed by ( and prefix with context.
|
|
200
|
-
// Use negative lookbehind to avoid double-replacing (e.g., context.SUM)
|
|
201
|
-
functions.forEach(fn => {
|
|
202
|
-
const funcCallRegex = new RegExp(`(?<!context\\.)\\b${fn}\\b(?=\\s*\\()`, 'g');
|
|
203
|
-
transformed = transformed.replace(funcCallRegex, `context.${fn}`);
|
|
204
|
-
});
|
|
205
|
-
constants.forEach(constant => {
|
|
206
|
-
const constantRegex = new RegExp(`\\b${constant}\\b`, 'g');
|
|
207
|
-
transformed = transformed.replace(constantRegex, `context.${constant}`);
|
|
208
|
-
});
|
|
209
|
-
// STEP 2: Replace other bare identifiers with context lookups
|
|
210
|
-
// Skip: operators, keywords, constants, and property/method access
|
|
211
|
-
// First, protect string literals by temporarily replacing them
|
|
212
|
-
const strings = [];
|
|
213
|
-
let stringProtected = transformed;
|
|
214
|
-
stringProtected = stringProtected.replace(/(['"`])(?:(?=(\\?))\2.)*?\1/g, (match) => {
|
|
215
|
-
strings.push(match);
|
|
216
|
-
return `__STRING_${strings.length - 1}__`;
|
|
217
|
-
});
|
|
218
|
-
// Now replace identifiers, but they won't match inside strings
|
|
219
|
-
stringProtected = stringProtected.replace(/(?<!context\[')\b([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*[(\.])/g, (word) => {
|
|
220
|
-
// Skip keywords and constants
|
|
221
|
-
if (keywords.includes(word.toLowerCase()) || constants.includes(word.toUpperCase())) {
|
|
222
|
-
return word;
|
|
223
|
-
}
|
|
224
|
-
// Skip function names (already handled in step 1)
|
|
225
|
-
if (functions.includes(word.toUpperCase())) {
|
|
226
|
-
return word;
|
|
227
|
-
}
|
|
228
|
-
// Replace identifier with context access (but only if not already context['...'])
|
|
229
|
-
if (!word.startsWith('context')) {
|
|
230
|
-
return `context['${word}']`;
|
|
231
|
-
}
|
|
232
|
-
return word;
|
|
233
|
-
});
|
|
234
|
-
// Return as parenthesized expression
|
|
235
|
-
return `(${transformed})`;
|
|
236
|
-
});
|
|
237
|
-
return compiled;
|
|
238
|
-
}
|
|
239
|
-
compileBareFunctionCalls(expression) {
|
|
240
|
-
let compiled = expression;
|
|
241
|
-
const functions = [
|
|
242
|
-
'SUM', 'AVG', 'MIN', 'MAX', 'COUNT',
|
|
243
|
-
'ROUND', 'FLOOR', 'CEIL', 'ABS', 'SQRT',
|
|
244
|
-
'UPPER', 'LOWER', 'CONCAT', 'LENGTH', 'TRIM', 'SUBSTRING',
|
|
245
|
-
'TODAY', 'AGE', 'DATEFORMAT', 'DATEDIFF', 'ADDDAYS', 'SUBDAYS'
|
|
246
|
-
];
|
|
247
|
-
// Handle bare function calls like SUM([...]) that are not inside ${...} or {...}
|
|
248
|
-
// Use negative lookbehind to avoid matching if already preceded by context.
|
|
249
|
-
functions.forEach(fn => {
|
|
250
|
-
const funcCallRegex = new RegExp(`(?<!context\\.)\\b${fn}\\b(?=\\s*\\()`, 'g');
|
|
251
|
-
compiled = compiled.replace(funcCallRegex, `context.${fn}`);
|
|
252
|
-
});
|
|
253
|
-
return compiled;
|
|
254
|
-
}
|
|
255
|
-
compileFieldReferences(expression) {
|
|
256
|
-
let compiled = expression;
|
|
257
|
-
// Create fresh regex each time to avoid lastIndex issues with g flag
|
|
258
|
-
const fieldRefPattern = /\{([^{}]+)\}/g;
|
|
259
|
-
// Replace {fieldName} with context['fieldName'] or field access
|
|
260
|
-
compiled = compiled.replace(fieldRefPattern, (match, ref) => {
|
|
261
|
-
// Handle escaped braces
|
|
262
|
-
if (ref.startsWith('\\') && ref.endsWith('\\')) {
|
|
263
|
-
return match; // Keep as is
|
|
264
|
-
}
|
|
265
|
-
// Handle nested paths like {customer.name}
|
|
266
|
-
return `context['${ref.replace(/\./g, '\'].[')}']`;
|
|
267
|
-
});
|
|
268
|
-
return compiled;
|
|
269
|
-
}
|
|
270
|
-
}
|
package/src/json-converter.d.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import type { FormSchema, FormComponent } from '../FormBuilder/types/FormSchema';
|
|
2
|
-
export declare const generateId: (type: string) => string;
|
|
3
|
-
export declare const generateName: (type: string) => string;
|
|
4
|
-
export interface UnifiedSchema {
|
|
5
|
-
version: string;
|
|
6
|
-
properties: Record<string, UnifiedField>;
|
|
7
|
-
theme?: string;
|
|
8
|
-
layout: LayoutNode[];
|
|
9
|
-
settings?: any;
|
|
10
|
-
}
|
|
11
|
-
export interface UnifiedField extends DataField, UIField {
|
|
12
|
-
}
|
|
13
|
-
export interface DataSchema {
|
|
14
|
-
version: string;
|
|
15
|
-
properties: Record<string, DataField>;
|
|
16
|
-
settings?: any;
|
|
17
|
-
}
|
|
18
|
-
export interface DataField {
|
|
19
|
-
id: string;
|
|
20
|
-
type: 'string' | 'number' | 'boolean' | 'array' | 'any' | 'button' | 'message' | 'sign' | 'image' | 'file' | 'color' | 'grid';
|
|
21
|
-
name: string;
|
|
22
|
-
label?: string;
|
|
23
|
-
format?: 'date' | 'date-time' | 'time' | 'email' | 'url';
|
|
24
|
-
defaultValue?: any;
|
|
25
|
-
expressionValue?: string;
|
|
26
|
-
content?: string;
|
|
27
|
-
buttonType?: string;
|
|
28
|
-
textboxType?: string;
|
|
29
|
-
options?: string[] | any[];
|
|
30
|
-
required?: boolean;
|
|
31
|
-
minLength?: number;
|
|
32
|
-
maxLength?: number;
|
|
33
|
-
minValue?: number;
|
|
34
|
-
maxValue?: number;
|
|
35
|
-
minDate?: string;
|
|
36
|
-
maxDate?: string;
|
|
37
|
-
minTime?: string;
|
|
38
|
-
maxTime?: string;
|
|
39
|
-
pattern?: string;
|
|
40
|
-
}
|
|
41
|
-
export interface UISchema {
|
|
42
|
-
properties: Record<string, UIField>;
|
|
43
|
-
layout: LayoutNode[];
|
|
44
|
-
}
|
|
45
|
-
export interface UIField {
|
|
46
|
-
widget: string;
|
|
47
|
-
_configured?: boolean;
|
|
48
|
-
[key: string]: any;
|
|
49
|
-
}
|
|
50
|
-
export interface SplitSchema {
|
|
51
|
-
dataSchema: DataSchema;
|
|
52
|
-
uiSchema: UISchema;
|
|
53
|
-
}
|
|
54
|
-
export type LayoutNode = FieldNode | TableNode | PanelNode | TabsNode | CardNode;
|
|
55
|
-
export interface FieldNode {
|
|
56
|
-
type: 'field';
|
|
57
|
-
propertyId: string;
|
|
58
|
-
}
|
|
59
|
-
export interface TableNode {
|
|
60
|
-
type: 'table';
|
|
61
|
-
id: string;
|
|
62
|
-
name: string;
|
|
63
|
-
label?: string;
|
|
64
|
-
cssClass?: string;
|
|
65
|
-
hideBorders?: boolean;
|
|
66
|
-
rows: number;
|
|
67
|
-
cols: number;
|
|
68
|
-
cells: TableCell[][];
|
|
69
|
-
columnWidths?: {
|
|
70
|
-
widths: (number | null)[];
|
|
71
|
-
};
|
|
72
|
-
conditions?: any;
|
|
73
|
-
}
|
|
74
|
-
export interface TableCell {
|
|
75
|
-
row: number;
|
|
76
|
-
col: number;
|
|
77
|
-
children: LayoutNode[];
|
|
78
|
-
}
|
|
79
|
-
export interface PanelNode {
|
|
80
|
-
type: 'panel';
|
|
81
|
-
id: string;
|
|
82
|
-
name: string;
|
|
83
|
-
label?: string;
|
|
84
|
-
cssClass?: string;
|
|
85
|
-
hideBorders?: boolean;
|
|
86
|
-
hideLabel?: boolean;
|
|
87
|
-
children: LayoutNode[];
|
|
88
|
-
conditions?: any;
|
|
89
|
-
}
|
|
90
|
-
export interface TabsNode {
|
|
91
|
-
type: 'tabs';
|
|
92
|
-
id: string;
|
|
93
|
-
name: string;
|
|
94
|
-
cssClass?: string;
|
|
95
|
-
label?: string;
|
|
96
|
-
tabOptions?: string[];
|
|
97
|
-
tabs: TabItem[];
|
|
98
|
-
conditions?: any;
|
|
99
|
-
}
|
|
100
|
-
export interface TabItem {
|
|
101
|
-
header: string;
|
|
102
|
-
children: LayoutNode[];
|
|
103
|
-
}
|
|
104
|
-
export interface CardNode {
|
|
105
|
-
type: 'card';
|
|
106
|
-
id: string;
|
|
107
|
-
name: string;
|
|
108
|
-
label?: string;
|
|
109
|
-
cssClass?: string;
|
|
110
|
-
hideLabel?: boolean;
|
|
111
|
-
cardTitle?: string;
|
|
112
|
-
cardSubtitle?: string;
|
|
113
|
-
enableAction?: boolean;
|
|
114
|
-
actionButtonLabel?: string;
|
|
115
|
-
actionButtonType?: 'button' | 'submit';
|
|
116
|
-
children: LayoutNode[];
|
|
117
|
-
conditions?: any;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Convert label to camelCase identifier
|
|
121
|
-
* Examples:
|
|
122
|
-
* "First Name" → "firstName"
|
|
123
|
-
* "Email Address" → "emailAddress"
|
|
124
|
-
* "Date of Birth" → "dateOfBirth"
|
|
125
|
-
* "Phone Number (Mobile)" → "phoneNumberMobile"
|
|
126
|
-
*/
|
|
127
|
-
export declare function labelToFieldKey(label: string, fallbackId?: string): string;
|
|
128
|
-
/**
|
|
129
|
-
* Ensure field key is unique by appending a number if needed
|
|
130
|
-
*/
|
|
131
|
-
export declare function makeUniqueFieldKey(baseKey: string, existingKeys: Set<string>, originalId?: string): string;
|
|
132
|
-
export declare const UI_TYPE_TO_SEMANTIC_TYPE: Record<string, {
|
|
133
|
-
type: DataField['type'];
|
|
134
|
-
format?: string;
|
|
135
|
-
} | null>;
|
|
136
|
-
export declare function splitSchema(heartSchema: FormSchema): UnifiedSchema;
|
|
137
|
-
/**
|
|
138
|
-
* Merge unified schema back into heart schema
|
|
139
|
-
* Reconstructs FormComponent array from unified properties and layout
|
|
140
|
-
*/
|
|
141
|
-
export declare function mergeSchema(unified: UnifiedSchema): FormSchema;
|
|
142
|
-
/**
|
|
143
|
-
* Infer widget type from semantic data type
|
|
144
|
-
*/
|
|
145
|
-
export declare function inferWidgetFromType(type: string, format?: string, hasOptions?: boolean): string;
|
|
146
|
-
export interface FieldMatchResult {
|
|
147
|
-
existingId: string;
|
|
148
|
-
newDataId: string;
|
|
149
|
-
confidence: 'exact' | 'high' | 'medium' | 'low';
|
|
150
|
-
method: 'id' | 'label-exact' | 'label-fuzzy' | 'new';
|
|
151
|
-
similarity?: number;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Match a new data field with existing UI schema field
|
|
155
|
-
* Preserves existing ID for stability
|
|
156
|
-
*/
|
|
157
|
-
export declare function matchFieldWithIDPreservation(newDataField: DataField, existingUISchema: UISchema): FieldMatchResult;
|
|
158
|
-
/**
|
|
159
|
-
* Merge with ID remapping for re-import scenarios
|
|
160
|
-
* Preserves existing IDs to maintain layout stability
|
|
161
|
-
*/
|
|
162
|
-
export declare function mergeWithIDRemapping(newDataSchema: DataSchema, existingUISchema: UISchema): FormSchema;
|
|
163
|
-
/**
|
|
164
|
-
* Extract all field IDs from layout tree
|
|
165
|
-
*/
|
|
166
|
-
export declare function extractFieldIdsFromLayout(layout: LayoutNode[]): Set<string>;
|
|
167
|
-
/**
|
|
168
|
-
* Convert parser output (FormComponent[]) to DataSchema format
|
|
169
|
-
* This allows parser results to be merged with existing UI schemas
|
|
170
|
-
*/
|
|
171
|
-
export declare function convertComponentsToDataSchema(components: FormComponent[]): DataSchema;
|