diff --git a/packages/core/src/canvas/index.ts b/packages/core/src/canvas/index.ts index c3c84fa1f..d4f3d841c 100644 --- a/packages/core/src/canvas/index.ts +++ b/packages/core/src/canvas/index.ts @@ -499,7 +499,7 @@ export default class CanvasModule extends Module { * @return {Object} * @private */ - getMouseRelativeCanvas(ev: MouseEvent, opts: any) { + getMouseRelativeCanvas(ev: MouseEvent | { clientX: number; clientY: number }, opts: any) { const zoom = this.getZoomDecimal(); const { top = 0, left = 0 } = this.getCanvasView().getPosition(opts) ?? {}; diff --git a/packages/core/src/utils/sorter/DropLocationDeterminer.ts b/packages/core/src/utils/sorter/DropLocationDeterminer.ts index b8466a6af..2e95f1b7e 100644 --- a/packages/core/src/utils/sorter/DropLocationDeterminer.ts +++ b/packages/core/src/utils/sorter/DropLocationDeterminer.ts @@ -116,8 +116,10 @@ export class DropLocationDeterminer> ext const { targetNode: lastTargetNode } = this.lastMoveData; this.eventHandlers.onMouseMove?.(mouseEvent); - const { mouseXRelativeToContainer: mouseX, mouseYRelativeToContainer: mouseY } = - this.getMousePositionRelativeToContainer(mouseEvent); + const { mouseXRelative: mouseX, mouseYRelative: mouseY } = this.getMousePositionRelativeToContainer( + mouseEvent.clientX, + mouseEvent.clientY, + ); const targetNode = this.getTargetNode(mouseEvent); const targetChanged = !targetNode?.equals(lastTargetNode); if (targetChanged) { @@ -138,6 +140,8 @@ export class DropLocationDeterminer> ext !placeholderDimensions.equals(this.lastMoveData.placeholderDimensions) || placement !== this.lastMoveData.placement; if (placeHolderMoved) { + console.log('🚀 ~ DropLocationDeterminer> ext */ private getTargetNode(mouseEvent: MouseEvent): NodeType | undefined { this.cacheContainerPosition(this.containerContext.container); + const { mouseXRelative, mouseYRelative } = this.getMousePositionRelativeToContainer( + mouseEvent.clientX, + mouseEvent.clientY, + ); // Get the element under the mouse const mouseTargetEl = this.getMouseTargetElement(mouseEvent); @@ -268,10 +276,10 @@ export class DropLocationDeterminer> ext let hoveredNode = this.getOrCreateHoveredNode(hoveredModel); // Get the drop position index based on the mouse position - const { index } = this.getDropPosition(hoveredNode, mouseEvent.clientX, mouseEvent.clientY); + const { index } = this.getDropPosition(hoveredNode, mouseXRelative, mouseYRelative); // Determine the valid target node (or its valid parent) - let targetNode = this.getValidParent(hoveredNode, index, mouseEvent.clientX, mouseEvent.clientY); + let targetNode = this.getValidParent(hoveredNode, index, mouseXRelative, mouseYRelative); return this.getOrReuseTargetNode(targetNode); } @@ -389,21 +397,44 @@ export class DropLocationDeterminer> ext private getValidParent(targetNode: NodeType, index: number, mouseX: number, mouseY: number): NodeType | undefined { if (!targetNode) return; - const positionNotChanged = targetNode.equals(this.lastMoveData.targetNode) && index === this.lastMoveData.index; - if (positionNotChanged) return targetNode; + + const lastTargetNode = this.lastMoveData.targetNode; + const targetNotChanged = targetNode.equals(lastTargetNode); + targetNode.nodeDimensions = targetNotChanged ? lastTargetNode.nodeDimensions! : this.getDim(targetNode.element!); + if (!targetNode.isWithinDropBounds(mouseX, mouseY)) { + return this.handleParentTraversal(targetNode, mouseX, mouseY); + } + + const positionNotChanged = targetNotChanged && index === this.lastMoveData.index; + if (positionNotChanged) return lastTargetNode; const canMove = this.sourceNodes.some((node) => targetNode.canMove(node, index)); this.triggerDragValidation(canMove, targetNode); if (canMove) return targetNode; + return this.handleParentTraversal(targetNode, mouseX, mouseY); + } + + private handleParentTraversal(targetNode: NodeType, mouseX: number, mouseY: number): NodeType | undefined { const parent = targetNode.getParent() as NodeType; if (!parent) return; + const indexInParent = this.getIndexInParent(parent, targetNode, targetNode.nodeDimensions!, mouseX, mouseY); + return this.getValidParent(parent, indexInParent, mouseX, mouseY); + } + + private getIndexInParent( + parent: NodeType, + targetNode: NodeType, + nodeDimensions: Dimension, + mouseX: number, + mouseY: number, + ) { let indexInParent = parent?.indexOfChild(targetNode); - const nodeDimensions = this.getDim(targetNode.element!); nodeDimensions.dir = this.getDirection(targetNode.element!, parent.element!); + indexInParent = indexInParent + (nodeDimensions.determinePlacement(mouseX, mouseY) == 'after' ? 1 : 0); - return this.getValidParent(parent, indexInParent, mouseX, mouseY); + return indexInParent; } private triggerDragValidation(canMove: boolean, targetNode: NodeType) { @@ -488,27 +519,26 @@ export class DropLocationDeterminer> ext /** * Gets the mouse position relative to the container, adjusting for scroll and canvas relative options. * - * @param {MouseEvent} mouseEvent - The current mouse event. * @return {{ mouseXRelativeToContainer: number, mouseYRelativeToContainer: number }} - The mouse X and Y positions relative to the container. * @private */ - private getMousePositionRelativeToContainer(mouseEvent: MouseEvent): { - mouseXRelativeToContainer: number; - mouseYRelativeToContainer: number; + private getMousePositionRelativeToContainer( + mouseX: number, + mouseY: number, + ): { + mouseXRelative: number; + mouseYRelative: number; } { - const { em } = this; - let mouseYRelativeToContainer = - mouseEvent.pageY - this.containerOffset.top + this.containerContext.container.scrollTop; - let mouseXRelativeToContainer = - mouseEvent.pageX - this.containerOffset.left + this.containerContext.container.scrollLeft; - - if (this.positionOptions.canvasRelative && !!em) { - const mousePos = em.Canvas.getMouseRelativeCanvas(mouseEvent, { noScroll: 1 }); - mouseXRelativeToContainer = mousePos.x; - mouseYRelativeToContainer = mousePos.y; + let mouseYRelative = mouseY - this.containerOffset.top + this.containerContext.container.scrollTop; + let mouseXRelative = mouseX - this.containerOffset.left + this.containerContext.container.scrollLeft; + + if (this.positionOptions.canvasRelative) { + const mousePos = this.em.Canvas.getMouseRelativeCanvas({ clientX: mouseX, clientY: mouseY }, { noScroll: 1 }); + mouseXRelative = mousePos.x; + mouseYRelative = mousePos.y; } - return { mouseXRelativeToContainer, mouseYRelativeToContainer }; + return { mouseXRelative, mouseYRelative }; } /** diff --git a/packages/core/src/utils/sorter/types.ts b/packages/core/src/utils/sorter/types.ts index 04d33baae..06cecf884 100644 --- a/packages/core/src/utils/sorter/types.ts +++ b/packages/core/src/utils/sorter/types.ts @@ -26,6 +26,12 @@ export type DragSource = DraggableContent & { export type Placement = 'inside' | 'before' | 'after'; +export type DroppableZoneConfig = { + ratio: number; + minDroppableDimension: number; // In px + maxUndroppableDimension: number; // In px +}; + export enum DragDirection { Vertical = 'Vertical', Horizontal = 'Horizontal',