diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index 27ae064a..cde43bd8 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -254,9 +254,10 @@ export function calcNextPosition( ]; } - const positionAbsolute = currentExtent - ? clampPosition(nextPosition, currentExtent as CoordinateExtent) - : nextPosition; + const positionAbsolute = + currentExtent && currentExtent !== 'parent' + ? clampPosition(nextPosition, currentExtent as CoordinateExtent) + : nextPosition; return { position: { diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index 920299df..674d678c 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -1,7 +1,15 @@ import { drag } from 'd3-drag'; import { select } from 'd3-selection'; -import { calcAutoPan, getEventPosition, getPointerPosition, calcNextPosition, snapPosition } from '../utils'; +import { + calcAutoPan, + getEventPosition, + getPointerPosition, + calcNextPosition, + snapPosition, + getRectOfNodes, + rectToBox, +} from '../utils'; import { getDragItems, getEventHandlerParams, hasSelector, wrapSelectionDragFunc } from './utils'; import type { NodeBase, @@ -18,6 +26,7 @@ import type { OnNodeDrag, OnSelectionDrag, UpdateNodePositions, + Box, } from '../types'; export type OnDrag = (event: MouseEvent, dragItems: NodeDragItem[], node: NodeBase, nodes: NodeBase[]) => void; @@ -105,6 +114,12 @@ export function XYDrag({ lastPos = { x, y }; let hasChange = false; + let nodesBox: Box = { x: 0, y: 0, x2: 0, y2: 0 }; + + if (dragItems.length > 1 && nodeExtent) { + const rect = getRectOfNodes(dragItems as unknown as NodeBase[], nodeOrigin); + nodesBox = rectToBox(rect); + } dragItems = dragItems.map((n) => { let nextPosition = { x: x - n.distance.x, y: y - n.distance.y }; @@ -113,9 +128,24 @@ export function XYDrag({ nextPosition = snapPosition(nextPosition, snapGrid); } - const updatedPos = calcNextPosition(n, nextPosition, nodes, nodeExtent, nodeOrigin, onError); + // if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node + // based on its position so that the node stays at it's position relative to the selection. + const adjustedNodeExtent: CoordinateExtent = [ + [nodeExtent[0][0], nodeExtent[0][1]], + [nodeExtent[1][0], nodeExtent[1][1]], + ]; - // we want to make sure that we only fire a change event when there is a changes + if (dragItems.length > 1 && nodeExtent && !n.extent) { + adjustedNodeExtent[0][0] = n.positionAbsolute.x - nodesBox.x + nodeExtent[0][0]; + adjustedNodeExtent[1][0] = n.positionAbsolute.x + (n.width ?? 0) - nodesBox.x2 + nodeExtent[1][0]; + + adjustedNodeExtent[0][1] = n.positionAbsolute.y - nodesBox.y + nodeExtent[0][1]; + adjustedNodeExtent[1][1] = n.positionAbsolute.y + (n.height ?? 0) - nodesBox.y2 + nodeExtent[1][1]; + } + + const updatedPos = calcNextPosition(n, nextPosition, nodes, adjustedNodeExtent, nodeOrigin, onError); + + // we want to make sure that we only fire a change event when there is a change hasChange = hasChange || n.position.x !== updatedPos.position.x || n.position.y !== updatedPos.position.y; n.position = updatedPos.position;