"use client" import type React from "react" import { useRef } from "react" import { useDrag, useDrop } from "react-dnd" import type { ComponentItem } from "@/contexts/composer-context" import { cn } from "@/lib/utils" import { X, GripVertical } from "lucide-react" import { Button } from "@/components/ui/button" interface DraggableComponentProps { component: ComponentItem index: number moveComponent: (dragIndex: number, hoverIndex: number) => void removeComponent: (id: string) => void children: React.ReactNode } interface DragItem { index: number id: string type: string } export function DraggableComponent({ component, index, moveComponent, removeComponent, children, }: DraggableComponentProps) { const ref = useRef(null) // Set up drag const [{ isDragging }, drag, preview] = useDrag({ type: "COMPONENT", item: { index, id: component.id, type: "COMPONENT" } as DragItem, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), }) // Set up drop const [{ handlerId }, drop] = useDrop({ accept: "COMPONENT", collect(monitor) { return { handlerId: monitor.getHandlerId(), } }, hover(item: DragItem, monitor) { if (!ref.current) { return } const dragIndex = item.index const hoverIndex = index // Don't replace items with themselves if (dragIndex === hoverIndex) { return } // Determine rectangle on screen const hoverBoundingRect = ref.current?.getBoundingClientRect() // Get vertical middle const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 // Determine mouse position const clientOffset = monitor.getClientOffset() // Get pixels to the top const hoverClientY = clientOffset!.y - hoverBoundingRect.top // Only perform the move when the mouse has crossed half of the items height // When dragging downwards, only move when the cursor is below 50% // When dragging upwards, only move when the cursor is above 50% if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { return } if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { return } // Time to actually perform the action moveComponent(dragIndex, hoverIndex) // Note: we're mutating the monitor item here! // Generally it's better to avoid mutations, // but it's good here for the sake of performance // to avoid expensive index searches. item.index = hoverIndex }, }) // Initialize drag and drop refs drag(drop(ref)) return (
{/* Component handle for dragging */}
{/* Remove button */} {/* Component content */}
{children}
) }