fixed widget dragging inside the canvas
This commit is contained in:
@@ -10,6 +10,8 @@ import DraggableWrapper from "../../components/draggable/draggable"
|
||||
import DroppableWrapper from "../../components/draggable/droppable"
|
||||
|
||||
import { ActiveWidgetContext } from "../activeWidgetContext"
|
||||
import { DragWidgetProvider } from "./draggableWidgetContext"
|
||||
import WidgetDraggable from "./widgetDragDrop"
|
||||
|
||||
|
||||
/**
|
||||
@@ -521,8 +523,13 @@ class Widget extends React.Component {
|
||||
|
||||
// console.log("selected: ", this.state.selected)
|
||||
return (
|
||||
<div data-id={this.__id} ref={this.elementRef} className="tw-absolute tw-shadow-xl tw-w-fit tw-h-fit"
|
||||
<WidgetDraggable widgetRef={this.elementRef}>
|
||||
<div data-widget-id={this.__id}
|
||||
ref={this.elementRef}
|
||||
className="tw-absolute tw-shadow-xl tw-w-fit tw-h-fit"
|
||||
style={outerStyle}
|
||||
data-draggable-type={this.getWidgetType()} // helps with droppable
|
||||
data-container={"canvas"} // indicates how the canvas should handle dragging, one is sidebar other is canvas
|
||||
>
|
||||
|
||||
{this.renderContent()}
|
||||
@@ -565,6 +572,7 @@ class Widget extends React.Component {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</WidgetDraggable>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
24
src/canvas/widgets/draggableWidgetContext.js
Normal file
24
src/canvas/widgets/draggableWidgetContext.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
|
||||
const DragWidgetContext = createContext()
|
||||
|
||||
export const useDragWidgetContext = () => useContext(DragWidgetContext)
|
||||
|
||||
// Provider component to wrap around parts of your app that need drag-and-drop functionality
|
||||
export const DragWidgetProvider = ({ children }) => {
|
||||
const [draggedElement, setDraggedElement] = useState(null)
|
||||
|
||||
const onDragStart = (element) => {
|
||||
setDraggedElement(element)
|
||||
}
|
||||
|
||||
const onDragEnd = () => {
|
||||
setDraggedElement(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<DragWidgetContext.Provider value={{ draggedElement, onDragStart, onDragEnd }}>
|
||||
{children}
|
||||
</DragWidgetContext.Provider>
|
||||
)
|
||||
}
|
||||
127
src/canvas/widgets/widgetDragDrop.js
Normal file
127
src/canvas/widgets/widgetDragDrop.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { memo, useState } from "react"
|
||||
import { useDragWidgetContext } from "./draggableWidgetContext"
|
||||
import { useDragContext } from "../../components/draggable/draggableContext"
|
||||
|
||||
|
||||
const WidgetDraggable = memo(({ widgetRef, dragElementType="widget", onDrop, droppableTags = ["widget"], ...props }) => {
|
||||
|
||||
|
||||
// const { draggedElement, onDragStart, onDragEnd } = useDragWidgetContext()
|
||||
const { draggedElement, onDragStart, onDragEnd } = useDragContext()
|
||||
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
|
||||
const [showDroppable, setShowDroppable] = useState({
|
||||
show: false,
|
||||
allow: false
|
||||
})
|
||||
|
||||
const handleDragStart = (e) => {
|
||||
setIsDragging(true)
|
||||
|
||||
console.log("Draggable widget ref: ", widgetRef)
|
||||
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")
|
||||
|
||||
if (droppableTags.length === 0 || droppableTags.includes(dragEleType)) {
|
||||
setShowDroppable({
|
||||
allow: true,
|
||||
show: true
|
||||
})
|
||||
} else {
|
||||
setShowDroppable({
|
||||
allow: false,
|
||||
show: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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) => {
|
||||
|
||||
setShowDroppable({
|
||||
allow: false,
|
||||
show: false
|
||||
})
|
||||
|
||||
if (onDrop) {
|
||||
onDrop(e, draggedElement)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleDragLeave = (e) => {
|
||||
if (!e.currentTarget.contains(e.relatedTarget)) {
|
||||
setShowDroppable({
|
||||
allow: false,
|
||||
show: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleDragEnd = () => {
|
||||
onDragEnd()
|
||||
setIsDragging(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${props.className} tw-w-fit tw-h-fit`}
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDropEvent}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragLeave={handleDragLeave}
|
||||
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
draggable
|
||||
style={{ opacity: isDragging ? 0 : 1}} // hide the initial position when dragging
|
||||
>
|
||||
{
|
||||
showDroppable.show &&
|
||||
<div className={`${showDroppable.allow ? "tw-border-green-600" : "tw-border-red-600"}
|
||||
tw-absolute tw-top-0 tw-left-0 tw-w-full tw-h-full tw-z-[2]
|
||||
tw-border-2 tw-border-dashed tw-rounded-lg
|
||||
`}>
|
||||
</div>
|
||||
}
|
||||
{props.children}
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
})
|
||||
|
||||
|
||||
export default WidgetDraggable
|
||||
Reference in New Issue
Block a user