WIP - add drag functionallity for page layouts

This commit is contained in:
Francisco Gaona
2025-12-23 00:04:06 +01:00
parent eff8f32c9d
commit 8090d6099c

View File

@@ -16,7 +16,12 @@
</div> </div>
<div class="border rounded-lg bg-slate-50 dark:bg-slate-900 p-4 min-h-[600px]"> <div class="border rounded-lg bg-slate-50 dark:bg-slate-900 p-4 min-h-[600px]">
<div ref="gridContainer" class="grid-stack"> <div
ref="gridContainer"
class="grid-stack"
@dragover.prevent="handleDragOver"
@drop="handleDrop"
>
<!-- Grid items will be dynamically added here --> <!-- Grid items will be dynamically added here -->
</div> </div>
</div> </div>
@@ -25,12 +30,15 @@
<!-- Available Fields Sidebar --> <!-- Available Fields Sidebar -->
<div class="w-80 border-l bg-white dark:bg-slate-950 p-4 overflow-auto"> <div class="w-80 border-l bg-white dark:bg-slate-950 p-4 overflow-auto">
<h3 class="text-lg font-semibold mb-4">Available Fields</h3> <h3 class="text-lg font-semibold mb-4">Available Fields</h3>
<p class="text-xs text-muted-foreground mb-4">Click to add field to grid</p> <p class="text-xs text-muted-foreground mb-4">Click or drag to add field to grid</p>
<div class="space-y-2"> <div class="space-y-2" id="sidebar-fields">
<div <div
v-for="field in availableFields" v-for="field in availableFields"
:key="field.id" :key="field.id"
class="p-3 border rounded cursor-pointer bg-white dark:bg-slate-900 hover:border-primary hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors" class="p-3 border rounded cursor-move bg-white dark:bg-slate-900 hover:border-primary hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors"
:data-field-id="field.id"
draggable="true"
@dragstart="handleDragStart($event, field)"
@click="addFieldToGrid(field)" @click="addFieldToGrid(field)"
> >
<div class="font-medium text-sm">{{ field.label }}</div> <div class="font-medium text-sm">{{ field.label }}</div>
@@ -44,7 +52,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue' import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import { GridStack } from 'gridstack' import { GridStack } from 'gridstack'
import 'gridstack/dist/gridstack.min.css' import 'gridstack/dist/gridstack.min.css'
import type { FieldLayoutItem } from '~/types/page-layout' import type { FieldLayoutItem } from '~/types/page-layout'
@@ -79,9 +87,11 @@ const initGrid = () => {
grid = GridStack.init({ grid = GridStack.init({
column: 6, column: 6,
cellHeight: 80, cellHeight: 80,
minRow: 1, minRow: 10,
float: true, float: true,
animate: true, animate: true,
acceptWidgets: true,
disableOneColumnMode: true,
resizable: { resizable: {
handles: 'e, w' handles: 'e, w'
} }
@@ -95,7 +105,8 @@ const initGrid = () => {
// Listen for item removal // Listen for item removal
grid.on('removed', (event, items) => { grid.on('removed', (event, items) => {
items.forEach(item => { items.forEach(item => {
const fieldId = item.el?.getAttribute('data-field-id') const contentEl = item.el?.querySelector('.grid-stack-item-content')
const fieldId = contentEl?.getAttribute('data-field-id')
if (fieldId) { if (fieldId) {
placedFieldIds.value.delete(fieldId) placedFieldIds.value.delete(fieldId)
gridItems.value.delete(fieldId) gridItems.value.delete(fieldId)
@@ -121,10 +132,46 @@ const loadLayout = (layout: FieldLayoutItem[]) => {
} }
const handleDragStart = (event: DragEvent, field: FieldConfig) => { const handleDragStart = (event: DragEvent, field: FieldConfig) => {
// Store field data for drop event if (event.dataTransfer) {
event.dataTransfer?.setData('field', JSON.stringify(field)) event.dataTransfer.effectAllowed = 'copy';
event.dataTransfer.setData('application/json', JSON.stringify(field));
}
} }
const handleDragOver = (event: DragEvent) => {
event.preventDefault();
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'copy';
}
};
const handleDrop = (event: DragEvent) => {
event.preventDefault();
const fieldData = event.dataTransfer?.getData('application/json');
if (!fieldData || !grid) return;
const field = JSON.parse(fieldData);
// Get the grid bounding rect
const gridRect = gridContainer.value?.getBoundingClientRect();
if (!gridRect) return;
// Calculate grid position from drop coordinates
const x = event.clientX - gridRect.left;
const y = event.clientY - gridRect.top;
// Convert pixels to grid coordinates (approx)
const cellWidth = gridRect.width / 6; // 6 columns
const cellHeight = 80; // from our config
const gridX = Math.floor(x / cellWidth);
const gridY = Math.floor(y / cellHeight);
// Add the field at the calculated position
addFieldToGrid(field, gridX, gridY);
};
const addFieldToGrid = (field: FieldConfig, x?: number, y?: number, w: number = 3, h: number = 1) => { const addFieldToGrid = (field: FieldConfig, x?: number, y?: number, w: number = 3, h: number = 1) => {
if (!grid || placedFieldIds.value.has(field.id)) return if (!grid || placedFieldIds.value.has(field.id)) return