df-ae-forms-package 1.1.3 → 1.1.5
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/dist/index.esm.js +150 -58
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +150 -58
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1002,20 +1002,15 @@ const DfFormInput = ({ id, properties, validationErrors = {}, formValue = '', in
|
|
|
1002
1002
|
touchedFields[id] = true;
|
|
1003
1003
|
}
|
|
1004
1004
|
}, [isTouched, id, touchedFields]);
|
|
1005
|
-
// Reset touched state
|
|
1005
|
+
// Reset touched state when switching modes
|
|
1006
1006
|
useEffect(() => {
|
|
1007
|
+
setIsTouched(false);
|
|
1008
|
+
// Only reset value when explicitly moving to edit mode to show current default
|
|
1007
1009
|
if (mode === 'edit') {
|
|
1008
|
-
setIsTouched(false);
|
|
1009
|
-
// Reset value to default value when switching to edit mode
|
|
1010
1010
|
const defaultValue = properties?.basic?.defaultValue || '';
|
|
1011
1011
|
setValue(defaultValue);
|
|
1012
1012
|
}
|
|
1013
|
-
|
|
1014
|
-
setIsTouched(false);
|
|
1015
|
-
// Reset value to empty when switching to test mode for fresh start
|
|
1016
|
-
setValue('');
|
|
1017
|
-
}
|
|
1018
|
-
}, [mode, properties?.basic?.defaultValue]);
|
|
1013
|
+
}, [mode]);
|
|
1019
1014
|
// Update value when formValue prop changes (but don't override user input)
|
|
1020
1015
|
// CRITICAL: Only update if formValue is actually for THIS component's ID
|
|
1021
1016
|
// Use componentIdRef to ensure we're checking against the correct component ID
|
|
@@ -2407,6 +2402,7 @@ const DfFormFileUpload = ({ id, properties, validationErrors = {}, formValue = n
|
|
|
2407
2402
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
2408
2403
|
const [isTouched, setIsTouched] = useState(false);
|
|
2409
2404
|
const fileInputRef = useRef(null);
|
|
2405
|
+
const lastFormValueRef = useRef(null); // Track the last formValue to prevent unnecessary resets
|
|
2410
2406
|
// Convert FileList or File[] to IFilePreview[]
|
|
2411
2407
|
const convertToFilePreviews = useCallback((fileList, startIndex = 0) => {
|
|
2412
2408
|
if (!fileList)
|
|
@@ -2499,12 +2495,20 @@ const DfFormFileUpload = ({ id, properties, validationErrors = {}, formValue = n
|
|
|
2499
2495
|
if (fileObject.url || fileObject.path) {
|
|
2500
2496
|
preview = fileObject.url || fileObject.path;
|
|
2501
2497
|
}
|
|
2502
|
-
else if (
|
|
2503
|
-
// Handle base64 data
|
|
2498
|
+
else if (fileObject.data) {
|
|
2499
|
+
// Handle base64 data - works for all file types with data
|
|
2500
|
+
const resolvedType = fileType || 'application/octet-stream';
|
|
2504
2501
|
const fileData = fileObject.data;
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2502
|
+
if (typeof fileData === 'string') {
|
|
2503
|
+
if (fileData.startsWith('data:')) {
|
|
2504
|
+
// Already a full data URI
|
|
2505
|
+
preview = fileData;
|
|
2506
|
+
}
|
|
2507
|
+
else {
|
|
2508
|
+
// Raw base64 - construct data URI
|
|
2509
|
+
preview = `data:${resolvedType};base64,${fileData}`;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2508
2512
|
}
|
|
2509
2513
|
// Use the original object to preserve data
|
|
2510
2514
|
fileObj = fileObject;
|
|
@@ -2689,10 +2693,35 @@ const DfFormFileUpload = ({ id, properties, validationErrors = {}, formValue = n
|
|
|
2689
2693
|
}
|
|
2690
2694
|
}, [mode]);
|
|
2691
2695
|
// Update value when formValue prop changes or on mount
|
|
2696
|
+
// CRITICAL: Use a stable reference check to prevent unnecessary resets
|
|
2692
2697
|
useEffect(() => {
|
|
2693
|
-
//
|
|
2698
|
+
// Skip if formValue is null/undefined (no data)
|
|
2699
|
+
if (formValue === null || formValue === undefined || (typeof formValue === 'string' && formValue === '')) {
|
|
2700
|
+
// Only clear files if we previously had files from formValue
|
|
2701
|
+
// Don't clear files that were added by user interaction
|
|
2702
|
+
if (lastFormValueRef.current !== null && lastFormValueRef.current !== undefined && lastFormValueRef.current !== '') {
|
|
2703
|
+
setFiles([]);
|
|
2704
|
+
lastFormValueRef.current = null;
|
|
2705
|
+
}
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
// Check if formValue actually changed (deep comparison for arrays)
|
|
2709
|
+
try {
|
|
2710
|
+
const currentStr = JSON.stringify(formValue);
|
|
2711
|
+
const prevStr = JSON.stringify(lastFormValueRef.current);
|
|
2712
|
+
if (currentStr === prevStr) {
|
|
2713
|
+
return; // No change, skip
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
catch {
|
|
2717
|
+
// If stringify fails, proceed with update
|
|
2718
|
+
}
|
|
2719
|
+
// Convert and set new files
|
|
2694
2720
|
const newFiles = convertToFilePreviews(formValue);
|
|
2695
|
-
|
|
2721
|
+
if (newFiles.length > 0) {
|
|
2722
|
+
setFiles(newFiles);
|
|
2723
|
+
lastFormValueRef.current = formValue;
|
|
2724
|
+
}
|
|
2696
2725
|
}, [formValue, convertToFilePreviews]);
|
|
2697
2726
|
// Mark as touched when form is submitted
|
|
2698
2727
|
useEffect(() => {
|
|
@@ -4965,8 +4994,7 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
4965
4994
|
// This allows the form data to be updated for interactive components
|
|
4966
4995
|
if (onValueChange) {
|
|
4967
4996
|
onValueChange({
|
|
4968
|
-
|
|
4969
|
-
value: change.value
|
|
4997
|
+
...change
|
|
4970
4998
|
});
|
|
4971
4999
|
}
|
|
4972
5000
|
}
|
|
@@ -5437,7 +5465,38 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5437
5465
|
const initializeComponentValues = (components, values) => {
|
|
5438
5466
|
components.forEach(component => {
|
|
5439
5467
|
const componentId = ensureStringId$1(component.id);
|
|
5440
|
-
if (componentId)
|
|
5468
|
+
if (!componentId)
|
|
5469
|
+
return; // Skip components without valid IDs
|
|
5470
|
+
// CRITICAL: Handle file component FIRST — file data is an array, not a simple string
|
|
5471
|
+
// The general handler below would misinterpret an empty file array
|
|
5472
|
+
if (component.name === 'file' && component.basic) {
|
|
5473
|
+
// Don't overwrite if already initialized
|
|
5474
|
+
if (values[componentId] !== undefined && values[componentId] !== '' && values[componentId] !== null) ;
|
|
5475
|
+
else {
|
|
5476
|
+
// Check all possible locations where file data could be stored
|
|
5477
|
+
const fileData = component.basic.value ||
|
|
5478
|
+
component.basic.files ||
|
|
5479
|
+
component.basic.attachments;
|
|
5480
|
+
if (fileData && (Array.isArray(fileData) ? fileData.length > 0 : true)) {
|
|
5481
|
+
values[componentId] = fileData;
|
|
5482
|
+
}
|
|
5483
|
+
else {
|
|
5484
|
+
values[componentId] = null; // Use null, not empty string, for file components
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
}
|
|
5488
|
+
// Handle instructions component
|
|
5489
|
+
else if (component.name === 'instructions' && component.basic) {
|
|
5490
|
+
if (!component.basic.instructions) {
|
|
5491
|
+
component.basic.instructions = [];
|
|
5492
|
+
}
|
|
5493
|
+
const instructionValue = component.basic.value || component.basic.instructions;
|
|
5494
|
+
if (instructionValue) {
|
|
5495
|
+
values[componentId] = instructionValue;
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
// General handler for all other components
|
|
5499
|
+
else {
|
|
5441
5500
|
// ALWAYS prioritize existing form state (values param) if it exists
|
|
5442
5501
|
if (values[componentId] !== undefined) ;
|
|
5443
5502
|
// Then use captured value in basic.value
|
|
@@ -5452,10 +5511,10 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5452
5511
|
else {
|
|
5453
5512
|
// For checkbox and multi-select, empty array
|
|
5454
5513
|
if (component.name === 'checkbox' || component.name === 'select') {
|
|
5455
|
-
values[
|
|
5514
|
+
values[componentId] = [];
|
|
5456
5515
|
}
|
|
5457
5516
|
else {
|
|
5458
|
-
values[
|
|
5517
|
+
values[componentId] = '';
|
|
5459
5518
|
}
|
|
5460
5519
|
}
|
|
5461
5520
|
}
|
|
@@ -5477,31 +5536,10 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5477
5536
|
}
|
|
5478
5537
|
});
|
|
5479
5538
|
}
|
|
5480
|
-
// Handle file component - initialize with file data if present
|
|
5481
|
-
if (component.name === 'file' && component.basic) {
|
|
5482
|
-
const fileData = component.basic.files || component.basic.attachments || component.basic.value;
|
|
5483
|
-
if (fileData) {
|
|
5484
|
-
values[component.id] = fileData;
|
|
5485
|
-
}
|
|
5486
|
-
}
|
|
5487
5539
|
// Initialize notes and attachments
|
|
5488
|
-
if (
|
|
5540
|
+
if (componentId && component.basic) {
|
|
5489
5541
|
if (component.basic.notes) ;
|
|
5490
5542
|
}
|
|
5491
|
-
// Handle instructions component - ensure instructions array exists and initialize formValue
|
|
5492
|
-
if (component.name === 'instructions' && component.basic) {
|
|
5493
|
-
if (!component.basic.instructions) {
|
|
5494
|
-
// Initialize empty instructions array if not present
|
|
5495
|
-
component.basic.instructions = [];
|
|
5496
|
-
}
|
|
5497
|
-
// CRITICAL: Initialize formValue for instructions from API data
|
|
5498
|
-
// Check if component has value from API (could be in basic.value, basic.instructions, or formData)
|
|
5499
|
-
const instructionValue = component.basic.value || component.basic.instructions;
|
|
5500
|
-
if (instructionValue) {
|
|
5501
|
-
// Store instruction data in formValues so it can be passed to DfFormInstruction
|
|
5502
|
-
values[component.id] = instructionValue;
|
|
5503
|
-
}
|
|
5504
|
-
}
|
|
5505
5543
|
// Handle nested components in section children
|
|
5506
5544
|
if (component.children && Array.isArray(component.children)) {
|
|
5507
5545
|
initializeComponentValues(component.children, values);
|
|
@@ -5661,8 +5699,34 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5661
5699
|
}
|
|
5662
5700
|
// Set the state
|
|
5663
5701
|
setFormValues(prev => {
|
|
5664
|
-
//
|
|
5665
|
-
|
|
5702
|
+
// Smart merge: initial values should win over empty/stale prev values
|
|
5703
|
+
// but user-entered values in prev should win over initial defaults
|
|
5704
|
+
const merged = { ...initialValues };
|
|
5705
|
+
Object.keys(prev).forEach(key => {
|
|
5706
|
+
const prevVal = prev[key];
|
|
5707
|
+
const initVal = initialValues[key];
|
|
5708
|
+
// If prev has a meaningful value (not empty/null/undefined), keep it
|
|
5709
|
+
if (prevVal !== undefined && prevVal !== null && prevVal !== '') {
|
|
5710
|
+
// But if initVal is an array (like file data) and prevVal is a simple empty value,
|
|
5711
|
+
// prefer the initialValue
|
|
5712
|
+
if (Array.isArray(initVal) && initVal.length > 0 && !Array.isArray(prevVal)) {
|
|
5713
|
+
// initialValue is a populated array but prev is not — keep initial
|
|
5714
|
+
merged[key] = initVal;
|
|
5715
|
+
}
|
|
5716
|
+
else {
|
|
5717
|
+
merged[key] = prevVal;
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
// If prev has empty string but initial has actual data, use initial
|
|
5721
|
+
else if (initVal !== undefined && initVal !== null && initVal !== '') {
|
|
5722
|
+
merged[key] = initVal;
|
|
5723
|
+
}
|
|
5724
|
+
// Otherwise keep prev (even if empty — preserves user clearing a field)
|
|
5725
|
+
else {
|
|
5726
|
+
merged[key] = prevVal;
|
|
5727
|
+
}
|
|
5728
|
+
});
|
|
5729
|
+
return merged;
|
|
5666
5730
|
});
|
|
5667
5731
|
// Initialize notes and attachments state
|
|
5668
5732
|
setComponentNotes(prev => ({ ...initialNotes, ...prev }));
|
|
@@ -5682,9 +5746,35 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5682
5746
|
return;
|
|
5683
5747
|
}
|
|
5684
5748
|
// CRITICAL: Check if multiple components share this ID (ID collision detection)
|
|
5685
|
-
|
|
5749
|
+
// Use recursive search for accurate detection across all levels
|
|
5750
|
+
const checkIdCollision = (components, targetId) => {
|
|
5751
|
+
let matches = [];
|
|
5752
|
+
components.forEach(comp => {
|
|
5753
|
+
if (ensureStringId$1(comp.id) === targetId)
|
|
5754
|
+
matches.push(comp);
|
|
5755
|
+
if (comp.name === 'section' && comp.children) {
|
|
5756
|
+
matches = [...matches, ...checkIdCollision(comp.children, targetId)];
|
|
5757
|
+
}
|
|
5758
|
+
if (comp.name === 'table' && comp.cells) {
|
|
5759
|
+
comp.cells.forEach((row) => {
|
|
5760
|
+
normalizeTableRow(row).forEach((cell) => {
|
|
5761
|
+
if (cell.components)
|
|
5762
|
+
matches = [...matches, ...checkIdCollision(cell.components, targetId)];
|
|
5763
|
+
});
|
|
5764
|
+
});
|
|
5765
|
+
}
|
|
5766
|
+
if (comp.name === 'datagrid' && comp.entries) {
|
|
5767
|
+
comp.entries.forEach((entry) => {
|
|
5768
|
+
if (entry.components)
|
|
5769
|
+
matches = [...matches, ...checkIdCollision(entry.components, targetId)];
|
|
5770
|
+
});
|
|
5771
|
+
}
|
|
5772
|
+
});
|
|
5773
|
+
return matches;
|
|
5774
|
+
};
|
|
5775
|
+
const componentsWithSameId = checkIdCollision(localFormComponents, change.id);
|
|
5686
5776
|
if (componentsWithSameId.length > 1) {
|
|
5687
|
-
console.error(`[DfFormPreview] ID COLLISION DETECTED! Multiple components share ID "${change.id}":`, componentsWithSameId.map(c => ({ id: c.id, name: c.name, label: c.basic?.label })));
|
|
5777
|
+
console.error(`[DfFormPreview] ID COLLISION DETECTED! Multiple components share ID "${change.id}":`, componentsWithSameId.map(c => ({ id: ensureStringId$1(c.id), name: c.name, label: c.basic?.label })));
|
|
5688
5778
|
// Don't update - this would cause all components with this ID to get the same value
|
|
5689
5779
|
return;
|
|
5690
5780
|
}
|
|
@@ -5703,7 +5793,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5703
5793
|
}
|
|
5704
5794
|
// Clear raised issues for this component when value changes
|
|
5705
5795
|
// This ensures that if threshold condition changes, user must raise issue again
|
|
5706
|
-
const component = localFormComponents
|
|
5796
|
+
const component = findComponentById(localFormComponents, change.id);
|
|
5707
5797
|
if (component) {
|
|
5708
5798
|
const threshold = component?.threshold;
|
|
5709
5799
|
if (threshold && threshold.conditions && threshold.conditions.length > 0) {
|
|
@@ -5719,7 +5809,8 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5719
5809
|
// Recursive function to update component values
|
|
5720
5810
|
const updateComponentValue = (components) => {
|
|
5721
5811
|
return components.map(component => {
|
|
5722
|
-
|
|
5812
|
+
const componentId = ensureStringId$1(component.id);
|
|
5813
|
+
if (componentId === change.id) {
|
|
5723
5814
|
// CRITICAL: Handle table/datagrid structure updates (cells, entries, etc.)
|
|
5724
5815
|
// When a table sends onValueChange with cells data, update the whole component structure
|
|
5725
5816
|
if (change.value && typeof change.value === 'object' && 'cells' in change.value) {
|
|
@@ -5735,13 +5826,12 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5735
5826
|
...change.value
|
|
5736
5827
|
};
|
|
5737
5828
|
}
|
|
5738
|
-
if ('defaultValue' in component.basic) {
|
|
5829
|
+
if ('value' in component.basic || 'defaultValue' in component.basic) {
|
|
5739
5830
|
return {
|
|
5740
5831
|
...component,
|
|
5741
5832
|
basic: {
|
|
5742
5833
|
...component.basic,
|
|
5743
|
-
value: change.value
|
|
5744
|
-
defaultValue: change.value
|
|
5834
|
+
value: change.value
|
|
5745
5835
|
}
|
|
5746
5836
|
};
|
|
5747
5837
|
}
|
|
@@ -6795,12 +6885,14 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6795
6885
|
} }));
|
|
6796
6886
|
case 'file':
|
|
6797
6887
|
// Get file value from formValues, or from component basic properties
|
|
6798
|
-
// CRITICAL:
|
|
6799
|
-
const
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
component.basic?.
|
|
6803
|
-
|
|
6888
|
+
// CRITICAL: formValue could be '' (empty string) which is falsy — check explicitly
|
|
6889
|
+
const hasFormValue = formValue !== undefined && formValue !== null && formValue !== '';
|
|
6890
|
+
const fileFormValue = hasFormValue
|
|
6891
|
+
? formValue
|
|
6892
|
+
: (component.basic?.value ||
|
|
6893
|
+
component.basic?.files ||
|
|
6894
|
+
component.basic?.attachments ||
|
|
6895
|
+
null);
|
|
6804
6896
|
return (jsx(DfFormFileUpload, { ...commonProps, properties: component, formValue: fileFormValue }));
|
|
6805
6897
|
default:
|
|
6806
6898
|
return (jsx("div", { className: "form-group", children: jsxs("div", { className: "form-group-label", children: ["Unsupported Component: ", component.name] }) }));
|