Merge pull request #4790 from xyflow/fix/node-drag

Fix node drag while zooming
This commit is contained in:
Peter Kogo
2024-11-08 11:21:51 +01:00
committed by GitHub
6 changed files with 39 additions and 12 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@xyflow/system': patch
'@xyflow/react': patch
'@xyflow/svelte': patch
---
Fix node dragging & resizing while zooming on flow that does not cover whole browser window.
@@ -56,13 +56,14 @@ function ResizeControl({
domNode: resizeControlRef.current, domNode: resizeControlRef.current,
nodeId: id, nodeId: id,
getStoreItems: () => { getStoreItems: () => {
const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin } = store.getState(); const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin, domNode } = store.getState();
return { return {
nodeLookup, nodeLookup,
transform, transform,
snapGrid, snapGrid,
snapToGrid, snapToGrid,
nodeOrigin, nodeOrigin,
paneDomNode: domNode,
}; };
}, },
onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => { onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => {
@@ -35,7 +35,7 @@
let className: $$Props['class'] = ''; let className: $$Props['class'] = '';
export { className as class }; export { className as class };
const { nodeLookup, snapGrid, viewport, nodes, nodeOrigin } = useStore(); const { nodeLookup, snapGrid, viewport, nodes, nodeOrigin, domNode } = useStore();
const contextNodeId = getContext<string>('svelteflow__node_id'); const contextNodeId = getContext<string>('svelteflow__node_id');
$: id = typeof nodeId === 'string' ? nodeId : contextNodeId; $: id = typeof nodeId === 'string' ? nodeId : contextNodeId;
@@ -65,7 +65,8 @@
transform: [$viewport.x, $viewport.y, $viewport.zoom], transform: [$viewport.x, $viewport.y, $viewport.zoom],
snapGrid: $snapGrid ?? undefined, snapGrid: $snapGrid ?? undefined,
snapToGrid: !!$snapGrid, snapToGrid: !!$snapGrid,
nodeOrigin: $nodeOrigin nodeOrigin: $nodeOrigin,
paneDomNode: $domNode
}; };
}, },
onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => { onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => {
+6 -2
View File
@@ -5,14 +5,18 @@ export type GetPointerPositionParams = {
transform: Transform; transform: Transform;
snapGrid?: SnapGrid; snapGrid?: SnapGrid;
snapToGrid?: boolean; snapToGrid?: boolean;
containerBounds: DOMRect | null;
}; };
export function getPointerPosition( export function getPointerPosition(
event: MouseEvent | TouchEvent, event: MouseEvent | TouchEvent,
{ snapGrid = [0, 0], snapToGrid = false, transform }: GetPointerPositionParams { snapGrid = [0, 0], snapToGrid = false, transform, containerBounds }: GetPointerPositionParams
): XYPosition & { xSnapped: number; ySnapped: number } { ): XYPosition & { xSnapped: number; ySnapped: number } {
const { x, y } = getEventPosition(event); const { x, y } = getEventPosition(event);
const pointerPos = pointToRendererPoint({ x, y }, transform); const pointerPos = pointToRendererPoint(
{ x: x - (containerBounds?.left ?? 0), y: y - (containerBounds?.top ?? 0) },
transform
);
const { x: xSnapped, y: ySnapped } = snapToGrid ? snapPosition(pointerPos, snapGrid) : pointerPos; const { x: xSnapped, y: ySnapped } = snapToGrid ? snapPosition(pointerPos, snapGrid) : pointerPos;
// we need the snapped position in order to be able to skip unnecessary drag events // we need the snapped position in order to be able to skip unnecessary drag events
+4 -4
View File
@@ -257,7 +257,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
onNodeMouseDown?.(nodeId); onNodeMouseDown?.(nodeId);
} }
const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid, containerBounds });
lastPos = pointerPos; lastPos = pointerPos;
dragItems = getDragItems(nodeLookup, nodesDraggable, pointerPos, nodeId); dragItems = getDragItems(nodeLookup, nodesDraggable, pointerPos, nodeId);
@@ -281,6 +281,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
.clickDistance(nodeClickDistance) .clickDistance(nodeClickDistance)
.on('start', (event: UseDragEvent) => { .on('start', (event: UseDragEvent) => {
const { domNode, nodeDragThreshold, transform, snapGrid, snapToGrid } = getStoreItems(); const { domNode, nodeDragThreshold, transform, snapGrid, snapToGrid } = getStoreItems();
containerBounds = domNode?.getBoundingClientRect() || null;
abortDrag = false; abortDrag = false;
@@ -288,14 +289,13 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
startDrag(event); startDrag(event);
} }
const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid, containerBounds });
lastPos = pointerPos; lastPos = pointerPos;
containerBounds = domNode?.getBoundingClientRect() || null;
mousePosition = getEventPosition(event.sourceEvent, containerBounds!); mousePosition = getEventPosition(event.sourceEvent, containerBounds!);
}) })
.on('drag', (event: UseDragEvent) => { .on('drag', (event: UseDragEvent) => {
const { autoPanOnNodeDrag, transform, snapGrid, snapToGrid, nodeDragThreshold, nodeLookup } = getStoreItems(); const { autoPanOnNodeDrag, transform, snapGrid, snapToGrid, nodeDragThreshold, nodeLookup } = getStoreItems();
const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid, containerBounds });
if ( if (
(event.sourceEvent.type === 'touchmove' && event.sourceEvent.touches.length > 1) || (event.sourceEvent.type === 'touchmove' && event.sourceEvent.touches.length > 1) ||
+17 -3
View File
@@ -45,6 +45,7 @@ type XYResizerParams = {
snapGrid?: [number, number]; snapGrid?: [number, number];
snapToGrid: boolean; snapToGrid: boolean;
nodeOrigin: NodeOrigin; nodeOrigin: NodeOrigin;
paneDomNode: HTMLDivElement | null;
}; };
onChange: (changes: XYResizerChange, childChanges: XYResizerChildChange[]) => void; onChange: (changes: XYResizerChange, childChanges: XYResizerChildChange[]) => void;
onEnd?: () => void; onEnd?: () => void;
@@ -109,6 +110,7 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X
const controlDirection = getControlDirection(controlPosition); const controlDirection = getControlDirection(controlPosition);
let node: InternalNodeBase | undefined = undefined; let node: InternalNodeBase | undefined = undefined;
let containerBounds: DOMRect | null = null;
let childNodes: XYResizerChildChange[] = []; let childNodes: XYResizerChildChange[] = [];
let parentNode: InternalNodeBase | undefined = undefined; // Needed to fix expandParent let parentNode: InternalNodeBase | undefined = undefined; // Needed to fix expandParent
let parentExtent: CoordinateExtent | undefined = undefined; let parentExtent: CoordinateExtent | undefined = undefined;
@@ -116,14 +118,20 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X
const dragHandler = drag<HTMLDivElement, unknown>() const dragHandler = drag<HTMLDivElement, unknown>()
.on('start', (event: ResizeDragEvent) => { .on('start', (event: ResizeDragEvent) => {
const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin } = getStoreItems(); const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin, paneDomNode } = getStoreItems();
node = nodeLookup.get(nodeId); node = nodeLookup.get(nodeId);
if (!node) { if (!node) {
return; return;
} }
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); containerBounds = paneDomNode?.getBoundingClientRect() ?? null;
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, {
transform,
snapGrid,
snapToGrid,
containerBounds,
});
prevValues = { prevValues = {
width: node.measured.width ?? 0, width: node.measured.width ?? 0,
@@ -178,7 +186,13 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X
}) })
.on('drag', (event: ResizeDragEvent) => { .on('drag', (event: ResizeDragEvent) => {
const { transform, snapGrid, snapToGrid, nodeOrigin: storeNodeOrigin } = getStoreItems(); const { transform, snapGrid, snapToGrid, nodeOrigin: storeNodeOrigin } = getStoreItems();
const pointerPosition = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
const pointerPosition = getPointerPosition(event.sourceEvent, {
transform,
snapGrid,
snapToGrid,
containerBounds,
});
const childChanges: XYResizerChildChange[] = []; const childChanges: XYResizerChildChange[] = [];
if (!node) { if (!node) {