Enhance ComponentPreview and FormBuilder Components with Drag-and-Drop Functionality

- Updated ComponentPreview.vue to implement a draggable container for nested components, allowing users to reorder fields within repeating groups.
- Enhanced the user interface with visual feedback during drag-and-drop actions, improving usability and interaction.
- Modified FormBuilderComponents.vue to include default properties for nested components, facilitating better organization and management of form fields.
- Improved FormBuilderFieldSettingsModal.vue to provide clearer information about repeating group containers and their functionalities.
- Refactored event handling for component updates and deletions to support nested components within sections and repeating groups, ensuring consistent data management.
- Updated styles across components to enhance the visual experience during drag-and-drop operations and improve overall aesthetics.
This commit is contained in:
Afiq 2025-08-07 10:28:57 +08:00
parent f86fe87fc5
commit 415ac5a0d1
5 changed files with 305 additions and 200 deletions

View File

@ -144,24 +144,81 @@
{{ component.props.help }} {{ component.props.help }}
</div> </div>
<!-- Default group preview (in edit mode) --> <!-- Builder mode - show draggable container -->
<div v-if="!isPreview" class="repeating-groups space-y-4"> <div v-if="!isPreview" class="repeating-groups space-y-4">
<div class="group-item border border-gray-200 rounded-md p-3 bg-gray-50"> <!-- Container for draggable fields -->
<div class="flex justify-between items-center mb-3"> <div
<h4 class="text-sm font-medium text-gray-700">Item 1</h4> class="repeating-group-container border-2 border-dashed border-gray-300 rounded-md p-4 min-h-[100px] bg-gray-50"
<button type="button" class="text-red-500 hover:text-red-700 text-sm"> :class="{
{{ component.props.removeText || 'Remove' }} 'border-blue-400 bg-blue-50': sectionDropStates[component.id]?.isDraggingOver
</button> }"
@dragover.prevent="handleSectionDragOver($event, component.id)"
@dragleave="handleSectionDragLeave($event, component.id)"
@drop="handleSectionDrop($event, component.id)"
@dragenter.prevent="handleSectionDragEnter($event, component.id)"
>
<div v-if="component.props.children && component.props.children.length > 0" class="nested-components">
<draggable
v-model="component.props.children"
group="form-components"
item-key="id"
handle=".drag-handle"
ghost-class="ghost"
animation="300"
class="grid grid-cols-12 gap-2"
@end="onNestedDragEnd"
@add="onNestedComponentAdd"
>
<template #item="{ element: childElement, index: childIndex }">
<div
class="form-component relative border rounded-md overflow-hidden transition-all duration-200 bg-white"
:class="'border-gray-200 hover:border-blue-300 hover:shadow-md'"
:style="{
gridColumn: childElement.props.gridColumn || 'span 6'
}"
>
<!-- Component actions -->
<div class="component-actions absolute right-1 top-1 flex space-x-1 z-10">
<button
class="p-1 text-gray-400 hover:text-blue-600 rounded"
title="Component settings"
@click.stop="openNestedComponentSettings(childElement)"
>
<Icon name="heroicons:cog-6-tooth" class="w-3 h-3" />
</button>
<button
class="p-1 text-gray-400 hover:text-gray-600 rounded"
title="Drag to reorder"
>
<span class="drag-handle cursor-move">
<Icon name="material-symbols:drag-indicator" class="w-3 h-3" />
</span>
</button>
<button
class="p-1 text-gray-400 hover:text-red-500 rounded"
title="Remove from group"
@click.stop="removeFromSection(component.id, childIndex)"
>
<Icon name="material-symbols:close" class="w-3 h-3" />
</button>
</div>
<div class="p-2">
<component-preview :component="childElement" :is-preview="false" />
</div>
</div>
</template>
</draggable>
</div> </div>
<div class="space-y-3"> <!-- Empty state -->
<template v-for="(field, fieldIndex) in component.props.fields" :key="fieldIndex"> <div v-else class="text-center py-8">
<FormKit :type="field.type" :label="field.label" :placeholder="field.placeholder" <Icon name="material-symbols:add-circle-outline" class="w-8 h-8 text-gray-400 mx-auto mb-2" />
:name="`${field.name}_1`" :options="field.options" disabled /> <p class="text-sm text-gray-500">Drag fields here to add them to this repeating group</p>
</template>
</div> </div>
</div> </div>
<!-- Add Item button for preview -->
<button type="button" <button type="button"
class="inline-flex items-center px-3 py-1.5 border border-blue-600 text-blue-600 bg-white hover:bg-blue-50 rounded text-sm"> class="inline-flex items-center px-3 py-1.5 border border-blue-600 text-blue-600 bg-white hover:bg-blue-50 rounded text-sm">
<Icon name="material-symbols:add-circle-outline" class="w-4 h-4 mr-1" /> <Icon name="material-symbols:add-circle-outline" class="w-4 h-4 mr-1" />
@ -169,7 +226,7 @@
</button> </button>
</div> </div>
<!-- Functional groups (in preview mode) --> <!-- Preview mode - show functional repeating groups -->
<div v-else class="repeating-groups space-y-4"> <div v-else class="repeating-groups space-y-4">
<div v-for="(group, groupIndex) in (safeGetField(component.props.name, previewFormData) || [])" :key="groupIndex" <div v-for="(group, groupIndex) in (safeGetField(component.props.name, previewFormData) || [])" :key="groupIndex"
class="group-item border border-gray-200 rounded-md p-3 bg-gray-50"> class="group-item border border-gray-200 rounded-md p-3 bg-gray-50">
@ -181,12 +238,17 @@
</button> </button>
</div> </div>
<div class="space-y-3"> <div class="grid grid-cols-12 gap-2">
<template v-for="(field, fieldIndex) in component.props.fields" :key="fieldIndex"> <!-- Render children components for each group item -->
<FormKit :type="field.type" :label="field.label" :placeholder="field.placeholder" <template v-for="(child, childIndex) in component.props.children" :key="childIndex">
:name="`${component.props.name}.${groupIndex}.${field.name}`" :options="field.options" <div
:value="group[field.name]" class="form-component"
@input="updateGroupField(component.props.name, groupIndex, field.name, $event)" /> :style="{
gridColumn: child.props.gridColumn || 'span 6'
}"
>
<component-preview :component="child" :is-preview="true" />
</div>
</template> </template>
</div> </div>
</div> </div>
@ -462,9 +524,16 @@
<!-- In preview mode, show only the nested components (no placeholder) --> <!-- In preview mode, show only the nested components (no placeholder) -->
<div v-if="isPreview" class="section-fields"> <div v-if="isPreview" class="section-fields">
<!-- Render nested components if they exist --> <!-- Render nested components if they exist -->
<div v-if="component.props.children && component.props.children.length > 0" class="space-y-3"> <div v-if="component.props.children && component.props.children.length > 0" class="grid grid-cols-12 gap-2">
<template v-for="(childComponent, childIndex) in component.props.children" :key="childIndex"> <template v-for="(childComponent, childIndex) in component.props.children" :key="childIndex">
<component-preview :component="childComponent" :is-preview="true" /> <div
class="form-component"
:style="{
gridColumn: childComponent.props.gridColumn || 'span 6'
}"
>
<component-preview :component="childComponent" :is-preview="true" />
</div>
</template> </template>
</div> </div>
<!-- Show subtle indication for empty sections in preview mode (optional) --> <!-- Show subtle indication for empty sections in preview mode (optional) -->
@ -579,6 +648,7 @@ import { useFormBuilderStore } from '~/stores/formBuilder';
import FormBuilderFieldSettingsModal from '~/components/FormBuilderFieldSettingsModal.vue'; import FormBuilderFieldSettingsModal from '~/components/FormBuilderFieldSettingsModal.vue';
import { safeGetField } from '~/composables/safeGetField'; import { safeGetField } from '~/composables/safeGetField';
import { onMounted, onUnmounted, watch, computed } from 'vue'; import { onMounted, onUnmounted, watch, computed } from 'vue';
import draggable from 'vuedraggable';
const props = defineProps({ const props = defineProps({
component: { component: {
@ -613,6 +683,9 @@ const sectionDropStates = ref({});
const showNestedSettingsModal = ref(false); const showNestedSettingsModal = ref(false);
const selectedNestedComponent = ref(null); const selectedNestedComponent = ref(null);
// Track selected component ID for highlighting
const selectedComponentId = ref(null);
// Track timers and DOM elements for cleanup // Track timers and DOM elements for cleanup
let lightbox = null; let lightbox = null;
let importInput = null; let importInput = null;
@ -1323,18 +1396,18 @@ const removeFromSection = (sectionId, childIndex) => {
} }
}; };
const handleSectionDragOver = (event, sectionId) => { const handleSectionDragOver = (event, containerId) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Initialize section drop state if it doesn't exist // Initialize container drop state if it doesn't exist
if (!sectionDropStates.value[sectionId]) { if (!sectionDropStates.value[containerId]) {
sectionDropStates.value[sectionId] = { isDraggingOver: false }; sectionDropStates.value[containerId] = { isDraggingOver: false };
} }
sectionDropStates.value[sectionId].isDraggingOver = true; sectionDropStates.value[containerId].isDraggingOver = true;
}; };
const handleSectionDragLeave = (event, sectionId) => { const handleSectionDragLeave = (event, containerId) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -1347,18 +1420,70 @@ const handleSectionDragLeave = (event, sectionId) => {
event.clientY > rect.bottom event.clientY > rect.bottom
); );
if (isOutside && sectionDropStates.value[sectionId]) { if (isOutside && sectionDropStates.value[containerId]) {
sectionDropStates.value[sectionId].isDraggingOver = false; sectionDropStates.value[containerId].isDraggingOver = false;
} }
}; };
const handleSectionDrop = (event, sectionId) => { // Container drag handling functions
const handleContainerDragStart = (event) => {
console.log('Container drag started:', event);
};
const handleContainerDragEnd = (event) => {
console.log('Container drag ended:', event);
// The draggable component automatically updates the array order
// We just need to update the parent container to trigger reactivity
if (props.component) {
formStore.updateComponent(props.component);
}
};
// Nested component selection
const selectNestedComponent = (nestedComponent) => {
if (!nestedComponent || !nestedComponent.id) return;
selectedComponentId.value = nestedComponent.id;
selectedNestedComponent.value = nestedComponent;
// Emit the selection event to parent
emit('select-nested-component', nestedComponent);
console.log('Selected nested component:', nestedComponent);
};
// Delete nested component
const deleteNestedComponent = (componentId) => {
if (!componentId || !props.component) return;
// Find the component in the children array
const childIndex = props.component.props.children.findIndex(child => child.id === componentId);
if (childIndex !== -1) {
// Remove the component
const deletedComponent = props.component.props.children.splice(childIndex, 1)[0];
// Update the container to trigger reactivity
formStore.updateComponent(props.component);
// Clear selection if the deleted component was selected
if (selectedComponentId.value === componentId) {
selectedComponentId.value = null;
selectedNestedComponent.value = null;
}
console.log('Deleted nested component:', deletedComponent);
}
};
const handleSectionDrop = (event, containerId) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Reset drag state // Reset drag state
if (sectionDropStates.value[sectionId]) { if (sectionDropStates.value[containerId]) {
sectionDropStates.value[sectionId].isDraggingOver = false; sectionDropStates.value[containerId].isDraggingOver = false;
} }
try { try {
@ -1384,7 +1509,7 @@ const handleSectionDrop = (event, sectionId) => {
name: componentData.name, name: componentData.name,
props: { props: {
...componentData.defaultProps, ...componentData.defaultProps,
gridColumn: 'span 6', // Default to half width in sections gridColumn: 'span 6', // Default to half width in containers
width: '50%', width: '50%',
// Ensure the component has a proper label // Ensure the component has a proper label
label: componentData.defaultProps.label || componentData.name || `${componentData.type.charAt(0).toUpperCase() + componentData.type.slice(1)} Field`, label: componentData.defaultProps.label || componentData.name || `${componentData.type.charAt(0).toUpperCase() + componentData.type.slice(1)} Field`,
@ -1392,41 +1517,42 @@ const handleSectionDrop = (event, sectionId) => {
} }
}; };
// Find the target section // Find the target container (section or repeating-group)
const section = formStore.formComponents.find(comp => comp.id === sectionId); const container = formStore.formComponents.find(comp => comp.id === containerId);
if (section) { if (container && (container.type === 'form-section' || container.type === 'repeating-group')) {
// Initialize children array if it doesn't exist // Initialize children array if it doesn't exist
if (!section.props.children) { if (!container.props.children) {
section.props.children = []; container.props.children = [];
} }
// Add the component to the section // Add the component to the container
section.props.children.push(newComponent); container.props.children.push(newComponent);
// Update the section in the form store // Update the container in the form store
formStore.updateComponent(section); formStore.updateComponent(container);
// Record the action in history // Record the action in history
formStore.recordHistory('add_component_to_section', { formStore.recordHistory('add_component_to_container', {
componentType: newComponent.type, componentType: newComponent.type,
componentName: newComponent.name, componentName: newComponent.name,
sectionId: sectionId containerId: containerId,
containerType: container.type
}); });
console.log('Component added to section:', newComponent); console.log('Component added to container:', newComponent);
} }
} catch (error) { } catch (error) {
console.error('Error dropping component into section:', error); console.error('Error dropping component into container:', error);
} }
}; };
const handleSectionDragEnter = (event, sectionId) => { const handleSectionDragEnter = (event, containerId) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Initialize section drop state if it doesn't exist // Initialize container drop state if it doesn't exist
if (!sectionDropStates.value[sectionId]) { if (!sectionDropStates.value[containerId]) {
sectionDropStates.value[sectionId] = { isDraggingOver: false }; sectionDropStates.value[containerId] = { isDraggingOver: false };
} }
}; };
@ -1445,20 +1571,20 @@ const closeNestedSettingsModal = () => {
const saveNestedComponentSettings = (updatedComponent) => { const saveNestedComponentSettings = (updatedComponent) => {
if (!updatedComponent || !selectedNestedComponent.value) return; if (!updatedComponent || !selectedNestedComponent.value) return;
// Find the parent section and update the nested component // Find the parent container (section or repeating-group) and update the nested component
const parentSection = formStore.formComponents.find(comp => const parentContainer = formStore.formComponents.find(comp =>
comp.type === 'form-section' && (comp.type === 'form-section' || comp.type === 'repeating-group') &&
comp.props.children && comp.props.children &&
comp.props.children.some(child => child.id === updatedComponent.id) comp.props.children.some(child => child.id === updatedComponent.id)
); );
if (parentSection) { if (parentContainer) {
const childIndex = parentSection.props.children.findIndex(child => child.id === updatedComponent.id); const childIndex = parentContainer.props.children.findIndex(child => child.id === updatedComponent.id);
if (childIndex !== -1) { if (childIndex !== -1) {
// Update the nested component // Update the nested component
parentSection.props.children[childIndex] = { ...updatedComponent }; parentContainer.props.children[childIndex] = { ...updatedComponent };
// Update the section to trigger reactivity // Update the container to trigger reactivity
formStore.updateComponent(parentSection); formStore.updateComponent(parentContainer);
console.log('Updated nested component:', updatedComponent); console.log('Updated nested component:', updatedComponent);
} }
@ -1658,6 +1784,65 @@ const getButtonSizeClass = (size) => {
border-color: #93c5fd; border-color: #93c5fd;
} }
.repeating-group-container {
transition: all 0.2s ease-in-out;
min-height: 100px;
}
.repeating-group-container:hover {
border-color: #93c5fd;
background-color: #f0f9ff;
}
/* Nested Component Styles */
.nested-component {
transition: all 0.2s ease-in-out;
margin-bottom: 0.75rem;
}
.nested-component:hover {
border-color: #93c5fd;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.nested-component-actions {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.nested-component:hover .nested-component-actions {
opacity: 1;
}
.draggable-children-container {
min-height: 20px;
}
/* Ghost class for drag preview */
.ghost {
opacity: 0.5;
background: #c1d5db;
}
/* Form component styles for nested components */
.form-component {
transition: all 0.2s ease-in-out;
}
.form-component:hover {
border-color: #93c5fd;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.component-actions {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.form-component:hover .component-actions {
opacity: 1;
}
/* Dynamic List Component */ /* Dynamic List Component */
.dynamic-list-container { .dynamic-list-container {
width: 100%; width: 100%;
@ -1773,6 +1958,19 @@ const getButtonSizeClass = (size) => {
opacity: 1; opacity: 1;
} }
/* Preview mode grid styles */
.repeating-groups .grid,
.section-fields .grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 0.5rem;
}
.repeating-groups .form-component,
.section-fields .form-component {
width: 100%;
}
.component-actions:hover { .component-actions:hover {
background-color: white; background-color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

View File

@ -508,11 +508,17 @@ const availableComponents = [
maxItems: 10, maxItems: 10,
buttonText: 'Add Person', buttonText: 'Add Person',
removeText: 'Remove', removeText: 'Remove',
fields: [ width: '100%',
{ type: 'text', name: 'name', label: 'Name', placeholder: 'Enter name' }, gridColumn: 'span 12',
{ type: 'number', name: 'age', label: 'Age', placeholder: 'Enter age' }, showPlaceholder: true, // Whether to show the placeholder in builder mode
{ type: 'email', name: 'email', label: 'Email', placeholder: 'Enter email' } children: [], // Array to hold nested components (draggable fields)
] // Conditional Logic Properties
conditionalLogic: {
enabled: false,
conditions: [],
action: 'show',
operator: 'and'
}
} }
}, },
{ {

View File

@ -1337,117 +1337,32 @@ if (name && email) {
/> />
</div> </div>
<!-- Group Fields Management --> <!-- Container Information -->
<div class="space-y-4"> <div class="bg-blue-50 border border-blue-200 rounded-md p-4">
<h5 class="text-sm font-medium text-gray-700 border-b pb-2">Group Fields</h5> <div class="flex items-start">
<Icon name="material-symbols:info" class="w-5 h-5 text-blue-600 mr-2 mt-0.5" />
<div class="border rounded-md p-3 bg-gray-50 space-y-3"> <div>
<div v-for="(field, index) in (configModel.fields || [])" :key="index" class="border p-3 rounded bg-white"> <h4 class="font-medium text-sm text-blue-800 mb-1">Repeating Group Container</h4>
<div class="flex justify-between items-center mb-3"> <p class="text-xs text-blue-700">
<h4 class="font-medium text-sm text-gray-800">Field {{ index + 1 }}</h4> This is a container component. Drag and drop fields into it in the form builder to create the repeating group structure.
<button Each field you add will be repeated for every group item.
@click="removeGroupField(index)" </p>
class="text-red-500 hover:text-red-700 p-1" </div>
type="button" </div>
title="Remove field" </div>
>
<Icon name="material-symbols:delete-outline" class="w-4 h-4" /> <!-- Children Count -->
</button> <div class="border rounded-md p-3 bg-gray-50">
</div> <div class="flex items-center justify-between">
<div>
<div class="grid grid-cols-2 gap-3 mb-3"> <h5 class="text-sm font-medium text-gray-700">Group Fields</h5>
<FormKit <p class="text-xs text-gray-500 mt-1">
type="select" {{ configModel.children?.length || 0 }} field(s) in this repeating group
label="Field Type" </p>
:options="[ </div>
{ label: 'Text', value: 'text' }, <div class="text-sm text-gray-500">
{ label: 'Number', value: 'number' }, <Icon name="material-symbols:drag-indicator" class="w-4 h-4" />
{ label: 'Email', value: 'email' },
{ label: 'Textarea', value: 'textarea' },
{ label: 'Select', value: 'select' },
{ label: 'Date', value: 'date' },
{ label: 'Time', value: 'time' },
{ label: 'Checkbox', value: 'checkbox' },
{ label: 'Radio', value: 'radio' }
]"
v-model="field.type"
:classes="{ outer: 'field-wrapper' }"
/>
<FormKit
type="text"
label="Field Name"
v-model="field.name"
:classes="{ outer: 'field-wrapper' }"
placeholder="e.g., name, age, email"
/>
</div>
<div class="grid grid-cols-2 gap-3">
<FormKit
type="text"
label="Field Label"
v-model="field.label"
:classes="{ outer: 'field-wrapper' }"
placeholder="e.g., Full Name, Age, Email"
/>
<FormKit
type="text"
label="Placeholder"
v-model="field.placeholder"
:classes="{ outer: 'field-wrapper' }"
placeholder="e.g., Enter your name"
/>
</div>
<!-- Options for select/radio/checkbox fields -->
<div v-if="['select', 'radio', 'checkbox'].includes(field.type)" class="mt-3">
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">Options</label>
<div class="border rounded-md p-2 bg-gray-50 space-y-2">
<div v-for="(option, optionIndex) in (field.options || [])" :key="optionIndex" class="flex items-center space-x-2">
<input
type="text"
v-model="field.options[optionIndex].label"
class="flex-1 border border-gray-300 rounded px-2 py-1 text-sm"
placeholder="Option label"
/>
<input
type="text"
v-model="field.options[optionIndex].value"
class="flex-1 border border-gray-300 rounded px-2 py-1 text-sm"
placeholder="Option value"
/>
<button
@click="removeFieldOption(field, optionIndex)"
class="text-red-500 hover:text-red-700 p-1"
type="button"
>
<Icon name="material-symbols:delete-outline" class="w-3 h-3" />
</button>
</div>
<button
@click="addFieldOption(field)"
class="text-sm text-blue-600 hover:text-blue-800 flex items-center"
type="button"
>
<Icon name="material-symbols:add-circle-outline" class="w-3 h-3 mr-1" />
Add Option
</button>
</div>
</div>
</div>
</div> </div>
<button
@click="addGroupField"
class="w-full text-sm text-blue-600 hover:text-blue-800 flex items-center justify-center py-2 border border-dashed border-blue-300 rounded-md hover:bg-blue-50"
type="button"
>
<Icon name="material-symbols:add-circle-outline" class="w-4 h-4 mr-1" />
Add Field to Group
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -59,11 +59,8 @@ function handleChange(event) {
} }
.switch-input:disabled + .switch-label .switch-track { .switch-input:disabled + .switch-label .switch-track {
@apply opacity-50 cursor-not-allowed; @apply opacity-50;
} cursor: not-allowed;
.switch-input:disabled + .switch-label {
@apply cursor-not-allowed;
} }
.switch-thumb { .switch-thumb {
@ -98,20 +95,9 @@ function handleChange(event) {
/* Readonly styles for Switch */ /* Readonly styles for Switch */
.switch-input:disabled + .switch-label .switch-track { .switch-input:disabled + .switch-label .switch-track {
@apply opacity-50 cursor-not-allowed; @apply opacity-50;
cursor: not-allowed;
} }
.switch-input:disabled + .switch-label {
@apply cursor-not-allowed;
}
/* Additional readonly styling */
.switch-label.cursor-not-allowed {
cursor: not-allowed !important;
}
.switch-label.cursor-not-allowed .switch-track {
opacity: 0.8;
background-color: #f3f4f6;
}
</style> </style>

View File

@ -2334,17 +2334,17 @@ const handleUpdateComponent = (updatedComponent) => {
// Check if this is a nested component inside a section // Check if this is a nested component inside a section
let foundInSection = false; let foundInSection = false;
// Look for the component in section children // Look for the component in section or repeating-group children
formStore.formComponents.forEach(component => { formStore.formComponents.forEach(component => {
if (component.type === 'form-section' && component.props.children) { if ((component.type === 'form-section' || component.type === 'repeating-group') && component.props.children) {
const nestedIndex = component.props.children.findIndex(child => child.id === updatedComponent.id); const nestedIndex = component.props.children.findIndex(child => child.id === updatedComponent.id);
if (nestedIndex !== -1) { if (nestedIndex !== -1) {
// Update the nested component // Update the nested component
component.props.children[nestedIndex] = updatedComponent; component.props.children[nestedIndex] = updatedComponent;
// Update the entire section to trigger reactivity // Update the entire container to trigger reactivity
formStore.updateComponent(component); formStore.updateComponent(component);
foundInSection = true; foundInSection = true;
console.log('Updated nested component in section:', updatedComponent); console.log('Updated nested component in container:', updatedComponent);
return; return;
} }
} }
@ -2366,14 +2366,14 @@ const handleDeleteComponent = (id) => {
// Check if this is a nested component inside a section // Check if this is a nested component inside a section
let foundInSection = false; let foundInSection = false;
// Look for the component in section children // Look for the component in section or repeating-group children
formStore.formComponents.forEach(component => { formStore.formComponents.forEach(component => {
if (component.type === 'form-section' && component.props.children) { if ((component.type === 'form-section' || component.type === 'repeating-group') && component.props.children) {
const nestedIndex = component.props.children.findIndex(child => child.id === id); const nestedIndex = component.props.children.findIndex(child => child.id === id);
if (nestedIndex !== -1) { if (nestedIndex !== -1) {
// Remove the nested component // Remove the nested component
const deletedComponent = component.props.children.splice(nestedIndex, 1)[0]; const deletedComponent = component.props.children.splice(nestedIndex, 1)[0];
// Update the entire section to trigger reactivity // Update the entire container to trigger reactivity
formStore.updateComponent(component); formStore.updateComponent(component);
foundInSection = true; foundInSection = true;
@ -2383,7 +2383,7 @@ const handleDeleteComponent = (id) => {
formStore.selectedComponentId = null; formStore.selectedComponentId = null;
} }
console.log('Deleted nested component from section:', deletedComponent); console.log('Deleted nested component from container:', deletedComponent);
return; return;
} }
} }