@syncfusion/ej2-react-form-renderer 0.1.1-beta → 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 +86 -110
- 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 -48
- package/src/ConditionalRuleEngine.d.ts +0 -253
- package/src/ConditionalRuleEngine.js +0 -489
- 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 -144
- package/src/FormRenderer.d.ts +0 -90
- package/src/FormRenderer.js +0 -649
- package/src/FormValidator.d.ts +0 -479
- package/src/FormValidator.js +0 -579
- package/src/StaticHtmlRenderer.d.ts +0 -8
- package/src/StaticHtmlRenderer.js +0 -21
- package/src/json-converter.d.ts +0 -180
- package/src/json-converter.js +0 -1329
- package/src/parser.d.ts +0 -99
- package/src/parser.js +0 -750
- package/src/renderFormField.d.ts +0 -20
- package/src/renderFormField.js +0 -203
- package/src/types/FormSchema.d.ts +0 -205
- package/src/types/FormSchema.js +0 -1
- package/src/utils.js +0 -146
- package/styles/bootstrap5.3-dark.css +0 -26
- package/styles/fluent2-dark.css +0 -26
- package/styles/form-renderer.base.css +0 -489
- package/styles/tailwind3-dark.css +0 -10
package/src/FormRenderer.js
DELETED
|
@@ -1,649 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Form Renderer Component
|
|
4
|
-
* Renders JSON-driven forms with React FormValidator
|
|
5
|
-
*/
|
|
6
|
-
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
7
|
-
import { Form, FormField } from './FormValidator';
|
|
8
|
-
import { enableRipple } from '@syncfusion/ej2-base';
|
|
9
|
-
import { renderFormField } from './renderFormField';
|
|
10
|
-
import { CustomTabComponent } from './CustomTabComponent';
|
|
11
|
-
import { mergeSchema, convertComponentsToDataSchema, generateUIPropertiesFromData, generateLayoutFromData, generateDataSchemaFromUI } from './json-converter';
|
|
12
|
-
import { UniversalJsonParser } from './parser';
|
|
13
|
-
import { TooltipComponent } from '@syncfusion/ej2-react-popups';
|
|
14
|
-
import { VALIDATION_REGEX } from './FormValidator';
|
|
15
|
-
import StaticHtmlRenderer from './StaticHtmlRenderer';
|
|
16
|
-
import { formatDateValues, splitWidths } from './utils';
|
|
17
|
-
import { ConditionalRuleEngine } from './ConditionalRuleEngine';
|
|
18
|
-
enableRipple(true);
|
|
19
|
-
/**
|
|
20
|
-
* Form layout options (only applies when UI schema is NOT provided)
|
|
21
|
-
*/
|
|
22
|
-
export const FormLayout = {
|
|
23
|
-
SingleColumn: 'single',
|
|
24
|
-
TwoColumn: 'twocolumn',
|
|
25
|
-
ThreeColumn: 'threecolumn',
|
|
26
|
-
FourColumn: 'fourcolumn'
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Helper: Normalize DataSchema input
|
|
30
|
-
* Converts any object to JSON string and parses with UniversalJsonParser
|
|
31
|
-
* Handles FormSchema, DataSchema, or raw data - parser infers the format
|
|
32
|
-
*/
|
|
33
|
-
function normalizeDataSchema(input) {
|
|
34
|
-
try {
|
|
35
|
-
// Convert object to JSON string
|
|
36
|
-
const jsonString = JSON.stringify(input);
|
|
37
|
-
const parser = new UniversalJsonParser();
|
|
38
|
-
const parseResult = parser.parse(jsonString);
|
|
39
|
-
if (parseResult.success && parseResult.schema) {
|
|
40
|
-
const dataSchema = convertComponentsToDataSchema(parseResult.schema.components);
|
|
41
|
-
// console.log(`✅ Parsed object as ${parseResult.metadata.detectedFormat} and converted to DataSchema`);
|
|
42
|
-
return dataSchema;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
console.error('❌ Failed to parse object:', parseResult.errors);
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
console.error('❌ DataSchema normalization error:', error);
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Helper: Parse string input using UniversalJsonParser
|
|
56
|
-
* Returns DataSchema if parseable, otherwise null
|
|
57
|
-
* Validates that UI schema was not accidentally provided
|
|
58
|
-
*/
|
|
59
|
-
function parseStringToDataSchema(input) {
|
|
60
|
-
try {
|
|
61
|
-
// First check if user accidentally pasted UI schema in data schema field
|
|
62
|
-
const testParse = JSON.parse(input);
|
|
63
|
-
if (testParse.layout || (testParse.theme && testParse.properties && testParse.layout)) {
|
|
64
|
-
console.error('The provided schema is a UI Schema in the data schema prop. Please provide a valid Data Schema with field definitions and validation rules.');
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
const parser = new UniversalJsonParser();
|
|
68
|
-
const parseResult = parser.parse(input);
|
|
69
|
-
if (parseResult.success && parseResult.schema) {
|
|
70
|
-
const dataSchema = convertComponentsToDataSchema(parseResult.schema.components);
|
|
71
|
-
return dataSchema;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
console.error('Failed to parse input:', parseResult.errors);
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
console.error('Parser error:', error);
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Helper: Parse string input as UISchema
|
|
85
|
-
* Validates JSON structure matches UISchema format
|
|
86
|
-
* Detects if data schema was accidentally provided
|
|
87
|
-
*/
|
|
88
|
-
function parseStringToUISchema(input) {
|
|
89
|
-
try {
|
|
90
|
-
const parsed = JSON.parse(input);
|
|
91
|
-
// Check if data schema was pasted in UI schema field
|
|
92
|
-
if (parsed.properties && !parsed.layout && !parsed.theme) {
|
|
93
|
-
const hasValidationProps = Object.values(parsed.properties).some((prop) => prop.required !== undefined || prop.minLength !== undefined || prop.maxLength !== undefined ||
|
|
94
|
-
prop.minValue !== undefined || prop.maxValue !== undefined || prop.pattern !== undefined);
|
|
95
|
-
if (hasValidationProps) {
|
|
96
|
-
console.error('The provided schema is a Data Schema in the UI schema prop. Please provide a valid UI Schema with theme, properties, and layout definitions.');
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// Validate UISchema structure
|
|
101
|
-
if (!parsed.theme || !parsed.properties || !parsed.layout) {
|
|
102
|
-
console.error('Invalid UISchema: missing theme, properties, or layout');
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
return parsed;
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
console.error('JSON parse error:', error);
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
// ForwardRef to expose public methods
|
|
113
|
-
export const FormRenderer = React.forwardRef(({ dataSchema, uiSchema, locale, onSubmit, onChange, onButtonClick, className = '', layout = FormLayout.SingleColumn, created, ...restProps }, ref) => {
|
|
114
|
-
const processedSchema = React.useMemo(() => {
|
|
115
|
-
try {
|
|
116
|
-
// Parse and normalize data schema
|
|
117
|
-
let parsedDataSchema = dataSchema
|
|
118
|
-
? ((typeof dataSchema === 'string'
|
|
119
|
-
? parseStringToDataSchema(dataSchema)
|
|
120
|
-
: normalizeDataSchema(dataSchema)) || undefined)
|
|
121
|
-
: undefined;
|
|
122
|
-
// Parse and normalize UI schema
|
|
123
|
-
let parsedUISchema = uiSchema
|
|
124
|
-
? (typeof uiSchema === 'string'
|
|
125
|
-
? parseStringToUISchema(uiSchema)
|
|
126
|
-
: uiSchema) || undefined
|
|
127
|
-
: undefined;
|
|
128
|
-
// Scenario 1: Both dataSchema and uiSchema provided - full merge
|
|
129
|
-
if (parsedDataSchema && parsedUISchema) {
|
|
130
|
-
const uiFieldIds = new Set(Object.keys(parsedUISchema.properties));
|
|
131
|
-
const dataFieldIds = Object.keys(parsedDataSchema.properties);
|
|
132
|
-
const missingFields = dataFieldIds.filter(id => !uiFieldIds.has(id));
|
|
133
|
-
if (missingFields.length > 0) {
|
|
134
|
-
console.warn(`⚠️ Found ${missingFields.length} fields in data schema missing from UI schema`);
|
|
135
|
-
}
|
|
136
|
-
const merged = mergeSchema({ dataSchema: parsedDataSchema, uiSchema: parsedUISchema });
|
|
137
|
-
return merged;
|
|
138
|
-
}
|
|
139
|
-
// Scenario 2: Only dataSchema provided - auto-generate UI schema
|
|
140
|
-
if (parsedDataSchema && !parsedUISchema) {
|
|
141
|
-
const autoUISchema = {
|
|
142
|
-
properties: generateUIPropertiesFromData(parsedDataSchema),
|
|
143
|
-
layout: generateLayoutFromData(parsedDataSchema)
|
|
144
|
-
};
|
|
145
|
-
const merged = mergeSchema({ dataSchema: parsedDataSchema, uiSchema: autoUISchema });
|
|
146
|
-
return merged;
|
|
147
|
-
}
|
|
148
|
-
// Scenario 3: Only uiSchema provided - auto-generate data schema
|
|
149
|
-
if (!parsedDataSchema && parsedUISchema) {
|
|
150
|
-
const autoDataSchema = generateDataSchemaFromUI(parsedUISchema);
|
|
151
|
-
const uiFieldIds = new Set(Object.keys(parsedUISchema.properties));
|
|
152
|
-
const dataFieldIds = Object.keys(autoDataSchema.properties);
|
|
153
|
-
const missingFields = dataFieldIds.filter(id => !uiFieldIds.has(id));
|
|
154
|
-
if (missingFields.length > 0) {
|
|
155
|
-
console.warn(`Found ${missingFields.length} fields in auto-generated data schema missing from UI schema`);
|
|
156
|
-
}
|
|
157
|
-
const merged = mergeSchema({ dataSchema: autoDataSchema, uiSchema: parsedUISchema });
|
|
158
|
-
return merged;
|
|
159
|
-
}
|
|
160
|
-
// Scenario 4: Neither provided - return empty form
|
|
161
|
-
console.warn('FormRenderer: No schema provided (dataSchema or uiSchema)');
|
|
162
|
-
return { version: '0.1.0', theme: 'tailwind3', components: [] };
|
|
163
|
-
}
|
|
164
|
-
catch (error) {
|
|
165
|
-
console.error('FormRenderer schema processing error:', error);
|
|
166
|
-
return { version: '0.1.0', theme: 'tailwind3', components: [] };
|
|
167
|
-
}
|
|
168
|
-
}, [dataSchema, uiSchema]);
|
|
169
|
-
const [formState, setFormState] = useState(undefined);
|
|
170
|
-
const isInitializedRef = useRef(false);
|
|
171
|
-
const formLocale = locale || 'en-US';
|
|
172
|
-
// Attach ref to the input element for getComponent
|
|
173
|
-
let setInputRef;
|
|
174
|
-
// Store input component refs by name/id
|
|
175
|
-
const inputRefs = useRef({});
|
|
176
|
-
// Track which fields have active conditional values applied (to prevent re-applying while condition stays true)
|
|
177
|
-
const conditionalValueAppliedRef = useRef({});
|
|
178
|
-
// Check if UI schema has layout components (table, tabs, or panel)
|
|
179
|
-
const hasLayoutComponents = React.useMemo(() => {
|
|
180
|
-
if (!processedSchema.components)
|
|
181
|
-
return false;
|
|
182
|
-
const checkForLayoutTypes = (components) => {
|
|
183
|
-
for (const comp of components) {
|
|
184
|
-
if (['table', 'tabs', 'panel', 'card'].includes(comp.type)) {
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
187
|
-
if (comp.children && checkForLayoutTypes(comp.children)) {
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
if (comp.tableCells) {
|
|
191
|
-
for (const row of comp.tableCells) {
|
|
192
|
-
for (const cell of row) {
|
|
193
|
-
if (checkForLayoutTypes(cell))
|
|
194
|
-
return true;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
if (comp.tabItems) {
|
|
199
|
-
for (const tab of comp.tabItems) {
|
|
200
|
-
if (tab.content && checkForLayoutTypes(tab.content))
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return false;
|
|
206
|
-
};
|
|
207
|
-
return checkForLayoutTypes(processedSchema.components);
|
|
208
|
-
}, [processedSchema]);
|
|
209
|
-
// Determine layout class (only apply if UI schema does NOT have table/tabs/panels)
|
|
210
|
-
const layoutClass = React.useMemo(() => {
|
|
211
|
-
if (hasLayoutComponents)
|
|
212
|
-
return ''; // Ignore layout if UI schema has table/tabs/panels
|
|
213
|
-
// Map enum to CSS class names
|
|
214
|
-
switch (layout) {
|
|
215
|
-
case FormLayout.TwoColumn:
|
|
216
|
-
return 'twolayout';
|
|
217
|
-
case FormLayout.ThreeColumn:
|
|
218
|
-
return 'threelayout';
|
|
219
|
-
case FormLayout.FourColumn:
|
|
220
|
-
return 'fourlayout';
|
|
221
|
-
case FormLayout.SingleColumn:
|
|
222
|
-
default:
|
|
223
|
-
return ''; // Default single column (no class needed)
|
|
224
|
-
}
|
|
225
|
-
}, [layout, hasLayoutComponents]);
|
|
226
|
-
/**
|
|
227
|
-
* Recursively collect all components including nested ones
|
|
228
|
-
*/
|
|
229
|
-
const collectAllComponents = useCallback((components) => {
|
|
230
|
-
const allComponents = [];
|
|
231
|
-
const traverse = (comps) => {
|
|
232
|
-
comps.forEach(comp => {
|
|
233
|
-
allComponents.push(comp);
|
|
234
|
-
// Handle nested components in panels
|
|
235
|
-
if (comp.children && comp.children.length > 0) {
|
|
236
|
-
traverse(comp.children);
|
|
237
|
-
}
|
|
238
|
-
// Handle components in table cells
|
|
239
|
-
if (comp.tableCells) {
|
|
240
|
-
comp.tableCells.forEach(row => {
|
|
241
|
-
row.forEach(cell => {
|
|
242
|
-
traverse(cell);
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
// Handle components in tabs
|
|
247
|
-
if (comp.tabItems) {
|
|
248
|
-
comp.tabItems.forEach(tab => {
|
|
249
|
-
if (tab.content) {
|
|
250
|
-
traverse(tab.content);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
};
|
|
256
|
-
traverse(components);
|
|
257
|
-
return allComponents;
|
|
258
|
-
}, []);
|
|
259
|
-
const idToLabel = React.useMemo(() => {
|
|
260
|
-
const map = {};
|
|
261
|
-
const traverse = (comps) => {
|
|
262
|
-
comps.forEach(comp => {
|
|
263
|
-
if (comp.label && !['panel', 'table', 'tabs', 'card', 'button', 'message', 'splitButton'].includes(comp.type)) {
|
|
264
|
-
map[comp.id] = comp.label;
|
|
265
|
-
}
|
|
266
|
-
if (comp.children)
|
|
267
|
-
traverse(comp.children);
|
|
268
|
-
if (comp.tableCells) {
|
|
269
|
-
comp.tableCells.forEach(row => {
|
|
270
|
-
row.forEach(cell => traverse(cell));
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
if (comp.tabItems) {
|
|
274
|
-
comp.tabItems.forEach(tab => {
|
|
275
|
-
if (tab.content)
|
|
276
|
-
traverse(tab.content);
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
};
|
|
281
|
-
traverse(processedSchema.components);
|
|
282
|
-
return map;
|
|
283
|
-
}, [processedSchema]);
|
|
284
|
-
/**
|
|
285
|
-
* Build EJ2 FormValidator rules from schema
|
|
286
|
-
*/
|
|
287
|
-
const buildValidationRules = useCallback(() => {
|
|
288
|
-
const rules = {};
|
|
289
|
-
const formValues = formState?.values || {};
|
|
290
|
-
const collectRules = (components) => {
|
|
291
|
-
components.forEach((component) => {
|
|
292
|
-
if (component) {
|
|
293
|
-
const componentRules = {};
|
|
294
|
-
const conditionalRequired = ConditionalRuleEngine.isFieldRequired(component.conditions, formValues);
|
|
295
|
-
const conditionalDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
296
|
-
const conditionalReadOnly = ConditionalRuleEngine.isFieldReadOnly(component.conditions, formValues);
|
|
297
|
-
const finalRequired = component.required || conditionalRequired;
|
|
298
|
-
const finalDisabled = component.disabled || conditionalDisabled;
|
|
299
|
-
const finalReadOnly = component.readOnly || conditionalReadOnly;
|
|
300
|
-
if (finalRequired && !finalDisabled && !finalReadOnly) {
|
|
301
|
-
componentRules.required = [true, component.validationMessage || `${component.label} is required`];
|
|
302
|
-
}
|
|
303
|
-
if (component.minLength && !finalDisabled && !finalReadOnly) {
|
|
304
|
-
componentRules.minLength = [component.minLength, `Minimum ${component.minLength} characters required`];
|
|
305
|
-
}
|
|
306
|
-
if (component.maxLength && !finalDisabled && !finalReadOnly) {
|
|
307
|
-
componentRules.maxLength = [component.maxLength, `Maximum ${component.maxLength} characters allowed`];
|
|
308
|
-
}
|
|
309
|
-
if (component?.type === 'textbox' && component?.textboxType && !component?.regex && !componentRules.regex && !finalDisabled && !finalReadOnly) {
|
|
310
|
-
if (component.textboxType === 'email') {
|
|
311
|
-
componentRules.regex = [VALIDATION_REGEX['EMAIL'], component?.validationMessage || 'Invalid format'];
|
|
312
|
-
}
|
|
313
|
-
else if (component.textboxType === 'url') {
|
|
314
|
-
componentRules.regex = [VALIDATION_REGEX['URL'], component?.validationMessage || 'Invalid format'];
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (component.regex && !finalDisabled && !finalReadOnly) {
|
|
318
|
-
componentRules.regex = [component.regex, component.validationMessage || 'Invalid format'];
|
|
319
|
-
}
|
|
320
|
-
if (Object.keys(componentRules).length > 0) {
|
|
321
|
-
rules[component.id] = componentRules;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
// Recursively collect rules from nested components
|
|
325
|
-
if (component.children) {
|
|
326
|
-
collectRules(component.children);
|
|
327
|
-
}
|
|
328
|
-
if (component.tableCells) {
|
|
329
|
-
component.tableCells.forEach(row => {
|
|
330
|
-
row.forEach(cell => collectRules(cell));
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
if (component.tabItems) {
|
|
334
|
-
component.tabItems.forEach(tab => {
|
|
335
|
-
if (tab.content)
|
|
336
|
-
collectRules(tab.content);
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
};
|
|
341
|
-
collectRules(processedSchema.components);
|
|
342
|
-
return rules;
|
|
343
|
-
}, [processedSchema, formState?.values]);
|
|
344
|
-
const validationRules = buildValidationRules();
|
|
345
|
-
/**
|
|
346
|
-
* Build initial form values from component defaultValues
|
|
347
|
-
*/
|
|
348
|
-
const initialValues = React.useMemo(() => {
|
|
349
|
-
const values = {};
|
|
350
|
-
const allComponents = collectAllComponents(processedSchema.components);
|
|
351
|
-
const componentIds = new Set();
|
|
352
|
-
allComponents.forEach(comp => {
|
|
353
|
-
// Do NOT store dataGrid data inside the form state; grids manage their own internal data.
|
|
354
|
-
if (comp.type === 'dataGrid' || comp.type === 'richTextEditor') {
|
|
355
|
-
componentIds.add(comp.id);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
if (comp.defaultValue !== undefined) {
|
|
359
|
-
values[comp.id] = comp.defaultValue;
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
// Initialize with type-appropriate defaults
|
|
363
|
-
switch (comp.type) {
|
|
364
|
-
case 'checkbox':
|
|
365
|
-
case 'switch':
|
|
366
|
-
values[comp.id] = false;
|
|
367
|
-
break;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
componentIds.add(comp.id);
|
|
371
|
-
});
|
|
372
|
-
if (formState) {
|
|
373
|
-
Object.keys(formState.values).filter(key => !componentIds.has(key)).forEach(key => {
|
|
374
|
-
delete formState.values[key];
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
return values;
|
|
378
|
-
}, [processedSchema, collectAllComponents, dataSchema, uiSchema]);
|
|
379
|
-
// Update the useEffect that sets default values
|
|
380
|
-
useEffect(() => {
|
|
381
|
-
if (formState && !isInitializedRef.current) {
|
|
382
|
-
isInitializedRef.current = true;
|
|
383
|
-
}
|
|
384
|
-
if (formState && initialValues) {
|
|
385
|
-
Object.keys(formState.values).filter(key => !(key in initialValues));
|
|
386
|
-
Object.entries(initialValues).forEach(([id, value]) => {
|
|
387
|
-
if (!formState.values[id]) {
|
|
388
|
-
formState.values[id] = value;
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
}, [formState, processedSchema, initialValues]);
|
|
393
|
-
/**
|
|
394
|
-
* Render a single field with label and error
|
|
395
|
-
*/
|
|
396
|
-
const renderField = (component) => {
|
|
397
|
-
const formValues = formState?.values || {};
|
|
398
|
-
// Skip layout and button components (handled separately)
|
|
399
|
-
if (['panel', 'table', 'tabs', 'card', 'message', 'button', 'splitButton', 'staticHtml'].includes(component.type)) {
|
|
400
|
-
return null;
|
|
401
|
-
}
|
|
402
|
-
const conditionalReadOnly = ConditionalRuleEngine.isFieldReadOnly(component.conditions, formValues);
|
|
403
|
-
const conditionalDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
404
|
-
const conditionalRequired = ConditionalRuleEngine.isFieldRequired(component.conditions, formValues);
|
|
405
|
-
const conditionalValue = ConditionalRuleEngine.getConditionalValue(component.conditions, formValues);
|
|
406
|
-
const computedReadOnly = component.readOnly || conditionalReadOnly;
|
|
407
|
-
const computedDisabled = component.disabled || conditionalDisabled;
|
|
408
|
-
const computedRequired = component.required || conditionalRequired;
|
|
409
|
-
const labelPos = component.labelPosition || 'top';
|
|
410
|
-
const labelWidth = component.labelWidth || 'auto';
|
|
411
|
-
const showLabel = !component.hideLabel && component.label && !['checkbox', 'button', 'staticHtml'].includes(component.type);
|
|
412
|
-
if ((component.type === 'radio' || component.type === 'checkboxGroup') && Array.isArray(component.options)) {
|
|
413
|
-
// For radio, store each option's ref with unique key
|
|
414
|
-
setInputRef = (el, idx) => {
|
|
415
|
-
if (el && typeof idx === 'number') {
|
|
416
|
-
inputRefs.current[`${component.id}_${idx}`] = el;
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
setInputRef = (el) => {
|
|
422
|
-
if (el) {
|
|
423
|
-
inputRefs.current[component.id] = el;
|
|
424
|
-
}
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
const shouldShowError = !!formState?.errors[component.id];
|
|
428
|
-
const isRequiredError = shouldShowError && formState?.errors[component.id].includes('required');
|
|
429
|
-
const hasError = shouldShowError && !(isRequiredError && !computedRequired);
|
|
430
|
-
// Apply conditional value only when condition transitions from false to true (start of new cycle)
|
|
431
|
-
const conditionIsActive = conditionalValue !== undefined;
|
|
432
|
-
const wasConditionActive = conditionalValueAppliedRef.current[component.id] || false;
|
|
433
|
-
if (formState) {
|
|
434
|
-
if (conditionIsActive && !wasConditionActive) {
|
|
435
|
-
formState.onChange(component.id, { value: conditionalValue });
|
|
436
|
-
conditionalValueAppliedRef.current[component.id] = true;
|
|
437
|
-
}
|
|
438
|
-
else if (!conditionIsActive && wasConditionActive) {
|
|
439
|
-
// Condition just became inactive - clear tracking (ready for next cycle)
|
|
440
|
-
conditionalValueAppliedRef.current[component.id] = false;
|
|
441
|
-
}
|
|
442
|
-
// If condition stays active (wasConditionActive && conditionIsActive), do nothing - let user edit
|
|
443
|
-
}
|
|
444
|
-
return (_jsx(FormField, { name: component.id, children: _jsx("div", { className: "form-group", children: _jsxs("div", { className: `field-wrapper label-${labelPos.toLowerCase()}`, children: [showLabel && (_jsxs("label", { className: "field-label", htmlFor: component.id, style: { width: labelPos.toLowerCase() === 'left' ? `${labelWidth}%` : 'auto' }, children: [_jsx("span", { children: component.label }), computedRequired && _jsx("span", { className: "required-asterisk", children: "*" }), component.tooltip && (_jsx(TooltipComponent, { content: component.tooltip, children: _jsx("span", { className: "e-msg-icon", style: {
|
|
445
|
-
cursor: 'pointer',
|
|
446
|
-
display: 'flex',
|
|
447
|
-
alignItems: 'center',
|
|
448
|
-
justifyContent: 'center',
|
|
449
|
-
flexShrink: 0,
|
|
450
|
-
} }) }))] })), _jsxs("div", { className: "field-input", children: [(component.type === 'radio' || component.type === 'checkboxGroup') && Array.isArray(component.options)
|
|
451
|
-
? renderFormField({
|
|
452
|
-
component: { ...component, readOnly: computedReadOnly, disabled: computedDisabled, required: computedRequired },
|
|
453
|
-
value: formState?.values[component.id],
|
|
454
|
-
onChange: (value) => {
|
|
455
|
-
formState?.onChange(component.id, { value });
|
|
456
|
-
if (onChange) {
|
|
457
|
-
onChange({ field: component.label || component.id, value });
|
|
458
|
-
}
|
|
459
|
-
},
|
|
460
|
-
locale: formLocale,
|
|
461
|
-
onBlur: () => formState?.onBlur(component.id),
|
|
462
|
-
ref: (el, idx) => setInputRef(el, idx),
|
|
463
|
-
hasError,
|
|
464
|
-
})
|
|
465
|
-
: renderFormField({
|
|
466
|
-
component: { ...component, readOnly: computedReadOnly, disabled: computedDisabled, required: computedRequired },
|
|
467
|
-
value: formState?.values[component.id],
|
|
468
|
-
onChange: (value) => {
|
|
469
|
-
if (component.type !== 'dataGrid' && component.type !== 'signature' && component.type !== 'richTextEditor') {
|
|
470
|
-
value = formatDateValues(component, value);
|
|
471
|
-
formState?.onChange(component.id, { value });
|
|
472
|
-
if (onChange) {
|
|
473
|
-
onChange({ field: component.label || component.id, value });
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
},
|
|
477
|
-
locale: formLocale,
|
|
478
|
-
onBlur: () => { if (component.type !== 'dataGrid' && component.type !== 'richTextEditor')
|
|
479
|
-
formState?.onBlur(component.id); },
|
|
480
|
-
ref: setInputRef,
|
|
481
|
-
hasError,
|
|
482
|
-
}), hasError && _jsx("div", { className: "e-error", children: formState.errors[component.id] })] }), component.description && _jsx("div", { className: 'field-description', children: component.description })] }) }) }, component.id));
|
|
483
|
-
};
|
|
484
|
-
/**
|
|
485
|
-
* Render component (including layouts)
|
|
486
|
-
*/
|
|
487
|
-
const renderComponent = (component) => {
|
|
488
|
-
const formValues = formState?.values || {};
|
|
489
|
-
const isVisible = component.visible === false
|
|
490
|
-
? false
|
|
491
|
-
: ConditionalRuleEngine.isFieldVisible(component.conditions, formValues);
|
|
492
|
-
const isHidden = ConditionalRuleEngine.isFieldHidden(component.conditions, formValues);
|
|
493
|
-
if (!isVisible || isHidden) {
|
|
494
|
-
if (formState && formState.values[component.id] !== undefined && formState.values[component.id] !== null) {
|
|
495
|
-
formState.onChange(component.id, { value: null });
|
|
496
|
-
}
|
|
497
|
-
return null;
|
|
498
|
-
}
|
|
499
|
-
switch (component.type) {
|
|
500
|
-
case 'panel':
|
|
501
|
-
return (_jsxs("fieldset", { className: `form-panel-renderer ${component.cssClass}`, children: [(component.label && !component.hideLabel) && _jsx("legend", { className: `${!component.hideBorders ? 'panel-legend' : ''}`, children: component.label }), _jsx("div", { className: "panel-content", children: component.children?.map(child => renderComponent(child)) })] }, component.id));
|
|
502
|
-
case 'table':
|
|
503
|
-
const tableRows = component.rows || (component.tableCells ? component.tableCells.length : 2);
|
|
504
|
-
const tableCols = component.columns || (component.tableCells && component.tableCells[0] ? component.tableCells[0].length : 2);
|
|
505
|
-
const defaultColSpan = Math.floor(12 / tableCols);
|
|
506
|
-
const columnWidthsProp = component.columnWidths;
|
|
507
|
-
const splittedWidths = columnWidthsProp ? splitWidths(columnWidthsProp.widths) : [];
|
|
508
|
-
return (_jsx("div", { className: `form-table-renderer ${component.hideBorders ? '' : 'table-border'} ${component.cssClass}`, children: Array.from({ length: tableRows }).map((_, rowIndex) => (_jsx("div", { className: "e-form-table-row", style: component.hideBorders ? { display: 'flex', gap: '1.5rem' } : {}, children: Array.from({ length: tableCols }).map((_, colIndex) => {
|
|
509
|
-
const cellComponents = component.tableCells?.[rowIndex]?.[colIndex] || [];
|
|
510
|
-
const cellSpan = defaultColSpan;
|
|
511
|
-
const colWidthValue = splittedWidths[colIndex];
|
|
512
|
-
const isSingleColumn = tableCols === 1;
|
|
513
|
-
const cellStyle = isSingleColumn ? { width: '100%' } : (colWidthValue ? { width: `${colWidthValue}%` } : {});
|
|
514
|
-
const allNullWidths = !columnWidthsProp || (Array.isArray(columnWidthsProp.widths) && columnWidthsProp.widths.every((w) => w === null));
|
|
515
|
-
const isFirstRow = rowIndex === 0;
|
|
516
|
-
return (_jsx("div", { className: `e-form-table-cell ${colWidthValue ? '' : allNullWidths ? `col-${cellSpan}` : ''}`, style: component.hideBorders ? { ...cellStyle, padding: isFirstRow ? '0' : '12px 0 0 0' } : cellStyle, children: cellComponents.map(comp => renderComponent(comp)) }, `cell-${rowIndex}-${colIndex}`));
|
|
517
|
-
}) }, `row-${rowIndex}`))) }, component.id));
|
|
518
|
-
case 'tabs':
|
|
519
|
-
return (_jsx("div", { className: `form-tabs-renderer ${component.cssClass || ''}`, children: _jsx(CustomTabComponent, { tabs: component.tabItems?.map(tab => ({
|
|
520
|
-
header: tab.header,
|
|
521
|
-
content: (_jsx("div", { className: "tab-content-renderer", children: tab.content?.map(comp => renderComponent(comp)) }))
|
|
522
|
-
})) || [], className: component.cssClass }) }, component.id));
|
|
523
|
-
case 'card':
|
|
524
|
-
return (_jsxs("fieldset", { className: `e-card form-card-renderer ${component.cssClass}`, style: { padding: 0 }, children: [_jsx("div", { className: "e-card-header", children: _jsxs("div", { className: "e-card-header-caption", children: [_jsx("div", { className: "e-card-title", children: component.cardTitle || component.label || 'Card Title' }), component.cardSubtitle && (_jsx("div", { className: "e-card-sub-title", children: component.cardSubtitle }))] }) }), _jsx("div", { className: "e-card-content card-content", children: component.children?.map(child => renderComponent(child)) })] }, component.id));
|
|
525
|
-
case 'staticHtml':
|
|
526
|
-
return (_jsx(StaticHtmlRenderer, { id: component.id, content: component.defaultValue || '<p>HTML content</p>', enableHtmlSanitizer: component.enableHtmlSanitizer }, component.id));
|
|
527
|
-
case 'message':
|
|
528
|
-
return (_jsx("div", { className: "form-group", children: renderFormField({
|
|
529
|
-
component,
|
|
530
|
-
locale: formLocale,
|
|
531
|
-
value: undefined,
|
|
532
|
-
onChange: () => { },
|
|
533
|
-
}) }, component.id));
|
|
534
|
-
case 'button':
|
|
535
|
-
const buttonlDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
536
|
-
const buttonComputed = component.disabled || buttonlDisabled;
|
|
537
|
-
const handleButtonClick = (event) => {
|
|
538
|
-
if (onButtonClick) {
|
|
539
|
-
onButtonClick({
|
|
540
|
-
buttonId: component.id,
|
|
541
|
-
buttonLabel: component.buttonsGroups ? event.target?.ej2_instances[0].content : component.label || '', event
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
return (_jsx("div", { className: "form-group", children: renderFormField({
|
|
546
|
-
component: { ...component, disabled: buttonComputed },
|
|
547
|
-
value: undefined,
|
|
548
|
-
onChange: () => { },
|
|
549
|
-
locale: formLocale,
|
|
550
|
-
onClick: (e) => { handleButtonClick(e); },
|
|
551
|
-
ref: (el, idx) => {
|
|
552
|
-
if (idx !== undefined) {
|
|
553
|
-
inputRefs.current[`${component.id}_${idx}`] = el;
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
inputRefs.current[component.id] = el;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}) }, component.id));
|
|
560
|
-
case 'splitButton':
|
|
561
|
-
const splitButtonDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
562
|
-
const computedDisabled = component.disabled || splitButtonDisabled;
|
|
563
|
-
setInputRef = (el) => {
|
|
564
|
-
if (el) {
|
|
565
|
-
inputRefs.current[component.id] = el;
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
const handleSplitButtonEvent = (event) => {
|
|
569
|
-
if (onButtonClick) {
|
|
570
|
-
onButtonClick({
|
|
571
|
-
buttonId: component.id,
|
|
572
|
-
buttonLabel: component.label || '',
|
|
573
|
-
event
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
|
-
return (_jsx("div", { className: "form-group", children: renderFormField({
|
|
578
|
-
component: { ...component, disabled: computedDisabled },
|
|
579
|
-
value: undefined,
|
|
580
|
-
locale: formLocale,
|
|
581
|
-
onChange: () => { },
|
|
582
|
-
onClick: (e) => { handleSplitButtonEvent(e); },
|
|
583
|
-
ref: setInputRef
|
|
584
|
-
}) }, component.id));
|
|
585
|
-
default:
|
|
586
|
-
return renderField(component);
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
// Notify when all form elements and refs are rendered
|
|
590
|
-
useEffect(() => {
|
|
591
|
-
if (created && formState) {
|
|
592
|
-
setTimeout(() => {
|
|
593
|
-
const hasInputs = Object.keys(inputRefs.current).length > 0;
|
|
594
|
-
if (hasInputs) {
|
|
595
|
-
created();
|
|
596
|
-
}
|
|
597
|
-
}, 0);
|
|
598
|
-
}
|
|
599
|
-
}, [created, formState]);
|
|
600
|
-
// Expose getComponent as a public method
|
|
601
|
-
React.useImperativeHandle(ref, () => ({
|
|
602
|
-
/**
|
|
603
|
-
* Get the instance of the form input by name/id
|
|
604
|
-
*/
|
|
605
|
-
getComponent: (name) => {
|
|
606
|
-
return inputRefs.current[name] || null;
|
|
607
|
-
},
|
|
608
|
-
}), []);
|
|
609
|
-
return (_jsx("div", { ...restProps, className: `form-renderer-container ${className}`, children: _jsx(Form, { rules: validationRules, initialValues: initialValues, onFormStateChange: (state) => setFormState(state), onSubmit: (data) => {
|
|
610
|
-
const isValid = (formState?.errors ? Object.keys(formState.errors).length === 0 : false);
|
|
611
|
-
const transformedData = {};
|
|
612
|
-
const formValues = formState?.values || {};
|
|
613
|
-
// Exclude non-data components (buttons, messages, and layout-only components)
|
|
614
|
-
const excludedTypes = new Set(['button', 'message', 'panel', 'table', 'tabs']);
|
|
615
|
-
const allComps = collectAllComponents(processedSchema.components || []);
|
|
616
|
-
const excludedIds = new Set(allComps.filter(c => excludedTypes.has(c.type)).map(c => c.id));
|
|
617
|
-
const hiddenFieldIds = new Set(allComps.filter(c => {
|
|
618
|
-
if (c.visible === false)
|
|
619
|
-
return true;
|
|
620
|
-
if (c.conditions?.visibleWhen) {
|
|
621
|
-
if (!ConditionalRuleEngine.isFieldVisible(c.conditions, formValues))
|
|
622
|
-
return true;
|
|
623
|
-
}
|
|
624
|
-
if (c.conditions?.hideWhen) {
|
|
625
|
-
if (ConditionalRuleEngine.isFieldHidden(c.conditions, formValues))
|
|
626
|
-
return true;
|
|
627
|
-
}
|
|
628
|
-
return false;
|
|
629
|
-
}).map(c => c.id));
|
|
630
|
-
const disabledFieldIds = new Set(allComps.filter(c => {
|
|
631
|
-
if (c.disabled)
|
|
632
|
-
return true;
|
|
633
|
-
if (c.conditions?.disabledWhen) {
|
|
634
|
-
return ConditionalRuleEngine.isFieldDisabled(c.conditions, formValues);
|
|
635
|
-
}
|
|
636
|
-
return false;
|
|
637
|
-
}).map(c => c.id));
|
|
638
|
-
Object.entries(data).forEach(([id, val]) => {
|
|
639
|
-
if (excludedIds.has(id) || hiddenFieldIds.has(id) || disabledFieldIds.has(id))
|
|
640
|
-
return;
|
|
641
|
-
const label = idToLabel[id] || id;
|
|
642
|
-
transformedData[label] = val;
|
|
643
|
-
});
|
|
644
|
-
if (onSubmit) {
|
|
645
|
-
onSubmit({ data: transformedData, isValid });
|
|
646
|
-
}
|
|
647
|
-
}, validateOnChange: true, className: `form-renderer ${layoutClass}`, children: processedSchema.components.map(component => renderComponent(component)) }) }));
|
|
648
|
-
});
|
|
649
|
-
export default FormRenderer;
|