@syncfusion/ej2-react-form-renderer 0.1.1-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 +2 -2
- package/src/CheckboxGroup.js +2 -1
- package/src/ConditionalRuleEngine.d.ts +43 -1
- package/src/ConditionalRuleEngine.js +77 -0
- package/src/DataGridComponent.js +8 -5
- package/src/FormRenderer.d.ts +1 -7
- package/src/FormRenderer.js +236 -178
- 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 +22 -31
- package/src/json-converter.js +141 -261
- package/src/parser.js +4 -1
- package/src/renderFormField.d.ts +2 -1
- package/src/renderFormField.js +37 -14
- package/src/types/FormSchema.d.ts +7 -1
- package/src/utils.js +262 -0
- package/styles/form-renderer.base.css +10 -0
package/src/json-converter.js
CHANGED
|
@@ -72,7 +72,7 @@ export function makeUniqueFieldKey(baseKey, existingKeys, originalId) {
|
|
|
72
72
|
// ============================================================================
|
|
73
73
|
// UI TYPE TO SEMANTIC TYPE MAPPING
|
|
74
74
|
// ============================================================================
|
|
75
|
-
const UI_TYPE_TO_SEMANTIC_TYPE = {
|
|
75
|
+
export const UI_TYPE_TO_SEMANTIC_TYPE = {
|
|
76
76
|
// String types
|
|
77
77
|
'textbox': { type: 'string' },
|
|
78
78
|
'textarea': { type: 'string' },
|
|
@@ -264,6 +264,34 @@ export function splitSchema(heartSchema) {
|
|
|
264
264
|
hasValidConditions = true;
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
+
if (component.conditions?.choiceBasedField) {
|
|
268
|
+
const rule = component.conditions.choiceBasedField;
|
|
269
|
+
// Validate that primary field exists
|
|
270
|
+
if (rule.primaryFieldId && validFieldIds.has(rule.primaryFieldId)) {
|
|
271
|
+
cleanedConditions.choiceBasedField = rule;
|
|
272
|
+
hasValidConditions = true;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (component.conditions.conditionalData && Array.isArray(component.conditions.conditionalData)) {
|
|
276
|
+
const cleanedDataRules = component.conditions.conditionalData
|
|
277
|
+
.map((rule) => {
|
|
278
|
+
const cleaned = cleanConditionRules(rule.condition, validFieldIds);
|
|
279
|
+
if (cleaned && rule.dataSource) {
|
|
280
|
+
return {
|
|
281
|
+
condition: cleaned,
|
|
282
|
+
dataSource: rule.dataSource,
|
|
283
|
+
columns: rule.columns,
|
|
284
|
+
includeColumns: rule.includeColumns
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
})
|
|
289
|
+
.filter((rule) => rule !== null && rule !== undefined);
|
|
290
|
+
if (cleanedDataRules.length > 0) {
|
|
291
|
+
cleanedConditions.conditionalData = cleanedDataRules;
|
|
292
|
+
hasValidConditions = true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
267
295
|
// Update component conditions (or remove if empty)
|
|
268
296
|
component.conditions = hasValidConditions ? cleanedConditions : undefined;
|
|
269
297
|
}
|
|
@@ -386,15 +414,23 @@ export function splitSchema(heartSchema) {
|
|
|
386
414
|
// Add property reference to layout (use fieldKey instead of component ID)
|
|
387
415
|
layoutParent.push({ type: 'field', propertyId: fieldKey });
|
|
388
416
|
}
|
|
417
|
+
// ✅ Merge data and UI fields into unified properties
|
|
418
|
+
const unifiedProperties = {};
|
|
419
|
+
Object.keys(dataFields).forEach(fieldKey => {
|
|
420
|
+
const dataField = dataFields[fieldKey];
|
|
421
|
+
const uiField = uiFields[fieldKey];
|
|
422
|
+
unifiedProperties[fieldKey] = {
|
|
423
|
+
...dataField,
|
|
424
|
+
...uiField,
|
|
425
|
+
// Ensure widget is always set
|
|
426
|
+
widget: uiField?.widget || inferWidgetFromType(dataField.type, dataField.format, dataField.options !== undefined)
|
|
427
|
+
};
|
|
428
|
+
});
|
|
389
429
|
return {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
uiSchema: {
|
|
395
|
-
properties: stripConfiguredFlag(uiFields),
|
|
396
|
-
layout
|
|
397
|
-
}
|
|
430
|
+
version: heartSchema.version,
|
|
431
|
+
properties: stripConfiguredFlag(unifiedProperties),
|
|
432
|
+
layout,
|
|
433
|
+
...(heartSchema.settings ? { settings: heartSchema.settings } : {})
|
|
398
434
|
};
|
|
399
435
|
}
|
|
400
436
|
function extractDataField(component, semanticType) {
|
|
@@ -412,6 +448,8 @@ function extractDataField(component, semanticType) {
|
|
|
412
448
|
dataField.label = component.label;
|
|
413
449
|
if (component.defaultValue !== undefined)
|
|
414
450
|
dataField.defaultValue = component.defaultValue;
|
|
451
|
+
if (component.expressionValue !== undefined)
|
|
452
|
+
dataField.expressionValue = component.expressionValue;
|
|
415
453
|
if (component.content !== undefined)
|
|
416
454
|
dataField.content = component.content;
|
|
417
455
|
if (component.buttonType !== undefined)
|
|
@@ -458,7 +496,7 @@ function extractUIField(component) {
|
|
|
458
496
|
};
|
|
459
497
|
// Copy all properties EXCEPT those in data schema allowed list
|
|
460
498
|
const dataSchemaProps = new Set([
|
|
461
|
-
'id', 'type', 'label', 'format', 'defaultValue', 'required', 'options', 'name',
|
|
499
|
+
'id', 'type', 'label', 'format', 'defaultValue', 'required', 'options', 'name', 'expressionValue',
|
|
462
500
|
'minLength', 'maxLength', 'minimum', 'maximum', 'minValue', 'maxValue',
|
|
463
501
|
'minDate', 'maxDate', 'minTime', 'maxTime', 'regex', 'pattern', 'content', 'buttonType', 'textboxType'
|
|
464
502
|
]);
|
|
@@ -494,30 +532,15 @@ function extractUIField(component) {
|
|
|
494
532
|
// MERGE ALGORITHM
|
|
495
533
|
// ============================================================================
|
|
496
534
|
/**
|
|
497
|
-
* Merge
|
|
498
|
-
*
|
|
535
|
+
* Merge unified schema back into heart schema
|
|
536
|
+
* Reconstructs FormComponent array from unified properties and layout
|
|
499
537
|
*/
|
|
500
|
-
export function mergeSchema(
|
|
501
|
-
// Create lookup map for O(1) access to
|
|
502
|
-
const
|
|
503
|
-
Object.entries(
|
|
504
|
-
|
|
505
|
-
});
|
|
506
|
-
// Use existing UI schema if provided (for re-import), otherwise use split UI schema
|
|
507
|
-
let uiSchema = existingUISchema || split.uiSchema;
|
|
508
|
-
// ✅ Ensure all UI properties have _configured flag (add if missing from imported JSON)
|
|
509
|
-
const enhancedUIProperties = {};
|
|
510
|
-
Object.entries(uiSchema.properties).forEach(([key, uiField]) => {
|
|
511
|
-
enhancedUIProperties[key] = {
|
|
512
|
-
...uiField,
|
|
513
|
-
_configured: uiField._configured !== undefined ? uiField._configured : true
|
|
514
|
-
};
|
|
538
|
+
export function mergeSchema(unified) {
|
|
539
|
+
// Create lookup map for O(1) access to unified properties
|
|
540
|
+
const propertyMap = new Map();
|
|
541
|
+
Object.entries(unified.properties).forEach(([id, field]) => {
|
|
542
|
+
propertyMap.set(id, field);
|
|
515
543
|
});
|
|
516
|
-
// Use enhanced UI schema with _configured flags
|
|
517
|
-
uiSchema = {
|
|
518
|
-
...uiSchema,
|
|
519
|
-
properties: enhancedUIProperties
|
|
520
|
-
};
|
|
521
544
|
// Track processed field IDs to prevent duplicates
|
|
522
545
|
const processedFieldIds = new Set();
|
|
523
546
|
// Recursive function to process layout nodes
|
|
@@ -528,25 +551,24 @@ export function mergeSchema(split, existingUISchema) {
|
|
|
528
551
|
const fieldNode = node;
|
|
529
552
|
// ✅ DEDUPLICATION: Skip if we've already processed this field
|
|
530
553
|
if (processedFieldIds.has(fieldNode.propertyId)) {
|
|
531
|
-
//console.log(`⚠️ [mergeSchema] Skipping duplicate field: ${fieldNode.propertyId}`);
|
|
532
554
|
continue;
|
|
533
555
|
}
|
|
534
556
|
processedFieldIds.add(fieldNode.propertyId);
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const fallbackDataField = {
|
|
557
|
+
const unifiedField = propertyMap.get(fieldNode.propertyId);
|
|
558
|
+
if (!unifiedField) {
|
|
559
|
+
console.warn(`Field ${fieldNode.propertyId} in layout not found in unified schema`);
|
|
560
|
+
// Generate fallback component
|
|
561
|
+
const fallbackComponent = {
|
|
541
562
|
id: fieldNode.propertyId,
|
|
542
|
-
name: generateName(
|
|
543
|
-
type: '
|
|
544
|
-
label:
|
|
563
|
+
name: generateName(''),
|
|
564
|
+
type: 'textbox',
|
|
565
|
+
label: 'Unknown Field',
|
|
566
|
+
_configured: true
|
|
545
567
|
};
|
|
546
|
-
components.push(
|
|
568
|
+
components.push(fallbackComponent);
|
|
547
569
|
}
|
|
548
570
|
else {
|
|
549
|
-
components.push(
|
|
571
|
+
components.push(mergeUnifiedField(unifiedField));
|
|
550
572
|
}
|
|
551
573
|
}
|
|
552
574
|
else if (node.type === 'table') {
|
|
@@ -569,7 +591,6 @@ export function mergeSchema(split, existingUISchema) {
|
|
|
569
591
|
tableNode.cells.forEach((row) => {
|
|
570
592
|
const cellRow = [];
|
|
571
593
|
row.forEach((cell) => {
|
|
572
|
-
// Recursively process children in each cell
|
|
573
594
|
const cellComponents = processLayoutNodes(cell.children);
|
|
574
595
|
cellRow.push(cellComponents);
|
|
575
596
|
});
|
|
@@ -610,7 +631,7 @@ export function mergeSchema(split, existingUISchema) {
|
|
|
610
631
|
tabsNode.tabs.forEach((tab) => {
|
|
611
632
|
tabsComponent.tabItems.push({
|
|
612
633
|
header: tab.header,
|
|
613
|
-
content: processLayoutNodes(tab.children)
|
|
634
|
+
content: processLayoutNodes(tab.children)
|
|
614
635
|
});
|
|
615
636
|
});
|
|
616
637
|
components.push(tabsComponent);
|
|
@@ -646,73 +667,76 @@ export function mergeSchema(split, existingUISchema) {
|
|
|
646
667
|
};
|
|
647
668
|
// Process root layout nodes
|
|
648
669
|
let components;
|
|
649
|
-
if (!
|
|
650
|
-
// If layout is undefined or empty, generate a flat layout from
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const uiField = uiSchema.properties[fieldKey];
|
|
654
|
-
return mergeField(dataField, uiField);
|
|
670
|
+
if (!unified.layout || unified.layout.length === 0) {
|
|
671
|
+
// If layout is undefined or empty, generate a flat layout from properties
|
|
672
|
+
components = Object.entries(unified.properties).map(([, unifiedField]) => {
|
|
673
|
+
return mergeUnifiedField(unifiedField);
|
|
655
674
|
});
|
|
656
675
|
}
|
|
657
676
|
else {
|
|
658
|
-
components = processLayoutNodes(
|
|
677
|
+
components = processLayoutNodes(unified.layout);
|
|
659
678
|
}
|
|
660
679
|
return {
|
|
661
|
-
version:
|
|
662
|
-
components
|
|
680
|
+
version: unified.version,
|
|
681
|
+
components,
|
|
682
|
+
...(unified.settings ? { settings: unified.settings } : {})
|
|
663
683
|
};
|
|
664
684
|
}
|
|
665
685
|
/**
|
|
666
|
-
* Merge a single field
|
|
686
|
+
* Merge a single unified field into FormComponent
|
|
667
687
|
*/
|
|
668
|
-
function
|
|
669
|
-
|
|
670
|
-
const hasOptions = dataField.options !== undefined && dataField.options.length > 0;
|
|
688
|
+
function mergeUnifiedField(unifiedField) {
|
|
689
|
+
const hasOptions = unifiedField.options !== undefined && unifiedField.options.length > 0;
|
|
671
690
|
const component = {
|
|
672
|
-
id:
|
|
673
|
-
name:
|
|
674
|
-
type: (
|
|
675
|
-
label:
|
|
691
|
+
id: unifiedField.id,
|
|
692
|
+
name: unifiedField.name,
|
|
693
|
+
type: (unifiedField.widget || inferWidgetFromType(unifiedField.type, unifiedField.format, hasOptions)),
|
|
694
|
+
label: unifiedField.label || unifiedField.id,
|
|
676
695
|
};
|
|
677
|
-
// Copy data properties
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
if (
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
696
|
+
// Copy all data and UI properties from unified field
|
|
697
|
+
const dataSchemaProps = new Set([
|
|
698
|
+
'id', 'type', 'name', 'label', 'format', 'widget', 'defaultValue', 'required', 'options', 'expressionValue',
|
|
699
|
+
'minLength', 'maxLength', 'minimum', 'maximum', 'minValue', 'maxValue',
|
|
700
|
+
'minDate', 'maxDate', 'minTime', 'maxTime', 'regex', 'pattern', 'content', 'buttonType', 'textboxType'
|
|
701
|
+
]);
|
|
702
|
+
Object.keys(unifiedField).forEach(key => {
|
|
703
|
+
if (!dataSchemaProps.has(key) && key !== 'id' && key !== 'name' && key !== 'type' && key !== 'label') {
|
|
704
|
+
component[key] = unifiedField[key];
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
// Copy specific data properties
|
|
708
|
+
if (unifiedField.defaultValue !== undefined)
|
|
709
|
+
component.defaultValue = unifiedField.defaultValue;
|
|
710
|
+
if (unifiedField.expressionValue !== undefined)
|
|
711
|
+
component.expressionValue = unifiedField.expressionValue;
|
|
712
|
+
if (unifiedField.required !== undefined)
|
|
713
|
+
component.required = unifiedField.required;
|
|
714
|
+
if (unifiedField.content !== undefined)
|
|
715
|
+
component.content = unifiedField.content;
|
|
716
|
+
if (unifiedField.buttonType !== undefined)
|
|
717
|
+
component.buttonType = unifiedField.buttonType;
|
|
718
|
+
if (unifiedField.textboxType !== undefined)
|
|
719
|
+
component.textboxType = unifiedField.textboxType;
|
|
720
|
+
if (unifiedField.options !== undefined)
|
|
721
|
+
component.options = unifiedField.options;
|
|
722
|
+
if (unifiedField.minLength !== undefined)
|
|
723
|
+
component.minLength = unifiedField.minLength;
|
|
724
|
+
if (unifiedField.maxLength !== undefined)
|
|
725
|
+
component.maxLength = unifiedField.maxLength;
|
|
726
|
+
if (unifiedField.minValue !== undefined)
|
|
727
|
+
component.minValue = unifiedField.minValue;
|
|
728
|
+
if (unifiedField.maxValue !== undefined)
|
|
729
|
+
component.maxValue = unifiedField.maxValue;
|
|
730
|
+
if (unifiedField.minDate !== undefined)
|
|
731
|
+
component.minDate = unifiedField.minDate;
|
|
732
|
+
if (unifiedField.maxDate !== undefined)
|
|
733
|
+
component.maxDate = unifiedField.maxDate;
|
|
734
|
+
if (unifiedField.minTime !== undefined)
|
|
735
|
+
component.minTime = unifiedField.minTime;
|
|
736
|
+
if (unifiedField.maxTime !== undefined)
|
|
737
|
+
component.maxTime = unifiedField.maxTime;
|
|
738
|
+
if (unifiedField.pattern !== undefined)
|
|
739
|
+
component.regex = unifiedField.pattern;
|
|
716
740
|
return component;
|
|
717
741
|
}
|
|
718
742
|
/**
|
|
@@ -988,21 +1012,23 @@ export function mergeWithIDRemapping(newDataSchema, existingUISchema) {
|
|
|
988
1012
|
//console.log(`📐 Layout rebuilt: new fields added at end`);
|
|
989
1013
|
}
|
|
990
1014
|
}
|
|
991
|
-
// Merge
|
|
1015
|
+
// Merge to FormSchema using unified schema
|
|
1016
|
+
const unifiedProperties = {};
|
|
1017
|
+
Object.keys(remappedDataFields).forEach(fieldKey => {
|
|
1018
|
+
const dataField = remappedDataFields[fieldKey];
|
|
1019
|
+
const uiField = enhancedUIProperties[fieldKey];
|
|
1020
|
+
unifiedProperties[fieldKey] = {
|
|
1021
|
+
...dataField,
|
|
1022
|
+
...uiField,
|
|
1023
|
+
widget: uiField?.widget || inferWidgetFromType(dataField.type, dataField.format, dataField.options !== undefined)
|
|
1024
|
+
};
|
|
1025
|
+
});
|
|
992
1026
|
const mergedSchema = mergeSchema({
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
}
|
|
997
|
-
uiSchema: {
|
|
998
|
-
properties: enhancedUIProperties,
|
|
999
|
-
layout: remappedLayout
|
|
1000
|
-
}
|
|
1027
|
+
version: newDataSchema.version,
|
|
1028
|
+
properties: unifiedProperties,
|
|
1029
|
+
layout: remappedLayout,
|
|
1030
|
+
...(newDataSchema.settings ? { settings: newDataSchema.settings } : {})
|
|
1001
1031
|
});
|
|
1002
|
-
// Log summary
|
|
1003
|
-
//const remappedCount = matchResults.filter(m => m.newDataId !== m.existingId).length;
|
|
1004
|
-
//const newFieldsCount = matchResults.filter(m => m.method === 'new').length;
|
|
1005
|
-
//console.log(`Merge complete: ${matchResults.length} fields, ${remappedCount} remapped, ${newFieldsCount} new`);
|
|
1006
1032
|
return mergedSchema;
|
|
1007
1033
|
}
|
|
1008
1034
|
/**
|
|
@@ -1055,9 +1081,6 @@ function remapLayoutIds(layout, idMap) {
|
|
|
1055
1081
|
return node;
|
|
1056
1082
|
});
|
|
1057
1083
|
}
|
|
1058
|
-
// ============================================================================
|
|
1059
|
-
// UTILITY FUNCTIONS
|
|
1060
|
-
// ============================================================================
|
|
1061
1084
|
/**
|
|
1062
1085
|
* Extract all field IDs from layout tree
|
|
1063
1086
|
*/
|
|
@@ -1084,14 +1107,6 @@ export function extractFieldIdsFromLayout(layout) {
|
|
|
1084
1107
|
}
|
|
1085
1108
|
return fieldIds;
|
|
1086
1109
|
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Find unmatched fields (in data schema but not in layout)
|
|
1089
|
-
*/
|
|
1090
|
-
export function findUnmatchedFields(dataSchema, layout) {
|
|
1091
|
-
const layoutFieldIds = extractFieldIdsFromLayout(layout);
|
|
1092
|
-
const dataFieldIds = Object.keys(dataSchema.properties);
|
|
1093
|
-
return dataFieldIds.filter(id => !layoutFieldIds.has(id));
|
|
1094
|
-
}
|
|
1095
1110
|
// ============================================================================
|
|
1096
1111
|
// PARSER OUTPUT CONVERSION
|
|
1097
1112
|
// ============================================================================
|
|
@@ -1192,138 +1207,3 @@ export function convertComponentsToDataSchema(components) {
|
|
|
1192
1207
|
properties
|
|
1193
1208
|
};
|
|
1194
1209
|
}
|
|
1195
|
-
/**
|
|
1196
|
-
* Convert parser output to a simple linear layout
|
|
1197
|
-
* Used when no existing UI schema is present
|
|
1198
|
-
*/
|
|
1199
|
-
export function convertComponentsToUISchema(components) {
|
|
1200
|
-
const properties = {};
|
|
1201
|
-
const layout = [];
|
|
1202
|
-
// Helper to recursively process components and build layout
|
|
1203
|
-
function processComponent(comp) {
|
|
1204
|
-
// Extract UI field
|
|
1205
|
-
const uiField = extractUIField(comp);
|
|
1206
|
-
properties[comp.id] = uiField;
|
|
1207
|
-
// Handle different component types
|
|
1208
|
-
if (comp.type === 'table' && comp.tableCells) {
|
|
1209
|
-
const tableCells = comp.tableCells.map((row, rowIndex) => row.map((cellComponents, colIndex) => ({
|
|
1210
|
-
row: rowIndex,
|
|
1211
|
-
col: colIndex,
|
|
1212
|
-
// cellComponents is FormComponent[], so map over it directly
|
|
1213
|
-
children: cellComponents.map(processComponent)
|
|
1214
|
-
})));
|
|
1215
|
-
return {
|
|
1216
|
-
type: 'table',
|
|
1217
|
-
id: comp.id,
|
|
1218
|
-
name: comp.name,
|
|
1219
|
-
label: comp.label,
|
|
1220
|
-
cssClass: comp.cssClass,
|
|
1221
|
-
hideBorders: comp.hideBorders,
|
|
1222
|
-
rows: comp.tableCells.length,
|
|
1223
|
-
cols: comp.tableCells[0]?.length || 0,
|
|
1224
|
-
cells: tableCells,
|
|
1225
|
-
columnWidths: comp.columnWidths
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
if (comp.type === 'panel' && comp.children) {
|
|
1229
|
-
return {
|
|
1230
|
-
type: 'panel',
|
|
1231
|
-
id: comp.id,
|
|
1232
|
-
name: comp.name,
|
|
1233
|
-
label: comp.label,
|
|
1234
|
-
cssClass: comp.cssClass,
|
|
1235
|
-
hideBorders: comp.hideBorders,
|
|
1236
|
-
hideLabel: comp.hideLabel,
|
|
1237
|
-
children: comp.children.map(processComponent)
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
if (comp.type === 'tabs' && comp.tabItems) {
|
|
1241
|
-
// tabs is an array of { header, children }
|
|
1242
|
-
const tabs = comp.tabItems.map((tab) => ({
|
|
1243
|
-
header: tab.header,
|
|
1244
|
-
children: tab.content?.map(processComponent) || []
|
|
1245
|
-
}));
|
|
1246
|
-
return {
|
|
1247
|
-
type: 'tabs',
|
|
1248
|
-
id: comp.id,
|
|
1249
|
-
name: comp.name,
|
|
1250
|
-
cssClass: comp.cssClass,
|
|
1251
|
-
tabs
|
|
1252
|
-
};
|
|
1253
|
-
}
|
|
1254
|
-
if (comp.type === 'card' && comp.children) {
|
|
1255
|
-
return {
|
|
1256
|
-
type: 'card',
|
|
1257
|
-
id: comp.id,
|
|
1258
|
-
name: comp.name,
|
|
1259
|
-
label: comp.label,
|
|
1260
|
-
cssClass: comp.cssClass,
|
|
1261
|
-
hideLabel: comp.hideLabel,
|
|
1262
|
-
cardTitle: comp.cardTitle,
|
|
1263
|
-
cardSubtitle: comp.cardSubtitle,
|
|
1264
|
-
enableAction: comp.enableAction,
|
|
1265
|
-
actionButtonLabel: comp.actionButtonLabel,
|
|
1266
|
-
actionButtonType: comp.actionButtonType,
|
|
1267
|
-
children: comp.children.map(processComponent)
|
|
1268
|
-
};
|
|
1269
|
-
}
|
|
1270
|
-
// Regular field
|
|
1271
|
-
return {
|
|
1272
|
-
type: 'field',
|
|
1273
|
-
propertyId: comp.id
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
// Process all root components
|
|
1277
|
-
for (const comp of components) {
|
|
1278
|
-
layout.push(processComponent(comp));
|
|
1279
|
-
}
|
|
1280
|
-
return {
|
|
1281
|
-
properties,
|
|
1282
|
-
layout
|
|
1283
|
-
};
|
|
1284
|
-
}
|
|
1285
|
-
// ============================================================================
|
|
1286
|
-
// RENDERER HELPER FUNCTIONS
|
|
1287
|
-
// ============================================================================
|
|
1288
|
-
/**
|
|
1289
|
-
* Generate default UI properties for each data field
|
|
1290
|
-
* Used by renderers when only data schema is provided
|
|
1291
|
-
*/
|
|
1292
|
-
export function generateUIPropertiesFromData(dataSchema) {
|
|
1293
|
-
const properties = {};
|
|
1294
|
-
Object.entries(dataSchema.properties).forEach(([fieldId, dataField]) => {
|
|
1295
|
-
const hasOptions = dataField.options !== undefined && dataField.options.length > 0;
|
|
1296
|
-
const widget = inferWidgetFromType(dataField.type, dataField.format, hasOptions);
|
|
1297
|
-
properties[fieldId] = { widget };
|
|
1298
|
-
});
|
|
1299
|
-
return properties;
|
|
1300
|
-
}
|
|
1301
|
-
/**
|
|
1302
|
-
* Generate sequential layout from data schema
|
|
1303
|
-
* Used by renderers when only data schema is provided
|
|
1304
|
-
*/
|
|
1305
|
-
export function generateLayoutFromData(dataSchema) {
|
|
1306
|
-
return Object.keys(dataSchema.properties).map(fieldId => ({
|
|
1307
|
-
type: 'field',
|
|
1308
|
-
propertyId: fieldId
|
|
1309
|
-
}));
|
|
1310
|
-
}
|
|
1311
|
-
/**
|
|
1312
|
-
* Generate minimal data schema from UI schema
|
|
1313
|
-
* Used by renderers when only UI schema is provided
|
|
1314
|
-
*/
|
|
1315
|
-
export function generateDataSchemaFromUI(uiSchema) {
|
|
1316
|
-
const dataProperties = {};
|
|
1317
|
-
Object.entries(uiSchema.properties).forEach(([fieldId, uiField]) => {
|
|
1318
|
-
dataProperties[fieldId] = {
|
|
1319
|
-
id: fieldId,
|
|
1320
|
-
type: 'string',
|
|
1321
|
-
name: generateName(uiField.label),
|
|
1322
|
-
label: uiField.label || fieldId
|
|
1323
|
-
};
|
|
1324
|
-
});
|
|
1325
|
-
return {
|
|
1326
|
-
version: '0.1.0',
|
|
1327
|
-
properties: dataProperties
|
|
1328
|
-
};
|
|
1329
|
-
}
|
package/src/parser.js
CHANGED
|
@@ -394,6 +394,7 @@ export class UniversalJsonParser {
|
|
|
394
394
|
type: type,
|
|
395
395
|
label: element.title || element.label || this.humanizeFieldName(element.name),
|
|
396
396
|
defaultValue: element.defaultValue,
|
|
397
|
+
expressionValue: element.expressionValue,
|
|
397
398
|
...Object.keys(validation).length > 0 ? validation : undefined
|
|
398
399
|
};
|
|
399
400
|
if (type === 'dropdown' || type === 'multiselect' || type === 'radio' || type === 'splitButton') {
|
|
@@ -437,7 +438,8 @@ export class UniversalJsonParser {
|
|
|
437
438
|
id: this.generateComponentId(typeInference.type),
|
|
438
439
|
type: typeInference.type,
|
|
439
440
|
label: this.humanizeFieldName(key),
|
|
440
|
-
defaultValue: value
|
|
441
|
+
defaultValue: value,
|
|
442
|
+
expressionValue: undefined
|
|
441
443
|
};
|
|
442
444
|
if (typeInference.textboxType) {
|
|
443
445
|
component.textboxType = typeInference.textboxType;
|
|
@@ -491,6 +493,7 @@ export class UniversalJsonParser {
|
|
|
491
493
|
type: typeInfo.type,
|
|
492
494
|
label: propDef.title || propDef.label || this.humanizeFieldName(fieldName),
|
|
493
495
|
defaultValue: propDef.default || propDef.defaultValue || undefined,
|
|
496
|
+
expressionValue: propDef.expressionValue || undefined,
|
|
494
497
|
content: propDef.content,
|
|
495
498
|
buttonType: propDef.buttonType,
|
|
496
499
|
textboxType: propDef.textboxType,
|
package/src/renderFormField.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface RenderFormFieldProps {
|
|
|
8
8
|
component: FormComponent;
|
|
9
9
|
value?: any;
|
|
10
10
|
onChange: (value: any) => void;
|
|
11
|
+
formState?: any;
|
|
11
12
|
onBlur?: () => void;
|
|
12
13
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
13
14
|
ref?: any;
|
|
@@ -17,4 +18,4 @@ export interface RenderFormFieldProps {
|
|
|
17
18
|
/**
|
|
18
19
|
* Renders a single form field component
|
|
19
20
|
*/
|
|
20
|
-
export declare const
|
|
21
|
+
export declare const RenderFormField: ({ component, value, onChange, onBlur, ref, hasError, onClick, locale, formState }: RenderFormFieldProps) => React.ReactNode;
|