@xyflow/react 12.0.0-next.13 → 12.0.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base.css +2 -0
- package/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
- package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -1
- package/dist/esm/components/NodeWrapper/useNodeObservation.d.ts +9 -0
- package/dist/esm/components/NodeWrapper/useNodeObservation.d.ts.map +1 -0
- package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts +15 -0
- package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts.map +1 -0
- package/dist/esm/components/NodesSelection/index.d.ts.map +1 -1
- package/dist/esm/components/ReactFlowProvider/index.d.ts +4 -3
- package/dist/esm/components/ReactFlowProvider/index.d.ts.map +1 -1
- package/dist/esm/components/SelectionListener/index.d.ts.map +1 -1
- package/dist/esm/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
- package/dist/esm/container/Pane/index.d.ts.map +1 -1
- package/dist/esm/contexts/RFStoreContext.d.ts +2 -2
- package/dist/esm/contexts/RFStoreContext.d.ts.map +1 -1
- package/dist/esm/contexts/StoreContext.d.ts +5 -0
- package/dist/esm/contexts/StoreContext.d.ts.map +1 -0
- package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
- package/dist/esm/hooks/useStore.d.ts +0 -1
- package/dist/esm/hooks/useStore.d.ts.map +1 -1
- package/dist/esm/hooks/useUpdateNodeInternals.d.ts.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +217 -167
- package/dist/esm/index.mjs +217 -167
- package/dist/esm/store/index.d.ts +2 -2
- package/dist/esm/store/index.d.ts.map +1 -1
- package/dist/esm/store/initialState.d.ts.map +1 -1
- package/dist/esm/types/nodes.d.ts +6 -0
- package/dist/esm/types/nodes.d.ts.map +1 -1
- package/dist/esm/types/store.d.ts +3 -2
- package/dist/esm/types/store.d.ts.map +1 -1
- package/dist/style.css +2 -0
- package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
- package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -1
- package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts +15 -0
- package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts.map +1 -0
- package/dist/umd/components/NodesSelection/index.d.ts.map +1 -1
- package/dist/umd/components/ReactFlowProvider/index.d.ts +4 -3
- package/dist/umd/components/ReactFlowProvider/index.d.ts.map +1 -1
- package/dist/umd/components/SelectionListener/index.d.ts.map +1 -1
- package/dist/umd/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
- package/dist/umd/container/Pane/index.d.ts.map +1 -1
- package/dist/umd/contexts/StoreContext.d.ts +5 -0
- package/dist/umd/contexts/StoreContext.d.ts.map +1 -0
- package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
- package/dist/umd/hooks/useStore.d.ts +0 -1
- package/dist/umd/hooks/useStore.d.ts.map +1 -1
- package/dist/umd/hooks/useUpdateNodeInternals.d.ts.map +1 -1
- package/dist/umd/index.d.ts +1 -1
- package/dist/umd/index.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/store/index.d.ts +2 -2
- package/dist/umd/store/index.d.ts.map +1 -1
- package/dist/umd/store/initialState.d.ts.map +1 -1
- package/dist/umd/types/nodes.d.ts +6 -0
- package/dist/umd/types/nodes.d.ts.map +1 -1
- package/dist/umd/types/store.d.ts +3 -2
- package/dist/umd/types/store.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import cc from 'classcat';
|
|
4
|
-
import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, nodeToRect, isRectObject, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calculateNodePosition, Position, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge,
|
|
4
|
+
import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, evaluateAbsolutePosition, nodeToRect, isRectObject, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calculateNodePosition, Position, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge, getInternalNodesBounds, nodeHasDimensions, getNodeDimensions, clampPosition, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionLineType, updateConnectionLookup, adoptUserNodes, devWarn, updateNodeInternals, updateAbsolutePositions, handleExpandParent, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, shallowNodeData, getNodePositionWithOrigin, XYMinimap, getBoundsOfRects, ResizeControlVariant, XYResizer, XY_RESIZER_LINE_POSITIONS, XY_RESIZER_HANDLE_POSITIONS, getNodesBounds, getNodeToolbarTransform } from '@xyflow/system';
|
|
5
5
|
export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, updateEdge } from '@xyflow/system';
|
|
6
6
|
import { createContext, useContext, useMemo, useEffect, useRef, useState, forwardRef, useLayoutEffect, useCallback, memo } from 'react';
|
|
7
7
|
import { useStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
|
|
@@ -40,7 +40,6 @@ function useStoreApi() {
|
|
|
40
40
|
getState: store.getState,
|
|
41
41
|
setState: store.setState,
|
|
42
42
|
subscribe: store.subscribe,
|
|
43
|
-
destroy: store.destroy,
|
|
44
43
|
}), [store]);
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -82,10 +81,21 @@ function Attribution({ proOptions, position = 'bottom-right' }) {
|
|
|
82
81
|
return (jsx(Panel, { position: position, className: "react-flow__attribution", "data-message": "Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev", children: jsx("a", { href: "https://reactflow.dev", target: "_blank", rel: "noopener noreferrer", "aria-label": "React Flow attribution", children: "React Flow" }) }));
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
const selector$o = (s) =>
|
|
86
|
-
selectedNodes
|
|
87
|
-
selectedEdges
|
|
88
|
-
|
|
84
|
+
const selector$o = (s) => {
|
|
85
|
+
const selectedNodes = [];
|
|
86
|
+
const selectedEdges = [];
|
|
87
|
+
for (const [, node] of s.nodeLookup) {
|
|
88
|
+
if (node.selected) {
|
|
89
|
+
selectedNodes.push(node.internals.userNode);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const [, edge] of s.edgeLookup) {
|
|
93
|
+
if (edge.selected) {
|
|
94
|
+
selectedEdges.push(edge);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { selectedNodes, selectedEdges };
|
|
98
|
+
};
|
|
89
99
|
const selectId = (obj) => obj.id;
|
|
90
100
|
function areEqual(a, b) {
|
|
91
101
|
return (shallow(a.selectedNodes.map(selectId), b.selectedNodes.map(selectId)) &&
|
|
@@ -548,7 +558,7 @@ function applyChange(change, element) {
|
|
|
548
558
|
element.measured ??= {};
|
|
549
559
|
element.measured.width = change.dimensions.width;
|
|
550
560
|
element.measured.height = change.dimensions.height;
|
|
551
|
-
if (change.
|
|
561
|
+
if (change.setAttributes) {
|
|
552
562
|
element.width = change.dimensions.width;
|
|
553
563
|
element.height = change.dimensions.height;
|
|
554
564
|
}
|
|
@@ -615,8 +625,8 @@ function createSelectionChange(id, selected) {
|
|
|
615
625
|
}
|
|
616
626
|
function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false) {
|
|
617
627
|
const changes = [];
|
|
618
|
-
for (const [, item] of items) {
|
|
619
|
-
const willBeSelected = selectedIds.has(
|
|
628
|
+
for (const [id, item] of items) {
|
|
629
|
+
const willBeSelected = selectedIds.has(id);
|
|
620
630
|
// we don't want to set all items to selected=false on the first selection
|
|
621
631
|
if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) {
|
|
622
632
|
if (mutateItem) {
|
|
@@ -692,10 +702,7 @@ function useReactFlow() {
|
|
|
692
702
|
const { edges = [] } = store.getState();
|
|
693
703
|
return edges.map((e) => ({ ...e }));
|
|
694
704
|
}, []);
|
|
695
|
-
const getEdge = useCallback((id) =>
|
|
696
|
-
const { edges = [] } = store.getState();
|
|
697
|
-
return edges.find((e) => e.id === id);
|
|
698
|
-
}, []);
|
|
705
|
+
const getEdge = useCallback((id) => store.getState().edgeLookup.get(id), []);
|
|
699
706
|
// A reference of all the batched updates to process before the next render. We
|
|
700
707
|
// want a mutable reference here so multiple synchronous calls to `setNodes` etc
|
|
701
708
|
// can be batched together.
|
|
@@ -825,13 +832,25 @@ function useReactFlow() {
|
|
|
825
832
|
}
|
|
826
833
|
return { deletedNodes: matchingNodes, deletedEdges: matchingEdges };
|
|
827
834
|
}, []);
|
|
828
|
-
const getNodeRect = useCallback((
|
|
829
|
-
const
|
|
830
|
-
|
|
835
|
+
const getNodeRect = useCallback((node) => {
|
|
836
|
+
const { nodeLookup, nodeOrigin } = store.getState();
|
|
837
|
+
const nodeToUse = isNode(node) ? node : nodeLookup.get(node.id);
|
|
838
|
+
const position = nodeToUse.parentId
|
|
839
|
+
? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.parentId, nodeLookup, nodeOrigin)
|
|
840
|
+
: nodeToUse.position;
|
|
841
|
+
const nodeWithPosition = {
|
|
842
|
+
id: nodeToUse.id,
|
|
843
|
+
position,
|
|
844
|
+
width: nodeToUse.measured?.width ?? nodeToUse.width,
|
|
845
|
+
height: nodeToUse.measured?.height ?? nodeToUse.height,
|
|
846
|
+
data: nodeToUse.data,
|
|
847
|
+
};
|
|
848
|
+
return nodeToRect(nodeWithPosition);
|
|
831
849
|
}, []);
|
|
832
850
|
const getIntersectingNodes = useCallback((nodeOrRect, partially = true, nodes) => {
|
|
833
851
|
const isRect = isRectObject(nodeOrRect);
|
|
834
852
|
const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
|
|
853
|
+
const hasNodesOption = nodes !== undefined;
|
|
835
854
|
if (!nodeRect) {
|
|
836
855
|
return [];
|
|
837
856
|
}
|
|
@@ -840,7 +859,7 @@ function useReactFlow() {
|
|
|
840
859
|
if (internalNode && !isRect && (n.id === nodeOrRect.id || !internalNode.internals.positionAbsolute)) {
|
|
841
860
|
return false;
|
|
842
861
|
}
|
|
843
|
-
const currNodeRect = nodeToRect(n);
|
|
862
|
+
const currNodeRect = nodeToRect(hasNodesOption ? n : internalNode);
|
|
844
863
|
const overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
|
|
845
864
|
const partiallyVisible = partially && overlappingArea > 0;
|
|
846
865
|
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
@@ -856,7 +875,7 @@ function useReactFlow() {
|
|
|
856
875
|
const partiallyVisible = partially && overlappingArea > 0;
|
|
857
876
|
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
858
877
|
}, []);
|
|
859
|
-
const updateNode = useCallback((id, nodeUpdate, options = { replace:
|
|
878
|
+
const updateNode = useCallback((id, nodeUpdate, options = { replace: false }) => {
|
|
860
879
|
setNodes((prevNodes) => prevNodes.map((node) => {
|
|
861
880
|
if (node.id === id) {
|
|
862
881
|
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node) : nodeUpdate;
|
|
@@ -1100,6 +1119,7 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1100
1119
|
const prevSelectedNodesCount = useRef(0);
|
|
1101
1120
|
const prevSelectedEdgesCount = useRef(0);
|
|
1102
1121
|
const containerBounds = useRef();
|
|
1122
|
+
const edgeIdLookup = useRef(new Map());
|
|
1103
1123
|
const { userSelectionActive, elementsSelectable, dragging } = useStore(selector$j, shallow);
|
|
1104
1124
|
const resetUserSelection = () => {
|
|
1105
1125
|
store.setState({ userSelectionActive: false, userSelectionRect: null });
|
|
@@ -1120,7 +1140,7 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1120
1140
|
};
|
|
1121
1141
|
const onWheel = onPaneScroll ? (event) => onPaneScroll(event) : undefined;
|
|
1122
1142
|
const onMouseDown = (event) => {
|
|
1123
|
-
const { resetSelectedElements, domNode } = store.getState();
|
|
1143
|
+
const { resetSelectedElements, domNode, edgeLookup } = store.getState();
|
|
1124
1144
|
containerBounds.current = domNode?.getBoundingClientRect();
|
|
1125
1145
|
if (!elementsSelectable ||
|
|
1126
1146
|
!isSelecting ||
|
|
@@ -1129,6 +1149,11 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1129
1149
|
!containerBounds.current) {
|
|
1130
1150
|
return;
|
|
1131
1151
|
}
|
|
1152
|
+
edgeIdLookup.current = new Map();
|
|
1153
|
+
for (const [id, edge] of edgeLookup) {
|
|
1154
|
+
edgeIdLookup.current.set(edge.source, edgeIdLookup.current.get(edge.source)?.add(id) || new Set([id]));
|
|
1155
|
+
edgeIdLookup.current.set(edge.target, edgeIdLookup.current.get(edge.target)?.add(id) || new Set([id]));
|
|
1156
|
+
}
|
|
1132
1157
|
const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current);
|
|
1133
1158
|
resetSelectedElements();
|
|
1134
1159
|
store.setState({
|
|
@@ -1148,24 +1173,24 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1148
1173
|
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
|
|
1149
1174
|
return;
|
|
1150
1175
|
}
|
|
1151
|
-
|
|
1152
|
-
const
|
|
1153
|
-
const startX = userSelectionRect.startX ?? 0;
|
|
1154
|
-
const startY = userSelectionRect.startY ?? 0;
|
|
1176
|
+
const { x: mouseX, y: mouseY } = getEventPosition(event.nativeEvent, containerBounds.current);
|
|
1177
|
+
const { startX, startY } = userSelectionRect;
|
|
1155
1178
|
const nextUserSelectRect = {
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1179
|
+
startX,
|
|
1180
|
+
startY,
|
|
1181
|
+
x: mouseX < startX ? mouseX : startX,
|
|
1182
|
+
y: mouseY < startY ? mouseY : startY,
|
|
1183
|
+
width: Math.abs(mouseX - startX),
|
|
1184
|
+
height: Math.abs(mouseY - startY),
|
|
1161
1185
|
};
|
|
1162
1186
|
const selectedNodes = getNodesInside(nodeLookup, nextUserSelectRect, transform, selectionMode === SelectionMode.Partial, true, nodeOrigin);
|
|
1163
1187
|
const selectedEdgeIds = new Set();
|
|
1164
1188
|
const selectedNodeIds = new Set();
|
|
1165
1189
|
for (const selectedNode of selectedNodes) {
|
|
1166
1190
|
selectedNodeIds.add(selectedNode.id);
|
|
1167
|
-
|
|
1168
|
-
|
|
1191
|
+
const edgeIds = edgeIdLookup.current.get(selectedNode.id);
|
|
1192
|
+
if (edgeIds) {
|
|
1193
|
+
for (const edgeId of edgeIds) {
|
|
1169
1194
|
selectedEdgeIds.add(edgeId);
|
|
1170
1195
|
}
|
|
1171
1196
|
}
|
|
@@ -1182,6 +1207,8 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1182
1207
|
}
|
|
1183
1208
|
store.setState({
|
|
1184
1209
|
userSelectionRect: nextUserSelectRect,
|
|
1210
|
+
userSelectionActive: true,
|
|
1211
|
+
nodesSelectionActive: false,
|
|
1185
1212
|
});
|
|
1186
1213
|
};
|
|
1187
1214
|
const onMouseUp = (event) => {
|
|
@@ -1288,7 +1315,7 @@ function useMoveSelectedNodes() {
|
|
|
1288
1315
|
const store = useStoreApi();
|
|
1289
1316
|
const moveSelectedNodes = useCallback((params) => {
|
|
1290
1317
|
const { nodeExtent, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin } = store.getState();
|
|
1291
|
-
const nodeUpdates =
|
|
1318
|
+
const nodeUpdates = new Map();
|
|
1292
1319
|
const isSelected = selectedAndDraggable(nodesDraggable);
|
|
1293
1320
|
// by default a node moves 5px on each key press
|
|
1294
1321
|
// if snap grid is enabled, we use that for the velocity
|
|
@@ -1317,7 +1344,7 @@ function useMoveSelectedNodes() {
|
|
|
1317
1344
|
});
|
|
1318
1345
|
node.position = position;
|
|
1319
1346
|
node.internals.positionAbsolute = positionAbsolute;
|
|
1320
|
-
nodeUpdates.
|
|
1347
|
+
nodeUpdates.set(node.id, node);
|
|
1321
1348
|
}
|
|
1322
1349
|
updateNodePositions(nodeUpdates);
|
|
1323
1350
|
}, []);
|
|
@@ -1515,13 +1542,10 @@ function getNodeInlineStyleDimensions(node) {
|
|
|
1515
1542
|
}
|
|
1516
1543
|
|
|
1517
1544
|
const selector$h = (s) => {
|
|
1518
|
-
const
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
const { width, height, x, y } = getNodesBounds(selectedNodes, { nodeOrigin: s.nodeOrigin });
|
|
1545
|
+
const { width, height, x, y } = getInternalNodesBounds(s.nodeLookup, {
|
|
1546
|
+
nodeOrigin: s.nodeOrigin,
|
|
1547
|
+
filter: (node) => !!node.selected,
|
|
1548
|
+
});
|
|
1525
1549
|
return {
|
|
1526
1550
|
width,
|
|
1527
1551
|
height,
|
|
@@ -1602,15 +1626,14 @@ function useVisibleNodeIds(onlyRenderVisible) {
|
|
|
1602
1626
|
return nodeIds;
|
|
1603
1627
|
}
|
|
1604
1628
|
|
|
1605
|
-
const selector$e = (s) => s.
|
|
1629
|
+
const selector$e = (s) => s.updateNodeInternals;
|
|
1606
1630
|
function useResizeObserver() {
|
|
1607
|
-
const
|
|
1608
|
-
const
|
|
1609
|
-
const resizeObserver = useMemo(() => {
|
|
1631
|
+
const updateNodeInternals = useStore(selector$e);
|
|
1632
|
+
const [resizeObserver] = useState(() => {
|
|
1610
1633
|
if (typeof ResizeObserver === 'undefined') {
|
|
1611
1634
|
return null;
|
|
1612
1635
|
}
|
|
1613
|
-
|
|
1636
|
+
return new ResizeObserver((entries) => {
|
|
1614
1637
|
const updates = new Map();
|
|
1615
1638
|
entries.forEach((entry) => {
|
|
1616
1639
|
const id = entry.target.getAttribute('data-id');
|
|
@@ -1619,33 +1642,76 @@ function useResizeObserver() {
|
|
|
1619
1642
|
nodeElement: entry.target,
|
|
1620
1643
|
});
|
|
1621
1644
|
});
|
|
1622
|
-
|
|
1645
|
+
updateNodeInternals(updates);
|
|
1623
1646
|
});
|
|
1624
|
-
|
|
1625
|
-
return observer;
|
|
1626
|
-
}, []);
|
|
1647
|
+
});
|
|
1627
1648
|
useEffect(() => {
|
|
1628
1649
|
return () => {
|
|
1629
|
-
|
|
1650
|
+
resizeObserver?.disconnect();
|
|
1630
1651
|
};
|
|
1631
|
-
}, []);
|
|
1652
|
+
}, [resizeObserver]);
|
|
1632
1653
|
return resizeObserver;
|
|
1633
1654
|
}
|
|
1634
1655
|
|
|
1656
|
+
/**
|
|
1657
|
+
* Hook to handle the resize observation + internal updates for the passed node.
|
|
1658
|
+
*
|
|
1659
|
+
* @internal
|
|
1660
|
+
* @returns nodeRef - reference to the node element
|
|
1661
|
+
*/
|
|
1662
|
+
function useNodeObserver({ node, nodeType, hasDimensions, resizeObserver, }) {
|
|
1663
|
+
const store = useStoreApi();
|
|
1664
|
+
const nodeRef = useRef(null);
|
|
1665
|
+
const observedNode = useRef(null);
|
|
1666
|
+
const prevSourcePosition = useRef(node.sourcePosition);
|
|
1667
|
+
const prevTargetPosition = useRef(node.targetPosition);
|
|
1668
|
+
const prevType = useRef(nodeType);
|
|
1669
|
+
const isInitialized = hasDimensions && !!node.internals.handleBounds && !node.hidden;
|
|
1670
|
+
useEffect(() => {
|
|
1671
|
+
if (nodeRef.current && (!isInitialized || observedNode.current !== nodeRef.current)) {
|
|
1672
|
+
if (observedNode.current) {
|
|
1673
|
+
resizeObserver?.unobserve(observedNode.current);
|
|
1674
|
+
}
|
|
1675
|
+
resizeObserver?.observe(nodeRef.current);
|
|
1676
|
+
observedNode.current = nodeRef.current;
|
|
1677
|
+
}
|
|
1678
|
+
}, [isInitialized]);
|
|
1679
|
+
useEffect(() => {
|
|
1680
|
+
return () => {
|
|
1681
|
+
if (observedNode.current) {
|
|
1682
|
+
resizeObserver?.unobserve(observedNode.current);
|
|
1683
|
+
observedNode.current = null;
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
}, []);
|
|
1687
|
+
useEffect(() => {
|
|
1688
|
+
if (nodeRef.current) {
|
|
1689
|
+
// when the user programmatically changes the source or handle position, we need to update the internals
|
|
1690
|
+
// to make sure the edges are updated correctly
|
|
1691
|
+
const typeChanged = prevType.current !== nodeType;
|
|
1692
|
+
const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
|
|
1693
|
+
const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
|
|
1694
|
+
if (typeChanged || sourcePosChanged || targetPosChanged) {
|
|
1695
|
+
prevType.current = nodeType;
|
|
1696
|
+
prevSourcePosition.current = node.sourcePosition;
|
|
1697
|
+
prevTargetPosition.current = node.targetPosition;
|
|
1698
|
+
store
|
|
1699
|
+
.getState()
|
|
1700
|
+
.updateNodeInternals(new Map([[node.id, { id: node.id, nodeElement: nodeRef.current, force: true }]]));
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}, [node.id, nodeType, node.sourcePosition, node.targetPosition]);
|
|
1704
|
+
return nodeRef;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1635
1707
|
function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeExtent, nodeOrigin, onError, }) {
|
|
1636
|
-
const { node,
|
|
1708
|
+
const { node, internals, isParent } = useStore((s) => {
|
|
1637
1709
|
const node = s.nodeLookup.get(id);
|
|
1638
|
-
const
|
|
1639
|
-
? clampPosition(node.internals.positionAbsolute, nodeExtent)
|
|
1640
|
-
: node.internals.positionAbsolute || { x: 0, y: 0 };
|
|
1710
|
+
const isParent = s.parentLookup.has(id);
|
|
1641
1711
|
return {
|
|
1642
1712
|
node,
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
positionAbsoluteX: positionAbsolute.x,
|
|
1646
|
-
positionAbsoluteY: positionAbsolute.y,
|
|
1647
|
-
zIndex: node.internals.z,
|
|
1648
|
-
isParent: node.internals.isParent,
|
|
1713
|
+
internals: node.internals,
|
|
1714
|
+
isParent,
|
|
1649
1715
|
};
|
|
1650
1716
|
}, shallow);
|
|
1651
1717
|
let nodeType = node.type || 'default';
|
|
@@ -1660,50 +1726,8 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1660
1726
|
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
|
1661
1727
|
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
|
|
1662
1728
|
const store = useStoreApi();
|
|
1663
|
-
const
|
|
1664
|
-
const
|
|
1665
|
-
const prevTargetPosition = useRef(node.targetPosition);
|
|
1666
|
-
const prevType = useRef(nodeType);
|
|
1667
|
-
const nodeDimensions = getNodeDimensions(node);
|
|
1668
|
-
const inlineDimensions = getNodeInlineStyleDimensions(node);
|
|
1669
|
-
const initialized = nodeHasDimensions(node);
|
|
1670
|
-
const hasHandleBounds = !!node.internals.handleBounds;
|
|
1671
|
-
const moveSelectedNodes = useMoveSelectedNodes();
|
|
1672
|
-
useEffect(() => {
|
|
1673
|
-
const currNode = nodeRef.current;
|
|
1674
|
-
return () => {
|
|
1675
|
-
if (currNode) {
|
|
1676
|
-
resizeObserver?.unobserve(currNode);
|
|
1677
|
-
}
|
|
1678
|
-
};
|
|
1679
|
-
}, []);
|
|
1680
|
-
useEffect(() => {
|
|
1681
|
-
if (nodeRef.current && !node.hidden) {
|
|
1682
|
-
const currNode = nodeRef.current;
|
|
1683
|
-
if (!initialized || !hasHandleBounds) {
|
|
1684
|
-
resizeObserver?.unobserve(currNode);
|
|
1685
|
-
resizeObserver?.observe(currNode);
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
}, [node.hidden, initialized, hasHandleBounds]);
|
|
1689
|
-
useEffect(() => {
|
|
1690
|
-
// when the user programmatically changes the source or handle position, we re-initialize the node
|
|
1691
|
-
const typeChanged = prevType.current !== nodeType;
|
|
1692
|
-
const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
|
|
1693
|
-
const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
|
|
1694
|
-
if (nodeRef.current && (typeChanged || sourcePosChanged || targetPosChanged)) {
|
|
1695
|
-
if (typeChanged) {
|
|
1696
|
-
prevType.current = nodeType;
|
|
1697
|
-
}
|
|
1698
|
-
if (sourcePosChanged) {
|
|
1699
|
-
prevSourcePosition.current = node.sourcePosition;
|
|
1700
|
-
}
|
|
1701
|
-
if (targetPosChanged) {
|
|
1702
|
-
prevTargetPosition.current = node.targetPosition;
|
|
1703
|
-
}
|
|
1704
|
-
store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, force: true }]]));
|
|
1705
|
-
}
|
|
1706
|
-
}, [id, nodeType, node.sourcePosition, node.targetPosition]);
|
|
1729
|
+
const hasDimensions = nodeHasDimensions(node);
|
|
1730
|
+
const nodeRef = useNodeObserver({ node, nodeType, hasDimensions, resizeObserver });
|
|
1707
1731
|
const dragging = useDrag({
|
|
1708
1732
|
nodeRef,
|
|
1709
1733
|
disabled: node.hidden || !isDraggable,
|
|
@@ -1712,12 +1736,17 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1712
1736
|
nodeId: id,
|
|
1713
1737
|
isSelectable,
|
|
1714
1738
|
});
|
|
1739
|
+
const moveSelectedNodes = useMoveSelectedNodes();
|
|
1715
1740
|
if (node.hidden) {
|
|
1716
1741
|
return null;
|
|
1717
1742
|
}
|
|
1718
|
-
const
|
|
1719
|
-
|
|
1720
|
-
|
|
1743
|
+
const nodeDimensions = getNodeDimensions(node);
|
|
1744
|
+
const inlineDimensions = getNodeInlineStyleDimensions(node);
|
|
1745
|
+
const clampedPosition = nodeExtent
|
|
1746
|
+
? clampPosition(internals.positionAbsolute, nodeExtent)
|
|
1747
|
+
: internals.positionAbsolute;
|
|
1748
|
+
const positionWithOrigin = getPositionWithOrigin({
|
|
1749
|
+
...clampedPosition,
|
|
1721
1750
|
...nodeDimensions,
|
|
1722
1751
|
origin: node.origin || nodeOrigin,
|
|
1723
1752
|
});
|
|
@@ -1759,7 +1788,7 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1759
1788
|
store.setState({
|
|
1760
1789
|
ariaLiveMessage: `Moved selected node ${event.key
|
|
1761
1790
|
.replace('Arrow', '')
|
|
1762
|
-
.toLowerCase()}. New position, x: ${~~
|
|
1791
|
+
.toLowerCase()}. New position, x: ${~~clampedPosition.x}, y: ${~~clampedPosition.y}`,
|
|
1763
1792
|
});
|
|
1764
1793
|
moveSelectedNodes({
|
|
1765
1794
|
direction: arrowKeyDiffs[event.key],
|
|
@@ -1783,13 +1812,13 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1783
1812
|
dragging,
|
|
1784
1813
|
},
|
|
1785
1814
|
]), ref: nodeRef, style: {
|
|
1786
|
-
zIndex,
|
|
1787
|
-
transform: `translate(${
|
|
1815
|
+
zIndex: internals.z,
|
|
1816
|
+
transform: `translate(${positionWithOrigin.x}px,${positionWithOrigin.y}px)`,
|
|
1788
1817
|
pointerEvents: hasPointerEvents ? 'all' : 'none',
|
|
1789
|
-
visibility:
|
|
1818
|
+
visibility: hasDimensions ? 'visible' : 'hidden',
|
|
1790
1819
|
...node.style,
|
|
1791
1820
|
...inlineDimensions,
|
|
1792
|
-
}, "data-id": id, "data-testid": `rf__node-${id}`, onMouseEnter: onMouseEnterHandler, onMouseMove: onMouseMoveHandler, onMouseLeave: onMouseLeaveHandler, onContextMenu: onContextMenuHandler, onClick: onSelectNodeHandler, onDoubleClick: onDoubleClickHandler, onKeyDown: isFocusable ? onKeyDown : undefined, tabIndex: isFocusable ? 0 : undefined, role: isFocusable ? 'button' : undefined, "aria-describedby": disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`, "aria-label": node.ariaLabel, children: jsx(Provider, { value: id, children: jsx(NodeComponent, { id: id, data: node.data, type: nodeType, positionAbsoluteX:
|
|
1821
|
+
}, "data-id": id, "data-testid": `rf__node-${id}`, onMouseEnter: onMouseEnterHandler, onMouseMove: onMouseMoveHandler, onMouseLeave: onMouseLeaveHandler, onContextMenu: onContextMenuHandler, onClick: onSelectNodeHandler, onDoubleClick: onDoubleClickHandler, onKeyDown: isFocusable ? onKeyDown : undefined, tabIndex: isFocusable ? 0 : undefined, role: isFocusable ? 'button' : undefined, "aria-describedby": disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`, "aria-label": node.ariaLabel, children: jsx(Provider, { value: id, children: jsx(NodeComponent, { id: id, data: node.data, type: nodeType, positionAbsoluteX: clampedPosition.x, positionAbsoluteY: clampedPosition.y, selected: node.selected, isConnectable: isConnectable, sourcePosition: node.sourcePosition, targetPosition: node.targetPosition, dragging: dragging, dragHandle: node.dragHandle, zIndex: internals.z, ...nodeDimensions }) }) }));
|
|
1793
1822
|
}
|
|
1794
1823
|
|
|
1795
1824
|
const selector$d = (s) => ({
|
|
@@ -2364,7 +2393,7 @@ const ConnectionLine = ({ nodeId, handleType, style, type = ConnectionLineType.B
|
|
|
2364
2393
|
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
|
2365
2394
|
connectionMode: s.connectionMode,
|
|
2366
2395
|
}), [nodeId]), shallow);
|
|
2367
|
-
const fromHandleBounds = fromNode?.internals
|
|
2396
|
+
const fromHandleBounds = fromNode?.internals.handleBounds;
|
|
2368
2397
|
let handleBounds = fromHandleBounds?.[handleType];
|
|
2369
2398
|
if (connectionMode === ConnectionMode.Loose) {
|
|
2370
2399
|
handleBounds = handleBounds ? handleBounds : fromHandleBounds?.[handleType === 'source' ? 'target' : 'source'];
|
|
@@ -2375,8 +2404,8 @@ const ConnectionLine = ({ nodeId, handleType, style, type = ConnectionLineType.B
|
|
|
2375
2404
|
const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
|
|
2376
2405
|
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.measured.width ?? 0) / 2;
|
|
2377
2406
|
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.measured.height ?? 0;
|
|
2378
|
-
const fromX =
|
|
2379
|
-
const fromY =
|
|
2407
|
+
const fromX = fromNode.internals.positionAbsolute.x + fromHandleX;
|
|
2408
|
+
const fromY = fromNode.internals.positionAbsolute.y + fromHandleY;
|
|
2380
2409
|
const fromPosition = fromHandle?.position;
|
|
2381
2410
|
const toPosition = fromPosition ? oppositePosition[fromPosition] : null;
|
|
2382
2411
|
if (!fromPosition || !toPosition) {
|
|
@@ -2460,20 +2489,23 @@ const GraphView = memo(GraphViewComponent);
|
|
|
2460
2489
|
|
|
2461
2490
|
const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView, } = {}) => {
|
|
2462
2491
|
const nodeLookup = new Map();
|
|
2492
|
+
const parentLookup = new Map();
|
|
2463
2493
|
const connectionLookup = new Map();
|
|
2464
2494
|
const edgeLookup = new Map();
|
|
2465
2495
|
const storeEdges = defaultEdges ?? edges ?? [];
|
|
2466
2496
|
const storeNodes = defaultNodes ?? nodes ?? [];
|
|
2467
2497
|
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
|
2468
|
-
adoptUserNodes(storeNodes, nodeLookup, {
|
|
2498
|
+
adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
|
2469
2499
|
nodeOrigin: [0, 0],
|
|
2470
2500
|
elevateNodesOnSelect: false,
|
|
2471
2501
|
});
|
|
2472
2502
|
let transform = [0, 0, 1];
|
|
2473
2503
|
if (fitView && width && height) {
|
|
2474
|
-
const nodesWithDimensions = storeNodes.filter((node) => (node.width || node.initialWidth) && (node.height || node.initialHeight));
|
|
2475
2504
|
// @todo users nodeOrigin should be used here
|
|
2476
|
-
const bounds =
|
|
2505
|
+
const bounds = getInternalNodesBounds(nodeLookup, {
|
|
2506
|
+
nodeOrigin: [0, 0],
|
|
2507
|
+
filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
|
|
2508
|
+
});
|
|
2477
2509
|
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
|
2478
2510
|
transform = [x, y, zoom];
|
|
2479
2511
|
}
|
|
@@ -2484,6 +2516,7 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
|
|
|
2484
2516
|
transform,
|
|
2485
2517
|
nodes: storeNodes,
|
|
2486
2518
|
nodeLookup,
|
|
2519
|
+
parentLookup,
|
|
2487
2520
|
edges: storeEdges,
|
|
2488
2521
|
edgeLookup,
|
|
2489
2522
|
connectionLookup,
|
|
@@ -2538,17 +2571,17 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
|
|
|
2538
2571
|
};
|
|
2539
2572
|
};
|
|
2540
2573
|
|
|
2541
|
-
const
|
|
2574
|
+
const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView: fitView$1, }) => createWithEqualityFn((set, get) => ({
|
|
2542
2575
|
...getInitialState({ nodes, edges, width, height, fitView: fitView$1, defaultNodes, defaultEdges }),
|
|
2543
2576
|
setNodes: (nodes) => {
|
|
2544
|
-
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2577
|
+
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2545
2578
|
// setNodes() is called exclusively in response to user actions:
|
|
2546
2579
|
// - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
|
2547
2580
|
// - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
|
|
2548
2581
|
//
|
|
2549
2582
|
// When this happens, we take the note objects passed by the user and extend them with fields
|
|
2550
2583
|
// relevant for internal React Flow operations.
|
|
2551
|
-
adoptUserNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
|
2584
|
+
adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin, elevateNodesOnSelect, checkEquality: true });
|
|
2552
2585
|
set({ nodes });
|
|
2553
2586
|
},
|
|
2554
2587
|
setEdges: (edges) => {
|
|
@@ -2571,10 +2604,10 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
|
|
|
2571
2604
|
// Every node gets registerd at a ResizeObserver. Whenever a node
|
|
2572
2605
|
// changes its dimensions, this function is called to measure the
|
|
2573
2606
|
// new dimensions and update the nodes.
|
|
2574
|
-
|
|
2575
|
-
const { onNodesChange, fitView, nodeLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, debug, } = get();
|
|
2576
|
-
const changes =
|
|
2577
|
-
if (
|
|
2607
|
+
updateNodeInternals: (updates) => {
|
|
2608
|
+
const { onNodesChange, fitView, nodeLookup, parentLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, debug, } = get();
|
|
2609
|
+
const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOrigin);
|
|
2610
|
+
if (!updatedInternals) {
|
|
2578
2611
|
return;
|
|
2579
2612
|
}
|
|
2580
2613
|
updateAbsolutePositions(nodeLookup, { nodeOrigin });
|
|
@@ -2600,33 +2633,34 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
|
|
|
2600
2633
|
}
|
|
2601
2634
|
},
|
|
2602
2635
|
updateNodePositions: (nodeDragItems, dragging = false) => {
|
|
2603
|
-
const
|
|
2604
|
-
const
|
|
2605
|
-
const
|
|
2636
|
+
const parentExpandChildren = [];
|
|
2637
|
+
const changes = [];
|
|
2638
|
+
for (const [id, dragItem] of nodeDragItems) {
|
|
2606
2639
|
// @todo add expandParent to drag item so that we can get rid of the look up here
|
|
2607
|
-
const internalNode = nodeLookup.get(node.id);
|
|
2608
2640
|
const change = {
|
|
2609
|
-
id
|
|
2641
|
+
id,
|
|
2610
2642
|
type: 'position',
|
|
2611
|
-
position:
|
|
2643
|
+
position: dragItem.position,
|
|
2612
2644
|
dragging,
|
|
2613
2645
|
};
|
|
2614
|
-
if (
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
...
|
|
2620
|
-
|
|
2646
|
+
if (dragItem?.expandParent && dragItem?.parentId && change.position) {
|
|
2647
|
+
parentExpandChildren.push({
|
|
2648
|
+
id,
|
|
2649
|
+
parentId: dragItem.parentId,
|
|
2650
|
+
rect: {
|
|
2651
|
+
...dragItem.internals.positionAbsolute,
|
|
2652
|
+
width: dragItem.measured.width,
|
|
2653
|
+
height: dragItem.measured.height,
|
|
2621
2654
|
},
|
|
2622
2655
|
});
|
|
2623
2656
|
change.position.x = Math.max(0, change.position.x);
|
|
2624
2657
|
change.position.y = Math.max(0, change.position.y);
|
|
2625
2658
|
}
|
|
2626
|
-
|
|
2627
|
-
}
|
|
2628
|
-
if (
|
|
2629
|
-
const
|
|
2659
|
+
changes.push(change);
|
|
2660
|
+
}
|
|
2661
|
+
if (parentExpandChildren.length > 0) {
|
|
2662
|
+
const { nodeLookup, parentLookup } = get();
|
|
2663
|
+
const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup);
|
|
2630
2664
|
changes.push(...parentExpandChanges);
|
|
2631
2665
|
}
|
|
2632
2666
|
get().triggerNodeChanges(changes);
|
|
@@ -2761,20 +2795,17 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
|
|
|
2761
2795
|
reset: () => set({ ...getInitialState() }),
|
|
2762
2796
|
}), Object.is);
|
|
2763
2797
|
|
|
2764
|
-
function ReactFlowProvider({
|
|
2765
|
-
const
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
});
|
|
2776
|
-
}
|
|
2777
|
-
return jsx(Provider$1, { value: storeRef.current, children: children });
|
|
2798
|
+
function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNodes, defaultEdges, initialWidth: width, initialHeight: height, fitView, children, }) {
|
|
2799
|
+
const [store] = useState(() => createStore({
|
|
2800
|
+
nodes,
|
|
2801
|
+
edges,
|
|
2802
|
+
defaultNodes,
|
|
2803
|
+
defaultEdges,
|
|
2804
|
+
width,
|
|
2805
|
+
height,
|
|
2806
|
+
fitView,
|
|
2807
|
+
}));
|
|
2808
|
+
return jsx(Provider$1, { value: store, children: children });
|
|
2778
2809
|
}
|
|
2779
2810
|
|
|
2780
2811
|
function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, }) {
|
|
@@ -2828,7 +2859,7 @@ function ViewportPortal({ children }) {
|
|
|
2828
2859
|
function useUpdateNodeInternals() {
|
|
2829
2860
|
const store = useStoreApi();
|
|
2830
2861
|
return useCallback((id) => {
|
|
2831
|
-
const { domNode,
|
|
2862
|
+
const { domNode, updateNodeInternals } = store.getState();
|
|
2832
2863
|
const updateIds = Array.isArray(id) ? id : [id];
|
|
2833
2864
|
const updates = new Map();
|
|
2834
2865
|
updateIds.forEach((updateId) => {
|
|
@@ -2837,7 +2868,7 @@ function useUpdateNodeInternals() {
|
|
|
2837
2868
|
updates.set(updateId, { id: updateId, nodeElement, force: true });
|
|
2838
2869
|
}
|
|
2839
2870
|
});
|
|
2840
|
-
requestAnimationFrame(() =>
|
|
2871
|
+
requestAnimationFrame(() => updateNodeInternals(updates));
|
|
2841
2872
|
}, []);
|
|
2842
2873
|
}
|
|
2843
2874
|
|
|
@@ -3193,7 +3224,7 @@ nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
|
|
|
3193
3224
|
function NodeComponentWrapperInner({ id, nodeOrigin, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, nodeBorderRadius, nodeStrokeWidth, shapeRendering, NodeComponent, onClick, }) {
|
|
3194
3225
|
const { node, x, y } = useStore((s) => {
|
|
3195
3226
|
const node = s.nodeLookup.get(id);
|
|
3196
|
-
const { x, y } = getNodePositionWithOrigin(node,
|
|
3227
|
+
const { x, y } = getNodePositionWithOrigin(node, nodeOrigin).positionAbsolute;
|
|
3197
3228
|
return {
|
|
3198
3229
|
node,
|
|
3199
3230
|
x,
|
|
@@ -3333,24 +3364,43 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
|
|
|
3333
3364
|
};
|
|
3334
3365
|
},
|
|
3335
3366
|
onChange: (change, childChanges) => {
|
|
3336
|
-
const { triggerNodeChanges } = store.getState();
|
|
3367
|
+
const { triggerNodeChanges, nodeLookup, parentLookup, nodeOrigin } = store.getState();
|
|
3337
3368
|
const changes = [];
|
|
3338
|
-
|
|
3369
|
+
const nextPosition = { x: change.x, y: change.y };
|
|
3370
|
+
const node = nodeLookup.get(id);
|
|
3371
|
+
if (node && node.expandParent && node.parentId) {
|
|
3372
|
+
const child = {
|
|
3373
|
+
id: node.id,
|
|
3374
|
+
parentId: node.parentId,
|
|
3375
|
+
rect: {
|
|
3376
|
+
width: change.width ?? node.measured.width,
|
|
3377
|
+
height: change.height ?? node.measured.height,
|
|
3378
|
+
...evaluateAbsolutePosition({
|
|
3379
|
+
x: change.x ?? node.position.x,
|
|
3380
|
+
y: change.y ?? node.position.y,
|
|
3381
|
+
}, node.parentId, nodeLookup, node.origin ?? nodeOrigin),
|
|
3382
|
+
},
|
|
3383
|
+
};
|
|
3384
|
+
const parentExpandChanges = handleExpandParent([child], nodeLookup, parentLookup, nodeOrigin);
|
|
3385
|
+
changes.push(...parentExpandChanges);
|
|
3386
|
+
// when the parent was expanded by the child node, its position will be clamped at 0,0
|
|
3387
|
+
nextPosition.x = change.x ? Math.max(0, change.x) : undefined;
|
|
3388
|
+
nextPosition.y = change.y ? Math.max(0, change.y) : undefined;
|
|
3389
|
+
}
|
|
3390
|
+
if (nextPosition.x !== undefined && nextPosition.y !== undefined) {
|
|
3339
3391
|
const positionChange = {
|
|
3340
3392
|
id,
|
|
3341
3393
|
type: 'position',
|
|
3342
|
-
position: {
|
|
3343
|
-
x: change.x,
|
|
3344
|
-
y: change.y,
|
|
3345
|
-
},
|
|
3394
|
+
position: { ...nextPosition },
|
|
3346
3395
|
};
|
|
3347
3396
|
changes.push(positionChange);
|
|
3348
3397
|
}
|
|
3349
|
-
if (change.
|
|
3398
|
+
if (change.width !== undefined && change.height !== undefined) {
|
|
3350
3399
|
const dimensionChange = {
|
|
3351
3400
|
id,
|
|
3352
3401
|
type: 'dimensions',
|
|
3353
3402
|
resizing: true,
|
|
3403
|
+
setAttributes: true,
|
|
3354
3404
|
dimensions: {
|
|
3355
3405
|
width: change.width,
|
|
3356
3406
|
height: change.height,
|
|
@@ -3470,7 +3520,7 @@ function NodeToolbar({ nodeId, children, className, style, isVisible, position =
|
|
|
3470
3520
|
return null;
|
|
3471
3521
|
}
|
|
3472
3522
|
const nodeRect = getNodesBounds(nodes, { nodeOrigin });
|
|
3473
|
-
const zIndex = Math.max(...nodes.map((node) =>
|
|
3523
|
+
const zIndex = Math.max(...nodes.map((node) => node.internals.z + 1));
|
|
3474
3524
|
const wrapperStyle = {
|
|
3475
3525
|
position: 'absolute',
|
|
3476
3526
|
transform: getNodeToolbarTransform(nodeRect, viewport, position, offset, align),
|