reworking on drag context
This commit is contained in:
36
src/App.js
36
src/App.js
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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`}
|
||||
|
||||
@@ -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>
|
||||
|
||||
51
src/components/draggable/draggable.js
Normal file
51
src/components/draggable/draggable.js
Normal 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
|
||||
24
src/components/draggable/draggableContext.js
Normal file
24
src/components/draggable/draggableContext.js
Normal 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>
|
||||
)
|
||||
}
|
||||
90
src/components/draggable/droppable.js
Normal file
90
src/components/draggable/droppable.js
Normal 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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user