2024-09-17 18:32:33 +05:30
|
|
|
import { memo, useEffect, useState } from "react"
|
2024-09-17 11:55:21 +05:30
|
|
|
import { useDragWidgetContext } from "./draggableWidgetContext"
|
|
|
|
|
import { useDragContext } from "../../components/draggable/draggableContext"
|
|
|
|
|
|
|
|
|
|
|
2024-09-17 21:23:01 +05:30
|
|
|
// FIXME: sometimes even after drag end the showDroppable is visible
|
2024-09-17 18:32:33 +05:30
|
|
|
/**
|
|
|
|
|
* @param {} - widgetRef - the widget ref for your widget
|
|
|
|
|
* @param {boolean} - enableDraggable - should the widget be draggable
|
|
|
|
|
* @param {string} - dragElementType - the widget type of widget so the droppable knows if the widget can be accepted
|
|
|
|
|
* @param {() => void} - onDrop - the widget type of widget so the droppable knows if the widget can be accepted
|
|
|
|
|
* @param {string[]} - droppableTags - array of widget that can be dropped on the widget
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const WidgetDraggable = memo(({ widgetRef, enableDrag=true, dragElementType="widget",
|
|
|
|
|
onDragEnter, onDragLeave, onDrop,
|
|
|
|
|
droppableTags = ["widget"], ...props }) => {
|
2024-09-17 11:55:21 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
// const { draggedElement, onDragStart, onDragEnd } = useDragWidgetContext()
|
2024-09-17 18:32:33 +05:30
|
|
|
const { draggedElement, onDragStart, onDragEnd, overElement, setOverElement } = useDragContext()
|
2024-09-17 11:55:21 +05:30
|
|
|
|
2024-09-17 18:32:33 +05:30
|
|
|
// const [dragEnabled, setDragEnabled] = useState(enableDraggable)
|
2024-09-17 11:55:21 +05:30
|
|
|
const [isDragging, setIsDragging] = useState(false)
|
|
|
|
|
|
|
|
|
|
const [showDroppable, setShowDroppable] = useState({
|
|
|
|
|
show: false,
|
|
|
|
|
allow: false
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const handleDragStart = (e) => {
|
|
|
|
|
setIsDragging(true)
|
|
|
|
|
|
|
|
|
|
onDragStart(widgetRef?.current || null)
|
|
|
|
|
|
|
|
|
|
// Create custom drag image with full opacity, this will ensure the image isn't taken from part of the canvas
|
|
|
|
|
const dragImage = widgetRef?.current.cloneNode(true)
|
|
|
|
|
dragImage.style.opacity = '1' // Ensure full opacity
|
|
|
|
|
dragImage.style.position = 'absolute'
|
|
|
|
|
dragImage.style.top = '-9999px' // Move it out of view
|
|
|
|
|
|
|
|
|
|
document.body.appendChild(dragImage)
|
|
|
|
|
const rect = widgetRef?.current.getBoundingClientRect()
|
|
|
|
|
const offsetX = e.clientX - rect.left
|
|
|
|
|
const offsetY = e.clientY - rect.top
|
|
|
|
|
|
|
|
|
|
// Set the custom drag image with correct offset to avoid snapping to the top-left corner
|
|
|
|
|
e.dataTransfer.setDragImage(dragImage, offsetX, offsetY)
|
|
|
|
|
|
|
|
|
|
// Remove the custom drag image after some time to avoid leaving it in the DOM
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
document.body.removeChild(dragImage)
|
|
|
|
|
}, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDragEnter = (e) => {
|
|
|
|
|
|
|
|
|
|
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
|
|
|
|
|
2024-09-17 18:32:33 +05:30
|
|
|
// console.log("Drag entering...", overElement === e.currentTarget)
|
|
|
|
|
|
|
|
|
|
setOverElement(e.currentTarget)
|
|
|
|
|
|
|
|
|
|
let showDrop = {
|
|
|
|
|
allow: true,
|
|
|
|
|
show: true
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 11:55:21 +05:30
|
|
|
if (droppableTags.length === 0 || droppableTags.includes(dragEleType)) {
|
2024-09-17 18:32:33 +05:30
|
|
|
showDrop = {
|
2024-09-17 11:55:21 +05:30
|
|
|
allow: true,
|
|
|
|
|
show: true
|
2024-09-17 18:32:33 +05:30
|
|
|
}
|
|
|
|
|
|
2024-09-17 11:55:21 +05:30
|
|
|
} else {
|
2024-09-17 18:32:33 +05:30
|
|
|
showDrop = {
|
2024-09-17 11:55:21 +05:30
|
|
|
allow: false,
|
|
|
|
|
show: true
|
2024-09-17 18:32:33 +05:30
|
|
|
}
|
2024-09-17 11:55:21 +05:30
|
|
|
}
|
2024-09-17 18:32:33 +05:30
|
|
|
|
|
|
|
|
setShowDroppable(showDrop)
|
|
|
|
|
if (onDragEnter)
|
|
|
|
|
onDragEnter({element: draggedElement, showDrop})
|
|
|
|
|
|
2024-09-17 11:55:21 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDragOver = (e) => {
|
|
|
|
|
// console.log("Drag over: ", e.dataTransfer.getData("text/plain"), e.dataTransfer)
|
|
|
|
|
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
|
|
|
|
|
|
|
|
|
if (droppableTags.length === 0 || droppableTags.includes(dragEleType)) {
|
|
|
|
|
e.preventDefault() // this is necessary to allow drop to take place
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDropEvent = (e) => {
|
2024-09-17 18:32:33 +05:30
|
|
|
e.preventDefault()
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
// console.log("Dropped")
|
2024-09-17 11:55:21 +05:30
|
|
|
|
|
|
|
|
setShowDroppable({
|
|
|
|
|
allow: false,
|
|
|
|
|
show: false
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (onDrop) {
|
|
|
|
|
onDrop(e, draggedElement)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDragLeave = (e) => {
|
|
|
|
|
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
|
|
|
setShowDroppable({
|
|
|
|
|
allow: false,
|
|
|
|
|
show: false
|
|
|
|
|
})
|
2024-09-17 18:32:33 +05:30
|
|
|
|
|
|
|
|
if (onDragLeave)
|
|
|
|
|
onDragLeave()
|
|
|
|
|
|
2024-09-17 11:55:21 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDragEnd = () => {
|
|
|
|
|
onDragEnd()
|
|
|
|
|
setIsDragging(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2024-09-17 18:32:33 +05:30
|
|
|
<div className={`${props.className} tw-w-fit tw-h-fit tw-bg-blue`}
|
2024-09-17 11:55:21 +05:30
|
|
|
onDragOver={handleDragOver}
|
|
|
|
|
onDrop={handleDropEvent}
|
|
|
|
|
onDragEnter={handleDragEnter}
|
|
|
|
|
onDragLeave={handleDragLeave}
|
|
|
|
|
onDragStart={handleDragStart}
|
|
|
|
|
onDragEnd={handleDragEnd}
|
2024-09-17 18:32:33 +05:30
|
|
|
draggable={enableDrag}
|
2024-09-17 11:55:21 +05:30
|
|
|
style={{ opacity: isDragging ? 0 : 1}} // hide the initial position when dragging
|
2024-09-17 18:32:33 +05:30
|
|
|
>
|
2024-09-17 11:55:21 +05:30
|
|
|
{props.children}
|
2024-09-17 18:32:33 +05:30
|
|
|
|
2024-09-17 11:55:21 +05:30
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default WidgetDraggable
|