working with dnd-kit/react

This commit is contained in:
paul
2025-03-02 19:20:45 +05:30
parent cc84a9f2d7
commit 62caafcf23
7 changed files with 149 additions and 107 deletions

18
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@ant-design/icons": "^5.4.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/dom": "^0.0.9",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/react": "^0.0.9",
@@ -2562,9 +2562,10 @@
}
},
"node_modules/@dnd-kit/accessibility": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.0.tgz",
"integrity": "sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
@@ -2584,11 +2585,12 @@
}
},
"node_modules/@dnd-kit/core": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.1.0.tgz",
"integrity": "sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==",
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT",
"dependencies": {
"@dnd-kit/accessibility": "^3.1.0",
"@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2",
"tslib": "^2.0.0"
},

View File

@@ -7,7 +7,7 @@
},
"dependencies": {
"@ant-design/icons": "^5.4.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/dom": "^0.0.9",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/react": "^0.0.9",

View File

@@ -34,6 +34,7 @@ import TkMainWindow from './frameworks/tkinter/widgets/mainWindow'
import CTkMainWindow from './frameworks/customtk/widgets/mainWindow'
import { DndContext, DragOverlay } from '@dnd-kit/core'
import { SidebarOverlayWidgetCard, SidebarWidgetCard } from './components/cards'
import { DragDropProvider } from '@dnd-kit/react'
function App() {
@@ -217,7 +218,7 @@ function App() {
<p>Are you sure you want to change the framework? This will clear the canvas.</p>
</Modal> */}
<DndContext autoScroll={false}>
<DragDropProvider onDragStart={(e) => {console.log("Drag start event: ", e)}}>
<div className="tw-w-full tw-h-[94vh] tw-flex">
<Sidebar tabs={sidebarTabs}/>
@@ -241,7 +242,7 @@ function App() {
)
}
</DndContext>
</DragDropProvider>
</div>
)

View File

