Enhance Form Builder with New Component Insertion and Styling Updates
- Added functionality to insert components at a specific index in the form builder, allowing for more flexible component arrangement. - Updated the form store to support the new insertion method, ensuring components can be added dynamically at designated positions. - Modified CSS styles for form components, including adjustments to disabled states and ghost component appearance for improved user experience. - Refined drag-and-drop behavior by removing animation for immediate feedback during reordering, enhancing usability. - Introduced a new background color for disabled states to maintain visual consistency across the form builder.
This commit is contained in:
parent
577128a799
commit
b29c035370
@ -10,6 +10,7 @@ html[data-theme="default"] {
|
|||||||
--border-color: 228, 228, 231;
|
--border-color: 228, 228, 231;
|
||||||
--bg-1: 243, 244, 246;
|
--bg-1: 243, 244, 246;
|
||||||
--bg-2: 255, 255, 255;
|
--bg-2: 255, 255, 255;
|
||||||
|
--bg-disabled: 250, 250, 250;
|
||||||
--scroll-color: 170, 170, 170;
|
--scroll-color: 170, 170, 170;
|
||||||
--scroll-hover-color: 155, 155, 155;
|
--scroll-hover-color: 155, 155, 155;
|
||||||
--fk-border-color: 228, 228, 231;
|
--fk-border-color: 228, 228, 231;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.formkit-outer-global {
|
.formkit-outer-global {
|
||||||
@apply mb-4 text-[rgb(var(--text-color))] formkit-disabled:opacity-50;
|
@apply mb-4 text-[rgb(var(--text-color))];
|
||||||
}
|
}
|
||||||
|
|
||||||
.formkit-help-global {
|
.formkit-help-global {
|
||||||
|
@ -26,9 +26,9 @@
|
|||||||
bg-[rgb(var(--bg-2))]
|
bg-[rgb(var(--bg-2))]
|
||||||
placeholder-[rgb(var(--fk-placeholder-color))]
|
placeholder-[rgb(var(--fk-placeholder-color))]
|
||||||
focus:outline-none
|
focus:outline-none
|
||||||
disabled:bg-[rgb(var(--bg-1))]
|
disabled:bg-[rgb(var(--bg-disabled))]
|
||||||
disabled:border-[rgb(var(--bg-1))]
|
disabled:border-[rgb(var(--bg-disabled))]
|
||||||
disabled:placeholder-[rgb(var(--bg-1))];
|
disabled:placeholder-[rgb(var(--bg-disabled))];
|
||||||
}
|
}
|
||||||
|
|
||||||
.formkit-prefix-text {
|
.formkit-prefix-text {
|
||||||
|
@ -29,13 +29,14 @@
|
|||||||
item-key="id"
|
item-key="id"
|
||||||
handle=".drag-handle"
|
handle=".drag-handle"
|
||||||
ghost-class="ghost"
|
ghost-class="ghost"
|
||||||
animation="300"
|
animation="0"
|
||||||
class="draggable-container"
|
class="draggable-container"
|
||||||
@end="onDragEnd"
|
@end="onDragEnd"
|
||||||
|
@start="onDragStart"
|
||||||
>
|
>
|
||||||
<template #item="{ element, index }">
|
<template #item="{ element, index }">
|
||||||
<div
|
<div
|
||||||
class="form-component relative border rounded-md overflow-hidden transition-all duration-200 cursor-pointer"
|
class="form-component relative border rounded-md overflow-hidden cursor-pointer"
|
||||||
:class="{
|
:class="{
|
||||||
'ring-2 ring-blue-500 bg-blue-50 border-blue-300 shadow-lg': selectedComponentId === element.id,
|
'ring-2 ring-blue-500 bg-blue-50 border-blue-300 shadow-lg': selectedComponentId === element.id,
|
||||||
'bg-white border-gray-200 hover:border-blue-300 hover:shadow-md hover:bg-blue-25': selectedComponentId !== element.id
|
'bg-white border-gray-200 hover:border-blue-300 hover:shadow-md hover:bg-blue-25': selectedComponentId !== element.id
|
||||||
@ -114,7 +115,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['select-component', 'move-component', 'delete-component', 'update-component', 'optimize-layout', 'select-nested-component']);
|
const emit = defineEmits(['select-component', 'move-component', 'delete-component', 'update-component', 'optimize-layout', 'select-nested-component', 'insert-component-at-index']);
|
||||||
|
|
||||||
const selectedComponentId = ref(null);
|
const selectedComponentId = ref(null);
|
||||||
const resizeMode = ref(false);
|
const resizeMode = ref(false);
|
||||||
@ -273,6 +274,11 @@ const stopResize = () => {
|
|||||||
document.removeEventListener('mouseup', stopResize);
|
document.removeEventListener('mouseup', stopResize);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle drag start from internal reordering
|
||||||
|
const onDragStart = (event) => {
|
||||||
|
// Internal reordering
|
||||||
|
};
|
||||||
|
|
||||||
// Handle drag end event for reordering
|
// Handle drag end event for reordering
|
||||||
const onDragEnd = (event) => {
|
const onDragEnd = (event) => {
|
||||||
if (event.oldIndex !== event.newIndex) {
|
if (event.oldIndex !== event.newIndex) {
|
||||||
@ -294,7 +300,7 @@ onUnmounted(() => {
|
|||||||
.grid-container {
|
.grid-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(12, 1fr);
|
grid-template-columns: repeat(12, 1fr);
|
||||||
grid-auto-flow: row dense; /* This enables automatic filling of gaps */
|
grid-auto-flow: row; /* Changed: Remove 'dense' to preserve intentional spacing */
|
||||||
column-gap: 16px;
|
column-gap: 16px;
|
||||||
row-gap: 16px;
|
row-gap: 16px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -307,20 +313,23 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ghost {
|
.ghost {
|
||||||
opacity: 0.5;
|
opacity: 0.3;
|
||||||
background: #e0f2fe;
|
background: #dbeafe;
|
||||||
border: 1px dashed #60a5fa;
|
border: 2px dashed #3b82f6;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
grid-column: span 12 !important;
|
grid-column: span 12 !important;
|
||||||
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-component {
|
.form-component {
|
||||||
transition: all 0.2s ease;
|
/* Removed transition for immediate snapping */
|
||||||
grid-column: span 12; /* Default to full width */
|
grid-column: span 12; /* Default to full width */
|
||||||
width: 100% !important; /* Force the width within the grid cell */
|
width: 100% !important; /* Force the width within the grid cell */
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.form-component:hover .component-actions {
|
.form-component:hover .component-actions {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
@ -330,6 +330,7 @@
|
|||||||
@delete-component="handleDeleteComponent"
|
@delete-component="handleDeleteComponent"
|
||||||
@update-component="handleUpdateComponent"
|
@update-component="handleUpdateComponent"
|
||||||
@optimize-layout="handleOptimizeLayout"
|
@optimize-layout="handleOptimizeLayout"
|
||||||
|
@insert-component-at-index="handleInsertComponentAtIndex"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Instruction Overlay when no component is selected -->
|
<!-- Instruction Overlay when no component is selected -->
|
||||||
@ -2727,6 +2728,11 @@ const handleOptimizeLayout = () => {
|
|||||||
formStore.optimizeGridLayout();
|
formStore.optimizeGridLayout();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle inserting component at specific index (for drop zones)
|
||||||
|
const handleInsertComponentAtIndex = ({ component, index }) => {
|
||||||
|
formStore.insertComponentAtIndex(component, index);
|
||||||
|
};
|
||||||
|
|
||||||
// Add the new handler function
|
// Add the new handler function
|
||||||
const handleDragEnter = (event) => {
|
const handleDragEnter = (event) => {
|
||||||
// Prevent default to allow drop
|
// Prevent default to allow drop
|
||||||
|
@ -425,7 +425,7 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Find optimal placement for a new component in the grid
|
// Find optimal placement for a new component in the grid
|
||||||
findOptimalGridPlacement() {
|
findOptimalGridPlacement(respectDesignSpacing = true) {
|
||||||
if (this.formComponents.length === 0) {
|
if (this.formComponents.length === 0) {
|
||||||
// First component - full width
|
// First component - full width
|
||||||
return {
|
return {
|
||||||
@ -435,6 +435,16 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If respecting design spacing, always create a new row (don't auto-fill gaps)
|
||||||
|
if (respectDesignSpacing) {
|
||||||
|
return {
|
||||||
|
gridColumn: 'span 12',
|
||||||
|
rowIndex: this.formComponents.length,
|
||||||
|
width: '100%'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy auto-fill behavior (kept for compatibility)
|
||||||
// Group components by their implicit row
|
// Group components by their implicit row
|
||||||
const rows = [];
|
const rows = [];
|
||||||
let currentRowY = 0;
|
let currentRowY = 0;
|
||||||
@ -505,6 +515,62 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Insert component at a specific index (for drop zones)
|
||||||
|
insertComponentAtIndex(component, index) {
|
||||||
|
console.log('FormStore: Inserting component at index', index, component.type);
|
||||||
|
|
||||||
|
// Store the state before the change for history
|
||||||
|
const beforeComponents = [...this.formComponents];
|
||||||
|
|
||||||
|
const newComponentId = uuidv4();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a deep copy of the default props to avoid reference issues
|
||||||
|
const defaultProps = component.defaultProps ? JSON.parse(JSON.stringify(component.defaultProps)) : {};
|
||||||
|
|
||||||
|
// Set default grid properties - respect design spacing by using full width
|
||||||
|
defaultProps.width = defaultProps.width || '100%';
|
||||||
|
defaultProps.gridColumn = defaultProps.gridColumn || 'span 12';
|
||||||
|
|
||||||
|
// Generate a default name based on component type if not provided
|
||||||
|
if (!defaultProps.name) {
|
||||||
|
defaultProps.name = `${component.type}_${this.formComponents.length + 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a default label based on component name if not provided
|
||||||
|
if (!defaultProps.label && !['heading', 'paragraph', 'divider'].includes(component.type)) {
|
||||||
|
defaultProps.label = `${component.name} ${this.formComponents.length + 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new component object
|
||||||
|
const newComponent = {
|
||||||
|
id: newComponentId,
|
||||||
|
type: component.type,
|
||||||
|
name: component.name,
|
||||||
|
icon: component.icon,
|
||||||
|
category: component.category,
|
||||||
|
props: defaultProps
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert the component at the specified index
|
||||||
|
this.formComponents.splice(index, 0, newComponent);
|
||||||
|
|
||||||
|
// Select the new component
|
||||||
|
this.selectedComponentId = newComponentId;
|
||||||
|
|
||||||
|
// Mark as having unsaved changes
|
||||||
|
this.hasUnsavedChanges = true;
|
||||||
|
|
||||||
|
// Record history
|
||||||
|
this.recordAction('Add component', beforeComponents, [...this.formComponents]);
|
||||||
|
|
||||||
|
console.log('FormStore: Component inserted at index', index, 'Total components:', this.formComponents.length);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error inserting component at index:', error);
|
||||||
|
console.error('Problematic component:', component);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
selectComponent(id) {
|
selectComponent(id) {
|
||||||
// Don't record history for selection changes
|
// Don't record history for selection changes
|
||||||
this.selectedComponentId = id;
|
this.selectedComponentId = id;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user