@xyflow/react 12.0.0-next.7 → 12.0.0-next.9
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 +21 -17
- package/dist/esm/additional-components/MiniMap/MiniMap.d.ts +1 -1
- package/dist/esm/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
- package/dist/esm/additional-components/MiniMap/types.d.ts +2 -0
- package/dist/esm/additional-components/MiniMap/types.d.ts.map +1 -1
- package/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
- package/dist/esm/components/EdgeWrapper/index.d.ts.map +1 -1
- package/dist/esm/components/Handle/index.d.ts +6 -2
- package/dist/esm/components/Handle/index.d.ts.map +1 -1
- package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -1
- package/dist/esm/components/NodesSelection/index.d.ts.map +1 -1
- package/dist/esm/components/ReactFlowProvider/index.d.ts +3 -1
- package/dist/esm/components/ReactFlowProvider/index.d.ts.map +1 -1
- package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -1
- package/dist/esm/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
- package/dist/esm/container/FlowRenderer/index.d.ts.map +1 -1
- package/dist/esm/container/GraphView/index.d.ts +1 -1
- package/dist/esm/container/GraphView/index.d.ts.map +1 -1
- package/dist/esm/container/ReactFlow/Wrapper.d.ts +3 -1
- package/dist/esm/container/ReactFlow/Wrapper.d.ts.map +1 -1
- package/dist/esm/container/ReactFlow/index.d.ts +3 -118
- package/dist/esm/container/ReactFlow/index.d.ts.map +1 -1
- package/dist/esm/container/ReactFlow/init-values.d.ts +4 -0
- package/dist/esm/container/ReactFlow/init-values.d.ts.map +1 -0
- package/dist/esm/hooks/useConnection.d.ts +13 -7
- package/dist/esm/hooks/useConnection.d.ts.map +1 -1
- package/dist/esm/hooks/useDrag.d.ts +1 -1
- package/dist/esm/hooks/useDrag.d.ts.map +1 -1
- package/dist/esm/hooks/useHandleConnections.d.ts +4 -4
- package/dist/esm/hooks/useHandleConnections.d.ts.map +1 -1
- package/dist/esm/hooks/useKeyPress.d.ts.map +1 -1
- package/dist/esm/hooks/useMoveSelectedNodes.d.ts +12 -0
- package/dist/esm/hooks/useMoveSelectedNodes.d.ts.map +1 -0
- package/dist/esm/hooks/useNodesInitialized.d.ts.map +1 -1
- package/dist/esm/hooks/useOnSelectionChange.d.ts +1 -1
- package/dist/esm/hooks/useReactFlow.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 +276 -283
- package/dist/esm/index.mjs +276 -283
- package/dist/esm/store/index.d.ts +3 -1
- package/dist/esm/store/index.d.ts.map +1 -1
- package/dist/esm/store/initialState.d.ts +3 -1
- package/dist/esm/store/initialState.d.ts.map +1 -1
- package/dist/esm/types/component-props.d.ts +9 -9
- package/dist/esm/types/component-props.d.ts.map +1 -1
- package/dist/esm/types/general.d.ts +79 -4
- package/dist/esm/types/general.d.ts.map +1 -1
- package/dist/esm/types/instance.d.ts +93 -0
- package/dist/esm/types/instance.d.ts.map +1 -1
- package/dist/esm/types/nodes.d.ts +3 -3
- package/dist/esm/types/nodes.d.ts.map +1 -1
- package/dist/esm/types/store.d.ts +5 -4
- package/dist/esm/types/store.d.ts.map +1 -1
- package/dist/esm/utils/changes.d.ts +1 -1
- package/dist/esm/utils/changes.d.ts.map +1 -1
- package/dist/style.css +29 -13
- package/dist/umd/additional-components/MiniMap/MiniMap.d.ts +1 -1
- package/dist/umd/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
- package/dist/umd/additional-components/MiniMap/types.d.ts +2 -0
- package/dist/umd/additional-components/MiniMap/types.d.ts.map +1 -1
- package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
- package/dist/umd/components/EdgeWrapper/index.d.ts.map +1 -1
- package/dist/umd/components/Handle/index.d.ts +6 -2
- package/dist/umd/components/Handle/index.d.ts.map +1 -1
- package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -1
- package/dist/umd/components/NodesSelection/index.d.ts.map +1 -1
- package/dist/umd/components/ReactFlowProvider/index.d.ts +3 -1
- package/dist/umd/components/ReactFlowProvider/index.d.ts.map +1 -1
- package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -1
- package/dist/umd/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
- package/dist/umd/container/FlowRenderer/index.d.ts.map +1 -1
- package/dist/umd/container/GraphView/index.d.ts +1 -1
- package/dist/umd/container/GraphView/index.d.ts.map +1 -1
- package/dist/umd/container/ReactFlow/Wrapper.d.ts +3 -1
- package/dist/umd/container/ReactFlow/Wrapper.d.ts.map +1 -1
- package/dist/umd/container/ReactFlow/index.d.ts +3 -118
- package/dist/umd/container/ReactFlow/index.d.ts.map +1 -1
- package/dist/umd/container/ReactFlow/init-values.d.ts +4 -0
- package/dist/umd/container/ReactFlow/init-values.d.ts.map +1 -0
- package/dist/umd/hooks/useConnection.d.ts +13 -7
- package/dist/umd/hooks/useConnection.d.ts.map +1 -1
- package/dist/umd/hooks/useDrag.d.ts +1 -1
- package/dist/umd/hooks/useDrag.d.ts.map +1 -1
- package/dist/umd/hooks/useHandleConnections.d.ts +4 -4
- package/dist/umd/hooks/useHandleConnections.d.ts.map +1 -1
- package/dist/umd/hooks/useKeyPress.d.ts.map +1 -1
- package/dist/umd/hooks/useMoveSelectedNodes.d.ts +12 -0
- package/dist/umd/hooks/useMoveSelectedNodes.d.ts.map +1 -0
- package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -1
- package/dist/umd/hooks/useOnSelectionChange.d.ts +1 -1
- package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
- package/dist/umd/hooks/useUpdateNodePositions.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 +3 -1
- package/dist/umd/store/index.d.ts.map +1 -1
- package/dist/umd/store/initialState.d.ts +3 -1
- package/dist/umd/store/initialState.d.ts.map +1 -1
- package/dist/umd/types/component-props.d.ts +9 -9
- package/dist/umd/types/component-props.d.ts.map +1 -1
- package/dist/umd/types/general.d.ts +79 -4
- package/dist/umd/types/general.d.ts.map +1 -1
- package/dist/umd/types/instance.d.ts +93 -0
- package/dist/umd/types/instance.d.ts.map +1 -1
- package/dist/umd/types/nodes.d.ts +3 -3
- package/dist/umd/types/nodes.d.ts.map +1 -1
- package/dist/umd/types/store.d.ts +5 -4
- package/dist/umd/types/store.d.ts.map +1 -1
- package/dist/umd/utils/changes.d.ts +1 -1
- package/dist/umd/utils/changes.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { createContext, useContext, useMemo, useEffect, useRef, useState, useCallback, forwardRef, memo } from 'react';
|
|
3
|
+
import { createContext, useContext, useMemo, useEffect, useRef, useState, useCallback, useLayoutEffect, forwardRef, memo } from 'react';
|
|
4
4
|
import cc from 'classcat';
|
|
5
|
-
import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition,
|
|
5
|
+
import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calculateNodePosition, Position, isMouseEvent, XYHandle, getHostForElement, addEdge, getNodesBounds, clampPosition, internalsSymbol, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionMode, ConnectionLineType, updateConnectionLookup, adoptUserProvidedNodes, devWarn, updateNodeDimensions, updateAbsolutePositions, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, getNodePositionWithOrigin, XYMinimap, getBoundsOfRects, ResizeControlVariant, XYResizer, XY_RESIZER_LINE_POSITIONS, XY_RESIZER_HANDLE_POSITIONS, getNodeToolbarTransform } from '@xyflow/system';
|
|
6
6
|
export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, internalsSymbol, updateEdge } from '@xyflow/system';
|
|
7
7
|
import { useStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
|
|
8
8
|
import { shallow } from 'zustand/shallow';
|
|
@@ -106,6 +106,9 @@ function SelectionListener({ onSelectionChange }) {
|
|
|
106
106
|
return null;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
const defaultNodeOrigin = [0, 0];
|
|
110
|
+
const defaultViewport = { x: 0, y: 0, zoom: 1 };
|
|
111
|
+
|
|
109
112
|
/*
|
|
110
113
|
* This component helps us to update the store with the vlues coming from the user.
|
|
111
114
|
* We distinguish between values we can update directly with `useDirectStoreUpdater` (like `snapGrid`)
|
|
@@ -171,35 +174,37 @@ const fieldsToTrack = [...reactFlowFieldsToTrack, 'rfId'];
|
|
|
171
174
|
const selector$n = (s) => ({
|
|
172
175
|
setNodes: s.setNodes,
|
|
173
176
|
setEdges: s.setEdges,
|
|
174
|
-
setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
|
|
175
177
|
setMinZoom: s.setMinZoom,
|
|
176
178
|
setMaxZoom: s.setMaxZoom,
|
|
177
179
|
setTranslateExtent: s.setTranslateExtent,
|
|
178
180
|
setNodeExtent: s.setNodeExtent,
|
|
179
181
|
reset: s.reset,
|
|
182
|
+
setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
|
|
180
183
|
});
|
|
184
|
+
const initPrevValues = {
|
|
185
|
+
// these are values that are also passed directly to other components
|
|
186
|
+
// than the StoreUpdater. We can reduce the number of setStore calls
|
|
187
|
+
// by setting the same values here as prev fields.
|
|
188
|
+
translateExtent: infiniteExtent,
|
|
189
|
+
nodeOrigin: defaultNodeOrigin,
|
|
190
|
+
minZoom: 0.5,
|
|
191
|
+
maxZoom: 2,
|
|
192
|
+
elementsSelectable: true,
|
|
193
|
+
noPanClassName: 'nopan',
|
|
194
|
+
rfId: '1',
|
|
195
|
+
};
|
|
181
196
|
function StoreUpdater(props) {
|
|
182
|
-
const { setNodes, setEdges,
|
|
197
|
+
const { setNodes, setEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset, setDefaultNodesAndEdges, } = useStore(selector$n, shallow);
|
|
183
198
|
const store = useStoreApi();
|
|
184
199
|
useEffect(() => {
|
|
185
|
-
|
|
186
|
-
setDefaultNodesAndEdges(props.defaultNodes, edgesWithDefaults);
|
|
200
|
+
setDefaultNodesAndEdges(props.defaultNodes, props.defaultEdges);
|
|
187
201
|
return () => {
|
|
202
|
+
// when we reset the store we also need to reset the previous fields
|
|
203
|
+
previousFields.current = initPrevValues;
|
|
188
204
|
reset();
|
|
189
205
|
};
|
|
190
206
|
}, []);
|
|
191
|
-
const previousFields = useRef(
|
|
192
|
-
// these are values that are also passed directly to other components
|
|
193
|
-
// than the StoreUpdater. We can reduce the number of setStore calls
|
|
194
|
-
// by setting the same values here as prev fields.
|
|
195
|
-
translateExtent: infiniteExtent,
|
|
196
|
-
nodeOrigin: initNodeOrigin,
|
|
197
|
-
minZoom: 0.5,
|
|
198
|
-
maxZoom: 2,
|
|
199
|
-
elementsSelectable: true,
|
|
200
|
-
noPanClassName: 'nopan',
|
|
201
|
-
rfId: '1',
|
|
202
|
-
});
|
|
207
|
+
const previousFields = useRef(initPrevValues);
|
|
203
208
|
useEffect(() => {
|
|
204
209
|
for (const fieldName of fieldsToTrack) {
|
|
205
210
|
const fieldValue = props[fieldName];
|
|
@@ -333,6 +338,10 @@ keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true
|
|
|
333
338
|
else {
|
|
334
339
|
pressedKeys.current.delete(event[keyOrCode]);
|
|
335
340
|
}
|
|
341
|
+
// fix for Mac: when cmd key is pressed, keyup is not triggered for any other key, see: https://stackoverflow.com/questions/27380018/when-cmd-key-is-kept-pressed-keyup-is-not-triggered-for-any-other-key
|
|
342
|
+
if (event.key === 'Meta') {
|
|
343
|
+
pressedKeys.current.clear();
|
|
344
|
+
}
|
|
336
345
|
modifierPressed.current = false;
|
|
337
346
|
};
|
|
338
347
|
const resetHandler = () => {
|
|
@@ -424,25 +433,25 @@ const useViewportHelper = () => {
|
|
|
424
433
|
const viewport = getViewportForBounds(bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1);
|
|
425
434
|
panZoom?.setViewport(viewport, { duration: options?.duration });
|
|
426
435
|
},
|
|
427
|
-
screenToFlowPosition: (
|
|
436
|
+
screenToFlowPosition: (clientPosition, options = { snapToGrid: true }) => {
|
|
428
437
|
const { transform, snapGrid, domNode } = store.getState();
|
|
429
438
|
if (!domNode) {
|
|
430
|
-
return
|
|
439
|
+
return clientPosition;
|
|
431
440
|
}
|
|
432
441
|
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
|
433
442
|
const correctedPosition = {
|
|
434
|
-
x:
|
|
435
|
-
y:
|
|
443
|
+
x: clientPosition.x - domX,
|
|
444
|
+
y: clientPosition.y - domY,
|
|
436
445
|
};
|
|
437
446
|
return pointToRendererPoint(correctedPosition, transform, options.snapToGrid, snapGrid);
|
|
438
447
|
},
|
|
439
|
-
flowToScreenPosition: (
|
|
448
|
+
flowToScreenPosition: (flowPosition) => {
|
|
440
449
|
const { transform, domNode } = store.getState();
|
|
441
450
|
if (!domNode) {
|
|
442
|
-
return
|
|
451
|
+
return flowPosition;
|
|
443
452
|
}
|
|
444
453
|
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
|
445
|
-
const rendererPosition = rendererPointToPoint(
|
|
454
|
+
const rendererPosition = rendererPointToPoint(flowPosition, transform);
|
|
446
455
|
return {
|
|
447
456
|
x: rendererPosition.x + domX,
|
|
448
457
|
y: rendererPosition.y + domY,
|
|
@@ -458,9 +467,7 @@ function handleParentExpand(updatedElements, updateItem) {
|
|
|
458
467
|
for (const [index, item] of updatedElements.entries()) {
|
|
459
468
|
if (item.id === updateItem.parentNode) {
|
|
460
469
|
const parent = { ...item };
|
|
461
|
-
|
|
462
|
-
parent.computed = {};
|
|
463
|
-
}
|
|
470
|
+
parent.computed ??= {};
|
|
464
471
|
const extendWidth = updateItem.position.x + updateItem.computed.width - parent.computed.width;
|
|
465
472
|
const extendHeight = updateItem.position.y + updateItem.computed.height - parent.computed.height;
|
|
466
473
|
if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
|
|
@@ -543,7 +550,7 @@ function applyChanges(changes, elements) {
|
|
|
543
550
|
/// each _mutate_ this object, so there's only ever one copy.
|
|
544
551
|
const updatedElement = { ...element };
|
|
545
552
|
for (const change of changes) {
|
|
546
|
-
applyChange(change, updatedElement,
|
|
553
|
+
applyChange(change, updatedElement, updatedElements);
|
|
547
554
|
}
|
|
548
555
|
updatedElements.push(updatedElement);
|
|
549
556
|
}
|
|
@@ -638,11 +645,13 @@ function applyNodeChanges(changes, nodes) {
|
|
|
638
645
|
function applyEdgeChanges(changes, edges) {
|
|
639
646
|
return applyChanges(changes, edges);
|
|
640
647
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
648
|
+
function createSelectionChange(id, selected) {
|
|
649
|
+
return {
|
|
650
|
+
id,
|
|
651
|
+
type: 'select',
|
|
652
|
+
selected,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
646
655
|
function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false) {
|
|
647
656
|
const changes = [];
|
|
648
657
|
for (const item of items) {
|
|
@@ -721,73 +730,86 @@ function useReactFlow() {
|
|
|
721
730
|
const { edges = [] } = store.getState();
|
|
722
731
|
return edges.find((e) => e.id === id);
|
|
723
732
|
}, []);
|
|
724
|
-
//
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
//
|
|
737
|
-
|
|
733
|
+
// A reference of all the batched updates to process before the next render. We
|
|
734
|
+
// want a mutable reference here so multiple synchronous calls to `setNodes` etc
|
|
735
|
+
// can be batched together.
|
|
736
|
+
const setElementsQueue = useRef({ nodes: [], edges: [] });
|
|
737
|
+
// Because we're using a ref above, we need some way to let React know when to
|
|
738
|
+
// actually process the queue. We flip this bit of state to `true` any time we
|
|
739
|
+
// mutate the queue and then flip it back to `false` after flushing the queue.
|
|
740
|
+
const [shouldFlushQueue, setShouldFlushQueue] = useState(false);
|
|
741
|
+
// Layout effects are guaranteed to run before the next render which means we
|
|
742
|
+
// shouldn't run into any issues with stale state or weird issues that come from
|
|
743
|
+
// rendering things one frame later than expected (we used to use `setTimeout`).
|
|
744
|
+
useLayoutEffect(() => {
|
|
745
|
+
// Because we need to flip the state back to false after flushing, this should
|
|
746
|
+
// trigger the hook again (!). If the hook is being run again we know that any
|
|
747
|
+
// updates should have been processed by now and we can safely clear the queue
|
|
748
|
+
// and bail early.
|
|
749
|
+
if (!shouldFlushQueue) {
|
|
750
|
+
setElementsQueue.current = { nodes: [], edges: [] };
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (setElementsQueue.current.nodes.length) {
|
|
754
|
+
const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup } = store.getState();
|
|
755
|
+
// This is essentially an `Array.reduce` in imperative clothing. Processing
|
|
756
|
+
// this queue is a relatively hot path so we'd like to avoid the overhead of
|
|
757
|
+
// array methods where we can.
|
|
758
|
+
let next = nodes;
|
|
759
|
+
for (const payload of setElementsQueue.current.nodes) {
|
|
760
|
+
next = typeof payload === 'function' ? payload(next) : payload;
|
|
761
|
+
}
|
|
738
762
|
if (hasDefaultNodes) {
|
|
739
|
-
setNodes(
|
|
763
|
+
setNodes(next);
|
|
740
764
|
}
|
|
741
765
|
else if (onNodesChange) {
|
|
742
|
-
|
|
743
|
-
|
|
766
|
+
onNodesChange(getElementsDiffChanges({
|
|
767
|
+
items: next,
|
|
768
|
+
lookup: nodeLookup,
|
|
769
|
+
}));
|
|
744
770
|
}
|
|
745
|
-
|
|
746
|
-
}, 0);
|
|
747
|
-
}, []);
|
|
748
|
-
// this is used to handle multiple syncronous setEdges calls
|
|
749
|
-
const setEdgesData = useRef();
|
|
750
|
-
const setEdgesTimeout = useRef();
|
|
751
|
-
const setEdges = useCallback((payload) => {
|
|
752
|
-
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
|
|
753
|
-
const nextEdges = typeof payload === 'function' ? payload(setEdgesData.current || edges) : payload;
|
|
754
|
-
setEdgesData.current = nextEdges;
|
|
755
|
-
if (setEdgesTimeout.current) {
|
|
756
|
-
clearTimeout(setEdgesTimeout.current);
|
|
771
|
+
setElementsQueue.current.nodes = [];
|
|
757
772
|
}
|
|
758
|
-
|
|
773
|
+
if (setElementsQueue.current.edges.length) {
|
|
774
|
+
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
|
|
775
|
+
let next = edges;
|
|
776
|
+
for (const payload of setElementsQueue.current.edges) {
|
|
777
|
+
next = typeof payload === 'function' ? payload(next) : payload;
|
|
778
|
+
}
|
|
759
779
|
if (hasDefaultEdges) {
|
|
760
|
-
setEdges(
|
|
780
|
+
setEdges(next);
|
|
761
781
|
}
|
|
762
782
|
else if (onEdgesChange) {
|
|
763
|
-
|
|
764
|
-
|
|
783
|
+
onEdgesChange(getElementsDiffChanges({
|
|
784
|
+
items: next,
|
|
785
|
+
lookup: edgeLookup,
|
|
786
|
+
}));
|
|
765
787
|
}
|
|
766
|
-
|
|
767
|
-
}
|
|
788
|
+
setElementsQueue.current.edges = [];
|
|
789
|
+
}
|
|
790
|
+
// Beacuse we're using reactive state to trigger this effect, we need to flip
|
|
791
|
+
// it back to false.
|
|
792
|
+
setShouldFlushQueue(false);
|
|
793
|
+
}, [shouldFlushQueue]);
|
|
794
|
+
const setNodes = useCallback((payload) => {
|
|
795
|
+
setElementsQueue.current.nodes.push(payload);
|
|
796
|
+
setShouldFlushQueue(true);
|
|
797
|
+
}, []);
|
|
798
|
+
const setEdges = useCallback((payload) => {
|
|
799
|
+
setElementsQueue.current.edges.push(payload);
|
|
800
|
+
setShouldFlushQueue(true);
|
|
768
801
|
}, []);
|
|
769
802
|
const addNodes = useCallback((payload) => {
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
else if (onNodesChange) {
|
|
777
|
-
const changes = nodes.map((node) => ({ item: node, type: 'add' }));
|
|
778
|
-
onNodesChange(changes);
|
|
779
|
-
}
|
|
803
|
+
const newNodes = Array.isArray(payload) ? payload : [payload];
|
|
804
|
+
// Queueing a functional update means that we won't worry about other calls
|
|
805
|
+
// to `setNodes` that might happen elsewhere.
|
|
806
|
+
setElementsQueue.current.nodes.push((nodes) => [...nodes, ...newNodes]);
|
|
807
|
+
setShouldFlushQueue(true);
|
|
780
808
|
}, []);
|
|
781
809
|
const addEdges = useCallback((payload) => {
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
setEdges([...edges, ...nextEdges]);
|
|
786
|
-
}
|
|
787
|
-
else if (onEdgesChange) {
|
|
788
|
-
const changes = nextEdges.map((edge) => ({ item: edge, type: 'add' }));
|
|
789
|
-
onEdgesChange(changes);
|
|
790
|
-
}
|
|
810
|
+
const newEdges = Array.isArray(payload) ? payload : [payload];
|
|
811
|
+
setElementsQueue.current.edges.push((edges) => [...edges, ...newEdges]);
|
|
812
|
+
setShouldFlushQueue(true);
|
|
791
813
|
}, []);
|
|
792
814
|
const toObject = useCallback(() => {
|
|
793
815
|
const { nodes = [], edges = [], transform } = store.getState();
|
|
@@ -1264,7 +1286,7 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
|
|
|
1264
1286
|
handleNodeClick({
|
|
1265
1287
|
id,
|
|
1266
1288
|
store,
|
|
1267
|
-
nodeRef
|
|
1289
|
+
nodeRef,
|
|
1268
1290
|
});
|
|
1269
1291
|
},
|
|
1270
1292
|
onDragStart: () => {
|
|
@@ -1298,44 +1320,47 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
|
|
|
1298
1320
|
|
|
1299
1321
|
const selectedAndDraggable = (nodesDraggable) => (n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'));
|
|
1300
1322
|
/**
|
|
1301
|
-
* Hook for updating node positions
|
|
1323
|
+
* Hook for updating node positions by passing a direction and factor
|
|
1302
1324
|
*
|
|
1303
1325
|
* @internal
|
|
1304
1326
|
* @returns function for updating node positions
|
|
1305
1327
|
*/
|
|
1306
|
-
function
|
|
1328
|
+
function useMoveSelectedNodes() {
|
|
1307
1329
|
const store = useStoreApi();
|
|
1308
|
-
const
|
|
1309
|
-
const { nodeExtent, nodes, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions } = store.getState();
|
|
1330
|
+
const moveSelectedNodes = useCallback((params) => {
|
|
1331
|
+
const { nodeExtent, nodes, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin, } = store.getState();
|
|
1310
1332
|
const selectedNodes = nodes.filter(selectedAndDraggable(nodesDraggable));
|
|
1311
|
-
// by default a node moves 5px on each key press
|
|
1312
|
-
// if snap grid is enabled, we use that for the velocity
|
|
1333
|
+
// by default a node moves 5px on each key press
|
|
1334
|
+
// if snap grid is enabled, we use that for the velocity
|
|
1313
1335
|
const xVelo = snapToGrid ? snapGrid[0] : 5;
|
|
1314
1336
|
const yVelo = snapToGrid ? snapGrid[1] : 5;
|
|
1315
|
-
const
|
|
1316
|
-
const
|
|
1317
|
-
const yDiff = params.y * yVelo * factor;
|
|
1337
|
+
const xDiff = params.direction.x * xVelo * params.factor;
|
|
1338
|
+
const yDiff = params.direction.y * yVelo * params.factor;
|
|
1318
1339
|
const nodeUpdates = selectedNodes.map((node) => {
|
|
1319
1340
|
if (node.computed?.positionAbsolute) {
|
|
1320
1341
|
let nextPosition = {
|
|
1321
|
-
x: node.computed
|
|
1322
|
-
y: node.computed
|
|
1342
|
+
x: node.computed.positionAbsolute.x + xDiff,
|
|
1343
|
+
y: node.computed.positionAbsolute.y + yDiff,
|
|
1323
1344
|
};
|
|
1324
1345
|
if (snapToGrid) {
|
|
1325
1346
|
nextPosition = snapPosition(nextPosition, snapGrid);
|
|
1326
1347
|
}
|
|
1327
|
-
const {
|
|
1348
|
+
const { position, positionAbsolute } = calculateNodePosition({
|
|
1349
|
+
nodeId: node.id,
|
|
1350
|
+
nextPosition,
|
|
1351
|
+
nodeLookup,
|
|
1352
|
+
nodeExtent,
|
|
1353
|
+
nodeOrigin,
|
|
1354
|
+
onError,
|
|
1355
|
+
});
|
|
1328
1356
|
node.position = position;
|
|
1329
|
-
if (!node.computed) {
|
|
1330
|
-
node.computed = {};
|
|
1331
|
-
}
|
|
1332
1357
|
node.computed.positionAbsolute = positionAbsolute;
|
|
1333
1358
|
}
|
|
1334
1359
|
return node;
|
|
1335
1360
|
});
|
|
1336
|
-
updateNodePositions(nodeUpdates
|
|
1361
|
+
updateNodePositions(nodeUpdates);
|
|
1337
1362
|
}, []);
|
|
1338
|
-
return
|
|
1363
|
+
return moveSelectedNodes;
|
|
1339
1364
|
}
|
|
1340
1365
|
|
|
1341
1366
|
const NodeIdContext = createContext(null);
|
|
@@ -1470,6 +1495,9 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
|
|
|
1470
1495
|
]), onMouseDown: onPointerDown, onTouchStart: onPointerDown, onClick: connectOnClick ? onClick : undefined, ref: ref, ...rest, children: children }));
|
|
1471
1496
|
});
|
|
1472
1497
|
HandleComponent.displayName = 'Handle';
|
|
1498
|
+
/**
|
|
1499
|
+
* The Handle component is the part of a node that can be used to connect nodes.
|
|
1500
|
+
*/
|
|
1473
1501
|
const Handle = memo(HandleComponent);
|
|
1474
1502
|
|
|
1475
1503
|
function InputNode({ data, isConnectable, sourcePosition = Position.Bottom }) {
|
|
@@ -1503,7 +1531,7 @@ const builtinNodeTypes = {
|
|
|
1503
1531
|
|
|
1504
1532
|
const selector$h = (s) => {
|
|
1505
1533
|
const selectedNodes = s.nodes.filter((n) => n.selected);
|
|
1506
|
-
const { width, height, x, y } = getNodesBounds(selectedNodes, s.nodeOrigin);
|
|
1534
|
+
const { width, height, x, y } = getNodesBounds(selectedNodes, { nodeOrigin: s.nodeOrigin });
|
|
1507
1535
|
return {
|
|
1508
1536
|
width,
|
|
1509
1537
|
height,
|
|
@@ -1514,7 +1542,7 @@ const selector$h = (s) => {
|
|
|
1514
1542
|
function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }) {
|
|
1515
1543
|
const store = useStoreApi();
|
|
1516
1544
|
const { width, height, transformString, userSelectionActive } = useStore(selector$h, shallow);
|
|
1517
|
-
const
|
|
1545
|
+
const moveSelectedNodes = useMoveSelectedNodes();
|
|
1518
1546
|
const nodeRef = useRef(null);
|
|
1519
1547
|
useEffect(() => {
|
|
1520
1548
|
if (!disableKeyboardA11y) {
|
|
@@ -1537,10 +1565,9 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
|
|
|
1537
1565
|
: undefined;
|
|
1538
1566
|
const onKeyDown = (event) => {
|
|
1539
1567
|
if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
isShiftPressed: event.shiftKey,
|
|
1568
|
+
moveSelectedNodes({
|
|
1569
|
+
direction: arrowKeyDiffs[event.key],
|
|
1570
|
+
factor: event.shiftKey ? 4 : 1,
|
|
1544
1571
|
});
|
|
1545
1572
|
}
|
|
1546
1573
|
};
|
|
@@ -1552,14 +1579,16 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
|
|
|
1552
1579
|
} }) }));
|
|
1553
1580
|
}
|
|
1554
1581
|
|
|
1555
|
-
const selector$g = (s) =>
|
|
1582
|
+
const selector$g = (s) => {
|
|
1583
|
+
return { nodesSelectionActive: s.nodesSelectionActive, userSelectionActive: s.userSelectionActive };
|
|
1584
|
+
};
|
|
1556
1585
|
const FlowRendererComponent = ({ children, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneContextMenu, onPaneScroll, deleteKeyCode, selectionKeyCode, selectionOnDrag, selectionMode, onSelectionStart, onSelectionEnd, multiSelectionKeyCode, panActivationKeyCode, zoomActivationKeyCode, elementsSelectable, zoomOnScroll, zoomOnPinch, panOnScroll: _panOnScroll, panOnScrollSpeed, panOnScrollMode, zoomOnDoubleClick, panOnDrag: _panOnDrag, defaultViewport, translateExtent, minZoom, maxZoom, preventScrolling, onSelectionContextMenu, noWheelClassName, noPanClassName, disableKeyboardA11y, onViewportChange, isControlledViewport, }) => {
|
|
1557
|
-
const nodesSelectionActive = useStore(selector$g);
|
|
1586
|
+
const { nodesSelectionActive, userSelectionActive } = useStore(selector$g);
|
|
1558
1587
|
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
|
1559
1588
|
const panActivationKeyPressed = useKeyPress(panActivationKeyCode);
|
|
1560
1589
|
const panOnDrag = panActivationKeyPressed || _panOnDrag;
|
|
1561
1590
|
const panOnScroll = panActivationKeyPressed || _panOnScroll;
|
|
1562
|
-
const isSelecting = selectionKeyPressed || (selectionOnDrag && panOnDrag !== true);
|
|
1591
|
+
const isSelecting = selectionKeyPressed || userSelectionActive || (selectionOnDrag && panOnDrag !== true);
|
|
1563
1592
|
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
|
1564
1593
|
return (jsx(ZoomPane, { onPaneContextMenu: onPaneContextMenu, elementsSelectable: elementsSelectable, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, zoomOnDoubleClick: zoomOnDoubleClick, panOnDrag: !selectionKeyPressed && panOnDrag, defaultViewport: defaultViewport, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, zoomActivationKeyCode: zoomActivationKeyCode, preventScrolling: preventScrolling, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, onViewportChange: onViewportChange, isControlledViewport: isControlledViewport, children: jsxs(Pane, { onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneContextMenu: onPaneContextMenu, onPaneScroll: onPaneScroll, panOnDrag: panOnDrag, isSelecting: !!isSelecting, selectionMode: selectionMode, children: [children, nodesSelectionActive && (jsx(NodesSelection, { onSelectionContextMenu: onSelectionContextMenu, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y }))] }) }));
|
|
1565
1594
|
};
|
|
@@ -1646,14 +1675,29 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1646
1675
|
const prevSourcePosition = useRef(node.sourcePosition);
|
|
1647
1676
|
const prevTargetPosition = useRef(node.targetPosition);
|
|
1648
1677
|
const prevType = useRef(nodeType);
|
|
1649
|
-
const
|
|
1678
|
+
const width = node.width ?? undefined;
|
|
1679
|
+
const height = node.height ?? undefined;
|
|
1680
|
+
const computedWidth = node.computed?.width;
|
|
1681
|
+
const computedHeight = node.computed?.height;
|
|
1682
|
+
const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height);
|
|
1683
|
+
const hasHandleBounds = !!node[internalsSymbol]?.handleBounds;
|
|
1684
|
+
const moveSelectedNodes = useMoveSelectedNodes();
|
|
1685
|
+
useEffect(() => {
|
|
1686
|
+
return () => {
|
|
1687
|
+
if (nodeRef.current) {
|
|
1688
|
+
resizeObserver?.unobserve(nodeRef.current);
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
}, []);
|
|
1650
1692
|
useEffect(() => {
|
|
1651
1693
|
if (nodeRef.current && !node.hidden) {
|
|
1652
1694
|
const currNode = nodeRef.current;
|
|
1653
|
-
|
|
1654
|
-
|
|
1695
|
+
if (!initialized || !hasHandleBounds) {
|
|
1696
|
+
resizeObserver?.unobserve(currNode);
|
|
1697
|
+
resizeObserver?.observe(currNode);
|
|
1698
|
+
}
|
|
1655
1699
|
}
|
|
1656
|
-
}, [node.hidden]);
|
|
1700
|
+
}, [node.hidden, initialized, hasHandleBounds]);
|
|
1657
1701
|
useEffect(() => {
|
|
1658
1702
|
// when the user programmatically changes the source or handle position, we re-initialize the node
|
|
1659
1703
|
const typeChanged = prevType.current !== nodeType;
|
|
@@ -1683,10 +1727,6 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1683
1727
|
if (node.hidden) {
|
|
1684
1728
|
return null;
|
|
1685
1729
|
}
|
|
1686
|
-
const width = node.width ?? undefined;
|
|
1687
|
-
const height = node.height ?? undefined;
|
|
1688
|
-
const computedWidth = node.computed?.width;
|
|
1689
|
-
const computedHeight = node.computed?.height;
|
|
1690
1730
|
const positionAbsoluteOrigin = getPositionWithOrigin({
|
|
1691
1731
|
x: positionAbsoluteX,
|
|
1692
1732
|
y: positionAbsoluteY,
|
|
@@ -1694,7 +1734,6 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1694
1734
|
height: computedHeight ?? height ?? 0,
|
|
1695
1735
|
origin: node.origin || nodeOrigin,
|
|
1696
1736
|
});
|
|
1697
|
-
const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height);
|
|
1698
1737
|
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
|
1699
1738
|
const onMouseEnterHandler = onMouseEnter ? (event) => onMouseEnter(event, { ...node }) : undefined;
|
|
1700
1739
|
const onMouseMoveHandler = onMouseMove ? (event) => onMouseMove(event, { ...node }) : undefined;
|
|
@@ -1738,10 +1777,9 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1738
1777
|
.replace('Arrow', '')
|
|
1739
1778
|
.toLowerCase()}. New position, x: ${~~positionAbsoluteX}, y: ${~~positionAbsoluteY}`,
|
|
1740
1779
|
});
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
isShiftPressed: event.shiftKey,
|
|
1780
|
+
moveSelectedNodes({
|
|
1781
|
+
direction: arrowKeyDiffs[event.key],
|
|
1782
|
+
factor: event.shiftKey ? 4 : 1,
|
|
1745
1783
|
});
|
|
1746
1784
|
}
|
|
1747
1785
|
};
|
|
@@ -1884,18 +1922,21 @@ const Marker = ({ id, type, color, width = 12.5, height = 12.5, markerUnits = 's
|
|
|
1884
1922
|
}
|
|
1885
1923
|
return (jsx("marker", { className: "react-flow__arrowhead", id: id, markerWidth: `${width}`, markerHeight: `${height}`, viewBox: "-10 -10 20 20", markerUnits: markerUnits, orient: orient, refX: "0", refY: "0", children: jsx(Symbol, { color: color, strokeWidth: strokeWidth }) }));
|
|
1886
1924
|
};
|
|
1887
|
-
const markerSelector = ({ defaultColor, rfId }) => (s) => {
|
|
1888
|
-
const markers = createMarkerIds(s.edges, { id: rfId, defaultColor });
|
|
1889
|
-
return markers;
|
|
1890
|
-
};
|
|
1891
|
-
const markersEqual = (a, b) =>
|
|
1892
|
-
// the id includes all marker options, so we just need to look at that part of the marker
|
|
1893
|
-
!(a.length !== b.length || a.some((m, i) => m.id !== b[i].id));
|
|
1894
1925
|
// when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
|
|
1895
1926
|
// when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
|
|
1896
1927
|
// that we can then use for creating our unique marker ids
|
|
1897
1928
|
const MarkerDefinitions = ({ defaultColor, rfId }) => {
|
|
1898
|
-
const
|
|
1929
|
+
const edges = useStore((s) => s.edges);
|
|
1930
|
+
const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions);
|
|
1931
|
+
const markers = useMemo(() => {
|
|
1932
|
+
const markers = createMarkerIds(edges, {
|
|
1933
|
+
id: rfId,
|
|
1934
|
+
defaultColor,
|
|
1935
|
+
defaultMarkerStart: defaultEdgeOptions?.markerStart,
|
|
1936
|
+
defaultMarkerEnd: defaultEdgeOptions?.markerEnd,
|
|
1937
|
+
});
|
|
1938
|
+
return markers;
|
|
1939
|
+
}, [edges, defaultEdgeOptions, rfId, defaultColor]);
|
|
1899
1940
|
if (!markers.length) {
|
|
1900
1941
|
return null;
|
|
1901
1942
|
}
|
|
@@ -2190,8 +2231,8 @@ function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, o
|
|
|
2190
2231
|
...(edgePosition || nullPosition),
|
|
2191
2232
|
};
|
|
2192
2233
|
}, [edge.source, edge.target, edge.sourceHandle, edge.targetHandle, edge.selected, edge.zIndex]), shallow);
|
|
2193
|
-
const markerStartUrl = useMemo(() => (edge.markerStart ? `url(#${getMarkerId(edge.markerStart, rfId)})` : undefined), [edge.markerStart, rfId]);
|
|
2194
|
-
const markerEndUrl = useMemo(() => (edge.markerEnd ? `url(#${getMarkerId(edge.markerEnd, rfId)})` : undefined), [edge.markerEnd, rfId]);
|
|
2234
|
+
const markerStartUrl = useMemo(() => (edge.markerStart ? `url('#${getMarkerId(edge.markerStart, rfId)}')` : undefined), [edge.markerStart, rfId]);
|
|
2235
|
+
const markerEndUrl = useMemo(() => (edge.markerEnd ? `url('#${getMarkerId(edge.markerEnd, rfId)}')` : undefined), [edge.markerEnd, rfId]);
|
|
2195
2236
|
if (edge.hidden || !sourceX || !sourceY || !targetX || !targetY) {
|
|
2196
2237
|
return null;
|
|
2197
2238
|
}
|
|
@@ -2259,6 +2300,7 @@ function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, o
|
|
|
2259
2300
|
animated: edge.animated,
|
|
2260
2301
|
inactive: !isSelectable && !onClick,
|
|
2261
2302
|
updating: updateHover,
|
|
2303
|
+
selectable: isSelectable,
|
|
2262
2304
|
},
|
|
2263
2305
|
]), onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, onMouseEnter: onEdgeMouseEnter, onMouseMove: onEdgeMouseMove, onMouseLeave: onEdgeMouseLeave, onKeyDown: isFocusable ? onKeyDown : undefined, tabIndex: isFocusable ? 0 : undefined, role: isFocusable ? 'button' : 'img', "data-id": id, "data-testid": `rf__edge-${id}`, "aria-label": edge.ariaLabel === null ? undefined : edge.ariaLabel || `Edge from ${edge.source} to ${edge.target}`, "aria-describedby": isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined, ref: edgeRef, children: [!updating && (jsx(EdgeComponent, { id: id, source: edge.source, target: edge.target, selected: edge.selected, animated: edge.animated, label: edge.label, labelStyle: edge.labelStyle, labelShowBg: edge.labelShowBg, labelBgStyle: edge.labelBgStyle, labelBgPadding: edge.labelBgPadding, labelBgBorderRadius: edge.labelBgBorderRadius, sourceX: sourceX, sourceY: sourceY, targetX: targetX, targetY: targetY, sourcePosition: sourcePosition, targetPosition: targetPosition, data: edge.data, style: edge.style, sourceHandleId: edge.sourceHandle, targetHandleId: edge.targetHandle, markerStart: markerStartUrl, markerEnd: markerEndUrl, pathOptions: 'pathOptions' in edge ? edge.pathOptions : undefined, interactionWidth: edge.interactionWidth })), isUpdatable && (jsx(EdgeUpdateAnchors, { edge: edge, isUpdatable: isUpdatable, edgeUpdaterRadius: edgeUpdaterRadius, onEdgeUpdate: onEdgeUpdate, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, sourceX: sourceX, sourceY: sourceY, targetX: targetX, targetY: targetY, sourcePosition: sourcePosition, targetPosition: targetPosition, setUpdateHover: setUpdateHover, setUpdating: setUpdating, sourceHandleId: edge.sourceHandle, targetHandleId: edge.targetHandle }))] }) }));
|
|
2264
2306
|
}
|
|
@@ -2431,44 +2473,22 @@ function GraphViewComponent({ nodeTypes, edgeTypes, onInit, onNodeClick, onEdgeC
|
|
|
2431
2473
|
GraphViewComponent.displayName = 'GraphView';
|
|
2432
2474
|
const GraphView = memo(GraphViewComponent);
|
|
2433
2475
|
|
|
2434
|
-
|
|
2435
|
-
return items.map((item) => {
|
|
2436
|
-
const change = changes.find((change) => change.id === item.id);
|
|
2437
|
-
if (change) {
|
|
2438
|
-
item.selected = change.selected;
|
|
2439
|
-
}
|
|
2440
|
-
return item;
|
|
2441
|
-
});
|
|
2442
|
-
}
|
|
2443
|
-
function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get, set }) {
|
|
2444
|
-
const { nodes, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get();
|
|
2445
|
-
if (changedNodes?.length) {
|
|
2446
|
-
if (hasDefaultNodes) {
|
|
2447
|
-
set({ nodes: handleControlledSelectionChange(changedNodes, nodes) });
|
|
2448
|
-
}
|
|
2449
|
-
onNodesChange?.(changedNodes);
|
|
2450
|
-
}
|
|
2451
|
-
if (changedEdges?.length) {
|
|
2452
|
-
if (hasDefaultEdges) {
|
|
2453
|
-
set({ edges: handleControlledSelectionChange(changedEdges, edges) });
|
|
2454
|
-
}
|
|
2455
|
-
onEdgesChange?.(changedEdges);
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
|
-
|
|
2459
|
-
const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {}) => {
|
|
2476
|
+
const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView, } = {}) => {
|
|
2460
2477
|
const nodeLookup = new Map();
|
|
2461
2478
|
const connectionLookup = new Map();
|
|
2462
2479
|
const edgeLookup = new Map();
|
|
2463
|
-
|
|
2464
|
-
const
|
|
2480
|
+
const storeEdges = defaultEdges ?? edges ?? [];
|
|
2481
|
+
const storeNodes = defaultNodes ?? nodes ?? [];
|
|
2482
|
+
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
|
2483
|
+
const nextNodes = adoptUserProvidedNodes(storeNodes, nodeLookup, {
|
|
2465
2484
|
nodeOrigin: [0, 0],
|
|
2466
2485
|
elevateNodesOnSelect: false,
|
|
2467
2486
|
});
|
|
2468
2487
|
let transform = [0, 0, 1];
|
|
2469
2488
|
if (fitView && width && height) {
|
|
2470
2489
|
const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height);
|
|
2471
|
-
|
|
2490
|
+
// @todo users nodeOrigin should be used here
|
|
2491
|
+
const bounds = getNodesBounds(nodesWithDimensions, { nodeOrigin: [0, 0] });
|
|
2472
2492
|
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
|
2473
2493
|
transform = [x, y, zoom];
|
|
2474
2494
|
}
|
|
@@ -2479,13 +2499,13 @@ const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {
|
|
|
2479
2499
|
transform,
|
|
2480
2500
|
nodes: nextNodes,
|
|
2481
2501
|
nodeLookup,
|
|
2482
|
-
edges,
|
|
2502
|
+
edges: storeEdges,
|
|
2483
2503
|
edgeLookup,
|
|
2484
2504
|
connectionLookup,
|
|
2485
2505
|
onNodesChange: null,
|
|
2486
2506
|
onEdgesChange: null,
|
|
2487
|
-
hasDefaultNodes:
|
|
2488
|
-
hasDefaultEdges:
|
|
2507
|
+
hasDefaultNodes: defaultNodes !== undefined,
|
|
2508
|
+
hasDefaultEdges: defaultEdges !== undefined,
|
|
2489
2509
|
panZoom: null,
|
|
2490
2510
|
minZoom: 0.5,
|
|
2491
2511
|
maxZoom: 2,
|
|
@@ -2525,15 +2545,15 @@ const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {
|
|
|
2525
2545
|
autoPanOnConnect: true,
|
|
2526
2546
|
autoPanOnNodeDrag: true,
|
|
2527
2547
|
connectionRadius: 20,
|
|
2528
|
-
onError:
|
|
2548
|
+
onError: devWarn,
|
|
2529
2549
|
isValidConnection: undefined,
|
|
2530
2550
|
onSelectionChangeHandlers: [],
|
|
2531
2551
|
lib: 'react',
|
|
2532
2552
|
};
|
|
2533
2553
|
};
|
|
2534
2554
|
|
|
2535
|
-
const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) => createWithEqualityFn((set, get) => ({
|
|
2536
|
-
...getInitialState({ nodes, edges, width, height, fitView: fitView$1 }),
|
|
2555
|
+
const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView: fitView$1, }) => createWithEqualityFn((set, get) => ({
|
|
2556
|
+
...getInitialState({ nodes, edges, width, height, fitView: fitView$1, defaultNodes, defaultEdges }),
|
|
2537
2557
|
setNodes: (nodes) => {
|
|
2538
2558
|
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2539
2559
|
// setNodes() is called exclusively in response to user actions:
|
|
@@ -2542,7 +2562,6 @@ const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) =>
|
|
|
2542
2562
|
//
|
|
2543
2563
|
// When this happens, we take the note objects passed by the user and extend them with fields
|
|
2544
2564
|
// relevant for internal React Flow operations.
|
|
2545
|
-
// TODO: consider updating the types to reflect the distinction between user-provided nodes and internal nodes.
|
|
2546
2565
|
const nodesWithInternalData = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
|
2547
2566
|
set({ nodes: nodesWithInternalData });
|
|
2548
2567
|
},
|
|
@@ -2551,28 +2570,17 @@ const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) =>
|
|
|
2551
2570
|
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
|
2552
2571
|
set({ edges });
|
|
2553
2572
|
},
|
|
2554
|
-
// when the user works with an uncontrolled flow,
|
|
2555
|
-
// we set a flag `hasDefaultNodes` / `hasDefaultEdges`
|
|
2556
2573
|
setDefaultNodesAndEdges: (nodes, edges) => {
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
hasDefaultNodes
|
|
2561
|
-
hasDefaultEdges,
|
|
2562
|
-
};
|
|
2563
|
-
if (hasDefaultNodes) {
|
|
2564
|
-
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2565
|
-
nextState.nodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
|
2566
|
-
nodeOrigin,
|
|
2567
|
-
elevateNodesOnSelect,
|
|
2568
|
-
});
|
|
2574
|
+
if (nodes) {
|
|
2575
|
+
const { setNodes } = get();
|
|
2576
|
+
setNodes(nodes);
|
|
2577
|
+
set({ hasDefaultNodes: true });
|
|
2569
2578
|
}
|
|
2570
|
-
if (
|
|
2571
|
-
const {
|
|
2572
|
-
|
|
2573
|
-
|
|
2579
|
+
if (edges) {
|
|
2580
|
+
const { setEdges } = get();
|
|
2581
|
+
setEdges(edges);
|
|
2582
|
+
set({ hasDefaultEdges: true });
|
|
2574
2583
|
}
|
|
2575
|
-
set(nextState);
|
|
2576
2584
|
},
|
|
2577
2585
|
// Every node gets registerd at a ResizeObserver. Whenever a node
|
|
2578
2586
|
// changes its dimensions, this function is called to measure the
|
|
@@ -2609,86 +2617,70 @@ const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) =>
|
|
|
2609
2617
|
onNodesChange?.(changes);
|
|
2610
2618
|
}
|
|
2611
2619
|
},
|
|
2612
|
-
updateNodePositions: (nodeDragItems,
|
|
2620
|
+
updateNodePositions: (nodeDragItems, dragging = false) => {
|
|
2613
2621
|
const changes = nodeDragItems.map((node) => {
|
|
2614
2622
|
const change = {
|
|
2615
2623
|
id: node.id,
|
|
2616
2624
|
type: 'position',
|
|
2625
|
+
position: node.position,
|
|
2626
|
+
positionAbsolute: node.computed?.positionAbsolute,
|
|
2617
2627
|
dragging,
|
|
2618
2628
|
};
|
|
2619
|
-
if (positionChanged) {
|
|
2620
|
-
change.positionAbsolute = node.computed?.positionAbsolute;
|
|
2621
|
-
change.position = node.position;
|
|
2622
|
-
}
|
|
2623
2629
|
return change;
|
|
2624
2630
|
});
|
|
2625
2631
|
get().triggerNodeChanges(changes);
|
|
2626
2632
|
},
|
|
2627
2633
|
triggerNodeChanges: (changes) => {
|
|
2628
|
-
const { onNodesChange,
|
|
2634
|
+
const { onNodesChange, setNodes, nodes, hasDefaultNodes } = get();
|
|
2629
2635
|
if (changes?.length) {
|
|
2630
2636
|
if (hasDefaultNodes) {
|
|
2631
2637
|
const updatedNodes = applyNodeChanges(changes, nodes);
|
|
2632
|
-
|
|
2633
|
-
nodeOrigin,
|
|
2634
|
-
elevateNodesOnSelect,
|
|
2635
|
-
});
|
|
2636
|
-
set({ nodes: nextNodes });
|
|
2638
|
+
setNodes(updatedNodes);
|
|
2637
2639
|
}
|
|
2638
2640
|
onNodesChange?.(changes);
|
|
2639
2641
|
}
|
|
2640
2642
|
},
|
|
2643
|
+
triggerEdgeChanges: (changes) => {
|
|
2644
|
+
const { onEdgesChange, setEdges, edges, hasDefaultEdges } = get();
|
|
2645
|
+
if (changes?.length) {
|
|
2646
|
+
if (hasDefaultEdges) {
|
|
2647
|
+
const updatedEdges = applyEdgeChanges(changes, edges);
|
|
2648
|
+
setEdges(updatedEdges);
|
|
2649
|
+
}
|
|
2650
|
+
onEdgesChange?.(changes);
|
|
2651
|
+
}
|
|
2652
|
+
},
|
|
2641
2653
|
addSelectedNodes: (selectedNodeIds) => {
|
|
2642
|
-
const { multiSelectionActive, edges, nodes } = get();
|
|
2643
|
-
let changedNodes;
|
|
2644
|
-
let changedEdges = null;
|
|
2654
|
+
const { multiSelectionActive, edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
|
2645
2655
|
if (multiSelectionActive) {
|
|
2646
|
-
|
|
2656
|
+
const nodeChanges = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
|
|
2657
|
+
triggerNodeChanges(nodeChanges);
|
|
2658
|
+
return;
|
|
2647
2659
|
}
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
changedEdges = getSelectionChanges(edges);
|
|
2651
|
-
}
|
|
2652
|
-
updateNodesAndEdgesSelections({
|
|
2653
|
-
changedNodes,
|
|
2654
|
-
changedEdges,
|
|
2655
|
-
get,
|
|
2656
|
-
set,
|
|
2657
|
-
});
|
|
2660
|
+
triggerNodeChanges(getSelectionChanges(nodes, new Set([...selectedNodeIds]), true));
|
|
2661
|
+
triggerEdgeChanges(getSelectionChanges(edges));
|
|
2658
2662
|
},
|
|
2659
2663
|
addSelectedEdges: (selectedEdgeIds) => {
|
|
2660
|
-
const { multiSelectionActive, edges, nodes } = get();
|
|
2661
|
-
let changedEdges;
|
|
2662
|
-
let changedNodes = null;
|
|
2664
|
+
const { multiSelectionActive, edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
|
2663
2665
|
if (multiSelectionActive) {
|
|
2664
|
-
changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
|
|
2666
|
+
const changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
|
|
2667
|
+
triggerEdgeChanges(changedEdges);
|
|
2668
|
+
return;
|
|
2665
2669
|
}
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
changedNodes = getSelectionChanges(nodes, new Set(), true);
|
|
2669
|
-
}
|
|
2670
|
-
updateNodesAndEdgesSelections({
|
|
2671
|
-
changedNodes,
|
|
2672
|
-
changedEdges,
|
|
2673
|
-
get,
|
|
2674
|
-
set,
|
|
2675
|
-
});
|
|
2670
|
+
triggerEdgeChanges(getSelectionChanges(edges, new Set([...selectedEdgeIds])));
|
|
2671
|
+
triggerNodeChanges(getSelectionChanges(nodes, new Set(), true));
|
|
2676
2672
|
},
|
|
2677
2673
|
unselectNodesAndEdges: ({ nodes, edges } = {}) => {
|
|
2678
|
-
const { edges: storeEdges, nodes: storeNodes } = get();
|
|
2674
|
+
const { edges: storeEdges, nodes: storeNodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
|
2679
2675
|
const nodesToUnselect = nodes ? nodes : storeNodes;
|
|
2680
2676
|
const edgesToUnselect = edges ? edges : storeEdges;
|
|
2681
|
-
const
|
|
2677
|
+
const nodeChanges = nodesToUnselect.map((n) => {
|
|
2682
2678
|
n.selected = false;
|
|
2683
2679
|
return createSelectionChange(n.id, false);
|
|
2684
2680
|
});
|
|
2685
|
-
const
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
changedEdges,
|
|
2689
|
-
get,
|
|
2690
|
-
set,
|
|
2691
|
-
});
|
|
2681
|
+
const edgeChanges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false));
|
|
2682
|
+
triggerNodeChanges(nodeChanges);
|
|
2683
|
+
triggerEdgeChanges(edgeChanges);
|
|
2692
2684
|
},
|
|
2693
2685
|
setMinZoom: (minZoom) => {
|
|
2694
2686
|
const { panZoom, maxZoom } = get();
|
|
@@ -2705,19 +2697,11 @@ const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) =>
|
|
|
2705
2697
|
set({ translateExtent });
|
|
2706
2698
|
},
|
|
2707
2699
|
resetSelectedElements: () => {
|
|
2708
|
-
const { edges, nodes } = get();
|
|
2709
|
-
const
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
.filter((e) => e.selected)
|
|
2714
|
-
.map((e) => createSelectionChange(e.id, false));
|
|
2715
|
-
updateNodesAndEdgesSelections({
|
|
2716
|
-
changedNodes: nodesToUnselect,
|
|
2717
|
-
changedEdges: edgesToUnselect,
|
|
2718
|
-
get,
|
|
2719
|
-
set,
|
|
2720
|
-
});
|
|
2700
|
+
const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
|
2701
|
+
const nodeChanges = nodes.reduce((res, node) => (node.selected ? [...res, createSelectionChange(node.id, false)] : res), []);
|
|
2702
|
+
const edgeChanges = edges.reduce((res, edge) => (edge.selected ? [...res, createSelectionChange(edge.id, false)] : res), []);
|
|
2703
|
+
triggerNodeChanges(nodeChanges);
|
|
2704
|
+
triggerEdgeChanges(edgeChanges);
|
|
2721
2705
|
},
|
|
2722
2706
|
setNodeExtent: (nodeExtent) => {
|
|
2723
2707
|
const { nodes } = get();
|
|
@@ -2769,21 +2753,17 @@ const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) =>
|
|
|
2769
2753
|
};
|
|
2770
2754
|
set(currentConnection);
|
|
2771
2755
|
},
|
|
2772
|
-
reset: () => {
|
|
2773
|
-
// @todo: what should we do about this? Do we still need it?
|
|
2774
|
-
// if you are on a SPA with multiple flows, we want to make sure that the store gets resetted
|
|
2775
|
-
// when you switch pages. Does this reset solves this? Currently it always gets called. This
|
|
2776
|
-
// leads to an emtpy nodes array at the beginning.
|
|
2777
|
-
// set({ ...getInitialState() });
|
|
2778
|
-
},
|
|
2756
|
+
reset: () => set({ ...getInitialState() }),
|
|
2779
2757
|
}), Object.is);
|
|
2780
2758
|
|
|
2781
|
-
function ReactFlowProvider({ children, initialNodes, initialEdges, initialWidth, initialHeight, fitView, }) {
|
|
2759
|
+
function ReactFlowProvider({ children, initialNodes, initialEdges, defaultNodes, defaultEdges, initialWidth, initialHeight, fitView, }) {
|
|
2782
2760
|
const storeRef = useRef(null);
|
|
2783
2761
|
if (!storeRef.current) {
|
|
2784
2762
|
storeRef.current = createRFStore({
|
|
2785
2763
|
nodes: initialNodes,
|
|
2786
2764
|
edges: initialEdges,
|
|
2765
|
+
defaultNodes,
|
|
2766
|
+
defaultEdges,
|
|
2787
2767
|
width: initialWidth,
|
|
2788
2768
|
height: initialHeight,
|
|
2789
2769
|
fitView,
|
|
@@ -2792,18 +2772,16 @@ function ReactFlowProvider({ children, initialNodes, initialEdges, initialWidth,
|
|
|
2792
2772
|
return jsx(Provider$1, { value: storeRef.current, children: children });
|
|
2793
2773
|
}
|
|
2794
2774
|
|
|
2795
|
-
function Wrapper({ children, nodes, edges, width, height, fitView, }) {
|
|
2775
|
+
function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, }) {
|
|
2796
2776
|
const isWrapped = useContext(StoreContext);
|
|
2797
2777
|
if (isWrapped) {
|
|
2798
2778
|
// we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
|
|
2799
2779
|
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
|
2800
2780
|
return jsx(Fragment, { children: children });
|
|
2801
2781
|
}
|
|
2802
|
-
return (jsx(ReactFlowProvider, { initialNodes: nodes, initialEdges: edges, initialWidth: width, initialHeight: height, fitView: fitView, children: children }));
|
|
2782
|
+
return (jsx(ReactFlowProvider, { initialNodes: nodes, initialEdges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, initialWidth: width, initialHeight: height, fitView: fitView, children: children }));
|
|
2803
2783
|
}
|
|
2804
2784
|
|
|
2805
|
-
const initNodeOrigin = [0, 0];
|
|
2806
|
-
const initDefaultViewport = { x: 0, y: 0, zoom: 1 };
|
|
2807
2785
|
const wrapperStyle = {
|
|
2808
2786
|
width: '100%',
|
|
2809
2787
|
height: '100%',
|
|
@@ -2811,10 +2789,10 @@ const wrapperStyle = {
|
|
|
2811
2789
|
position: 'relative',
|
|
2812
2790
|
zIndex: 0,
|
|
2813
2791
|
};
|
|
2814
|
-
const ReactFlow = forwardRef(({ nodes, edges, defaultNodes, defaultEdges, className, nodeTypes, edgeTypes, onNodeClick, onEdgeClick, onInit, onMove, onMoveStart, onMoveEnd, onConnect, onConnectStart, onConnectEnd, onClickConnectStart, onClickConnectEnd, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, onNodeContextMenu, onNodeDoubleClick, onNodeDragStart, onNodeDrag, onNodeDragStop, onNodesDelete, onEdgesDelete, onDelete, onSelectionChange, onSelectionDragStart, onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, onSelectionStart, onSelectionEnd, onBeforeDelete, connectionMode, connectionLineType = ConnectionLineType.Bezier, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, deleteKeyCode = 'Backspace', selectionKeyCode = 'Shift', selectionOnDrag = false, selectionMode = SelectionMode.Full, panActivationKeyCode = 'Space', multiSelectionKeyCode = isMacOs() ? 'Meta' : 'Control', zoomActivationKeyCode = isMacOs() ? 'Meta' : 'Control', snapToGrid, snapGrid, onlyRenderVisibleElements = false, selectNodesOnDrag, nodesDraggable, nodesConnectable, nodesFocusable, nodeOrigin =
|
|
2792
|
+
const ReactFlow = forwardRef(({ nodes, edges, defaultNodes, defaultEdges, className, nodeTypes, edgeTypes, onNodeClick, onEdgeClick, onInit, onMove, onMoveStart, onMoveEnd, onConnect, onConnectStart, onConnectEnd, onClickConnectStart, onClickConnectEnd, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, onNodeContextMenu, onNodeDoubleClick, onNodeDragStart, onNodeDrag, onNodeDragStop, onNodesDelete, onEdgesDelete, onDelete, onSelectionChange, onSelectionDragStart, onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, onSelectionStart, onSelectionEnd, onBeforeDelete, connectionMode, connectionLineType = ConnectionLineType.Bezier, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, deleteKeyCode = 'Backspace', selectionKeyCode = 'Shift', selectionOnDrag = false, selectionMode = SelectionMode.Full, panActivationKeyCode = 'Space', multiSelectionKeyCode = isMacOs() ? 'Meta' : 'Control', zoomActivationKeyCode = isMacOs() ? 'Meta' : 'Control', snapToGrid, snapGrid, onlyRenderVisibleElements = false, selectNodesOnDrag, nodesDraggable, nodesConnectable, nodesFocusable, nodeOrigin = defaultNodeOrigin, edgesFocusable, edgesUpdatable, elementsSelectable = true, defaultViewport: defaultViewport$1 = defaultViewport, minZoom = 0.5, maxZoom = 2, translateExtent = infiniteExtent, preventScrolling = true, nodeExtent, defaultMarkerColor = '#b1b1b7', zoomOnScroll = true, zoomOnPinch = true, panOnScroll = false, panOnScrollSpeed = 0.5, panOnScrollMode = PanOnScrollMode.Free, zoomOnDoubleClick = true, panOnDrag = true, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneScroll, onPaneContextMenu, children, onEdgeUpdate, onEdgeContextMenu, onEdgeDoubleClick, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, onEdgeUpdateStart, onEdgeUpdateEnd, edgeUpdaterRadius = 10, onNodesChange, onEdgesChange, noDragClassName = 'nodrag', noWheelClassName = 'nowheel', noPanClassName = 'nopan', fitView, fitViewOptions, connectOnClick, attributionPosition, proOptions, defaultEdgeOptions, elevateNodesOnSelect, elevateEdgesOnSelect, disableKeyboardA11y = false, autoPanOnConnect, autoPanOnNodeDrag, connectionRadius, isValidConnection, onError, style, id, nodeDragThreshold, viewport, onViewportChange, width, height, colorMode = 'light', ...rest }, ref) => {
|
|
2815
2793
|
const rfId = id || '1';
|
|
2816
2794
|
const colorModeClassName = useColorModeClass(colorMode);
|
|
2817
|
-
return (jsx("div", { ...rest, style: { ...style, ...wrapperStyle }, ref: ref, className: cc(['react-flow', className, colorModeClassName]), "data-testid": "rf__wrapper", id: id, children: jsxs(Wrapper, { nodes: nodes, edges: edges, width: width, height: height, fitView: fitView, children: [jsx(GraphView, { onInit: onInit, onNodeClick: onNodeClick, onEdgeClick: onEdgeClick, onNodeMouseEnter: onNodeMouseEnter, onNodeMouseMove: onNodeMouseMove, onNodeMouseLeave: onNodeMouseLeave, onNodeContextMenu: onNodeContextMenu, onNodeDoubleClick: onNodeDoubleClick, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineType: connectionLineType, connectionLineStyle: connectionLineStyle, connectionLineComponent: connectionLineComponent, connectionLineContainerStyle: connectionLineContainerStyle, selectionKeyCode: selectionKeyCode, selectionOnDrag: selectionOnDrag, selectionMode: selectionMode, deleteKeyCode: deleteKeyCode, multiSelectionKeyCode: multiSelectionKeyCode, panActivationKeyCode: panActivationKeyCode, zoomActivationKeyCode: zoomActivationKeyCode, onlyRenderVisibleElements: onlyRenderVisibleElements, defaultViewport: defaultViewport, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, preventScrolling: preventScrolling, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, zoomOnDoubleClick: zoomOnDoubleClick, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, panOnDrag: panOnDrag, onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneScroll: onPaneScroll, onPaneContextMenu: onPaneContextMenu, onSelectionContextMenu: onSelectionContextMenu, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onEdgeUpdate: onEdgeUpdate, onEdgeContextMenu: onEdgeContextMenu, onEdgeDoubleClick: onEdgeDoubleClick, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, edgeUpdaterRadius: edgeUpdaterRadius, defaultMarkerColor: defaultMarkerColor, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, rfId: rfId, disableKeyboardA11y: disableKeyboardA11y, nodeOrigin: nodeOrigin, nodeExtent: nodeExtent, viewport: viewport, onViewportChange: onViewportChange }), jsx(StoreUpdater, { nodes: nodes, edges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, onConnect: onConnect, onConnectStart: onConnectStart, onConnectEnd: onConnectEnd, onClickConnectStart: onClickConnectStart, onClickConnectEnd: onClickConnectEnd, nodesDraggable: nodesDraggable, nodesConnectable: nodesConnectable, nodesFocusable: nodesFocusable, edgesFocusable: edgesFocusable, edgesUpdatable: edgesUpdatable, elementsSelectable: elementsSelectable, elevateNodesOnSelect: elevateNodesOnSelect, elevateEdgesOnSelect: elevateEdgesOnSelect, minZoom: minZoom, maxZoom: maxZoom, nodeExtent: nodeExtent, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, snapToGrid: snapToGrid, snapGrid: snapGrid, connectionMode: connectionMode, translateExtent: translateExtent, connectOnClick: connectOnClick, defaultEdgeOptions: defaultEdgeOptions, fitView: fitView, fitViewOptions: fitViewOptions, onNodesDelete: onNodesDelete, onEdgesDelete: onEdgesDelete, onDelete: onDelete, onNodeDragStart: onNodeDragStart, onNodeDrag: onNodeDrag, onNodeDragStop: onNodeDragStop, onSelectionDrag: onSelectionDrag, onSelectionDragStart: onSelectionDragStart, onSelectionDragStop: onSelectionDragStop, onMove: onMove, onMoveStart: onMoveStart, onMoveEnd: onMoveEnd, noPanClassName: noPanClassName, nodeOrigin: nodeOrigin, rfId: rfId, autoPanOnConnect: autoPanOnConnect, autoPanOnNodeDrag: autoPanOnNodeDrag, onError: onError, connectionRadius: connectionRadius, isValidConnection: isValidConnection, selectNodesOnDrag: selectNodesOnDrag, nodeDragThreshold: nodeDragThreshold, onBeforeDelete: onBeforeDelete }), jsx(SelectionListener, { onSelectionChange: onSelectionChange }), children, jsx(Attribution, { proOptions: proOptions, position: attributionPosition }), jsx(A11yDescriptions, { rfId: rfId, disableKeyboardA11y: disableKeyboardA11y })] }) }));
|
|
2795
|
+
return (jsx("div", { ...rest, style: { ...style, ...wrapperStyle }, ref: ref, className: cc(['react-flow', className, colorModeClassName]), "data-testid": "rf__wrapper", id: id, children: jsxs(Wrapper, { nodes: nodes, edges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, width: width, height: height, fitView: fitView, children: [jsx(GraphView, { onInit: onInit, onNodeClick: onNodeClick, onEdgeClick: onEdgeClick, onNodeMouseEnter: onNodeMouseEnter, onNodeMouseMove: onNodeMouseMove, onNodeMouseLeave: onNodeMouseLeave, onNodeContextMenu: onNodeContextMenu, onNodeDoubleClick: onNodeDoubleClick, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineType: connectionLineType, connectionLineStyle: connectionLineStyle, connectionLineComponent: connectionLineComponent, connectionLineContainerStyle: connectionLineContainerStyle, selectionKeyCode: selectionKeyCode, selectionOnDrag: selectionOnDrag, selectionMode: selectionMode, deleteKeyCode: deleteKeyCode, multiSelectionKeyCode: multiSelectionKeyCode, panActivationKeyCode: panActivationKeyCode, zoomActivationKeyCode: zoomActivationKeyCode, onlyRenderVisibleElements: onlyRenderVisibleElements, defaultViewport: defaultViewport$1, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, preventScrolling: preventScrolling, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, zoomOnDoubleClick: zoomOnDoubleClick, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, panOnDrag: panOnDrag, onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneScroll: onPaneScroll, onPaneContextMenu: onPaneContextMenu, onSelectionContextMenu: onSelectionContextMenu, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onEdgeUpdate: onEdgeUpdate, onEdgeContextMenu: onEdgeContextMenu, onEdgeDoubleClick: onEdgeDoubleClick, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, edgeUpdaterRadius: edgeUpdaterRadius, defaultMarkerColor: defaultMarkerColor, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, rfId: rfId, disableKeyboardA11y: disableKeyboardA11y, nodeOrigin: nodeOrigin, nodeExtent: nodeExtent, viewport: viewport, onViewportChange: onViewportChange }), jsx(StoreUpdater, { nodes: nodes, edges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, onConnect: onConnect, onConnectStart: onConnectStart, onConnectEnd: onConnectEnd, onClickConnectStart: onClickConnectStart, onClickConnectEnd: onClickConnectEnd, nodesDraggable: nodesDraggable, nodesConnectable: nodesConnectable, nodesFocusable: nodesFocusable, edgesFocusable: edgesFocusable, edgesUpdatable: edgesUpdatable, elementsSelectable: elementsSelectable, elevateNodesOnSelect: elevateNodesOnSelect, elevateEdgesOnSelect: elevateEdgesOnSelect, minZoom: minZoom, maxZoom: maxZoom, nodeExtent: nodeExtent, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, snapToGrid: snapToGrid, snapGrid: snapGrid, connectionMode: connectionMode, translateExtent: translateExtent, connectOnClick: connectOnClick, defaultEdgeOptions: defaultEdgeOptions, fitView: fitView, fitViewOptions: fitViewOptions, onNodesDelete: onNodesDelete, onEdgesDelete: onEdgesDelete, onDelete: onDelete, onNodeDragStart: onNodeDragStart, onNodeDrag: onNodeDrag, onNodeDragStop: onNodeDragStop, onSelectionDrag: onSelectionDrag, onSelectionDragStart: onSelectionDragStart, onSelectionDragStop: onSelectionDragStop, onMove: onMove, onMoveStart: onMoveStart, onMoveEnd: onMoveEnd, noPanClassName: noPanClassName, nodeOrigin: nodeOrigin, rfId: rfId, autoPanOnConnect: autoPanOnConnect, autoPanOnNodeDrag: autoPanOnNodeDrag, onError: onError, connectionRadius: connectionRadius, isValidConnection: isValidConnection, selectNodesOnDrag: selectNodesOnDrag, nodeDragThreshold: nodeDragThreshold, onBeforeDelete: onBeforeDelete }), jsx(SelectionListener, { onSelectionChange: onSelectionChange }), children, jsx(Attribution, { proOptions: proOptions, position: attributionPosition }), jsx(A11yDescriptions, { rfId: rfId, disableKeyboardA11y: disableKeyboardA11y })] }) }));
|
|
2818
2796
|
});
|
|
2819
2797
|
ReactFlow.displayName = 'ReactFlow';
|
|
2820
2798
|
|
|
@@ -2948,7 +2926,7 @@ function useOnViewportChange({ onStart, onChange, onEnd }) {
|
|
|
2948
2926
|
* Hook for registering an onSelectionChange handler.
|
|
2949
2927
|
*
|
|
2950
2928
|
* @public
|
|
2951
|
-
* @
|
|
2929
|
+
* @param params.onChange - The handler to register
|
|
2952
2930
|
*/
|
|
2953
2931
|
function useOnSelectionChange({ onChange }) {
|
|
2954
2932
|
const store = useStoreApi();
|
|
@@ -2966,9 +2944,14 @@ const selector$6 = (options) => (s) => {
|
|
|
2966
2944
|
if (s.nodes.length === 0) {
|
|
2967
2945
|
return false;
|
|
2968
2946
|
}
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2947
|
+
for (const node of s.nodes) {
|
|
2948
|
+
if (options.includeHiddenNodes || !node.hidden) {
|
|
2949
|
+
if (node[internalsSymbol]?.handleBounds === undefined) {
|
|
2950
|
+
return false;
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return true;
|
|
2972
2955
|
};
|
|
2973
2956
|
const defaultOptions = {
|
|
2974
2957
|
includeHiddenNodes: false,
|
|
@@ -2986,7 +2969,7 @@ function useNodesInitialized(options = defaultOptions) {
|
|
|
2986
2969
|
}
|
|
2987
2970
|
|
|
2988
2971
|
/**
|
|
2989
|
-
*
|
|
2972
|
+
* Hook to check if a <Handle /> is connected to another <Handle /> and get the connections.
|
|
2990
2973
|
*
|
|
2991
2974
|
* @public
|
|
2992
2975
|
* @param param.type - handle type 'source' or 'target'
|
|
@@ -2994,12 +2977,12 @@ function useNodesInitialized(options = defaultOptions) {
|
|
|
2994
2977
|
* @param param.id - the handle id (this is only needed if the node has multiple handles of the same type)
|
|
2995
2978
|
* @param param.onConnect - gets called when a connection is established
|
|
2996
2979
|
* @param param.onDisconnect - gets called when a connection is removed
|
|
2997
|
-
* @returns an array with connections
|
|
2980
|
+
* @returns an array with handle connections
|
|
2998
2981
|
*/
|
|
2999
2982
|
function useHandleConnections({ type, id = null, nodeId, onConnect, onDisconnect, }) {
|
|
3000
2983
|
const _nodeId = useNodeId();
|
|
2984
|
+
const currentNodeId = nodeId ?? _nodeId;
|
|
3001
2985
|
const prevConnections = useRef(null);
|
|
3002
|
-
const currentNodeId = nodeId || _nodeId;
|
|
3003
2986
|
const connections = useStore((state) => state.connectionLookup.get(`${currentNodeId}-${type}-${id}`), areConnectionMapsEqual);
|
|
3004
2987
|
useEffect(() => {
|
|
3005
2988
|
// @todo dicuss if onConnect/onDisconnect should be called when the component mounts/unmounts
|
|
@@ -3041,7 +3024,7 @@ const selector$5 = (s) => ({
|
|
|
3041
3024
|
* Hook for accessing the ongoing connection.
|
|
3042
3025
|
*
|
|
3043
3026
|
* @public
|
|
3044
|
-
* @returns ongoing connection
|
|
3027
|
+
* @returns ongoing connection
|
|
3045
3028
|
*/
|
|
3046
3029
|
function useConnection() {
|
|
3047
3030
|
const ongoingConnection = useStore(selector$5, shallow);
|
|
@@ -3214,7 +3197,7 @@ const selector$1 = (s) => {
|
|
|
3214
3197
|
};
|
|
3215
3198
|
return {
|
|
3216
3199
|
viewBB,
|
|
3217
|
-
boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getNodesBounds(s.nodes, s.nodeOrigin), viewBB) : viewBB,
|
|
3200
|
+
boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getNodesBounds(s.nodes, { nodeOrigin: s.nodeOrigin }), viewBB) : viewBB,
|
|
3218
3201
|
rfId: s.rfId,
|
|
3219
3202
|
nodeOrigin: s.nodeOrigin,
|
|
3220
3203
|
panZoom: s.panZoom,
|
|
@@ -3227,7 +3210,7 @@ const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
|
|
|
3227
3210
|
function MiniMapComponent({ style, className, nodeStrokeColor, nodeColor, nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth,
|
|
3228
3211
|
// We need to rename the prop to be `CapitalCase` so that JSX will render it as
|
|
3229
3212
|
// a component properly.
|
|
3230
|
-
nodeComponent, maskColor, maskStrokeColor
|
|
3213
|
+
nodeComponent, bgColor, maskColor, maskStrokeColor, maskStrokeWidth, position = 'bottom-right', onClick, onNodeClick, pannable = false, zoomable = false, ariaLabel = 'React Flow mini map', inversePan, zoomStep = 10, offsetScale = 5, }) {
|
|
3231
3214
|
const store = useStoreApi();
|
|
3232
3215
|
const svg = useRef(null);
|
|
3233
3216
|
const { boundingRect, viewBB, rfId, panZoom, translateExtent, flowWidth, flowHeight } = useStore(selector$1, shallow);
|
|
@@ -3285,12 +3268,15 @@ nodeComponent, maskColor, maskStrokeColor = 'none', maskStrokeWidth = 1, positio
|
|
|
3285
3268
|
: undefined;
|
|
3286
3269
|
return (jsx(Panel, { position: position, style: {
|
|
3287
3270
|
...style,
|
|
3288
|
-
'--xy-minimap-
|
|
3271
|
+
'--xy-minimap-background-color-props': typeof bgColor === 'string' ? bgColor : undefined,
|
|
3272
|
+
'--xy-minimap-mask-background-color-props': typeof maskColor === 'string' ? maskColor : undefined,
|
|
3273
|
+
'--xy-minimap-mask-stroke-color-props': typeof maskStrokeColor === 'string' ? maskStrokeColor : undefined,
|
|
3274
|
+
'--xy-minimap-mask-stroke-width-props': typeof maskStrokeWidth === 'number' ? maskStrokeWidth * viewScale : undefined,
|
|
3289
3275
|
'--xy-minimap-node-background-color-props': typeof nodeColor === 'string' ? nodeColor : undefined,
|
|
3290
3276
|
'--xy-minimap-node-stroke-color-props': typeof nodeStrokeColor === 'string' ? nodeStrokeColor : undefined,
|
|
3291
3277
|
'--xy-minimap-node-stroke-width-props': typeof nodeStrokeWidth === 'string' ? nodeStrokeWidth : undefined,
|
|
3292
|
-
}, className: cc(['react-flow__minimap', className]), "data-testid": "rf__minimap", children: jsxs("svg", { width: elementWidth, height: elementHeight, viewBox: `${x} ${y} ${width} ${height}`, role: "img", "aria-labelledby": labelledBy, ref: svg, onClick: onSvgClick, children: [ariaLabel && jsx("title", { id: labelledBy, children: ariaLabel }), jsx(MiniMapNodes$1, { onClick: onSvgNodeClick, nodeColor: nodeColor, nodeStrokeColor: nodeStrokeColor, nodeBorderRadius: nodeBorderRadius, nodeClassName: nodeClassName, nodeStrokeWidth: nodeStrokeWidth, nodeComponent: nodeComponent }), jsx("path", { className: "react-flow__minimap-mask", d: `M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
|
3293
|
-
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`, fillRule: "evenodd",
|
|
3278
|
+
}, className: cc(['react-flow__minimap', className]), "data-testid": "rf__minimap", children: jsxs("svg", { width: elementWidth, height: elementHeight, viewBox: `${x} ${y} ${width} ${height}`, className: "react-flow__minimap-svg", role: "img", "aria-labelledby": labelledBy, ref: svg, onClick: onSvgClick, children: [ariaLabel && jsx("title", { id: labelledBy, children: ariaLabel }), jsx(MiniMapNodes$1, { onClick: onSvgNodeClick, nodeColor: nodeColor, nodeStrokeColor: nodeStrokeColor, nodeBorderRadius: nodeBorderRadius, nodeClassName: nodeClassName, nodeStrokeWidth: nodeStrokeWidth, nodeComponent: nodeComponent }), jsx("path", { className: "react-flow__minimap-mask", d: `M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
|
3279
|
+
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`, fillRule: "evenodd", pointerEvents: "none" })] }) }));
|
|
3294
3280
|
}
|
|
3295
3281
|
MiniMapComponent.displayName = 'MiniMap';
|
|
3296
3282
|
const MiniMap = memo(MiniMapComponent);
|
|
@@ -3320,7 +3306,7 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
|
|
|
3320
3306
|
snapToGrid,
|
|
3321
3307
|
};
|
|
3322
3308
|
},
|
|
3323
|
-
onChange: (change) => {
|
|
3309
|
+
onChange: (change, childChanges) => {
|
|
3324
3310
|
const { triggerNodeChanges } = store.getState();
|
|
3325
3311
|
const changes = [];
|
|
3326
3312
|
if (change.isXPosChange || change.isYPosChange) {
|
|
@@ -3346,6 +3332,13 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
|
|
|
3346
3332
|
};
|
|
3347
3333
|
changes.push(dimensionChange);
|
|
3348
3334
|
}
|
|
3335
|
+
for (const childChange of childChanges) {
|
|
3336
|
+
const positionChange = {
|
|
3337
|
+
...childChange,
|
|
3338
|
+
type: 'position',
|
|
3339
|
+
};
|
|
3340
|
+
changes.push(positionChange);
|
|
3341
|
+
}
|
|
3349
3342
|
triggerNodeChanges(changes);
|
|
3350
3343
|
},
|
|
3351
3344
|
onEnd: () => {
|
|
@@ -3450,7 +3443,7 @@ function NodeToolbar({ nodeId, children, className, style, isVisible, position =
|
|
|
3450
3443
|
if (!isActive || !nodes.length) {
|
|
3451
3444
|
return null;
|
|
3452
3445
|
}
|
|
3453
|
-
const nodeRect = getNodesBounds(nodes, nodeOrigin);
|
|
3446
|
+
const nodeRect = getNodesBounds(nodes, { nodeOrigin });
|
|
3454
3447
|
const zIndex = Math.max(...nodes.map((node) => (node[internalsSymbol]?.z || 1) + 1));
|
|
3455
3448
|
const wrapperStyle = {
|
|
3456
3449
|
position: 'absolute',
|