diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index c4ca752e..401d7712 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -171,6 +171,7 @@ export default (NodeComponent: ComponentType) => { dragging: false, }); + // @TODO: Fix the bug "onNodeDragStop not getting called on first render" if (onNodeDragStop && node) { onNodeDragStop(event.sourceEvent as MouseEvent, { ...node, dragging: false }); } diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index c697ff98..3f20adfc 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -16,7 +16,6 @@ type UseDragParams = { nodeRef: RefObject; disabled?: boolean; noDragClassName?: string; - // @TODO: implement handleSelector functionality handleSelector?: string; nodeId?: string; }; @@ -42,7 +41,27 @@ function getParentNodePosition(nodeInternals: NodeInternals, nodeId?: string): X }; } -function useDrag({ onStart, onDrag, onStop, nodeRef, disabled = false, noDragClassName, nodeId }: UseDragParams) { +export function selectorExistsTargetToNode(target: Element, selector: string, nodeRef: RefObject): boolean { + let current = target; + do { + if (current?.matches(selector)) return true; + if (current === nodeRef.current) return false; + current = current.parentElement as Element; + } while (current); + + return false; +} + +function useDrag({ + onStart, + onDrag, + onStop, + nodeRef, + disabled = false, + noDragClassName, + handleSelector, + nodeId, +}: UseDragParams) { const store = useStoreApi(); const startPos = useRef({ x: 0, y: 0 }); const lastPos = useRef<{ x: number | null; y: number | null }>({ x: null, y: null }); @@ -52,48 +71,60 @@ function useDrag({ onStart, onDrag, onStop, nodeRef, disabled = false, noDragCla if (nodeRef?.current) { const selection = select(nodeRef.current); + let isDragAllowedBySelector = true; if (disabled) { selection.on('.drag', null); } else { const dragHandler = drag() .on('start', (event: UseDragEvent) => { - const { transform, nodeInternals } = store.getState(); - const offset = getOffset(event, nodeRef); - parentPos.current = getParentNodePosition(nodeInternals, nodeId); + if (handleSelector) { + isDragAllowedBySelector = selectorExistsTargetToNode(event.sourceEvent.target, handleSelector, nodeRef); + } + if (isDragAllowedBySelector) { + const { transform, nodeInternals } = store.getState(); + const offset = getOffset(event, nodeRef); + parentPos.current = getParentNodePosition(nodeInternals, nodeId); - startPos.current = { - x: offset.x - transform[0], - y: offset.y - transform[1], - }; + startPos.current = { + x: offset.x - transform[0], + y: offset.y - transform[1], + }; - onStart(event); + onStart(event); + } }) .on('drag', (event: UseDragEvent) => { - const { transform, snapGrid, snapToGrid } = store.getState(); - const pos = pointToRendererPoint( - { - x: event.x - startPos.current.x, - y: event.y - startPos.current.y, - }, - transform, - snapToGrid, - snapGrid - ); + if (isDragAllowedBySelector) { + const { transform, snapGrid, snapToGrid } = store.getState(); + const pos = pointToRendererPoint( + { + x: event.x - startPos.current.x, + y: event.y - startPos.current.y, + }, + transform, + snapToGrid, + snapGrid + ); - pos.x -= parentPos.current.x; - pos.y -= parentPos.current.y; + pos.x -= parentPos.current.x; + pos.y -= parentPos.current.y; - // skip events without movement - if (lastPos.current.x !== pos.x || lastPos.current.y !== pos.y) { - lastPos.current = pos; + // skip events without movement + if (lastPos.current.x !== pos.x || lastPos.current.y !== pos.y) { + lastPos.current = pos; - onDrag(event, { - dx: pos.x, - dy: pos.y, - }); + onDrag(event, { + dx: pos.x, + dy: pos.y, + }); + } + } + }) + .on('end', (event) => { + if (isDragAllowedBySelector) { + onStop(event); } }) - .on('end', onStop) .filter((event: any) => !event.ctrlKey && !event.button && !event.target.className.includes(noDragClassName)); selection.call(dragHandler);