diff --git a/components/ComponentPreview.vue b/components/ComponentPreview.vue index bff41e7..60a8293 100644 --- a/components/ComponentPreview.vue +++ b/components/ComponentPreview.vue @@ -184,7 +184,8 @@ @@ -278,11 +279,12 @@ :checked="isItemSelected(component.props.name, index)" @change="toggleItemSelection(component.props.name, index)" class="mr-2 h-4 w-4 rounded border-gray-300" /> - + @input="updateListItem(component.props.name, index, $event.target.value)" />
@@ -324,7 +326,7 @@
@@ -646,8 +648,10 @@ onMounted(() => { const defaultItems = props.component.props.defaultItems; if ((!currentFormData || currentFormData.length === 0) && defaultItems && defaultItems.length > 0) { - const updatedData = { ...formStore.previewFormData, [listName]: [...defaultItems] }; - formStore.updatePreviewFormData(updatedData); + nextTick(() => { + const updatedData = { ...formStore.previewFormData, [listName]: [...defaultItems] }; + formStore.updatePreviewFormData(updatedData); + }); } } } @@ -677,8 +681,10 @@ onMounted(() => { initialGroups.push(newGroup); } - const updatedData = { ...formStore.previewFormData, [groupName]: initialGroups }; - formStore.updatePreviewFormData(updatedData); + nextTick(() => { + const updatedData = { ...formStore.previewFormData, [groupName]: initialGroups }; + formStore.updatePreviewFormData(updatedData); + }); } } } @@ -690,11 +696,19 @@ watch(() => props.component.props.defaultItems, (newDefaultItems, oldDefaultItem const listName = props.component.props.name; if (!listName) return; + // Check if defaultItems actually changed to prevent unnecessary updates + const newItemsStr = JSON.stringify(newDefaultItems); + const oldItemsStr = JSON.stringify(oldDefaultItems); + + if (newItemsStr === oldItemsStr) return; + // Always update when defaultItems change, regardless of current form data const items = newDefaultItems || []; - const updatedData = { ...formStore.previewFormData, [listName]: [...items] }; - formStore.updatePreviewFormData(updatedData); + nextTick(() => { + const updatedData = { ...formStore.previewFormData, [listName]: [...items] }; + formStore.updatePreviewFormData(updatedData); + }); } }, { deep: true, immediate: true }); @@ -702,7 +716,7 @@ watch(() => props.component.props.defaultItems, (newDefaultItems, oldDefaultItem watch(() => props.component.props.minItems, (newMinItems, oldMinItems) => { if (props.component.type === 'repeating-group') { const groupName = props.component.props.name; - if (!groupName) return; + if (!groupName || newMinItems === oldMinItems) return; const currentGroups = safeGetField(groupName, formStore.previewFormData); const minItems = newMinItems || 1; @@ -725,12 +739,51 @@ watch(() => props.component.props.minItems, (newMinItems, oldMinItems) => { updatedGroups.push(newGroup); } - const updatedData = { ...formStore.previewFormData, [groupName]: updatedGroups }; - formStore.updatePreviewFormData(updatedData); + nextTick(() => { + const updatedData = { ...formStore.previewFormData, [groupName]: updatedGroups }; + formStore.updatePreviewFormData(updatedData); + }); } } }, { deep: true, immediate: true }); +// Controlled update methods to prevent circular reactivity +const updateGroupField = (groupName, groupIndex, fieldName, newValue) => { + if (!props.isPreview) return; + + const currentGroups = [...(safeGetField(groupName, formStore.previewFormData) || [])]; + if (!currentGroups[groupIndex]) return; + + // Only update if value actually changed + if (currentGroups[groupIndex][fieldName] === newValue) return; + + currentGroups[groupIndex][fieldName] = newValue; + + nextTick(() => { + const updatedData = { ...formStore.previewFormData, [groupName]: currentGroups }; + formStore.updatePreviewFormData(updatedData); + emit('form-data-updated', updatedData); + }); +}; + +const updateListItem = (listName, index, newValue) => { + if (!props.isPreview) return; + + const currentItems = [...(safeGetField(listName, formStore.previewFormData) || [])]; + if (currentItems[index] === newValue) return; // No change + + // Validate and handle duplicates + if (!checkDuplicates(listName, newValue, index)) return; + if (!validateItem(listName, index, newValue)) return; + + currentItems[index] = newValue; + + nextTick(() => { + const updatedData = { ...formStore.previewFormData, [listName]: currentItems }; + formStore.updatePreviewFormData(updatedData); + }); +}; + // Repeating group and dynamic list functionality const addGroupItem = () => { if (!props.isPreview) return; @@ -1181,13 +1234,48 @@ const updateListItems = (listName, newItems) => { formStore.updatePreviewFormData(updatedData); }; +// Get table data safely and initialize if needed +const getTableData = (tableName) => { + if (!tableName) return []; + + // Directly check the form store without using safeGetField to avoid warnings + const formData = formStore.previewFormData || {}; + + // If field exists and is an array, return it + if (formData.hasOwnProperty(tableName) && Array.isArray(formData[tableName])) { + return formData[tableName]; + } + + // If data doesn't exist, initialize it immediately (no nextTick needed for initial render) + const initialData = []; + const updatedFormData = { ...formData, [tableName]: initialData }; + formStore.updatePreviewFormData(updatedFormData); + + return initialData; +}; + // Update table data for repeating-table component const updateTableData = (newData) => { const tableName = props.component.props.name; if (!tableName) return; - const updatedFormData = { ...formStore.previewFormData, [tableName]: newData }; - formStore.updatePreviewFormData(updatedFormData); + // Ensure newData is always an array + const safeNewData = Array.isArray(newData) ? newData : []; + + // Check if data actually changed to prevent unnecessary updates + const currentData = safeGetField(tableName, formStore.previewFormData) || []; + const currentDataStr = JSON.stringify(currentData); + const newDataStr = JSON.stringify(safeNewData); + + if (currentDataStr === newDataStr) return; + + nextTick(() => { + const updatedFormData = { ...formStore.previewFormData, [tableName]: safeNewData }; + formStore.updatePreviewFormData(updatedFormData); + + // Emit the change for workflow page to sync with its local formData + emit('form-data-updated', updatedFormData); + }); }; // Form Section Component diff --git a/components/RepeatingTable.vue b/components/RepeatingTable.vue index bd34e27..397f2dd 100644 --- a/components/RepeatingTable.vue +++ b/components/RepeatingTable.vue @@ -70,9 +70,9 @@
-
+
- +
#
- { - if (cellRef.value) { - observer.value = new IntersectionObserver( - (entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - isVisible.value = true - observer.value?.disconnect() - } - }) - }, - { threshold: 0.1 } - ) - observer.value.observe(cellRef.value) - } - }) - - onUnmounted(() => { - observer.value?.disconnect() - }) - const formatValue = computed(() => { - if (!isVisible.value) return '' - const value = props.value if (value === null || value === undefined || value === '') { return '-' @@ -370,13 +342,11 @@ const LazyCellValue = defineComponent({ }) return { - isVisible, - cellRef, formatValue } }, template: ` - + {{ formatValue }} ` @@ -400,7 +370,7 @@ const props = defineProps({ const emit = defineEmits(['update:modelValue']) // Reactive state -const data = ref([...props.modelValue]) +const data = ref([...(props.modelValue || [])]) const searchQuery = ref('') const showModal = ref(false) const showDeleteConfirm = ref(false) @@ -487,7 +457,7 @@ const isAddDisabled = computed(() => { }) const showActions = computed(() => { - return (props.config.allowEdit || props.config.allowDelete) && !props.isPreview + return props.config.allowEdit || props.config.allowDelete }) const showRecordCount = computed(() => { @@ -543,23 +513,48 @@ const visiblePages = computed(() => { return rangeWithDots.filter((item, index, array) => array.indexOf(item) === index) }) +// Guard to prevent recursive updates +const isUpdatingFromProps = ref(false) + // Watch for external data changes watch(() => props.modelValue, (newValue) => { - data.value = [...newValue] + // Handle the case where newValue might be undefined/null + const safeNewValue = newValue || [] + + // Prevent circular updates by checking if data actually changed + const newDataStr = JSON.stringify(safeNewValue) + const currentDataStr = JSON.stringify(data.value) + + if (newDataStr === currentDataStr) return + + isUpdatingFromProps.value = true + data.value = [...safeNewValue] // Clear caches when data changes columnCache.value.clear() recordKeys.value.clear() -}, { deep: true }) + + nextTick(() => { + isUpdatingFromProps.value = false + }) +}, { deep: true, immediate: true }) -// Watch for internal data changes and emit -watch(data, (newData) => { - emit('update:modelValue', [...newData]) +// Watch for internal data changes and emit (only when not updating from props) +watch(data, (newData, oldData) => { + if (isUpdatingFromProps.value) return + + // Check if data actually changed + const newDataStr = JSON.stringify(newData) + const oldDataStr = JSON.stringify(oldData) + + if (newDataStr === oldDataStr) return + + nextTick(() => { + emit('update:modelValue', [...newData]) + }) }, { deep: true }) // Methods const openAddModal = () => { - if (props.isPreview) return - editingIndex.value = null formData.value = {} @@ -572,8 +567,6 @@ const openAddModal = () => { } const openEditModal = (record, index) => { - if (props.isPreview) return - editingIndex.value = index formData.value = { ...record } showModal.value = true @@ -606,8 +599,6 @@ const saveRecord = (formData) => { } const deleteRecord = (index) => { - if (props.isPreview) return - if (props.config.confirmDelete) { deleteIndex.value = index showDeleteConfirm.value = true @@ -693,18 +684,56 @@ onUnmounted(() => {