@syncfusion/ej2-react-form-renderer 0.1.0-beta → 0.1.2-beta
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 +3 -10
- package/package.json +1 -1
- package/src/CheckboxGroup.js +3 -2
- package/src/ConditionalRuleEngine.d.ts +295 -0
- package/src/ConditionalRuleEngine.js +566 -0
- package/src/DataGridComponent.d.ts +0 -1
- package/src/DataGridComponent.js +9 -22
- package/src/FormRenderer.d.ts +28 -7
- package/src/FormRenderer.js +342 -202
- package/src/MinMaxRuleEngine.d.ts +95 -0
- package/src/MinMaxRuleEngine.js +361 -0
- package/src/expressions/DependencyTracker.d.ts +30 -0
- package/src/expressions/DependencyTracker.js +128 -0
- package/src/expressions/ExpressionEngine.d.ts +81 -0
- package/src/expressions/ExpressionEngine.js +503 -0
- package/src/expressions/ExpressionParser.d.ts +39 -0
- package/src/expressions/ExpressionParser.js +270 -0
- package/src/json-converter.d.ts +32 -31
- package/src/json-converter.js +313 -284
- package/src/parser.js +22 -18
- package/src/renderFormField.d.ts +3 -1
- package/src/renderFormField.js +70 -46
- package/src/types/FormSchema.d.ts +36 -2
- package/src/utils.js +348 -0
- package/styles/form-renderer.base.css +49 -23
|
@@ -0,0 +1,270 @@
|
|
|
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
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import type { FormSchema, FormComponent } from '../FormBuilder/types/FormSchema';
|
|
2
2
|
export declare const generateId: (type: string) => string;
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
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 {
|
|
6
12
|
}
|
|
7
13
|
export interface DataSchema {
|
|
8
14
|
version: string;
|
|
9
15
|
properties: Record<string, DataField>;
|
|
16
|
+
settings?: any;
|
|
10
17
|
}
|
|
11
18
|
export interface DataField {
|
|
12
19
|
id: string;
|
|
13
20
|
type: 'string' | 'number' | 'boolean' | 'array' | 'any' | 'button' | 'message' | 'sign' | 'image' | 'file' | 'color' | 'grid';
|
|
21
|
+
name: string;
|
|
14
22
|
label?: string;
|
|
15
23
|
format?: 'date' | 'date-time' | 'time' | 'email' | 'url';
|
|
16
24
|
defaultValue?: any;
|
|
25
|
+
expressionValue?: string;
|
|
17
26
|
content?: string;
|
|
18
27
|
buttonType?: string;
|
|
19
28
|
textboxType?: string;
|
|
@@ -38,6 +47,10 @@ export interface UIField {
|
|
|
38
47
|
_configured?: boolean;
|
|
39
48
|
[key: string]: any;
|
|
40
49
|
}
|
|
50
|
+
export interface SplitSchema {
|
|
51
|
+
dataSchema: DataSchema;
|
|
52
|
+
uiSchema: UISchema;
|
|
53
|
+
}
|
|
41
54
|
export type LayoutNode = FieldNode | TableNode | PanelNode | TabsNode | CardNode;
|
|
42
55
|
export interface FieldNode {
|
|
43
56
|
type: 'field';
|
|
@@ -46,6 +59,7 @@ export interface FieldNode {
|
|
|
46
59
|
export interface TableNode {
|
|
47
60
|
type: 'table';
|
|
48
61
|
id: string;
|
|
62
|
+
name: string;
|
|
49
63
|
label?: string;
|
|
50
64
|
cssClass?: string;
|
|
51
65
|
hideBorders?: boolean;
|
|
@@ -55,6 +69,7 @@ export interface TableNode {
|
|
|
55
69
|
columnWidths?: {
|
|
56
70
|
widths: (number | null)[];
|
|
57
71
|
};
|
|
72
|
+
conditions?: any;
|
|
58
73
|
}
|
|
59
74
|
export interface TableCell {
|
|
60
75
|
row: number;
|
|
@@ -64,19 +79,23 @@ export interface TableCell {
|
|
|
64
79
|
export interface PanelNode {
|
|
65
80
|
type: 'panel';
|
|
66
81
|
id: string;
|
|
82
|
+
name: string;
|
|
67
83
|
label?: string;
|
|
68
84
|
cssClass?: string;
|
|
69
85
|
hideBorders?: boolean;
|
|
70
86
|
hideLabel?: boolean;
|
|
71
87
|
children: LayoutNode[];
|
|
88
|
+
conditions?: any;
|
|
72
89
|
}
|
|
73
90
|
export interface TabsNode {
|
|
74
91
|
type: 'tabs';
|
|
75
92
|
id: string;
|
|
93
|
+
name: string;
|
|
76
94
|
cssClass?: string;
|
|
77
95
|
label?: string;
|
|
78
96
|
tabOptions?: string[];
|
|
79
97
|
tabs: TabItem[];
|
|
98
|
+
conditions?: any;
|
|
80
99
|
}
|
|
81
100
|
export interface TabItem {
|
|
82
101
|
header: string;
|
|
@@ -85,6 +104,7 @@ export interface TabItem {
|
|
|
85
104
|
export interface CardNode {
|
|
86
105
|
type: 'card';
|
|
87
106
|
id: string;
|
|
107
|
+
name: string;
|
|
88
108
|
label?: string;
|
|
89
109
|
cssClass?: string;
|
|
90
110
|
hideLabel?: boolean;
|
|
@@ -94,6 +114,7 @@ export interface CardNode {
|
|
|
94
114
|
actionButtonLabel?: string;
|
|
95
115
|
actionButtonType?: 'button' | 'submit';
|
|
96
116
|
children: LayoutNode[];
|
|
117
|
+
conditions?: any;
|
|
97
118
|
}
|
|
98
119
|
/**
|
|
99
120
|
* Convert label to camelCase identifier
|
|
@@ -108,12 +129,16 @@ export declare function labelToFieldKey(label: string, fallbackId?: string): str
|
|
|
108
129
|
* Ensure field key is unique by appending a number if needed
|
|
109
130
|
*/
|
|
110
131
|
export declare function makeUniqueFieldKey(baseKey: string, existingKeys: Set<string>, originalId?: string): string;
|
|
111
|
-
export declare
|
|
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;
|
|
112
137
|
/**
|
|
113
|
-
* Merge
|
|
114
|
-
*
|
|
138
|
+
* Merge unified schema back into heart schema
|
|
139
|
+
* Reconstructs FormComponent array from unified properties and layout
|
|
115
140
|
*/
|
|
116
|
-
export declare function mergeSchema(
|
|
141
|
+
export declare function mergeSchema(unified: UnifiedSchema): FormSchema;
|
|
117
142
|
/**
|
|
118
143
|
* Infer widget type from semantic data type
|
|
119
144
|
*/
|
|
@@ -139,32 +164,8 @@ export declare function mergeWithIDRemapping(newDataSchema: DataSchema, existing
|
|
|
139
164
|
* Extract all field IDs from layout tree
|
|
140
165
|
*/
|
|
141
166
|
export declare function extractFieldIdsFromLayout(layout: LayoutNode[]): Set<string>;
|
|
142
|
-
/**
|
|
143
|
-
* Find unmatched fields (in data schema but not in layout)
|
|
144
|
-
*/
|
|
145
|
-
export declare function findUnmatchedFields(dataSchema: DataSchema, layout: LayoutNode[]): string[];
|
|
146
167
|
/**
|
|
147
168
|
* Convert parser output (FormComponent[]) to DataSchema format
|
|
148
169
|
* This allows parser results to be merged with existing UI schemas
|
|
149
170
|
*/
|
|
150
171
|
export declare function convertComponentsToDataSchema(components: FormComponent[]): DataSchema;
|
|
151
|
-
/**
|
|
152
|
-
* Convert parser output to a simple linear layout
|
|
153
|
-
* Used when no existing UI schema is present
|
|
154
|
-
*/
|
|
155
|
-
export declare function convertComponentsToUISchema(components: FormComponent[]): UISchema;
|
|
156
|
-
/**
|
|
157
|
-
* Generate default UI properties for each data field
|
|
158
|
-
* Used by renderers when only data schema is provided
|
|
159
|
-
*/
|
|
160
|
-
export declare function generateUIPropertiesFromData(dataSchema: DataSchema): Record<string, UIField>;
|
|
161
|
-
/**
|
|
162
|
-
* Generate sequential layout from data schema
|
|
163
|
-
* Used by renderers when only data schema is provided
|
|
164
|
-
*/
|
|
165
|
-
export declare function generateLayoutFromData(dataSchema: DataSchema): FieldNode[];
|
|
166
|
-
/**
|
|
167
|
-
* Generate minimal data schema from UI schema
|
|
168
|
-
* Used by renderers when only UI schema is provided
|
|
169
|
-
*/
|
|
170
|
-
export declare function generateDataSchemaFromUI(uiSchema: UISchema): DataSchema;
|