@@ -687,30 +687,38 @@ class Canvas extends React.Component {
}
const container = draggedElement.getAttribute("data-container")
const canvasRect = this.canvasRef.current.getBoundingClientRect()
const draggedElementRect = draggedElement.getBoundingClientRect()
const elementWidth = draggedElementRect.width
const elementHeight = draggedElementRect.height
const {x: draggedElementInitialX, y: draggedElementInitialY} = draggedElement.getBoundingClientRect()
// const {x: draggedElementInitialX, y: draggedElementInitialY} = draggedElement.getBoundingClientRect()
// const { clientX, clientY } = e
const { x: clientX, y: clientY} = e.delta;
const {clientX: draggedElementInitialX, clientY: draggedElementInitialY} = e.activatorEvent
console.log("wirking: ", clientX, clientY, draggedElement.getBoundingClientRect())
console.log("wirking: ", clientX, clientY, e, draggedElementInitialX, draggedElementInitialY)
// let finalPosition = {
// x: (clientX - canvasRect.left) / this.state.zoom,
// y: (clientY - canvasRect.top) / this.state.zoom,
// }
// let finalPosition = {
// x: ((draggedElementInitialX + clientX) - canvasRect.left) / (this.state.zoom),
// y: ((draggedElementInitialY + clientY) - canvasRect.top) / (this.state.zoom),
// }
let finalPosition = {
x: ((draggedElementInitialX + clientX) - canvasRect.left) / (1 || this.state.zoom),
y: ((draggedElementInitialY + clientY) - canvasRect.top) / (1 ||this.state.zoom),
x: (e.activatorEvent.pageX - canvasRect.left) / (this.state.zoom),
y: (e.activatorEvent.pageY - canvasRect.top) / (this.state.zoom),
}
console.log("final position: ", finalPosition, draggedElementInitialX, clientX, canvasRect.left)
// FIXME: error in canvasRect.top
console.log("final position: ", finalPosition, draggedElementInitialX, clientX, canvasRect, "Top: ",
canvasRect.top, clientY, draggedElementInitialY)
if (container === WidgetContainer.SIDEBAR) {

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useRef } from "react"
import { useDndMonitor, useDraggable } from "@dnd-kit/core"
import { useDraggable } from "@dnd-kit/react"
import { CSS } from "@dnd-kit/utilities"
import { useDragContext } from "../draggableContext"
@@ -8,37 +8,40 @@ function Draggable(props) {
const draggableRef = useRef(null)
const { attributes, listeners, setNodeRef, transform } = useDraggable({
const { ref } = useDraggable({
id: props.id,
data: { title: props.children }
feedback: "clone"
// data: { title: props.children }
})
const {onDragStart, onDragEnd, disableStyle=false} = useDragContext()
useDndMonitor({
onDragStart(event){
if (event.active.id === props.id) { // Ensure only this element triggers it
handleDragStart()
}
},
onDragEnd(event){
if (event.active.id === props.id) { // Ensure only this element triggers it
handleDragEnd()
}
},
})
// TODO: add monitor and handle drag events ASAP
useEffect(() => {
// useDndMonitor({
// onDragStart(event){
// if (event.active.id === props.id) { // Ensure only this element triggers it
// handleDragStart()
// }
// },
// onDragEnd(event){
// if (event.active.id === props.id) { // Ensure only this element triggers it
// handleDragEnd()
// }
// },
// })
// useEffect(() => {
if (draggableRef.current)
setNodeRef(draggableRef.current)
// if (draggableRef.current)
// setNodeRef(draggableRef.current)
}, [draggableRef.current, setNodeRef])
// }, [draggableRef.current, setNodeRef])
const { dragElementType, dragWidgetClass = null, elementMetaData } = props
const style = transform ? {
transform: CSS.Translate.toString(transform),
} : undefined
// const style = transform ? {
// transform: CSS.Translate.toString(transform),
// } : undefined
const handleDragStart = (event) => {
@@ -61,15 +64,13 @@ function Draggable(props) {
return (
<div
{...props}
ref={draggableRef}
ref={ref}
className={`${props.className}`}
// style={!disableStyle ? style : null} //enable this to show like the original item is moving, if commented out the original item will not have css effects
draggable
data-drag-start-within // this attribute indicates that the drag is occurring from within the project and not a outside file drop
data-draggable-type={dragElementType}
{...listeners}
{...attributes}
>
{props.children}
</div>

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from 'react'
import { useDndMonitor, useDroppable } from '@dnd-kit/core'
import { useDragDropManager, useDroppable } from '@dnd-kit/react'
import { useDragContext } from '../draggableContext'
@@ -7,49 +7,60 @@ function Droppable(props) {
const droppableRef = useRef(null)
// const [dragPosition, setDragPosition] = useState({
// startX: 0,
// startY: 0,
// endX: 0,
// endY: 0
// })
const { isOver, setNodeRef } = useDroppable({
const { isOver, ref, isDropTarget, droppable } = useDroppable({
id: props.id,
})
const style = {
backgroundColor: isOver ? 'green' : '',
}
useDndMonitor({
onDragStart: (event) => {
if (event.over?.id === props.id){
handleDragEnter(event)
// setDragPosition({
// ...dragPosition,
// startX: event.over.
// })
// const manager = useDragDropManager({
// onDragStart: (event) => {
// if (event.over?.id === props.id){
// handleDragEnter(event)
// // setDragPosition({
// // ...dragPosition,
// // startX: event.over.
// // })
console.log("starting: ", event)
}
},
onDragMove: (event) => {
console.log("Drag move: ", event.active.rect)
if (event.over?.id === props.id)
handleDragOver(event)
},
onDragEnd: (event) => {
// }
// console.log("starting: ", event)
// },
// onDragMove: (event) => {
// console.log("Drag move: ", event.active.rect)
// if (event.over?.id === props.id)
// handleDragOver(event)
// },
// onDragEnd: (event) => {
// if (event.over?.id === props.id){
// if (event.over) {
// handleDropEvent(event) // Item was dropped inside a valid container
// } else {
// handleDragLeave(event) // Drag was canceled
// }
// }
// },
// })
const manager = useDragDropManager()
useEffect(() => {
manager?.monitor?.addEventListener("dragstart", handleDragEnter)
manager?.monitor?.addEventListener("dragend", handleDragLeave)
manager?.monitor?.addEventListener("dragover", handleDragOver)
return () => {
manager?.monitor?.removeEventListener("dragstart", handleDragEnter)
manager?.monitor?.removeEventListener("dragend", handleDragLeave)
manager?.monitor?.removeEventListener("dragover", handleDragOver)
}
}, [manager])
if (event.over?.id === props.id){
if (event.over) {
handleDropEvent(event) // Item was dropped inside a valid container
} else {
handleDragLeave(event) // Drag was canceled
}
}
},
})
const { droppableTags, onDrop } = props
@@ -59,27 +70,24 @@ function Droppable(props) {
const [allowDrop, setAllowDrop] = useState(false) // indicator if the draggable can be dropped on the droppable
useEffect(() => {
// useEffect(() => {
if (droppableRef.current)
setNodeRef(droppableRef.current)
// if (droppableRef.current)
// setNodeRef(droppableRef.current)
}, [droppableRef.current, setNodeRef])
// }, [droppableRef.current, setNodeRef])
useEffect(() => {
if (draggedElement === null) {
setAllowDrop({
show: false,
allow: false
})
}
}, [draggedElement])
// TODO: handle Drop on Canvas
const handleDragEnter = (e) => {
const {target} = e.operation
if (target && target?.id !== props?.id){
return
}
if (!draggedElement || !draggedElement.getAttribute("data-drag-start-within")) {
// if the drag is starting from outside (eg: file drop) or if drag doesn't exist
return
@@ -100,6 +108,14 @@ function Droppable(props) {
const handleDragOver = (e) => {
const {target} = e.operation
if (target && target?.id !== props?.id){
return
}
console.log("Over sir1: ", draggedElement)
if (!draggedElement || !draggedElement.getAttribute("data-drag-start-within")) {
// if the drag is starting from outside (eg: file drop) or if drag doesn't exist
return
@@ -107,12 +123,15 @@ function Droppable(props) {
// console.log("Drag over: ", e.dataTransfer.getData("text/plain"), e.dataTransfer)
const dragElementType = draggedElement.getAttribute("data-draggable-type")
const allowDrop = (droppableTags && droppableTags !== null && (Object.keys(droppableTags).length === 0 ||
(droppableTags.include?.length > 0 && droppableTags.include?.includes(dragElementType)) ||
(droppableTags.exclude?.length > 0 && !droppableTags.exclude?.includes(dragElementType))
))
console.log("Over sir: ", allowDrop)
setAllowDrop(allowDrop)
// if (allowDrop) {
// e.preventDefault() // this is necessary to allow drop to take place
@@ -122,10 +141,13 @@ function Droppable(props) {
const handleDropEvent = (e) => {
setAllowDrop({
allow: false,
show: false
})
const {target} = e.operation
if (target && target?.id !== props?.id){
return
}
setAllowDrop(false)
if (!draggedElement || !draggedElement.getAttribute("data-drag-start-within")) {
// if the drag is starting from outside (eg: file drop) or if drag doesn't exist
@@ -150,16 +172,20 @@ function Droppable(props) {
const handleDragLeave = (e) => {
if (!e.currentTarget.contains(e.relatedTarget)) {
setAllowDrop({
allow: false,
show: false
})
const {target} = e.operation
console.log("Drag: ", target?.id, props.id)
if (target && target.id === props.id){
handleDropEvent(e)
}else{
setAllowDrop(false)
}
}
return (
<div ref={droppableRef} style={style} className={props.className || ''}>
<div ref={ref} style={style} className={props.className || ''}>
{props.children}
{/* {
showDroppable.show &&
@@ -171,7 +197,7 @@ function Droppable(props) {
} */}
{
isOver &&
isDropTarget &&
<div className={`${allowDrop ? "tw-bg-[#82ff1c6e]" : "tw-bg-[#eb5d366e]"}
tw-absolute tw-top-0 tw-left-0 tw-w-full tw-h-full tw-z-[999]
tw-border-2 tw-border-dashed tw-rounded-lg tw-pointer-events-none

View File

@@ -1,5 +1,6 @@
import React, { createContext, useContext, useState } from 'react'
import { isSubClassOfWidget } from '../../utils/widget'
import { useDndContext } from '@dnd-kit/core'
// import Widget from '../../canvas/widgets/base'
export const DragContext = createContext()
@@ -11,13 +12,14 @@ export const DragProvider = ({ children }) => {
const [draggedElement, setDraggedElement] = useState(null)
const [overElement, setOverElement] = useState(null) // the element the dragged items is over
const [dragElementMetaData, setDragElementMetaData] = useState({})
const [widgetClass, setWidgetClass] = useState(null) // helper to help pass the widget type from sidebar to canvas
const [isDragging, setIsDragging] = useState(false)
const onDragStart = (element, widgetClass=null, metaData={}) => {
const onDragStart = (element, widgetClass=null, metaData={}, pos={x: 0, y: 0}) => {
setDraggedElement(element)
setIsDragging(true)
setDragElementMetaData(metaData)
@@ -26,9 +28,11 @@ export const DragProvider = ({ children }) => {
throw new Error("widgetClass must inherit from the Widget base class")
setWidgetClass(() => widgetClass) // store the class so later it can be passed to the canvas from sidebar
}
const onDragEnd = () => {
const onDragEnd = (pos) => {
setDraggedElement(null)
setWidgetClass(null)
setIsDragging(false)
@@ -40,7 +44,7 @@ export const DragProvider = ({ children }) => {
return (
<DragContext.Provider value={{ draggedElement, overElement, setOverElement,
widgetClass, onDragStart, onDragEnd, isDragging,
dragElementMetaData, setDragElementMetaData
dragElementMetaData, setDragElementMetaData,
}}>
{children}
</DragContext.Provider>