reworking on drag context

This commit is contained in:
paul
2024-09-16 12:23:15 +05:30
parent f692af7f3f
commit f79b6514db
10 changed files with 184 additions and 151 deletions

View File

@@ -12,6 +12,7 @@ import WidgetsContainer from './sidebar/widgetsContainer'
import Widget from './canvas/widgets/base'
import { DraggableWidgetCard } from './components/cards'
import { DragProvider } from './components/draggable/draggableContext'
function App() {
@@ -95,15 +96,6 @@ function App() {
return
}
setDropAnimation(null)
// FIXME: drop offset is not correct
// Calculate the dragged item's bounding rectangle
const itemRect = activeItemElement.getBoundingClientRect();
const itemCenterX = itemRect.left + itemRect.width / 2
const itemCenterY = itemRect.top + itemRect.height / 2
console.log("widget overlay: ", delta, itemRect)
// Get widget dimensions (assuming you have a way to get these)
const widgetWidth = activeItemElement.offsetWidth; // Adjust this based on how you get widget size
@@ -114,11 +106,6 @@ function App() {
const canvasTranslate = canvasRef.current.getCanvasTranslation()
const zoom = canvasRef.current.getZoom()
// let finalPosition = {
// x: (delta.x - canvasContainerRect.x - canvasTranslate.x) / zoom,
// y: (delta.y - canvasContainerRect.y - canvasTranslate.y) / zoom,
// }
let finalPosition = {
x: (initialPosition.x + delta.x - canvasContainerRect.x - canvasTranslate.x) / zoom - (widgetWidth / 2),
y: (initialPosition.y + delta.y - canvasContainerRect.y - canvasTranslate.y) / zoom - (widgetHeight / 2),
@@ -151,32 +138,15 @@ function App() {
<div className="tw-w-full tw-h-[100vh] tw-flex tw-flex-col tw-bg-primaryBg">
<Header className="tw-h-[6vh]"/>
<DndContext
modifiers={[snapCenterToCursor]}
collisionDetection={rectIntersection}
onDragStart={handleDragStart}
onDragMove={handleDragMove}
// onDragEnd={handleDragEnd}
>
<DragProvider>
<div className="tw-w-full tw-h-[94vh] tw-flex">
<Sidebar tabs={sidebarTabs}/>
<Canvas ref={canvasRef} widgets={canvasWidgets} onWidgetAdded={handleWidgetAddedToCanvas}/>
</div>
{/* dragOverlay (dnd-kit) helps move items from one container to another */}
<DragOverlay dropAnimation={dropAnimation}>
{activeSidebarWidget ? (
<DraggableWidgetCard name={activeSidebarWidget.name}
img={activeSidebarWidget.img}
url={activeSidebarWidget.link}
innerRef={widgetOverlayRef}
/>
):
null}
</DragOverlay>
</DndContext>
</DragProvider>
</div>
)
}

View File

@@ -18,7 +18,7 @@ import { WidgetContext } from './context/widgetContext'
// import {ReactComponent as DotsBackground} from "../assets/background/dots.svg"
import DotsBackground from "../assets/background/dots.svg"
import DroppableWrapper from "../components/utils/droppable"
import DroppableWrapper from "../components/draggable/droppable"
// const DotsBackground = require("../assets/background/dots.svg")
@@ -527,28 +527,23 @@ class Canvas extends React.Component {
}
/**
* Handles drop event to canvas from the sidebar
* @param {DragEvent} e
*/
handleDropEvent = (e) => {
e.preventDefault()
console.log("event: ", e, this.canvasContainerRef.current.offsetTop)
// const canvasContainerRect = this.getCanvasContainerBoundingRect()
const canvasRect = this.canvasRef.current.getBoundingClientRect()
console.log("canvas rect: ", canvasRect)
const { clientX, clientY } = e
let finalPosition = {
x: (e.clientX - canvasRect.left) / this.state.zoom,
y: (e.clientY - canvasRect.top) / this.state.zoom,
const finalPosition = {
x: (clientX - canvasRect.left) / this.state.zoom,
y: (clientY - canvasRect.top) / this.state.zoom,
}
console.log("final: ", finalPosition, finalPosition.x*this.state.zoom, finalPosition.y*this.state.zoom, this.state.zoom)
this.addWidget(Widget, ({id, widgetRef}) => {
widgetRef.current.setPos(finalPosition.x, finalPosition.y)
})
@@ -606,10 +601,10 @@ class Canvas extends React.Component {
{/* </Dropdown> */}
</DroppableWrapper>
{/* <CanvasToolBar isOpen={this.state.toolbarOpen}
<CanvasToolBar isOpen={this.state.toolbarOpen}
widgetType={this.state.selectedWidgets?.at(0)?.getWidgetType() || ""}
attrs={this.state.toolbarAttrs}
/> */}
/>
</div>
)
}

View File

@@ -116,7 +116,7 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
return (
<div
className={`tw-absolute tw-top-20 tw-right-5 tw-bg-white ${toolbarOpen ? "tw-w-[320px]" : "tw-w-0"
className={`tw-absolute tw-top-20 tw-right-5 tw-bg-white ${toolbarOpen ? "tw-w-[280px]" : "tw-w-0"
} tw-px-4 tw-p-2 tw-h-[600px] tw-rounded-md tw-z-20 tw-shadow-lg
tw-transition-transform tw-duration-75
tw-flex tw-flex-col tw-gap-2 tw-overflow-y-auto`}

View File

@@ -4,7 +4,7 @@ import Draggable from "./utils/draggableDnd"
import { FileImageOutlined, GithubOutlined, GitlabOutlined, LinkOutlined,
AudioOutlined, VideoCameraOutlined,
FileTextOutlined} from "@ant-design/icons"
import DraggableWrapper from "./utils/draggable"
import DraggableWrapper from "./draggable/draggable"
export function DraggableWidgetCard({ name, img, url, innerRef}){
@@ -30,7 +30,8 @@ export function DraggableWidgetCard({ name, img, url, innerRef}){
return (
// <Draggable className="tw-cursor-pointer" id={name}>
<DraggableWrapper className="tw-cursor-pointer tw-w-fit tw-h-fit">
<DraggableWrapper dragElementType={"widget"} className="tw-cursor-pointer tw-w-fit tw-h-fit">
<div ref={innerRef} className="tw-select-none tw-pointer-events-none tw-h-[240px] tw-w-[280px] tw-flex tw-flex-col
tw-rounded-md tw-overflow-hidden
tw-gap-2 tw-text-gray-600 tw-bg-[#ffffff44] tw-border-solid tw-border-[1px]
@@ -45,6 +46,7 @@ export function DraggableWidgetCard({ name, img, url, innerRef}){
{urlIcon}
</a>
</div>
</div>
</DraggableWrapper>
// </Draggable>

View File

@@ -0,0 +1,51 @@
import { memo, useRef } from "react"
import { useDragContext } from "./draggableContext"
/**
*
* @param {string} - dragElementType - this will set the data-draggable-type which can be accessed on droppable to check if its allowed or not
* @returns
*/
const DraggableWrapper = memo(({dragElementType, className, children}) => {
const { onDragStart, onDragEnd } = useDragContext()
const draggableRef = useRef(null)
/**
*
* @param {DragEvent} event
*/
const handleDragStart = (event) => {
// event.dataTransfer.setData("text/plain", "")
if (onDragStart)
onDragStart(draggableRef?.current)
}
const handleDragEnd = (e) => {
// console.log("Drag end: ", e, e.target.closest('div'))
onDragEnd()
}
return (
<div className={`${className}`}
draggable
data-draggable-type={dragElementType}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
ref={draggableRef}
>
{children}
</div>
)
})
export default DraggableWrapper

View File

@@ -0,0 +1,24 @@
import React, { createContext, useContext, useState } from 'react';
const DragContext = createContext()
export const useDragContext = () => useContext(DragContext)
// Provider component to wrap around parts of your app that need drag-and-drop functionality
export const DragProvider = ({ children }) => {
const [draggedElement, setDraggedElement] = useState(null)
const onDragStart = (element) => {
setDraggedElement(element)
}
const onDragEnd = () => {
setDraggedElement(null)
}
return (
<DragContext.Provider value={{ draggedElement, onDragStart, onDragEnd }}>
{children}
</DragContext.Provider>
)
}

View File

@@ -0,0 +1,90 @@
import { memo, useState } from "react"
import { useDragContext } from "./draggableContext"
const DroppableWrapper = memo(({onDrop, droppableTags=["widget"], ...props}) => {
const { draggedElement } = useDragContext()
const [showDroppable, setShowDroppable] = useState({
show: false,
allow: false
})
const handleDragEnter = (e) => {
console.log("Drag Enter", draggedElement)
const dragElementType = draggedElement.getAttribute("data-draggable-type")
if (droppableTags.length === 0 || droppableTags.includes(dragElementType)){
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 dragElementType = draggedElement.getAttribute("data-draggable-type")
if (droppableTags.length === 0 || droppableTags.includes(dragElementType)){
e.preventDefault() // this is necessary to allow drop to take place
}
}
const handleDropEvent = (e) => {
console.log("Drag over: ", e.dataTransfer.getData("text/plain"), e.dataTransfer)
setShowDroppable({
allow: false,
show: false
})
if(onDrop){
onDrop(e)
}
}
const handleDragLeave = (e) => {
if (!e.currentTarget.contains(e.relatedTarget)) {
setShowDroppable({
allow: false,
show: false
})
}
}
return (
<div className={`${props.className}`}
onDragOver={handleDragOver}
onDrop={handleDropEvent}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
>
{
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 DroppableWrapper

View File

@@ -1,32 +0,0 @@
const DraggableWrapper = (props) => {
const handleDragStart = () => {
console.log("Drag start")
}
const handleDragOver = () => {
// console.log("Drag over")
}
const handleDragEnd = (e) => {
// console.log("Drag end: ", e, e.target.closest('div'))
}
return (
<div className={`${props.className}`} draggable
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
>
{props.children}
</div>
)
}
export default DraggableWrapper

View File

@@ -2,6 +2,7 @@ import React from "react"
import {useDraggable} from "@dnd-kit/core"
import { CSS } from "@dnd-kit/utilities"
function Draggable(props) {
const {attributes, listeners, setNodeRef, transform} = useDraggable({
id: props.id,

View File

@@ -1,68 +0,0 @@
import { useState } from "react"
const DroppableWrapper = ({onDrop, ...props}) => {
const [showDroppable, setShowDroppable] = useState({
show: false,
allow: false
})
const handleDragEnter = () => {
console.log("Drag start")
setShowDroppable({
allow: true,
show: true
})
}
const handleDragOver = (e) => {
// console.log("Drag over: ", e)
e.preventDefault()
}
const handleDropEvent = (e) => {
setShowDroppable({
allow: false,
show: false
})
if(onDrop){
onDrop(e)
}
}
const handleDragLeave = (e) => {
if (!e.currentTarget.contains(e.relatedTarget)) {
setShowDroppable({
allow: false,
show: false
})
}
}
return (
<div className={`${props.className}`}
onDragOver={handleDragOver}
onDrop={handleDropEvent}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
>
{/* {
showDroppable.show &&
<div className={`${showDroppable.allow ? "tw-bg-[#b9f18e77]" : "tw-bg-[#ff7a7a6d]"}
tw-absolute tw-top-0 tw-left-0 tw-w-full tw-h-full tw-z-[2]`}>
</div>
} */}
{props.children}
</div>
)
}
export default DroppableWrapper