From 1c74eb1d94def518dff1ffda42a865535c2085a1 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Thu, 11 Apr 2024 12:34:51 +0200 Subject: [PATCH 01/35] removed usage of movementX and movementY and calculate delta by hand --- packages/system/src/xyminimap/index.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/system/src/xyminimap/index.ts b/packages/system/src/xyminimap/index.ts index 7681253f..7e7a1ebd 100644 --- a/packages/system/src/xyminimap/index.ts +++ b/packages/system/src/xyminimap/index.ts @@ -54,17 +54,34 @@ export function XYMinimap({ domNode, panZoom, getTransform, getViewScale }: XYMi panZoom.scaleTo(nextZoom); }; + let panStart = [0, 0]; + const panStartHandler = (event: D3ZoomEvent) => { + if (event.sourceEvent.type === 'mousedown' || event.sourceEvent.type === 'touchstart') { + panStart = [ + event.sourceEvent.clientX ?? event.sourceEvent.touches[0].clientX, + event.sourceEvent.clientY ?? event.sourceEvent.touches[0].clientY, + ]; + } + }; + const panHandler = (event: D3ZoomEvent) => { const transform = getTransform(); - if (event.sourceEvent.type !== 'mousemove' || !panZoom) { + if ((event.sourceEvent.type !== 'mousemove' && event.sourceEvent.type !== 'touchmove') || !panZoom) { return; } + const panNow = [ + event.sourceEvent.clientX ?? event.sourceEvent.touches[0].clientX, + event.sourceEvent.clientY ?? event.sourceEvent.touches[0].clientY, + ]; + const panDelta = [panNow[0] - panStart[0], panNow[1] - panStart[1]]; + panStart = panNow; + const moveScale = getViewScale() * Math.max(transform[2], Math.log(transform[2])) * (inversePan ? -1 : 1); const position = { - x: transform[0] - event.sourceEvent.movementX * moveScale, - y: transform[1] - event.sourceEvent.movementY * moveScale, + x: transform[0] - panDelta[0] * moveScale, + y: transform[1] - panDelta[1] * moveScale, }; const extent: CoordinateExtent = [ [0, 0], @@ -83,6 +100,7 @@ export function XYMinimap({ domNode, panZoom, getTransform, getViewScale }: XYMi }; const zoomAndPanHandler = zoom() + .on('start', panStartHandler) // @ts-ignore .on('zoom', pannable ? panHandler : null) // @ts-ignore From 6edd5121b612cec216bad4eb9f3d3c3cf768bb28 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Thu, 11 Apr 2024 12:41:27 +0200 Subject: [PATCH 02/35] renamed now to current --- packages/system/src/xyminimap/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/system/src/xyminimap/index.ts b/packages/system/src/xyminimap/index.ts index 7e7a1ebd..d065990b 100644 --- a/packages/system/src/xyminimap/index.ts +++ b/packages/system/src/xyminimap/index.ts @@ -71,12 +71,12 @@ export function XYMinimap({ domNode, panZoom, getTransform, getViewScale }: XYMi return; } - const panNow = [ + const panCurrent = [ event.sourceEvent.clientX ?? event.sourceEvent.touches[0].clientX, event.sourceEvent.clientY ?? event.sourceEvent.touches[0].clientY, ]; - const panDelta = [panNow[0] - panStart[0], panNow[1] - panStart[1]]; - panStart = panNow; + const panDelta = [panCurrent[0] - panStart[0], panCurrent[1] - panStart[1]]; + panStart = panCurrent; const moveScale = getViewScale() * Math.max(transform[2], Math.log(transform[2])) * (inversePan ? -1 : 1); const position = { From 72d775b8ebc1c6996c581cfe8c297aa93fa89f58 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Thu, 11 Apr 2024 15:14:39 +0200 Subject: [PATCH 03/35] simplified resize events --- .../NodeResizer/NodeResizeControl.tsx | 4 +- .../plugins/NodeResizer/ResizeControl.svelte | 36 ++- packages/system/src/xyresizer/XYResizer.ts | 285 +++++++++--------- 3 files changed, 163 insertions(+), 162 deletions(-) diff --git a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx index 2052c98b..4ee8ff4a 100644 --- a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx @@ -66,7 +66,7 @@ function ResizeControl({ const changes: NodeChange[] = []; - if (change.isXPosChange || change.isYPosChange) { + if (change.x !== undefined && change.y !== undefined) { const positionChange: NodePositionChange = { id, type: 'position', @@ -79,7 +79,7 @@ function ResizeControl({ changes.push(positionChange); } - if (change.isWidthChange || change.isHeightChange) { + if (change.width !== undefined && change.height !== undefined) { const dimensionChange: NodeDimensionChange = { id, type: 'dimensions', diff --git a/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte b/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte index c346bd53..1e5f39e2 100644 --- a/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte +++ b/packages/svelte/src/lib/plugins/NodeResizer/ResizeControl.svelte @@ -70,23 +70,27 @@ }, onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => { const node = $nodeLookup.get(id)?.internals.userNode; - if (node) { - node.height = change.isHeightChange ? change.height : node.height; - node.width = change.isWidthChange ? change.width : node.width; - node.position = - change.isXPosChange || change.isYPosChange - ? { x: change.x, y: change.y } - : node.position; - - for (const childChange of childChanges) { - const childNode = $nodeLookup.get(childChange.id)?.internals.userNode; - if (childNode) { - childNode.position = childChange.position; - } - } - - $nodes = $nodes; + if (!node) { + return; } + + if (change.x !== undefined && change.y !== undefined) { + node.position = { x: change.x, y: change.y }; + } + + if (change.width !== undefined && change.height !== undefined) { + node.width = change.width; + node.height = change.height; + } + + for (const childChange of childChanges) { + const childNode = $nodeLookup.get(childChange.id)?.internals.userNode; + if (childNode) { + childNode.position = childChange.position; + } + } + + $nodes = $nodes; } }); } diff --git a/packages/system/src/xyresizer/XYResizer.ts b/packages/system/src/xyresizer/XYResizer.ts index 50a3e4aa..abce3e14 100644 --- a/packages/system/src/xyresizer/XYResizer.ts +++ b/packages/system/src/xyresizer/XYResizer.ts @@ -15,19 +15,13 @@ const initStartValues = { aspectRatio: 1, }; -const initChange = { - x: 0, - y: 0, - width: 0, - height: 0, - isXPosChange: false, - isYPosChange: false, - isWidthChange: false, - isHeightChange: false, +export type XYResizerChange = { + x?: number; + y?: number; + width?: number; + height?: number; }; -export type XYResizerChange = typeof initChange; - export type XYResizerChildChange = { id: string; position: XYPosition; @@ -117,158 +111,161 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange }: XYResize const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin } = getStoreItems(); node = nodeLookup.get(nodeId); - if (node) { - const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); + if (!node) { + return; + } - prevValues = { - width: node.measured?.width ?? 0, - height: node.measured?.height ?? 0, - x: node.position.x ?? 0, - y: node.position.y ?? 0, - }; + const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); - startValues = { - ...prevValues, - pointerX: xSnapped, - pointerY: ySnapped, - aspectRatio: prevValues.width / prevValues.height, - }; + prevValues = { + width: node.measured?.width ?? 0, + height: node.measured?.height ?? 0, + x: node.position.x ?? 0, + y: node.position.y ?? 0, + }; - parentNode = undefined; - if (node.extent === 'parent' || node.expandParent) { - parentNode = nodeLookup.get(node.parentId!); - if (parentNode && node.extent === 'parent') { - parentExtent = nodeToParentExtent(parentNode); - } + startValues = { + ...prevValues, + pointerX: xSnapped, + pointerY: ySnapped, + aspectRatio: prevValues.width / prevValues.height, + }; + + parentNode = undefined; + if (node.extent === 'parent' || node.expandParent) { + parentNode = nodeLookup.get(node.parentId!); + if (parentNode && node.extent === 'parent') { + parentExtent = nodeToParentExtent(parentNode); } + } - // Collect all child nodes to correct their relative positions when top/left changes - // Determine largest minimal extent the parent node is allowed to resize to - childNodes = []; - childExtent = undefined; + // Collect all child nodes to correct their relative positions when top/left changes + // Determine largest minimal extent the parent node is allowed to resize to + childNodes = []; + childExtent = undefined; - for (const [childId, child] of nodeLookup) { - if (child.parentId === nodeId) { - childNodes.push({ - id: childId, - position: { ...child.position }, - extent: child.extent, - }); + for (const [childId, child] of nodeLookup) { + if (child.parentId === nodeId) { + childNodes.push({ + id: childId, + position: { ...child.position }, + extent: child.extent, + }); - if (child.extent === 'parent' || child.expandParent) { - const extent = nodeToChildExtent(child, node!, child.origin ?? nodeOrigin); + if (child.extent === 'parent' || child.expandParent) { + const extent = nodeToChildExtent(child, node!, child.origin ?? nodeOrigin); - if (childExtent) { - childExtent = [ - [Math.min(extent[0][0], childExtent[0][0]), Math.min(extent[0][1], childExtent[0][1])], - [Math.max(extent[1][0], childExtent[1][0]), Math.max(extent[1][1], childExtent[1][1])], - ]; - } else { - childExtent = extent; - } + if (childExtent) { + childExtent = [ + [Math.min(extent[0][0], childExtent[0][0]), Math.min(extent[0][1], childExtent[0][1])], + [Math.max(extent[1][0], childExtent[1][0]), Math.max(extent[1][1], childExtent[1][1])], + ]; + } else { + childExtent = extent; } } } - - onResizeStart?.(event, { ...prevValues }); } + + onResizeStart?.(event, { ...prevValues }); }) .on('drag', (event: ResizeDragEvent) => { const { transform, snapGrid, snapToGrid, nodeOrigin: storeNodeOrigin } = getStoreItems(); const pointerPosition = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); const childChanges: XYResizerChildChange[] = []; - if (node) { - const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues; - const change = { ...initChange }; - const nodeOrigin = node.origin ?? storeNodeOrigin; - - const { width, height, x, y } = getDimensionsAfterResize( - startValues, - controlDirection, - pointerPosition, - boundaries, - keepAspectRatio, - nodeOrigin, - parentExtent, - childExtent - ); - - const isWidthChange = width !== prevWidth; - const isHeightChange = height !== prevHeight; - - const isXPosChange = x !== prevX && isWidthChange; - const isYPosChange = y !== prevY && isHeightChange; - - if (isXPosChange || isYPosChange || nodeOrigin[0] === 1 || nodeOrigin[1] == 1) { - change.isXPosChange = isXPosChange; - change.isYPosChange = isYPosChange; - change.x = isXPosChange ? x : prevX; - change.y = isYPosChange ? y : prevY; - - prevValues.x = change.x; - prevValues.y = change.y; - - // Fix expandParent when resizing from top/left - if (parentNode && node.expandParent) { - if (change.x < 0) { - prevValues.x = 0; - startValues.x = startValues.x - change.x; - } - - if (change.y < 0) { - prevValues.y = 0; - startValues.y = startValues.y - change.y; - } - } - - if (childNodes.length > 0) { - const xChange = x - prevX; - const yChange = y - prevY; - - for (const childNode of childNodes) { - childNode.position = { - x: childNode.position.x - xChange + nodeOrigin[0] * (width - prevWidth), - y: childNode.position.y - yChange + nodeOrigin[1] * (height - prevHeight), - }; - childChanges.push(childNode); - } - } - } - - if (isWidthChange || isHeightChange) { - change.isWidthChange = isWidthChange; - change.isHeightChange = isHeightChange; - change.width = width; - change.height = height; - prevValues.width = change.width; - prevValues.height = change.height; - } - - if (!change.isXPosChange && !change.isYPosChange && !isWidthChange && !isHeightChange) { - return; - } - - const direction = getResizeDirection({ - width: prevValues.width, - prevWidth, - height: prevValues.height, - prevHeight, - affectsX: controlDirection.affectsX, - affectsY: controlDirection.affectsY, - }); - - const nextValues = { ...prevValues, direction }; - - const callResize = shouldResize?.(event, nextValues); - - if (callResize === false) { - return; - } - - onResize?.(event, nextValues); - onChange(change, childChanges); + if (!node) { + return; } + const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues; + const change: XYResizerChange = {}; + const nodeOrigin = node.origin ?? storeNodeOrigin; + + const { width, height, x, y } = getDimensionsAfterResize( + startValues, + controlDirection, + pointerPosition, + boundaries, + keepAspectRatio, + nodeOrigin, + parentExtent, + childExtent + ); + + const isWidthChange = width !== prevWidth; + const isHeightChange = height !== prevHeight; + + const isXPosChange = x !== prevX && isWidthChange; + const isYPosChange = y !== prevY && isHeightChange; + + if (!isXPosChange && !isYPosChange && !isWidthChange && !isHeightChange) { + return; + } + + console.log(isXPosChange, x, isYPosChange, y); + + if (isXPosChange || isYPosChange || nodeOrigin[0] === 1 || nodeOrigin[1] == 1) { + change.x = isXPosChange ? x : prevValues.x; + change.y = isYPosChange ? y : prevValues.y; + + prevValues.x = change.x; + prevValues.y = change.y; + + // Fix expandParent when resizing from top/left + if (parentNode && node.expandParent) { + if (change.x && change.x < 0) { + prevValues.x = 0; + startValues.x = startValues.x - change.x; + } + + if (change.y && change.y < 0) { + prevValues.y = 0; + startValues.y = startValues.y - change.y; + } + } + + if (childNodes.length > 0) { + const xChange = x - prevX; + const yChange = y - prevY; + + for (const childNode of childNodes) { + childNode.position = { + x: childNode.position.x - xChange + nodeOrigin[0] * (width - prevWidth), + y: childNode.position.y - yChange + nodeOrigin[1] * (height - prevHeight), + }; + childChanges.push(childNode); + } + } + } + + if (isWidthChange || isHeightChange) { + change.width = isWidthChange ? width : prevValues.width; + change.height = isHeightChange ? height : prevValues.height; + prevValues.width = change.width; + prevValues.height = change.height; + } + + console.log(change); + + const direction = getResizeDirection({ + width: prevValues.width, + prevWidth, + height: prevValues.height, + prevHeight, + affectsX: controlDirection.affectsX, + affectsY: controlDirection.affectsY, + }); + + const nextValues = { ...prevValues, direction }; + + const callResize = shouldResize?.(event, nextValues); + + if (callResize === false) { + return; + } + + onResize?.(event, nextValues); + onChange(change, childChanges); }) .on('end', (event: ResizeDragEvent) => { onResizeEnd?.(event, { ...prevValues }); From 256fad3053cfd0fb0792ba78ccdcdb061f90f377 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Thu, 11 Apr 2024 15:17:36 +0200 Subject: [PATCH 04/35] removed consolelog --- packages/system/src/xyresizer/XYResizer.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/system/src/xyresizer/XYResizer.ts b/packages/system/src/xyresizer/XYResizer.ts index abce3e14..093bdf94 100644 --- a/packages/system/src/xyresizer/XYResizer.ts +++ b/packages/system/src/xyresizer/XYResizer.ts @@ -202,8 +202,6 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange }: XYResize return; } - console.log(isXPosChange, x, isYPosChange, y); - if (isXPosChange || isYPosChange || nodeOrigin[0] === 1 || nodeOrigin[1] == 1) { change.x = isXPosChange ? x : prevValues.x; change.y = isYPosChange ? y : prevValues.y; @@ -245,8 +243,6 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange }: XYResize prevValues.height = change.height; } - console.log(change); - const direction = getResizeDirection({ width: prevValues.width, prevWidth, From 979fe7d56539414d03b79b77eea1b1c3d9ae3192 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Thu, 11 Apr 2024 18:34:17 +0200 Subject: [PATCH 05/35] broken parent expand on resize but almost there --- .../NodeResizer/NodeResizeControl.tsx | 40 ++++++++++++++++--- packages/system/src/utils/store.ts | 39 +++++++++--------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx index 4ee8ff4a..725639b2 100644 --- a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx @@ -9,11 +9,15 @@ import { type NodeChange, type NodeDimensionChange, type NodePositionChange, + handleParentExpand, } from '@xyflow/system'; import { useStoreApi } from '../../hooks/useStore'; import { useNodeId } from '../../contexts/NodeIdContext'; import type { ResizeControlProps, ResizeControlLineProps } from './types'; +import { InternalNode } from '../../types'; + +type InternalNodeWithParentExpand = InternalNode & { expandParent: true; parentId: string }; function ResizeControl({ nodeId, @@ -62,20 +66,44 @@ function ResizeControl({ }; }, onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => { - const { triggerNodeChanges } = store.getState(); + const { triggerNodeChanges, nodeLookup } = store.getState(); const changes: NodeChange[] = []; + const newPosition = { x: change.x, y: change.y }; - if (change.x !== undefined && change.y !== undefined) { - const positionChange: NodePositionChange = { - id, - type: 'position', + const node = nodeLookup.get(id); + if (node && node.expandParent && node.parentId) { + const nodeWithChange = { + ...node, position: { x: change.x, y: change.y, }, - }; + width: change.width, + height: change.height, + measured: { + width: change.width ?? node.measured?.width, + height: change.height ?? node.measured?.height, + }, + } as InternalNodeWithParentExpand; + const parentExpandChanges = handleParentExpand([nodeWithChange], nodeLookup); + if (parentExpandChanges.length > 0) { + newPosition.x = change.x && change.x < 0 ? 0 : change.x; + newPosition.y = change.y && change.y < 0 ? 0 : change.y; + } else { + console.log('there was no parent expand'); + } + changes.push(...parentExpandChanges); + } + + if (change.x !== undefined && change.y !== undefined) { + console.log(newPosition); + const positionChange: NodePositionChange = { + id, + type: 'position', + position: { ...newPosition } as { x: number; y: number }, + }; changes.push(positionChange); } diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 0d7f2645..11b5e3c6 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -145,25 +145,25 @@ function calculateXYZPosition( ); } +type NodeWithExpandParent = NodeType & { parentId: string; expandParent: true }; + export function handleParentExpand( - nodes: InternalNodeBase[], + nodes: NodeWithExpandParent[], nodeLookup: NodeLookup ): (NodeDimensionChange | NodePositionChange)[] { const changes: (NodeDimensionChange | NodePositionChange)[] = []; const chilNodeRects = new Map(); - nodes.forEach((node) => { - const parentId = node.parentId; - if (node.expandParent && parentId) { - const parentNode = nodeLookup.get(parentId); - - if (parentNode) { - const parentRect = chilNodeRects.get(parentId) || nodeToRect(parentNode, node.origin); - const expandedRect = getBoundsOfRects(parentRect, nodeToRect(node, node.origin)); - chilNodeRects.set(parentId, expandedRect); - } + for (const node of nodes) { + const parentNode = nodeLookup.get(node.parentId); + if (!parentNode) { + continue; } - }); + + const parentRect = chilNodeRects.get(node.parentId) || nodeToRect(parentNode, node.origin); + const expandedRect = getBoundsOfRects(parentRect, nodeToRect(node, node.origin)); + chilNodeRects.set(node.parentId, expandedRect); + } if (chilNodeRects.size > 0) { chilNodeRects.forEach((rect, id) => { @@ -193,9 +193,8 @@ export function handleParentExpand( height: dimensions.height + yChange, }, }); - - // @todo we need to reset child node positions if < 0 - } else if (dimensions.width < rect.width || dimensions.height < rect.height) { + } + if (dimensions.width < rect.width || dimensions.height < rect.height) { changes.push({ id, type: 'dimensions', @@ -229,7 +228,7 @@ export function updateNodeInternals( const style = window.getComputedStyle(viewportNode); const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform); // in this array we collect nodes, that might trigger changes (like expanding parent) - const triggerChangeNodes: NodeType[] = []; + const parentExpandNodes: NodeWithExpandParent[] = []; updates.forEach((update) => { const node = nodeLookup.get(update.id); @@ -275,16 +274,16 @@ export function updateNodeInternals( dimensions, }); - if (newNode.expandParent) { - triggerChangeNodes.push(newNode); + if (newNode.expandParent && newNode.parentId) { + parentExpandNodes.push(newNode as NodeWithExpandParent); } } } } }); - if (triggerChangeNodes.length > 0) { - const parentExpandChanges = handleParentExpand(triggerChangeNodes, nodeLookup); + if (parentExpandNodes.length > 0) { + const parentExpandChanges = handleParentExpand(parentExpandNodes, nodeLookup); changes.push(...parentExpandChanges); } From 8a56311e52490af722958288cd943e3b1a2cfc5c Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Apr 2024 13:18:57 +0200 Subject: [PATCH 06/35] fix(intersection-helpers): use correct positions closes #4138 --- packages/react/src/hooks/useReactFlow.ts | 21 ++++++++--- packages/system/src/utils/general.ts | 46 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index 6be3f90b..31035bd1 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -1,5 +1,12 @@ import { useCallback, useMemo, useRef, useState } from 'react'; -import { getElementsToRemove, getOverlappingArea, isRectObject, nodeToRect, type Rect } from '@xyflow/system'; +import { + evaluateNodePosition, + getElementsToRemove, + getOverlappingArea, + isRectObject, + nodeToRect, + type Rect, +} from '@xyflow/system'; import useViewportHelper from './useViewportHelper'; import { useStoreApi } from './useStore'; @@ -223,15 +230,19 @@ export function useReactFlow { - const internalNode = store.getState().nodeLookup.get(id); - return internalNode ? nodeToRect(internalNode) : null; + const getNodeRect = useCallback((node: NodeType | { id: string }): Rect | null => { + const { nodeLookup, nodeOrigin } = store.getState(); + const nodeToUse = isNode(node) ? node : nodeLookup.get(node.id)!; + const nodeWithPos = evaluateNodePosition(nodeToUse, nodeLookup, nodeOrigin); + + return nodeWithPos ? nodeToRect(nodeWithPos) : null; }, []); const getIntersectingNodes = useCallback>( (nodeOrRect, partially = true, nodes) => { const isRect = isRectObject(nodeOrRect); const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect); + const hasNodesOption = nodes !== undefined; if (!nodeRect) { return []; @@ -244,7 +255,7 @@ export function useReactFlow 0; diff --git a/packages/system/src/utils/general.ts b/packages/system/src/utils/general.ts index b8af19bc..e76d24a4 100644 --- a/packages/system/src/utils/general.ts +++ b/packages/system/src/utils/general.ts @@ -9,6 +9,7 @@ import type { SnapGrid, Transform, InternalNodeBase, + NodeLookup, } from '../types'; import { type Viewport } from '../types'; import { getNodePositionWithOrigin } from './graph'; @@ -219,3 +220,48 @@ export function nodeHasDimensions(node: No (node.measured?.height ?? node.height ?? node.initialHeight) !== undefined ); } + +/** + * Helper to calculate the absolute position of a node + * + * @internal + * @param node + * @param nodeLookup + * @param nodeOrigin + * @returns an internal node with an absolute position + */ +export function evaluateNodePosition( + node: NodeBase | InternalNodeBase, + nodeLookup: NodeLookup, + nodeOrigin: NodeOrigin = [0, 0] +): InternalNodeBase | null { + const internalNode = nodeLookup.get(node.id); + + if (!internalNode) { + return null; + } + + let parentId = internalNode.parentId; + const positionAbsolute = { ...node.position }; + + while (parentId) { + const parent = nodeLookup.get(parentId); + parentId = parent?.parentId; + + if (parent) { + const origin = parent.origin || nodeOrigin; + const xOffset = (parent.measured.width ?? 0) * origin[0]; + const yOffset = (parent.measured.height ?? 0) * origin[1]; + positionAbsolute.x += parent.position.x - xOffset; + positionAbsolute.y += parent.position.y - yOffset; + } + } + + return { + ...internalNode, + internals: { + ...internalNode.internals, + positionAbsolute, + }, + }; +} From 28f4b4e5134c3176335038968c33a45b70ebb0c7 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Apr 2024 13:19:43 +0200 Subject: [PATCH 07/35] chore(changelog): update --- packages/react/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index b210cd0c..bf119621 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,11 @@ # @xyflow/react +## 12.0.0-next.xx + +## Patch changes + +- use correct positions for intersection helpers + ## 12.0.0-next.14 ## Patch changes From c5f394e1f77ce6ed7a6487d74b07dd4b1c22a488 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 15 Apr 2024 15:58:25 +0200 Subject: [PATCH 08/35] use evaluateNodePosition for parentExpand --- .../NodeResizer/NodeResizeControl.tsx | 28 +++++++--------- packages/react/src/store/index.ts | 15 +++++---- packages/system/src/utils/store.ts | 32 +++++++------------ 3 files changed, 30 insertions(+), 45 deletions(-) diff --git a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx index 725639b2..6ca54676 100644 --- a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx @@ -10,12 +10,13 @@ import { type NodeDimensionChange, type NodePositionChange, handleParentExpand, + evaluateNodePosition, } from '@xyflow/system'; import { useStoreApi } from '../../hooks/useStore'; import { useNodeId } from '../../contexts/NodeIdContext'; import type { ResizeControlProps, ResizeControlLineProps } from './types'; -import { InternalNode } from '../../types'; +import { InternalNode, Node } from '../../types'; type InternalNodeWithParentExpand = InternalNode & { expandParent: true; parentId: string }; @@ -73,32 +74,25 @@ function ResizeControl({ const node = nodeLookup.get(id); if (node && node.expandParent && node.parentId) { - const nodeWithChange = { + const nodeWithChange: Node = { ...node, position: { - x: change.x, - y: change.y, + x: change.x ?? node.position.x, + y: change.y ?? node.position.y, }, width: change.width, height: change.height, - measured: { - width: change.width ?? node.measured?.width, - height: change.height ?? node.measured?.height, - }, - } as InternalNodeWithParentExpand; + }; - const parentExpandChanges = handleParentExpand([nodeWithChange], nodeLookup); - if (parentExpandChanges.length > 0) { - newPosition.x = change.x && change.x < 0 ? 0 : change.x; - newPosition.y = change.y && change.y < 0 ? 0 : change.y; - } else { - console.log('there was no parent expand'); - } + const nodeWith = evaluateNodePosition(nodeWithChange, nodeLookup) as InternalNodeWithParentExpand; + + const parentExpandChanges = handleParentExpand([nodeWith], nodeLookup); changes.push(...parentExpandChanges); + newPosition.x = change.x ? Math.max(0, change.x) : undefined; + newPosition.y = change.y ? Math.max(0, change.y) : undefined; } if (change.x !== undefined && change.y !== undefined) { - console.log(newPosition); const positionChange: NodePositionChange = { id, type: 'position', diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 7ce263c9..f02e74f4 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -117,7 +117,8 @@ const createRFStore = ({ }, updateNodePositions: (nodeDragItems, dragging = false) => { const { nodeLookup } = get(); - const triggerChangeNodes: InternalNode[] = []; + type ExpandParentInternalNode = InternalNode & { parentId: string; expandParent: true }; + const expandParentNodes: ExpandParentInternalNode[] = []; const changes: NodeChange[] = nodeDragItems.map((node) => { // @todo add expandParent to drag item so that we can get rid of the look up here @@ -129,15 +130,15 @@ const createRFStore = ({ dragging, }; - if (internalNode?.expandParent && change.position) { - triggerChangeNodes.push({ + if (internalNode?.expandParent && internalNode?.parentId && change.position) { + expandParentNodes.push({ ...internalNode, - position: change.position, + position: { ...node.position }, internals: { ...internalNode.internals, positionAbsolute: node.internals.positionAbsolute, }, - }); + } as ExpandParentInternalNode); change.position.x = Math.max(0, change.position.x); change.position.y = Math.max(0, change.position.y); @@ -146,8 +147,8 @@ const createRFStore = ({ return change; }); - if (triggerChangeNodes.length > 0) { - const parentExpandChanges = handleParentExpand(triggerChangeNodes, nodeLookup); + if (expandParentNodes.length > 0) { + const parentExpandChanges = handleParentExpand(expandParentNodes, nodeLookup); changes.push(...parentExpandChanges); } diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 11b5e3c6..3d8727c2 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -145,10 +145,10 @@ function calculateXYZPosition( ); } -type NodeWithExpandParent = NodeType & { parentId: string; expandParent: true }; +type ExpandParentNode = NodeType & { parentId: string; expandParent: true }; export function handleParentExpand( - nodes: NodeWithExpandParent[], + nodes: ExpandParentNode[], nodeLookup: NodeLookup ): (NodeDimensionChange | NodePositionChange)[] { const changes: (NodeDimensionChange | NodePositionChange)[] = []; @@ -160,7 +160,7 @@ export function handleParentExpand( continue; } - const parentRect = chilNodeRects.get(node.parentId) || nodeToRect(parentNode, node.origin); + const parentRect = chilNodeRects.get(node.parentId) ?? nodeToRect(parentNode, node.origin); const expandedRect = getBoundsOfRects(parentRect, nodeToRect(node, node.origin)); chilNodeRects.set(node.parentId, expandedRect); } @@ -171,10 +171,9 @@ export function handleParentExpand( const { position } = getNodePositionWithOrigin(origParent, origParent.origin); const dimensions = getNodeDimensions(origParent); - if (rect.x < position.x || rect.y < position.y) { - const xChange = Math.round(Math.abs(position.x - rect.x)); - const yChange = Math.round(Math.abs(position.y - rect.y)); - + let xChange = rect.x < position.x ? Math.round(Math.abs(position.x - rect.x)) : 0; + let yChange = rect.y < position.y ? Math.round(Math.abs(position.y - rect.y)) : 0; + if (xChange > 0 || yChange > 0) { changes.push({ id, type: 'position', @@ -183,25 +182,16 @@ export function handleParentExpand( y: position.y - yChange, }, }); - - changes.push({ - id, - type: 'dimensions', - resizing: true, - dimensions: { - width: dimensions.width + xChange, - height: dimensions.height + yChange, - }, - }); } + if (dimensions.width < rect.width || dimensions.height < rect.height) { changes.push({ id, type: 'dimensions', resizing: true, dimensions: { - width: Math.max(dimensions.width, rect.width), - height: Math.max(dimensions.height, rect.height), + width: Math.max(dimensions.width, Math.round(rect.width)), + height: Math.max(dimensions.height, Math.round(rect.height)), }, }); } @@ -228,7 +218,7 @@ export function updateNodeInternals( const style = window.getComputedStyle(viewportNode); const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform); // in this array we collect nodes, that might trigger changes (like expanding parent) - const parentExpandNodes: NodeWithExpandParent[] = []; + const parentExpandNodes: ExpandParentNode[] = []; updates.forEach((update) => { const node = nodeLookup.get(update.id); @@ -275,7 +265,7 @@ export function updateNodeInternals( }); if (newNode.expandParent && newNode.parentId) { - parentExpandNodes.push(newNode as NodeWithExpandParent); + parentExpandNodes.push(newNode as ExpandParentNode); } } } From ccf6578824a9832ebb2fd2c690abeb23cd786c79 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 15 Apr 2024 17:03:03 +0200 Subject: [PATCH 09/35] added parentLookup --- .../src/components/NodeWrapper/index.tsx | 13 ++--- packages/react/src/store/index.ts | 4 +- packages/react/src/store/initialState.ts | 4 +- packages/react/src/types/store.ts | 1 + .../svelte/src/lib/store/initial-store.ts | 6 ++- packages/svelte/src/lib/store/utils.ts | 5 +- packages/system/src/utils/store.ts | 47 ++++++++++++------- 7 files changed, 49 insertions(+), 31 deletions(-) diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index 18c2d314..c26c91c2 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -42,7 +42,7 @@ export function NodeWrapper({ nodeOrigin, onError, }: NodeWrapperProps) { - const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => { + const { node, positionAbsoluteX, positionAbsoluteY, internals } = useStore((s) => { const node = s.nodeLookup.get(id)! as InternalNode; const positionAbsolute = nodeExtent @@ -55,8 +55,9 @@ export function NodeWrapper({ // so we we need to force a re-render when some change positionAbsoluteX: positionAbsolute.x, positionAbsoluteY: positionAbsolute.y, - zIndex: node.internals.z, - isParent: node.internals.isParent, + internals: node.internals, + // zIndex: node.internals.z, + // isParent: node.internals.isParent, }; }, shallow); @@ -213,14 +214,14 @@ export function NodeWrapper({ { selected: node.selected, selectable: isSelectable, - parent: isParent, + parent: internals.isParent, draggable: isDraggable, dragging, }, ])} ref={nodeRef} style={{ - zIndex, + zIndex: internals.z, transform: `translate(${positionAbsoluteOrigin.x}px,${positionAbsoluteOrigin.y}px)`, pointerEvents: hasPointerEvents ? 'all' : 'none', visibility: initialized ? 'visible' : 'hidden', @@ -254,7 +255,7 @@ export function NodeWrapper({ targetPosition={node.targetPosition} dragging={dragging} dragHandle={node.dragHandle} - zIndex={zIndex} + zIndex={internals.z} {...nodeDimensions} /> diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index f02e74f4..682915da 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -38,14 +38,14 @@ const createRFStore = ({ (set, get) => ({ ...getInitialState({ nodes, edges, width, height, fitView, defaultNodes, defaultEdges }), setNodes: (nodes: Node[]) => { - const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get(); + const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get(); // setNodes() is called exclusively in response to user actions: // - either when the `` prop is updated in the controlled ReactFlow setup, // - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup. // // When this happens, we take the note objects passed by the user and extend them with fields // relevant for internal React Flow operations. - adoptUserNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect }); + adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin, elevateNodesOnSelect }); set({ nodes }); }, diff --git a/packages/react/src/store/initialState.ts b/packages/react/src/store/initialState.ts index cc754976..d3812725 100644 --- a/packages/react/src/store/initialState.ts +++ b/packages/react/src/store/initialState.ts @@ -29,13 +29,14 @@ const getInitialState = ({ fitView?: boolean; } = {}): ReactFlowStore => { const nodeLookup = new Map(); + const parentLookup = new Map(); const connectionLookup = new Map(); const edgeLookup = new Map(); const storeEdges = defaultEdges ?? edges ?? []; const storeNodes = defaultNodes ?? nodes ?? []; updateConnectionLookup(connectionLookup, edgeLookup, storeEdges); - adoptUserNodes(storeNodes, nodeLookup, { + adoptUserNodes(storeNodes, nodeLookup, parentLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false, }); @@ -59,6 +60,7 @@ const getInitialState = ({ transform, nodes: storeNodes, nodeLookup, + parentLookup, edges: storeEdges, edgeLookup, connectionLookup, diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 397ca6f1..bd5f5720 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -54,6 +54,7 @@ export type ReactFlowStore>; + parentLookup: Map[]>; edges: Edge[]; edgeLookup: EdgeLookup; connectionLookup: ConnectionLookup; diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index 7d2b2444..073f206c 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -81,7 +81,8 @@ export const getInitialStore = ({ fitView?: boolean; }) => { const nodeLookup: NodeLookup = new Map(); - adoptUserNodes(nodes, nodeLookup, { + const parentLookup = new Map(); + adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false, checkEquality: false @@ -104,8 +105,9 @@ export const getInitialStore = ({ return { flowId: writable(null), - nodes: createNodesStore(nodes, nodeLookup), + nodes: createNodesStore(nodes, nodeLookup, parentLookup), nodeLookup: readable>(nodeLookup), + parentLookup: readable>(parentLookup), edgeLookup: readable>(edgeLookup), visibleNodes: readable([]), edges: createEdgesStore(edges, connectionLookup, edgeLookup), diff --git a/packages/svelte/src/lib/store/utils.ts b/packages/svelte/src/lib/store/utils.ts index 4dabbd33..5fd66cfe 100644 --- a/packages/svelte/src/lib/store/utils.ts +++ b/packages/svelte/src/lib/store/utils.ts @@ -127,7 +127,8 @@ export type NodeStoreOptions = { // The user only passes in relative positions, so we need to calculate the absolute positions based on the parent nodes. export const createNodesStore = ( nodes: Node[], - nodeLookup: NodeLookup + nodeLookup: NodeLookup, + parentLookup: Map ): { subscribe: (this: void, run: Subscriber) => Unsubscriber; update: (this: void, updater: Updater) => void; @@ -141,7 +142,7 @@ export const createNodesStore = ( let elevateNodesOnSelect = true; const _set = (nds: Node[]): Node[] => { - adoptUserNodes(nds, nodeLookup, { + adoptUserNodes(nds, nodeLookup, parentLookup, { elevateNodesOnSelect, defaults, checkEquality: false diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 3d8727c2..55aa368b 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -27,7 +27,7 @@ export function updateAbsolutePositions( elevateNodesOnSelect: true, defaults: {}, }, - parentNodeIds?: Set + parentLookup?: Map[]> ) { const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0; @@ -38,7 +38,7 @@ export function updateAbsolutePositions( throw new Error(`Parent node ${parentId} not found`); } - if (parentId || node.internals.isParent || parentNodeIds?.has(id)) { + if (parentId || node.internals.isParent || parentLookup?.has(id)) { const parentNode = parentId ? nodeLookup.get(parentId) : null; const { x, y, z } = calculateXYZPosition( node, @@ -47,20 +47,21 @@ export function updateAbsolutePositions( ...node.position, z: (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0), }, - parentNode?.origin || options.nodeOrigin + parentNode?.origin ?? options.nodeOrigin ); const currPosition = node.internals.positionAbsolute; const positionChanged = x !== currPosition.x || y !== currPosition.y; - node.internals.positionAbsolute = positionChanged ? { x, y } : currPosition; - node.internals.z = z; + node.internals = { + ...node.internals, + positionAbsolute: positionChanged ? { x, y } : currPosition, + z, + }; - if (parentNodeIds !== undefined) { - node.internals.isParent = !!parentNodeIds?.has(id); + if (parentLookup !== undefined) { + node.internals.isParent = !!parentLookup.has(id); } - - nodeLookup.set(id, node); } } } @@ -75,6 +76,7 @@ type UpdateNodesOptions = { export function adoptUserNodes( nodes: NodeType[], nodeLookup: Map>, + parentLookup: Map[]>, options: UpdateNodesOptions = { nodeOrigin: [0, 0] as NodeOrigin, elevateNodesOnSelect: true, @@ -84,20 +86,19 @@ export function adoptUserNodes( ) { const tmpLookup = new Map(nodeLookup); nodeLookup.clear(); + parentLookup.clear(); + const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0; - const parentNodeIds = new Set(); + // const parentNodeIds = new Set(); nodes.forEach((userNode) => { const currentStoreNode = tmpLookup.get(userNode.id); - if (userNode.parentId) { - parentNodeIds.add(userNode.parentId); - } - + let internalNode = currentStoreNode!; if (options.checkEquality && userNode === currentStoreNode?.internals.userNode) { nodeLookup.set(userNode.id, currentStoreNode); } else { - nodeLookup.set(userNode.id, { + internalNode = { ...options.defaults, ...userNode, measured: { @@ -111,12 +112,22 @@ export function adoptUserNodes( userNode, isParent: false, }, - }); + }; + nodeLookup.set(userNode.id, internalNode); + } + + if (userNode.parentId) { + const childNodes = parentLookup.get(userNode.parentId); + if (childNodes) { + childNodes.push(internalNode); + } else { + parentLookup.set(userNode.parentId, [internalNode]); + } } }); - if (parentNodeIds.size > 0) { - updateAbsolutePositions(nodeLookup, options, parentNodeIds); + if (parentLookup.size > 0) { + updateAbsolutePositions(nodeLookup, options, parentLookup); } } From 87086e99975dfbdd0b4424c640e7075cf94c07dd Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 15 Apr 2024 17:23:14 +0200 Subject: [PATCH 10/35] added correct expandParent offset of children --- .../NodeResizer/NodeResizeControl.tsx | 4 ++-- packages/react/src/store/index.ts | 13 ++++++++--- packages/svelte/src/lib/store/index.ts | 1 + packages/system/src/types/nodes.ts | 1 + packages/system/src/utils/store.ts | 23 ++++++++++++++++--- 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx index 6ca54676..b661ebde 100644 --- a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx @@ -67,7 +67,7 @@ function ResizeControl({ }; }, onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => { - const { triggerNodeChanges, nodeLookup } = store.getState(); + const { triggerNodeChanges, nodeLookup, parentLookup } = store.getState(); const changes: NodeChange[] = []; const newPosition = { x: change.x, y: change.y }; @@ -86,7 +86,7 @@ function ResizeControl({ const nodeWith = evaluateNodePosition(nodeWithChange, nodeLookup) as InternalNodeWithParentExpand; - const parentExpandChanges = handleParentExpand([nodeWith], nodeLookup); + const parentExpandChanges = handleParentExpand([nodeWith], nodeLookup, parentLookup); changes.push(...parentExpandChanges); newPosition.x = change.x ? Math.max(0, change.x) : undefined; newPosition.y = change.y ? Math.max(0, change.y) : undefined; diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 682915da..c38a980f 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -76,6 +76,7 @@ const createRFStore = ({ onNodesChange, fitView, nodeLookup, + parentLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, @@ -84,7 +85,13 @@ const createRFStore = ({ debug, } = get(); - const { changes, updatedInternals } = updateNodeInternalsSystem(updates, nodeLookup, domNode, nodeOrigin); + const { changes, updatedInternals } = updateNodeInternalsSystem( + updates, + nodeLookup, + parentLookup, + domNode, + nodeOrigin + ); if (!updatedInternals) { return; @@ -116,7 +123,7 @@ const createRFStore = ({ } }, updateNodePositions: (nodeDragItems, dragging = false) => { - const { nodeLookup } = get(); + const { nodeLookup, parentLookup } = get(); type ExpandParentInternalNode = InternalNode & { parentId: string; expandParent: true }; const expandParentNodes: ExpandParentInternalNode[] = []; @@ -148,7 +155,7 @@ const createRFStore = ({ }); if (expandParentNodes.length > 0) { - const parentExpandChanges = handleParentExpand(expandParentNodes, nodeLookup); + const parentExpandChanges = handleParentExpand(expandParentNodes, nodeLookup, parentLookup); changes.push(...parentExpandChanges); } diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts index c808d2af..07d4ab76 100644 --- a/packages/svelte/src/lib/store/index.ts +++ b/packages/svelte/src/lib/store/index.ts @@ -83,6 +83,7 @@ export function createStore({ const { changes, updatedInternals } = updateNodeInternalsSystem( updates, nodeLookup, + get(store.parentLookup), get(store.domNode), get(store.nodeOrigin) ); diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index d802dad8..7e6d7f14 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -144,3 +144,4 @@ export type NodeHandle = Optional; export type Align = 'center' | 'start' | 'end'; export type NodeLookup = Map; +export type ParentLookup = Map; diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 55aa368b..07044503 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -15,6 +15,7 @@ import { Rect, NodeDimensionChange, NodePositionChange, + ParentLookup, } from '../types'; import { getDimensions, getHandleBounds } from './dom'; import { getBoundsOfRects, getNodeDimensions, isNumeric, nodeToRect } from './general'; @@ -160,7 +161,8 @@ type ExpandParentNode = NodeType & { parentId: string; expandParent: t export function handleParentExpand( nodes: ExpandParentNode[], - nodeLookup: NodeLookup + nodeLookup: NodeLookup, + parentLookup: ParentLookup ): (NodeDimensionChange | NodePositionChange)[] { const changes: (NodeDimensionChange | NodePositionChange)[] = []; const chilNodeRects = new Map(); @@ -193,6 +195,20 @@ export function handleParentExpand( y: position.y - yChange, }, }); + + const childNodes = parentLookup.get(id); + childNodes?.forEach((childNode) => { + if (!nodes.find((n) => n.id === childNode.id)) { + changes.push({ + id: childNode.id, + type: 'position', + position: { + x: childNode.position.x + xChange, + y: childNode.position.y + yChange, + }, + }); + } + }); } if (dimensions.width < rect.width || dimensions.height < rect.height) { @@ -214,7 +230,8 @@ export function handleParentExpand( export function updateNodeInternals( updates: Map, - nodeLookup: Map, + nodeLookup: NodeLookup, + parentLookup: ParentLookup, domNode: HTMLElement | null, nodeOrigin?: NodeOrigin ): { changes: (NodeDimensionChange | NodePositionChange)[]; updatedInternals: boolean } { @@ -284,7 +301,7 @@ export function updateNodeInternals( }); if (parentExpandNodes.length > 0) { - const parentExpandChanges = handleParentExpand(parentExpandNodes, nodeLookup); + const parentExpandChanges = handleParentExpand(parentExpandNodes, nodeLookup, parentLookup); changes.push(...parentExpandChanges); } From 81bdaf6b68f56e3c2bed9ca0c7ebfbeabbdd6f78 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 15 Apr 2024 17:31:48 +0200 Subject: [PATCH 11/35] prevent falsely adjusting currently dragged item on parentExpand --- packages/system/src/utils/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 07044503..11826898 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -198,7 +198,7 @@ export function handleParentExpand( const childNodes = parentLookup.get(id); childNodes?.forEach((childNode) => { - if (!nodes.find((n) => n.id === childNode.id)) { + if (!nodes.some((n) => n.id === childNode.id)) { changes.push({ id: childNode.id, type: 'position', From 53ea72e85b4b6f84b14fcae459fffb2d5d2ed14b Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 16 Apr 2024 10:55:22 +0200 Subject: [PATCH 12/35] chore(examples): cleanup --- .../src/routes/examples/intersections/Flow.svelte | 4 ++-- examples/svelte/src/routes/examples/reset/+page.svelte | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/examples/svelte/src/routes/examples/intersections/Flow.svelte b/examples/svelte/src/routes/examples/intersections/Flow.svelte index f3f80335..12c98e00 100644 --- a/examples/svelte/src/routes/examples/intersections/Flow.svelte +++ b/examples/svelte/src/routes/examples/intersections/Flow.svelte @@ -12,8 +12,8 @@ const { getIntersectingNodes } = useSvelteFlow(); - function onNodeDrag({ detail: { node } }) { - const intersections = getIntersectingNodes(node).map((n) => n.id); + function onNodeDrag({ detail: { targetNode } }) { + const intersections = getIntersectingNodes(targetNode).map((n) => n.id); $nodes.forEach((n) => { n.class = intersections.includes(n.id) ? 'highlight' : ''; diff --git a/examples/svelte/src/routes/examples/reset/+page.svelte b/examples/svelte/src/routes/examples/reset/+page.svelte index 78b1b245..e7857d70 100644 --- a/examples/svelte/src/routes/examples/reset/+page.svelte +++ b/examples/svelte/src/routes/examples/reset/+page.svelte @@ -1,14 +1,6 @@