From 217dedc1214c85840ef9abe3494f4c2d3326b061 Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 7 Mar 2025 17:09:35 +0530 Subject: [PATCH] fixed drag and drop problem when within parent and dragImage problem --- src/canvas/canvas.js | 36 +++++++++-- src/canvas/widgets/base.js | 87 ++++++++++++++++++--------- src/components/draggable/draggable.js | 5 +- 3 files changed, 93 insertions(+), 35 deletions(-) diff --git a/src/canvas/canvas.js b/src/canvas/canvas.js index 50eca86..38c779e 100644 --- a/src/canvas/canvas.js +++ b/src/canvas/canvas.js @@ -683,9 +683,9 @@ 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 draggedElementRect = draggedElement.getBoundingClientRect() + // const elementWidth = draggedElementRect.width + // const elementHeight = draggedElementRect.height const { clientX, clientY } = e @@ -752,6 +752,7 @@ class Canvas extends React.Component { parent: "", initialData: { ...childData, + parentWidgetRef: null, pos: { x: finalPosition.x, y: finalPosition.y }, positionType: PosType.ABSOLUTE, // makes sure that after dropping the position is set to absolute value parentLayout: null,// reset the parent layout when its put on the canvas @@ -795,7 +796,7 @@ class Canvas extends React.Component { * @param {object} dragElement * @param {boolean} create - if create is set to true the widget will be created before adding to the child tree */ - handleAddWidgetChild = ({event, parentWidgetId, dragElementID, swap = false }) => { + handleAddWidgetChild = ({event, parentWidgetId, dragElementID, swap = false, posMetaData }) => { // console.log("event: ", event) // widgets data structure { id, widgetType: widgetComponentType, children: [], parent: "" } @@ -813,13 +814,30 @@ class Canvas extends React.Component { const parentWidget = this.widgetRefs[parentWidgetId].current const parentRect = parentWidget.getBoundingRect() const { clientX, clientY } = event - + + const {dragStartCursorPos, initialPos} = posMetaData + + console.log("Pos meyta data: ", posMetaData) let finalPosition = { x: (clientX - parentRect.left) / this.state.zoom, y: (clientY - parentRect.top) / this.state.zoom, } + + const canvasBoundingRect = this.getCanvasBoundingRect() + + const initialOffset = { + x: ((dragStartCursorPos.x - canvasBoundingRect.left) / this.state.zoom - this.state.currentTranslate.x) - initialPos.x, + y: ((dragStartCursorPos.y - canvasBoundingRect.top) / this.state.zoom - this.state.currentTranslate.y) - initialPos.y + } + + + finalPosition = { + x: finalPosition.x - initialOffset.x - this.state.currentTranslate.x, + y: finalPosition.y - initialOffset.y - this.state.currentTranslate.y + } + // TODO: fix swapping for grid layouts if (swap) { // If swapping, we need to find the common parent @@ -860,15 +878,18 @@ class Canvas extends React.Component { const updatedDragWidget = { ...dragWidgetObj, parent: dropWidgetObj.id, // Keep the parent reference + initialData: { ...dragData, positionType: parentLayout === Layouts.PLACE ? PosType.ABSOLUTE : PosType.NONE, parentLayout: parentWidget.getLayout() || null, // pass everything about the parent layout + parentWidgetRef: this.widgetRefs[parentWidgetId], zIndex: 0, pos: {x: finalPosition.x, y: finalPosition.y}, widgetContainer: WidgetContainer.WIDGET } } + console.log("added parent: ", updatedDragWidget) const updatedDropWidget = { ...dropWidgetObj, @@ -1044,9 +1065,10 @@ class Canvas extends React.Component { renderWidget = (widget) => { + // FIXME: initial data parentWidgetRef is empty const { id, widgetType: ComponentType, children = [], parent, initialData = {} } = widget - + console.log("parent: ", parent) const renderChildren = (childrenData) => { // recursively render the child elements return childrenData.map((child) => { @@ -1057,6 +1079,7 @@ class Canvas extends React.Component { return null }) } + console.log("initial data: ", initialData, initialData.parentWidgetRef) return ( @@ -1065,6 +1088,7 @@ class Canvas extends React.Component { id={id} ref={this.widgetRefs[id]} initialData={initialData} + parentWidgetRef={initialData.parentWidgetRef || null} canvasRef={this.canvasContainerRef} canvasInnerContainerRef={this.canvasRef} canvasMetaData={{ diff --git a/src/canvas/widgets/base.js b/src/canvas/widgets/base.js index 0e2805b..17cb059 100644 --- a/src/canvas/widgets/base.js +++ b/src/canvas/widgets/base.js @@ -782,9 +782,6 @@ class Widget extends React.Component { if (parentLayout?.layout === Layouts.FLEX || parentLayout?.layout === Layouts.GRID){ - const elementRect = this.elementRef.current.getBoundingClientRect() - const {pan: canvasPan, zoom: canvasZoom} = this.canvasMetaData - console.log("elemnt rect2: ", elementRect) layoutUpdates = { ...layoutUpdates, @@ -845,31 +842,30 @@ class Widget extends React.Component { // this.props.onWidgetDragStart(this.elementRef?.current) // Create custom drag image with full opacity, this will ensure the image isn't taken from part of the canvas - const dragImage = this.elementRef?.current.cloneNode(true) - dragImage.style.opacity = '1' // Ensure full opacity - dragImage.style.position = 'absolute' - dragImage.style.top = '-9999px' // Move it out of view + // const dragImage = this.elementRef?.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 = this.elementRef?.current.getBoundingClientRect() + // document.body.appendChild(dragImage) + // const elementRect = this.elementRef.current.getBoundingClientRect() + + // const canvasRect = this.props.canvasInnerContainerRef.current.getBoundingClientRect() // snap to mouse pos - // const offsetX = e.clientX - rect.left - // const offsetY = e.clientY - rect.top + // const offsetX = e.clientX - elementRect.left + // const offsetY = e.clientY - elementRect.top + // console.log("element rect: ", elementRect, e.clientX, e.clientY, "offset: ", offsetX, offsetY) // snap to middle // const offsetX = rect.width / 2 // const offsetY = rect.height / 2 - - const offsetX = e.clientX - rect.left; - const offsetY = e.clientY - rect.top; - - e.dataTransfer.setDragImage(dragImage, offsetX, offsetY); + // 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) + // setTimeout(() => { + // document.body.removeChild(dragImage) + // }, 0) // NOTE: this line will prevent problem's such as self-drop or dropping inside its own children setTimeout(this.disablePointerEvents, 1) @@ -951,7 +947,7 @@ class Widget extends React.Component { } - handleDropEvent = (e, draggedElement, widgetClass = null) => { + handleDropEvent = (e, draggedElement, widgetClass = null, posMetaData) => { if (!draggedElement || !draggedElement.getAttribute("data-drag-start-within")){ // if the drag is starting from outside (eg: file drop) or if drag doesn't exist @@ -1004,6 +1000,8 @@ class Widget extends React.Component { return } // TODO: check if the drop is allowed + + console.log("Meta data: ", posMetaData) if ([WidgetContainer.CANVAS, WidgetContainer.WIDGET].includes(container)) { // console.log("Dropped on meee: ", swapArea, this.swappableAreaRef.current.contains(e.target), thisContainer) @@ -1011,7 +1009,8 @@ class Widget extends React.Component { event: e, parentWidgetId: this.__id, dragElementID: draggedElement.getAttribute("data-widget-id"), - swap: swapArea || false + swap: swapArea || false, + posMetaData }) } else if (container === WidgetContainer.SIDEBAR) { @@ -1021,7 +1020,8 @@ class Widget extends React.Component { this.props.onAddChildWidget({ event: e, parentWidgetId: this.__id, - dragElementID: id + dragElementID: id, + posMetaData }) // if dragged from the sidebar create the widget first }) @@ -1152,14 +1152,47 @@ class Widget extends React.Component { } const handleSetInitialPosition = (e, setPosMetaData) => { + + e.stopPropagation() // prevent this event from bubbling up to parents + const {clientX, clientY} = e - + const elementRect = this.elementRef.current.getBoundingClientRect() + const canvasInnerRect = this.props.canvasInnerContainerRef.current.getBoundingClientRect() + + const {zoom, pan} = this.props.canvasMetaData + + console.log("Loss: ", this.props.parentWidgetRef) + + // TODO: if parent exist also subtract it + + let initialPos = { + x: elementRect.left - canvasInnerRect.left, + y: elementRect.top - canvasInnerRect.top + } + + let parent = this.props.parentWidgetRef?.current; + + console.log("parent1111: ", this.__id, parent, this.props) + + while (parent) { + // accounting for nested parents + const parentRect = parent.getBoundingRect() + console.log("parent: ", parentRect) + initialPos.x -= parentRect.left - canvasInnerRect.left + initialPos.y -= parentRect.top - canvasInnerRect.top + + // Move up to the next parent (if any) + parent = parent.parentWidgetRef?.current + } + const posMetaData = { dragStartCursorPos: {x: clientX, y: clientY}, - initialPos: {...this.state.pos} + initialPos: {...initialPos} } - + + console.log("initial pos: ", posMetaData) + setPosMetaData(posMetaData) } @@ -1174,7 +1207,7 @@ class Widget extends React.Component { { - ({ draggedElement, widgetClass, onDragStart, onDragEnd, overElement, setOverElement, setPosMetaData }) => { + ({ draggedElement, widgetClass, onDragStart, onDragEnd, overElement, setOverElement, posMetaData, setPosMetaData }) => { const canvasRect = this.canvas.getBoundingClientRect() const canvasRectInner = this.props.canvasInnerContainerRef?.current.getBoundingClientRect() @@ -1198,7 +1231,7 @@ class Widget extends React.Component { draggable={this.state.dragEnabled} onDragOver={(e) => this.handleDragOver(e, draggedElement)} - onDrop={(e) => {this.handleDropEvent(e, draggedElement, widgetClass); onDragEnd()}} + onDrop={(e) => {this.handleDropEvent(e, draggedElement, widgetClass, posMetaData); onDragEnd()}} onDragEnter={(e) => this.handleDragEnter(e, draggedElement, setOverElement)} onDragLeave={(e) => this.handleDragLeave(e, draggedElement, overElement)} diff --git a/src/components/draggable/draggable.js b/src/components/draggable/draggable.js index 9bdebe8..b51bdbd 100644 --- a/src/components/draggable/draggable.js +++ b/src/components/draggable/draggable.js @@ -16,10 +16,11 @@ const DraggableWrapper = memo(({dragElementType, dragWidgetClass=null, currentPo const setInitialPos = (e) => { const {clientX, clientY} = e - + const draggableRect = draggableRef.current.getBoundingClientRect() + const posMetaData = { dragStartCursorPos: {x: clientX, y: clientY}, - initialPos: currentPos + initialPos: {x: draggableRect.x, y: draggableRect.y} } setPosMetaData(posMetaData)