diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index bf119621..244efb64 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -5,6 +5,7 @@ ## Patch changes - use correct positions for intersection helpers +- fix minimap interaction for touch devices ## 12.0.0-next.14 diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index d802dad8..6f853e9f 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -122,8 +122,8 @@ export type NodeDragItem = { // distance from the mouse cursor to the node when start dragging distance: XYPosition; measured: { - width: number | null; - height: number | null; + width?: number; + height?: number; }; internals: { positionAbsolute: XYPosition; diff --git a/packages/system/src/utils/general.ts b/packages/system/src/utils/general.ts index e76d24a4..55c11646 100644 --- a/packages/system/src/utils/general.ts +++ b/packages/system/src/utils/general.ts @@ -205,9 +205,13 @@ export function isCoordinateExtent(extent?: CoordinateExtent | 'parent'): extent return extent !== undefined && extent !== 'parent'; } -export function getNodeDimensions( - node: NodeType -): { width: number; height: number } { +export function getNodeDimensions(node: { + measured?: { width?: number; height?: number }; + width?: number; + height?: number; + initialWidth?: number; + initialHeight?: number; +}): { width: number; height: number } { return { width: node.measured?.width ?? node.width ?? node.initialWidth ?? 0, height: node.measured?.height ?? node.height ?? node.initialHeight ?? 0, diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index 3bf70267..e8bb5357 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -10,6 +10,7 @@ import { getViewportForBounds, isCoordinateExtent, getNodeDimensions, + getPositionWithOrigin, } from './general'; import { type Transform, @@ -25,6 +26,7 @@ import { OnBeforeDeleteBase, NodeLookup, InternalNodeBase, + NodeDragItem, } from '../types'; import { errorMessages } from '../constants'; @@ -186,7 +188,7 @@ export const getNodesBounds = ( export type GetInternalNodesBoundsParams = { nodeOrigin?: NodeOrigin; useRelativePosition?: boolean; - filter?: (node: NodeBase) => boolean; + filter?: (node: NodeBase | NodeDragItem) => boolean; }; /** @@ -194,10 +196,9 @@ export type GetInternalNodesBoundsParams = { * @internal */ export const getInternalNodesBounds = ( - nodeLookup: NodeLookup, + nodeLookup: NodeLookup | Map, params: GetInternalNodesBoundsParams = { nodeOrigin: [0, 0], - useRelativePosition: false, } ): Rect => { if (nodeLookup.size === 0) { @@ -208,12 +209,22 @@ export const getInternalNodesBounds = ( nodeLookup.forEach((node) => { if (params.filter == undefined || params.filter(node)) { - const nodePos = getNodePositionWithOrigin(node, node.origin || params.nodeOrigin); + const { width, height } = getNodeDimensions(node); + const { x, y } = getPositionWithOrigin({ + x: node.internals.positionAbsolute.x, + y: node.internals.positionAbsolute.x, + width, + height, + origin: node.origin || params.nodeOrigin, + }); + box = getBoundsOfBoxes( box, rectToBox({ - ...nodePos[params.useRelativePosition ? 'position' : 'positionAbsolute'], - ...getNodeDimensions(node), + x, + y, + width, + height, }) ); } diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index 573b87af..3f26ba06 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -7,7 +7,7 @@ import { getPointerPosition, calculateNodePosition, snapPosition, - getNodesBounds, + getInternalNodesBounds, rectToBox, } from '../utils'; import { getDragItems, getEventHandlerParams, hasSelector, wrapSelectionDragFunc } from './utils'; @@ -29,7 +29,12 @@ import type { InternalNodeBase, } from '../types'; -export type OnDrag = (event: MouseEvent, dragItems: NodeDragItem[], node: NodeBase, nodes: NodeBase[]) => void; +export type OnDrag = ( + event: MouseEvent, + dragItems: Map, + node: NodeBase, + nodes: NodeBase[] +) => void; type StoreItems = { nodes: NodeBase[]; @@ -89,7 +94,7 @@ export function XYDrag voi }: XYDragParams): XYDragInstance { let lastPos: { x: number | null; y: number | null } = { x: null, y: null }; let autoPanId = 0; - let dragItems: NodeDragItem[] = []; + let dragItems = new Map(); let autoPanStarted = false; let mousePosition: XYPosition = { x: 0, y: 0 }; let containerBounds: DOMRect | null = null; @@ -117,13 +122,13 @@ export function XYDrag voi let hasChange = false; let nodesBox: Box = { x: 0, y: 0, x2: 0, y2: 0 }; - if (dragItems.length > 1 && nodeExtent) { - const rect = getNodesBounds(dragItems as unknown as NodeBase[], { nodeOrigin }); + if (dragItems.size > 1 && nodeExtent) { + const rect = getInternalNodesBounds(dragItems, { nodeOrigin }); nodesBox = rectToBox(rect); } - dragItems = dragItems.map((n) => { - let nextPosition = { x: x - n.distance.x, y: y - n.distance.y }; + for (const [id, dragItem] of dragItems) { + let nextPosition = { x: x - dragItem.distance.x, y: y - dragItem.distance.y }; if (snapToGrid) { nextPosition = snapPosition(nextPosition, snapGrid); @@ -136,13 +141,13 @@ export function XYDrag voi [nodeExtent[1][0], nodeExtent[1][1]], ]; - if (dragItems.length > 1 && nodeExtent && !n.extent) { - const { positionAbsolute } = n.internals; + if (dragItems.size > 1 && nodeExtent && !dragItem.extent) { + const { positionAbsolute } = dragItem.internals; const x1 = positionAbsolute.x - nodesBox.x + nodeExtent[0][0]; - const x2 = positionAbsolute.x + (n.measured?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0]; + const x2 = positionAbsolute.x + (dragItem.measured?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0]; const y1 = positionAbsolute.y - nodesBox.y + nodeExtent[0][1]; - const y2 = positionAbsolute.y + (n.measured?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1]; + const y2 = positionAbsolute.y + (dragItem.measured?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1]; adjustedNodeExtent = [ [x1, y1], @@ -151,7 +156,7 @@ export function XYDrag voi } const { position, positionAbsolute } = calculateNodePosition({ - nodeId: n.id, + nodeId: id, nextPosition, nodeLookup, nodeExtent: adjustedNodeExtent, @@ -160,13 +165,11 @@ export function XYDrag voi }); // we want to make sure that we only fire a change event when there is a change - hasChange = hasChange || n.position.x !== position.x || n.position.y !== position.y; + hasChange = hasChange || dragItem.position.x !== position.x || dragItem.position.y !== position.y; - n.position = position; - n.internals.positionAbsolute = positionAbsolute; - - return n; - }); + dragItem.position = position; + dragItem.internals.positionAbsolute = positionAbsolute; + } if (!hasChange) { return; @@ -174,7 +177,7 @@ export function XYDrag voi updateNodePositions(dragItems, true); - if (dragEvent && (onDrag || onNodeDrag || (!nodeId && onSelectionDrag))) { + if (dragEvent && (true || onDrag || onNodeDrag || (!nodeId && onSelectionDrag))) { const [currentNode, currentNodes] = getEventHandlerParams({ nodeId, dragItems, @@ -185,8 +188,7 @@ export function XYDrag voi onNodeDrag?.(dragEvent, currentNode, currentNodes); if (!nodeId) { - const _onSelectionDrag = wrapSelectionDragFunc(onSelectionDrag); - _onSelectionDrag(dragEvent, currentNode, currentNodes); + onSelectionDrag?.(dragEvent, currentNodes); } } } @@ -242,7 +244,7 @@ export function XYDrag voi lastPos = pointerPos; dragItems = getDragItems(nodeLookup, nodesDraggable, pointerPos, nodeId); - if (dragItems.length > 0 && (onDragStart || onNodeDragStart || (!nodeId && onSelectionDragStart))) { + if (dragItems.size > 0 && (onDragStart || onNodeDragStart || (!nodeId && onSelectionDragStart))) { const [currentNode, currentNodes] = getEventHandlerParams({ nodeId, dragItems, @@ -253,8 +255,7 @@ export function XYDrag voi onNodeDragStart?.(event.sourceEvent as MouseEvent, currentNode, currentNodes); if (!nodeId) { - const _onSelectionDragStart = wrapSelectionDragFunc(onSelectionDragStart); - _onSelectionDragStart(event.sourceEvent as MouseEvent, currentNode, currentNodes); + onSelectionDragStart?.(event.sourceEvent as MouseEvent, currentNodes); } } } @@ -308,7 +309,7 @@ export function XYDrag voi dragStarted = false; cancelAnimationFrame(autoPanId); - if (dragItems.length > 0) { + if (dragItems.size > 0) { const { nodeLookup, updateNodePositions, onNodeDragStop, onSelectionDragStop } = getStoreItems(); updateNodePositions(dragItems, false); @@ -324,8 +325,7 @@ export function XYDrag voi onNodeDragStop?.(event.sourceEvent as MouseEvent, currentNode, currentNodes); if (!nodeId) { - const _onSelectionDragStop = wrapSelectionDragFunc(onSelectionDragStop); - _onSelectionDragStop(event.sourceEvent as MouseEvent, currentNode, currentNodes); + onSelectionDragStop?.(event.sourceEvent as MouseEvent, currentNodes); } } } diff --git a/packages/system/src/xydrag/utils.ts b/packages/system/src/xydrag/utils.ts index b1cd0597..1611cf6b 100644 --- a/packages/system/src/xydrag/utils.ts +++ b/packages/system/src/xydrag/utils.ts @@ -40,8 +40,8 @@ export function getDragItems( nodesDraggable: boolean, mousePos: XYPosition, nodeId?: string -): NodeDragItem[] { - const dragItems: NodeDragItem[] = []; +): Map { + const dragItems = new Map(); for (const [id, node] of nodeLookup) { if ( @@ -49,29 +49,32 @@ export function getDragItems( (!node.parentId || !isParentSelected(node, nodeLookup)) && (node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')) ) { - const internalNode = nodeLookup.get(id)!; + const internalNode = nodeLookup.get(id); - dragItems.push({ - id: internalNode.id, - position: internalNode.position || { x: 0, y: 0 }, - distance: { - x: mousePos.x - internalNode.internals.positionAbsolute.x, - y: mousePos.y - internalNode.internals.positionAbsolute.y, - }, - extent: internalNode.extent, - parentId: internalNode.parentId, - origin: internalNode.origin, - expandParent: internalNode.expandParent, - internals: { - positionAbsolute: internalNode.internals.positionAbsolute || { x: 0, y: 0 }, - }, - measured: { - width: internalNode.measured.width || 0, - height: internalNode.measured.height || 0, - }, - }); + if (internalNode) { + dragItems.set(id, { + id, + position: internalNode.position || { x: 0, y: 0 }, + distance: { + x: mousePos.x - internalNode.internals.positionAbsolute.x, + y: mousePos.y - internalNode.internals.positionAbsolute.y, + }, + extent: internalNode.extent, + parentId: internalNode.parentId, + origin: internalNode.origin, + expandParent: internalNode.expandParent, + internals: { + positionAbsolute: internalNode.internals.positionAbsolute || { x: 0, y: 0 }, + }, + measured: { + width: internalNode.measured.width || 0, + height: internalNode.measured.height || 0, + }, + }); + } } } + return dragItems; } @@ -84,20 +87,32 @@ export function getEventHandlerParams({ nodeLookup, }: { nodeId?: string; - dragItems: NodeDragItem[]; + dragItems: Map; nodeLookup: Map; }): [NodeType, NodeType[]] { - const nodesFromDragItems: NodeType[] = dragItems.map((n) => { - const node = nodeLookup.get(n.id)!; + const nodesFromDragItems: NodeType[] = []; - return { + for (const [id, dragItem] of dragItems) { + const node = nodeLookup.get(id); + + if (node) { + nodesFromDragItems.push({ + ...node, + position: dragItem.position, + }); + } + } + + if (!nodeId) { + return [nodesFromDragItems[0], nodesFromDragItems]; + } + + const node = nodeLookup.get(nodeId)!; + return [ + { ...node, - position: n.position, - measured: { - ...n.measured, - }, - }; - }); - - return [nodeId ? nodesFromDragItems.find((n) => n.id === nodeId)! : nodesFromDragItems[0], nodesFromDragItems]; + position: dragItems.get(nodeId)?.position || node.position, + }, + nodesFromDragItems, + ]; }