@xyflow/react 12.0.0-next.0
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/LICENSE +21 -0
- package/README.md +134 -0
- package/dist/base.css +364 -0
- package/dist/esm/additional-components/Background/Background.d.ts +9 -0
- package/dist/esm/additional-components/Background/Background.d.ts.map +1 -0
- package/dist/esm/additional-components/Background/Patterns.d.ts +15 -0
- package/dist/esm/additional-components/Background/Patterns.d.ts.map +1 -0
- package/dist/esm/additional-components/Background/index.d.ts +3 -0
- package/dist/esm/additional-components/Background/index.d.ts.map +1 -0
- package/dist/esm/additional-components/Background/types.d.ts +20 -0
- package/dist/esm/additional-components/Background/types.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/ControlButton.d.ts +5 -0
- package/dist/esm/additional-components/Controls/ControlButton.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/Controls.d.ts +5 -0
- package/dist/esm/additional-components/Controls/Controls.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/Icons/FitView.d.ts +3 -0
- package/dist/esm/additional-components/Controls/Icons/FitView.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/Icons/Lock.d.ts +3 -0
- package/dist/esm/additional-components/Controls/Icons/Lock.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/Icons/Minus.d.ts +3 -0
- package/dist/esm/additional-components/Controls/Icons/Minus.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/Icons/Plus.d.ts +3 -0
- package/dist/esm/additional-components/Controls/Icons/Plus.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/Icons/Unlock.d.ts +3 -0
- package/dist/esm/additional-components/Controls/Icons/Unlock.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/index.d.ts +4 -0
- package/dist/esm/additional-components/Controls/index.d.ts.map +1 -0
- package/dist/esm/additional-components/Controls/types.d.ts +16 -0
- package/dist/esm/additional-components/Controls/types.d.ts.map +1 -0
- package/dist/esm/additional-components/MiniMap/MiniMap.d.ts +9 -0
- package/dist/esm/additional-components/MiniMap/MiniMap.d.ts.map +1 -0
- package/dist/esm/additional-components/MiniMap/MiniMapNode.d.ts +6 -0
- package/dist/esm/additional-components/MiniMap/MiniMapNode.d.ts.map +1 -0
- package/dist/esm/additional-components/MiniMap/MiniMapNodes.d.ts +6 -0
- package/dist/esm/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -0
- package/dist/esm/additional-components/MiniMap/index.d.ts +3 -0
- package/dist/esm/additional-components/MiniMap/index.d.ts.map +1 -0
- package/dist/esm/additional-components/MiniMap/types.d.ts +44 -0
- package/dist/esm/additional-components/MiniMap/types.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeResizer/NodeResizer.d.ts +3 -0
- package/dist/esm/additional-components/NodeResizer/NodeResizer.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeResizer/ResizeControl.d.ts +7 -0
- package/dist/esm/additional-components/NodeResizer/ResizeControl.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeResizer/index.d.ts +4 -0
- package/dist/esm/additional-components/NodeResizer/index.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeResizer/types.d.ts +53 -0
- package/dist/esm/additional-components/NodeResizer/types.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeResizer/utils.d.ts +11 -0
- package/dist/esm/additional-components/NodeResizer/utils.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeToolbar/NodeToolbar.d.ts +4 -0
- package/dist/esm/additional-components/NodeToolbar/NodeToolbar.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeToolbar/NodeToolbarPortal.d.ts +6 -0
- package/dist/esm/additional-components/NodeToolbar/NodeToolbarPortal.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeToolbar/index.d.ts +3 -0
- package/dist/esm/additional-components/NodeToolbar/index.d.ts.map +1 -0
- package/dist/esm/additional-components/NodeToolbar/types.d.ts +10 -0
- package/dist/esm/additional-components/NodeToolbar/types.d.ts.map +1 -0
- package/dist/esm/additional-components/index.d.ts +6 -0
- package/dist/esm/additional-components/index.d.ts.map +1 -0
- package/dist/esm/components/A11yDescriptions/index.d.ts +9 -0
- package/dist/esm/components/A11yDescriptions/index.d.ts.map +1 -0
- package/dist/esm/components/Attribution/index.d.ts +8 -0
- package/dist/esm/components/Attribution/index.d.ts.map +1 -0
- package/dist/esm/components/ConnectionLine/index.d.ts +12 -0
- package/dist/esm/components/ConnectionLine/index.d.ts.map +1 -0
- package/dist/esm/components/EdgeLabelRenderer/index.d.ts +6 -0
- package/dist/esm/components/EdgeLabelRenderer/index.d.ts.map +1 -0
- package/dist/esm/components/EdgeWrapper/EdgeUpdateAnchors.d.ts +17 -0
- package/dist/esm/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -0
- package/dist/esm/components/EdgeWrapper/index.d.ts +9 -0
- package/dist/esm/components/EdgeWrapper/index.d.ts.map +1 -0
- package/dist/esm/components/EdgeWrapper/utils.d.ts +11 -0
- package/dist/esm/components/EdgeWrapper/utils.d.ts.map +1 -0
- package/dist/esm/components/Edges/BaseEdge.d.ts +7 -0
- package/dist/esm/components/Edges/BaseEdge.d.ts.map +1 -0
- package/dist/esm/components/Edges/BezierEdge.d.ts +6 -0
- package/dist/esm/components/Edges/BezierEdge.d.ts.map +1 -0
- package/dist/esm/components/Edges/EdgeAnchor.d.ts +14 -0
- package/dist/esm/components/Edges/EdgeAnchor.d.ts.map +1 -0
- package/dist/esm/components/Edges/EdgeText.d.ts +5 -0
- package/dist/esm/components/Edges/EdgeText.d.ts.map +1 -0
- package/dist/esm/components/Edges/SimpleBezierEdge.d.ts +16 -0
- package/dist/esm/components/Edges/SimpleBezierEdge.d.ts.map +1 -0
- package/dist/esm/components/Edges/SmoothStepEdge.d.ts +6 -0
- package/dist/esm/components/Edges/SmoothStepEdge.d.ts.map +1 -0
- package/dist/esm/components/Edges/StepEdge.d.ts +6 -0
- package/dist/esm/components/Edges/StepEdge.d.ts.map +1 -0
- package/dist/esm/components/Edges/StraightEdge.d.ts +6 -0
- package/dist/esm/components/Edges/StraightEdge.d.ts.map +1 -0
- package/dist/esm/components/Edges/index.d.ts +6 -0
- package/dist/esm/components/Edges/index.d.ts.map +1 -0
- package/dist/esm/components/Handle/index.d.ts +6 -0
- package/dist/esm/components/Handle/index.d.ts.map +1 -0
- package/dist/esm/components/NodeWrapper/index.d.ts +8 -0
- package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -0
- package/dist/esm/components/NodeWrapper/utils.d.ts +5 -0
- package/dist/esm/components/NodeWrapper/utils.d.ts.map +1 -0
- package/dist/esm/components/Nodes/DefaultNode.d.ts +8 -0
- package/dist/esm/components/Nodes/DefaultNode.d.ts.map +1 -0
- package/dist/esm/components/Nodes/GroupNode.d.ts +6 -0
- package/dist/esm/components/Nodes/GroupNode.d.ts.map +1 -0
- package/dist/esm/components/Nodes/InputNode.d.ts +8 -0
- package/dist/esm/components/Nodes/InputNode.d.ts.map +1 -0
- package/dist/esm/components/Nodes/OutputNode.d.ts +8 -0
- package/dist/esm/components/Nodes/OutputNode.d.ts.map +1 -0
- package/dist/esm/components/Nodes/utils.d.ts +13 -0
- package/dist/esm/components/Nodes/utils.d.ts.map +1 -0
- package/dist/esm/components/NodesSelection/index.d.ts +15 -0
- package/dist/esm/components/NodesSelection/index.d.ts.map +1 -0
- package/dist/esm/components/Panel/index.d.ts +9 -0
- package/dist/esm/components/Panel/index.d.ts.map +1 -0
- package/dist/esm/components/ReactFlowProvider/index.d.ts +15 -0
- package/dist/esm/components/ReactFlowProvider/index.d.ts.map +1 -0
- package/dist/esm/components/SelectionListener/index.d.ts +7 -0
- package/dist/esm/components/SelectionListener/index.d.ts.map +1 -0
- package/dist/esm/components/StoreUpdater/index.d.ts +9 -0
- package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -0
- package/dist/esm/components/UserSelection/index.d.ts +3 -0
- package/dist/esm/components/UserSelection/index.d.ts.map +1 -0
- package/dist/esm/components/ViewportPortal/index.d.ts +6 -0
- package/dist/esm/components/ViewportPortal/index.d.ts.map +1 -0
- package/dist/esm/container/EdgeRenderer/MarkerDefinitions.d.ts +11 -0
- package/dist/esm/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -0
- package/dist/esm/container/EdgeRenderer/MarkerSymbols.d.ts +9 -0
- package/dist/esm/container/EdgeRenderer/MarkerSymbols.d.ts.map +1 -0
- package/dist/esm/container/EdgeRenderer/index.d.ts +11 -0
- package/dist/esm/container/EdgeRenderer/index.d.ts.map +1 -0
- package/dist/esm/container/FlowRenderer/index.d.ts +12 -0
- package/dist/esm/container/FlowRenderer/index.d.ts.map +1 -0
- package/dist/esm/container/GraphView/index.d.ts +11 -0
- package/dist/esm/container/GraphView/index.d.ts.map +1 -0
- package/dist/esm/container/GraphView/useNodeOrEdgeTypesWarning.d.ts +5 -0
- package/dist/esm/container/GraphView/useNodeOrEdgeTypesWarning.d.ts.map +1 -0
- package/dist/esm/container/NodeRenderer/index.d.ts +9 -0
- package/dist/esm/container/NodeRenderer/index.d.ts.map +1 -0
- package/dist/esm/container/NodeRenderer/useResizeObserver.d.ts +2 -0
- package/dist/esm/container/NodeRenderer/useResizeObserver.d.ts.map +1 -0
- package/dist/esm/container/Pane/index.d.ts +12 -0
- package/dist/esm/container/Pane/index.d.ts.map +1 -0
- package/dist/esm/container/ReactFlow/Wrapper.d.ts +15 -0
- package/dist/esm/container/ReactFlow/Wrapper.d.ts.map +1 -0
- package/dist/esm/container/ReactFlow/index.d.ts +120 -0
- package/dist/esm/container/ReactFlow/index.d.ts.map +1 -0
- package/dist/esm/container/Viewport/index.d.ts +7 -0
- package/dist/esm/container/Viewport/index.d.ts.map +1 -0
- package/dist/esm/container/ZoomPane/index.d.ts +7 -0
- package/dist/esm/container/ZoomPane/index.d.ts.map +1 -0
- package/dist/esm/contexts/NodeIdContext.d.ts +7 -0
- package/dist/esm/contexts/NodeIdContext.d.ts.map +1 -0
- package/dist/esm/contexts/RFStoreContext.d.ts +5 -0
- package/dist/esm/contexts/RFStoreContext.d.ts.map +1 -0
- package/dist/esm/hooks/useColorModeClass.d.ts +9 -0
- package/dist/esm/hooks/useColorModeClass.d.ts.map +1 -0
- package/dist/esm/hooks/useConnection.d.ts +14 -0
- package/dist/esm/hooks/useConnection.d.ts.map +1 -0
- package/dist/esm/hooks/useDrag.d.ts +17 -0
- package/dist/esm/hooks/useDrag.d.ts.map +1 -0
- package/dist/esm/hooks/useEdges.d.ts +10 -0
- package/dist/esm/hooks/useEdges.d.ts.map +1 -0
- package/dist/esm/hooks/useGlobalKeyHandler.d.ts +12 -0
- package/dist/esm/hooks/useGlobalKeyHandler.d.ts.map +1 -0
- package/dist/esm/hooks/useHandleConnections.d.ts +22 -0
- package/dist/esm/hooks/useHandleConnections.d.ts.map +1 -0
- package/dist/esm/hooks/useKeyPress.d.ts +16 -0
- package/dist/esm/hooks/useKeyPress.d.ts.map +1 -0
- package/dist/esm/hooks/useNodes.d.ts +10 -0
- package/dist/esm/hooks/useNodes.d.ts.map +1 -0
- package/dist/esm/hooks/useNodesData.d.ts +13 -0
- package/dist/esm/hooks/useNodesData.d.ts.map +1 -0
- package/dist/esm/hooks/useNodesEdgesState.d.ts +19 -0
- package/dist/esm/hooks/useNodesEdgesState.d.ts.map +1 -0
- package/dist/esm/hooks/useNodesInitialized.d.ts +13 -0
- package/dist/esm/hooks/useNodesInitialized.d.ts.map +1 -0
- package/dist/esm/hooks/useOnInitHandler.d.ts +9 -0
- package/dist/esm/hooks/useOnInitHandler.d.ts.map +1 -0
- package/dist/esm/hooks/useOnSelectionChange.d.ts +13 -0
- package/dist/esm/hooks/useOnSelectionChange.d.ts.map +1 -0
- package/dist/esm/hooks/useOnViewportChange.d.ts +17 -0
- package/dist/esm/hooks/useOnViewportChange.d.ts.map +1 -0
- package/dist/esm/hooks/useReactFlow.d.ts +9 -0
- package/dist/esm/hooks/useReactFlow.d.ts.map +1 -0
- package/dist/esm/hooks/useResizeHandler.d.ts +9 -0
- package/dist/esm/hooks/useResizeHandler.d.ts.map +1 -0
- package/dist/esm/hooks/useStore.d.ts +22 -0
- package/dist/esm/hooks/useStore.d.ts.map +1 -0
- package/dist/esm/hooks/useUpdateNodeInternals.d.ts +10 -0
- package/dist/esm/hooks/useUpdateNodeInternals.d.ts.map +1 -0
- package/dist/esm/hooks/useUpdateNodePositions.d.ts +13 -0
- package/dist/esm/hooks/useUpdateNodePositions.d.ts.map +1 -0
- package/dist/esm/hooks/useViewport.d.ts +10 -0
- package/dist/esm/hooks/useViewport.d.ts.map +1 -0
- package/dist/esm/hooks/useViewportHelper.d.ts +10 -0
- package/dist/esm/hooks/useViewportHelper.d.ts.map +1 -0
- package/dist/esm/hooks/useViewportSync.d.ts +9 -0
- package/dist/esm/hooks/useViewportSync.d.ts.map +1 -0
- package/dist/esm/hooks/useVisibleEdgeIds.d.ts +10 -0
- package/dist/esm/hooks/useVisibleEdgeIds.d.ts.map +1 -0
- package/dist/esm/hooks/useVisibleNodeIds.d.ts +10 -0
- package/dist/esm/hooks/useVisibleNodeIds.d.ts.map +1 -0
- package/dist/esm/index.d.ts +35 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +3547 -0
- package/dist/esm/index.mjs +3547 -0
- package/dist/esm/store/index.d.ts +10 -0
- package/dist/esm/store/index.d.ts.map +1 -0
- package/dist/esm/store/initialState.d.ts +10 -0
- package/dist/esm/store/initialState.d.ts.map +1 -0
- package/dist/esm/store/utils.d.ts +12 -0
- package/dist/esm/store/utils.d.ts.map +1 -0
- package/dist/esm/styles/utils.d.ts +3 -0
- package/dist/esm/styles/utils.d.ts.map +1 -0
- package/dist/esm/types/changes.d.ts +50 -0
- package/dist/esm/types/changes.d.ts.map +1 -0
- package/dist/esm/types/component-props.d.ts +124 -0
- package/dist/esm/types/component-props.d.ts.map +1 -0
- package/dist/esm/types/edges.d.ts +148 -0
- package/dist/esm/types/edges.d.ts.map +1 -0
- package/dist/esm/types/general.d.ts +45 -0
- package/dist/esm/types/general.d.ts.map +1 -0
- package/dist/esm/types/index.d.ts +8 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/instance.d.ts +70 -0
- package/dist/esm/types/instance.d.ts.map +1 -0
- package/dist/esm/types/nodes.d.ts +39 -0
- package/dist/esm/types/nodes.d.ts.map +1 -0
- package/dist/esm/types/store.d.ts +105 -0
- package/dist/esm/types/store.d.ts.map +1 -0
- package/dist/esm/utils/changes.d.ts +47 -0
- package/dist/esm/utils/changes.d.ts.map +1 -0
- package/dist/esm/utils/general.d.ts +65 -0
- package/dist/esm/utils/general.d.ts.map +1 -0
- package/dist/esm/utils/index.d.ts +3 -0
- package/dist/esm/utils/index.d.ts.map +1 -0
- package/dist/style.css +560 -0
- package/dist/umd/additional-components/Background/Background.d.ts +9 -0
- package/dist/umd/additional-components/Background/Background.d.ts.map +1 -0
- package/dist/umd/additional-components/Background/Patterns.d.ts +15 -0
- package/dist/umd/additional-components/Background/Patterns.d.ts.map +1 -0
- package/dist/umd/additional-components/Background/index.d.ts +3 -0
- package/dist/umd/additional-components/Background/index.d.ts.map +1 -0
- package/dist/umd/additional-components/Background/types.d.ts +20 -0
- package/dist/umd/additional-components/Background/types.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/ControlButton.d.ts +5 -0
- package/dist/umd/additional-components/Controls/ControlButton.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/Controls.d.ts +5 -0
- package/dist/umd/additional-components/Controls/Controls.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/Icons/FitView.d.ts +3 -0
- package/dist/umd/additional-components/Controls/Icons/FitView.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/Icons/Lock.d.ts +3 -0
- package/dist/umd/additional-components/Controls/Icons/Lock.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/Icons/Minus.d.ts +3 -0
- package/dist/umd/additional-components/Controls/Icons/Minus.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/Icons/Plus.d.ts +3 -0
- package/dist/umd/additional-components/Controls/Icons/Plus.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/Icons/Unlock.d.ts +3 -0
- package/dist/umd/additional-components/Controls/Icons/Unlock.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/index.d.ts +4 -0
- package/dist/umd/additional-components/Controls/index.d.ts.map +1 -0
- package/dist/umd/additional-components/Controls/types.d.ts +16 -0
- package/dist/umd/additional-components/Controls/types.d.ts.map +1 -0
- package/dist/umd/additional-components/MiniMap/MiniMap.d.ts +9 -0
- package/dist/umd/additional-components/MiniMap/MiniMap.d.ts.map +1 -0
- package/dist/umd/additional-components/MiniMap/MiniMapNode.d.ts +6 -0
- package/dist/umd/additional-components/MiniMap/MiniMapNode.d.ts.map +1 -0
- package/dist/umd/additional-components/MiniMap/MiniMapNodes.d.ts +6 -0
- package/dist/umd/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -0
- package/dist/umd/additional-components/MiniMap/index.d.ts +3 -0
- package/dist/umd/additional-components/MiniMap/index.d.ts.map +1 -0
- package/dist/umd/additional-components/MiniMap/types.d.ts +44 -0
- package/dist/umd/additional-components/MiniMap/types.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeResizer/NodeResizer.d.ts +3 -0
- package/dist/umd/additional-components/NodeResizer/NodeResizer.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeResizer/ResizeControl.d.ts +7 -0
- package/dist/umd/additional-components/NodeResizer/ResizeControl.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeResizer/index.d.ts +4 -0
- package/dist/umd/additional-components/NodeResizer/index.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeResizer/types.d.ts +53 -0
- package/dist/umd/additional-components/NodeResizer/types.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeResizer/utils.d.ts +11 -0
- package/dist/umd/additional-components/NodeResizer/utils.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeToolbar/NodeToolbar.d.ts +4 -0
- package/dist/umd/additional-components/NodeToolbar/NodeToolbar.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeToolbar/NodeToolbarPortal.d.ts +6 -0
- package/dist/umd/additional-components/NodeToolbar/NodeToolbarPortal.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeToolbar/index.d.ts +3 -0
- package/dist/umd/additional-components/NodeToolbar/index.d.ts.map +1 -0
- package/dist/umd/additional-components/NodeToolbar/types.d.ts +10 -0
- package/dist/umd/additional-components/NodeToolbar/types.d.ts.map +1 -0
- package/dist/umd/additional-components/index.d.ts +6 -0
- package/dist/umd/additional-components/index.d.ts.map +1 -0
- package/dist/umd/components/A11yDescriptions/index.d.ts +9 -0
- package/dist/umd/components/A11yDescriptions/index.d.ts.map +1 -0
- package/dist/umd/components/Attribution/index.d.ts +8 -0
- package/dist/umd/components/Attribution/index.d.ts.map +1 -0
- package/dist/umd/components/ConnectionLine/index.d.ts +12 -0
- package/dist/umd/components/ConnectionLine/index.d.ts.map +1 -0
- package/dist/umd/components/EdgeLabelRenderer/index.d.ts +6 -0
- package/dist/umd/components/EdgeLabelRenderer/index.d.ts.map +1 -0
- package/dist/umd/components/EdgeWrapper/EdgeUpdateAnchors.d.ts +17 -0
- package/dist/umd/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -0
- package/dist/umd/components/EdgeWrapper/index.d.ts +9 -0
- package/dist/umd/components/EdgeWrapper/index.d.ts.map +1 -0
- package/dist/umd/components/EdgeWrapper/utils.d.ts +11 -0
- package/dist/umd/components/EdgeWrapper/utils.d.ts.map +1 -0
- package/dist/umd/components/Edges/BaseEdge.d.ts +7 -0
- package/dist/umd/components/Edges/BaseEdge.d.ts.map +1 -0
- package/dist/umd/components/Edges/BezierEdge.d.ts +6 -0
- package/dist/umd/components/Edges/BezierEdge.d.ts.map +1 -0
- package/dist/umd/components/Edges/EdgeAnchor.d.ts +14 -0
- package/dist/umd/components/Edges/EdgeAnchor.d.ts.map +1 -0
- package/dist/umd/components/Edges/EdgeText.d.ts +5 -0
- package/dist/umd/components/Edges/EdgeText.d.ts.map +1 -0
- package/dist/umd/components/Edges/SimpleBezierEdge.d.ts +16 -0
- package/dist/umd/components/Edges/SimpleBezierEdge.d.ts.map +1 -0
- package/dist/umd/components/Edges/SmoothStepEdge.d.ts +6 -0
- package/dist/umd/components/Edges/SmoothStepEdge.d.ts.map +1 -0
- package/dist/umd/components/Edges/StepEdge.d.ts +6 -0
- package/dist/umd/components/Edges/StepEdge.d.ts.map +1 -0
- package/dist/umd/components/Edges/StraightEdge.d.ts +6 -0
- package/dist/umd/components/Edges/StraightEdge.d.ts.map +1 -0
- package/dist/umd/components/Edges/index.d.ts +6 -0
- package/dist/umd/components/Edges/index.d.ts.map +1 -0
- package/dist/umd/components/Handle/index.d.ts +6 -0
- package/dist/umd/components/Handle/index.d.ts.map +1 -0
- package/dist/umd/components/NodeWrapper/index.d.ts +8 -0
- package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -0
- package/dist/umd/components/NodeWrapper/utils.d.ts +5 -0
- package/dist/umd/components/NodeWrapper/utils.d.ts.map +1 -0
- package/dist/umd/components/Nodes/DefaultNode.d.ts +8 -0
- package/dist/umd/components/Nodes/DefaultNode.d.ts.map +1 -0
- package/dist/umd/components/Nodes/GroupNode.d.ts +6 -0
- package/dist/umd/components/Nodes/GroupNode.d.ts.map +1 -0
- package/dist/umd/components/Nodes/InputNode.d.ts +8 -0
- package/dist/umd/components/Nodes/InputNode.d.ts.map +1 -0
- package/dist/umd/components/Nodes/OutputNode.d.ts +8 -0
- package/dist/umd/components/Nodes/OutputNode.d.ts.map +1 -0
- package/dist/umd/components/Nodes/utils.d.ts +13 -0
- package/dist/umd/components/Nodes/utils.d.ts.map +1 -0
- package/dist/umd/components/NodesSelection/index.d.ts +15 -0
- package/dist/umd/components/NodesSelection/index.d.ts.map +1 -0
- package/dist/umd/components/Panel/index.d.ts +9 -0
- package/dist/umd/components/Panel/index.d.ts.map +1 -0
- package/dist/umd/components/ReactFlowProvider/index.d.ts +15 -0
- package/dist/umd/components/ReactFlowProvider/index.d.ts.map +1 -0
- package/dist/umd/components/SelectionListener/index.d.ts +7 -0
- package/dist/umd/components/SelectionListener/index.d.ts.map +1 -0
- package/dist/umd/components/StoreUpdater/index.d.ts +9 -0
- package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -0
- package/dist/umd/components/UserSelection/index.d.ts +3 -0
- package/dist/umd/components/UserSelection/index.d.ts.map +1 -0
- package/dist/umd/components/ViewportPortal/index.d.ts +6 -0
- package/dist/umd/components/ViewportPortal/index.d.ts.map +1 -0
- package/dist/umd/container/EdgeRenderer/MarkerDefinitions.d.ts +11 -0
- package/dist/umd/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -0
- package/dist/umd/container/EdgeRenderer/MarkerSymbols.d.ts +9 -0
- package/dist/umd/container/EdgeRenderer/MarkerSymbols.d.ts.map +1 -0
- package/dist/umd/container/EdgeRenderer/index.d.ts +11 -0
- package/dist/umd/container/EdgeRenderer/index.d.ts.map +1 -0
- package/dist/umd/container/FlowRenderer/index.d.ts +12 -0
- package/dist/umd/container/FlowRenderer/index.d.ts.map +1 -0
- package/dist/umd/container/GraphView/index.d.ts +11 -0
- package/dist/umd/container/GraphView/index.d.ts.map +1 -0
- package/dist/umd/container/GraphView/useNodeOrEdgeTypesWarning.d.ts +5 -0
- package/dist/umd/container/GraphView/useNodeOrEdgeTypesWarning.d.ts.map +1 -0
- package/dist/umd/container/NodeRenderer/index.d.ts +9 -0
- package/dist/umd/container/NodeRenderer/index.d.ts.map +1 -0
- package/dist/umd/container/NodeRenderer/useResizeObserver.d.ts +2 -0
- package/dist/umd/container/NodeRenderer/useResizeObserver.d.ts.map +1 -0
- package/dist/umd/container/Pane/index.d.ts +12 -0
- package/dist/umd/container/Pane/index.d.ts.map +1 -0
- package/dist/umd/container/ReactFlow/Wrapper.d.ts +15 -0
- package/dist/umd/container/ReactFlow/Wrapper.d.ts.map +1 -0
- package/dist/umd/container/ReactFlow/index.d.ts +120 -0
- package/dist/umd/container/ReactFlow/index.d.ts.map +1 -0
- package/dist/umd/container/Viewport/index.d.ts +7 -0
- package/dist/umd/container/Viewport/index.d.ts.map +1 -0
- package/dist/umd/container/ZoomPane/index.d.ts +7 -0
- package/dist/umd/container/ZoomPane/index.d.ts.map +1 -0
- package/dist/umd/contexts/NodeIdContext.d.ts +7 -0
- package/dist/umd/contexts/NodeIdContext.d.ts.map +1 -0
- package/dist/umd/contexts/RFStoreContext.d.ts +5 -0
- package/dist/umd/contexts/RFStoreContext.d.ts.map +1 -0
- package/dist/umd/hooks/useColorModeClass.d.ts +9 -0
- package/dist/umd/hooks/useColorModeClass.d.ts.map +1 -0
- package/dist/umd/hooks/useConnection.d.ts +14 -0
- package/dist/umd/hooks/useConnection.d.ts.map +1 -0
- package/dist/umd/hooks/useDrag.d.ts +17 -0
- package/dist/umd/hooks/useDrag.d.ts.map +1 -0
- package/dist/umd/hooks/useEdges.d.ts +10 -0
- package/dist/umd/hooks/useEdges.d.ts.map +1 -0
- package/dist/umd/hooks/useGlobalKeyHandler.d.ts +12 -0
- package/dist/umd/hooks/useGlobalKeyHandler.d.ts.map +1 -0
- package/dist/umd/hooks/useHandleConnections.d.ts +22 -0
- package/dist/umd/hooks/useHandleConnections.d.ts.map +1 -0
- package/dist/umd/hooks/useKeyPress.d.ts +16 -0
- package/dist/umd/hooks/useKeyPress.d.ts.map +1 -0
- package/dist/umd/hooks/useNodes.d.ts +10 -0
- package/dist/umd/hooks/useNodes.d.ts.map +1 -0
- package/dist/umd/hooks/useNodesData.d.ts +13 -0
- package/dist/umd/hooks/useNodesData.d.ts.map +1 -0
- package/dist/umd/hooks/useNodesEdgesState.d.ts +19 -0
- package/dist/umd/hooks/useNodesEdgesState.d.ts.map +1 -0
- package/dist/umd/hooks/useNodesInitialized.d.ts +13 -0
- package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -0
- package/dist/umd/hooks/useOnInitHandler.d.ts +9 -0
- package/dist/umd/hooks/useOnInitHandler.d.ts.map +1 -0
- package/dist/umd/hooks/useOnSelectionChange.d.ts +13 -0
- package/dist/umd/hooks/useOnSelectionChange.d.ts.map +1 -0
- package/dist/umd/hooks/useOnViewportChange.d.ts +17 -0
- package/dist/umd/hooks/useOnViewportChange.d.ts.map +1 -0
- package/dist/umd/hooks/useReactFlow.d.ts +9 -0
- package/dist/umd/hooks/useReactFlow.d.ts.map +1 -0
- package/dist/umd/hooks/useResizeHandler.d.ts +9 -0
- package/dist/umd/hooks/useResizeHandler.d.ts.map +1 -0
- package/dist/umd/hooks/useStore.d.ts +22 -0
- package/dist/umd/hooks/useStore.d.ts.map +1 -0
- package/dist/umd/hooks/useUpdateNodeInternals.d.ts +10 -0
- package/dist/umd/hooks/useUpdateNodeInternals.d.ts.map +1 -0
- package/dist/umd/hooks/useUpdateNodePositions.d.ts +13 -0
- package/dist/umd/hooks/useUpdateNodePositions.d.ts.map +1 -0
- package/dist/umd/hooks/useViewport.d.ts +10 -0
- package/dist/umd/hooks/useViewport.d.ts.map +1 -0
- package/dist/umd/hooks/useViewportHelper.d.ts +10 -0
- package/dist/umd/hooks/useViewportHelper.d.ts.map +1 -0
- package/dist/umd/hooks/useViewportSync.d.ts +9 -0
- package/dist/umd/hooks/useViewportSync.d.ts.map +1 -0
- package/dist/umd/hooks/useVisibleEdgeIds.d.ts +10 -0
- package/dist/umd/hooks/useVisibleEdgeIds.d.ts.map +1 -0
- package/dist/umd/hooks/useVisibleNodeIds.d.ts +10 -0
- package/dist/umd/hooks/useVisibleNodeIds.d.ts.map +1 -0
- package/dist/umd/index.d.ts +35 -0
- package/dist/umd/index.d.ts.map +1 -0
- package/dist/umd/index.js +10 -0
- package/dist/umd/store/index.d.ts +10 -0
- package/dist/umd/store/index.d.ts.map +1 -0
- package/dist/umd/store/initialState.d.ts +10 -0
- package/dist/umd/store/initialState.d.ts.map +1 -0
- package/dist/umd/store/utils.d.ts +12 -0
- package/dist/umd/store/utils.d.ts.map +1 -0
- package/dist/umd/styles/utils.d.ts +3 -0
- package/dist/umd/styles/utils.d.ts.map +1 -0
- package/dist/umd/types/changes.d.ts +50 -0
- package/dist/umd/types/changes.d.ts.map +1 -0
- package/dist/umd/types/component-props.d.ts +124 -0
- package/dist/umd/types/component-props.d.ts.map +1 -0
- package/dist/umd/types/edges.d.ts +148 -0
- package/dist/umd/types/edges.d.ts.map +1 -0
- package/dist/umd/types/general.d.ts +45 -0
- package/dist/umd/types/general.d.ts.map +1 -0
- package/dist/umd/types/index.d.ts +8 -0
- package/dist/umd/types/index.d.ts.map +1 -0
- package/dist/umd/types/instance.d.ts +70 -0
- package/dist/umd/types/instance.d.ts.map +1 -0
- package/dist/umd/types/nodes.d.ts +39 -0
- package/dist/umd/types/nodes.d.ts.map +1 -0
- package/dist/umd/types/store.d.ts +105 -0
- package/dist/umd/types/store.d.ts.map +1 -0
- package/dist/umd/utils/changes.d.ts +47 -0
- package/dist/umd/utils/changes.d.ts.map +1 -0
- package/dist/umd/utils/general.d.ts +65 -0
- package/dist/umd/utils/general.d.ts.map +1 -0
- package/dist/umd/utils/index.d.ts +3 -0
- package/dist/umd/utils/index.d.ts.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,3547 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { createContext, useContext, useMemo, memo, useEffect, useRef, useState, useCallback, forwardRef } from 'react';
|
|
4
|
+
import cc from 'classcat';
|
|
5
|
+
import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getOutgoersBase, getIncomersBase, addEdgeBase, updateEdgeBase, getConnectedEdgesBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calcNextPosition, Position, isMouseEvent, XYHandle, getHostForElement, getNodesBounds, clampPosition, internalsSymbol, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionMode, ConnectionLineType, updateConnectionLookup, adoptUserProvidedNodes, updateNodeDimensions, updateAbsolutePositions, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, getNodePositionWithOrigin, XYMinimap, getBoundsOfRects, getPointerPosition, clamp, getNodeToolbarTransform } from '@xyflow/system';
|
|
6
|
+
export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, getBezierEdgeCenter, getBezierPath, getEdgeCenter, getNodesBounds, getSmoothStepPath, getStraightPath, getViewportForBounds, internalsSymbol } from '@xyflow/system';
|
|
7
|
+
import { useStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
|
|
8
|
+
import { shallow } from 'zustand/shallow';
|
|
9
|
+
import { createPortal } from 'react-dom';
|
|
10
|
+
import { drag } from 'd3-drag';
|
|
11
|
+
import { select } from 'd3-selection';
|
|
12
|
+
|
|
13
|
+
const StoreContext = createContext(null);
|
|
14
|
+
const Provider$1 = StoreContext.Provider;
|
|
15
|
+
|
|
16
|
+
const zustandErrorMessage = errorMessages['error001']();
|
|
17
|
+
/**
|
|
18
|
+
* Hook for accessing the internal store. Should only be used in rare cases.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
* @param selector
|
|
22
|
+
* @param equalityFn
|
|
23
|
+
* @returns The selected state slice
|
|
24
|
+
*/
|
|
25
|
+
function useStore(selector, equalityFn) {
|
|
26
|
+
const store = useContext(StoreContext);
|
|
27
|
+
if (store === null) {
|
|
28
|
+
throw new Error(zustandErrorMessage);
|
|
29
|
+
}
|
|
30
|
+
return useStoreWithEqualityFn(store, selector, equalityFn);
|
|
31
|
+
}
|
|
32
|
+
const useStoreApi = () => {
|
|
33
|
+
const store = useContext(StoreContext);
|
|
34
|
+
if (store === null) {
|
|
35
|
+
throw new Error(zustandErrorMessage);
|
|
36
|
+
}
|
|
37
|
+
return useMemo(() => ({
|
|
38
|
+
getState: store.getState,
|
|
39
|
+
setState: store.setState,
|
|
40
|
+
subscribe: store.subscribe,
|
|
41
|
+
destroy: store.destroy,
|
|
42
|
+
}), [store]);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const selector$q = (s) => (s.userSelectionActive ? 'none' : 'all');
|
|
46
|
+
function Panel({ position, children, className, style, ...rest }) {
|
|
47
|
+
const pointerEvents = useStore(selector$q);
|
|
48
|
+
const positionClasses = `${position}`.split('-');
|
|
49
|
+
return (jsx("div", { className: cc(['react-flow__panel', className, ...positionClasses]), style: { ...style, pointerEvents }, ...rest, children: children }));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function Attribution({ proOptions, position = 'bottom-right' }) {
|
|
53
|
+
if (proOptions?.hideAttribution) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
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" }) }));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const selector$p = (s) => ({
|
|
60
|
+
selectedNodes: s.nodes.filter((n) => n.selected),
|
|
61
|
+
selectedEdges: s.edges.filter((e) => e.selected),
|
|
62
|
+
});
|
|
63
|
+
const selectId = (obj) => obj.id;
|
|
64
|
+
function areEqual(a, b) {
|
|
65
|
+
return (shallow(a.selectedNodes.map(selectId), b.selectedNodes.map(selectId)) &&
|
|
66
|
+
shallow(a.selectedEdges.map(selectId), b.selectedEdges.map(selectId)));
|
|
67
|
+
}
|
|
68
|
+
const SelectionListener = memo(({ onSelectionChange }) => {
|
|
69
|
+
const store = useStoreApi();
|
|
70
|
+
const { selectedNodes, selectedEdges } = useStore(selector$p, areEqual);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const params = { nodes: selectedNodes, edges: selectedEdges };
|
|
73
|
+
onSelectionChange?.(params);
|
|
74
|
+
store.getState().onSelectionChangeHandlers.forEach((fn) => fn(params));
|
|
75
|
+
}, [selectedNodes, selectedEdges, onSelectionChange]);
|
|
76
|
+
return null;
|
|
77
|
+
});
|
|
78
|
+
SelectionListener.displayName = 'SelectionListener';
|
|
79
|
+
const changeSelector = (s) => !!s.onSelectionChangeHandlers;
|
|
80
|
+
function Wrapper$1({ onSelectionChange }) {
|
|
81
|
+
const storeHasSelectionChangeHandlers = useStore(changeSelector);
|
|
82
|
+
if (onSelectionChange || storeHasSelectionChangeHandlers) {
|
|
83
|
+
return jsx(SelectionListener, { onSelectionChange: onSelectionChange });
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* This component helps us to update the store with the vlues coming from the user.
|
|
90
|
+
* We distinguish between values we can update directly with `useDirectStoreUpdater` (like `snapGrid`)
|
|
91
|
+
* and values that have a dedicated setter function in the store (like `setNodes`).
|
|
92
|
+
*/
|
|
93
|
+
// these fields exist in the global store and we need to keep them up to date
|
|
94
|
+
const reactFlowFieldsToTrack = [
|
|
95
|
+
'nodes',
|
|
96
|
+
'edges',
|
|
97
|
+
'defaultNodes',
|
|
98
|
+
'defaultEdges',
|
|
99
|
+
'onConnect',
|
|
100
|
+
'onConnectStart',
|
|
101
|
+
'onConnectEnd',
|
|
102
|
+
'onClickConnectStart',
|
|
103
|
+
'onClickConnectEnd',
|
|
104
|
+
'nodesDraggable',
|
|
105
|
+
'nodesConnectable',
|
|
106
|
+
'nodesFocusable',
|
|
107
|
+
'edgesFocusable',
|
|
108
|
+
'edgesUpdatable',
|
|
109
|
+
'elevateNodesOnSelect',
|
|
110
|
+
'elevateEdgesOnSelect',
|
|
111
|
+
'minZoom',
|
|
112
|
+
'maxZoom',
|
|
113
|
+
'nodeExtent',
|
|
114
|
+
'onNodesChange',
|
|
115
|
+
'onEdgesChange',
|
|
116
|
+
'elementsSelectable',
|
|
117
|
+
'connectionMode',
|
|
118
|
+
'snapGrid',
|
|
119
|
+
'snapToGrid',
|
|
120
|
+
'translateExtent',
|
|
121
|
+
'connectOnClick',
|
|
122
|
+
'defaultEdgeOptions',
|
|
123
|
+
'fitView',
|
|
124
|
+
'fitViewOptions',
|
|
125
|
+
'onNodesDelete',
|
|
126
|
+
'onEdgesDelete',
|
|
127
|
+
'onDelete',
|
|
128
|
+
'onNodeDrag',
|
|
129
|
+
'onNodeDragStart',
|
|
130
|
+
'onNodeDragStop',
|
|
131
|
+
'onSelectionDrag',
|
|
132
|
+
'onSelectionDragStart',
|
|
133
|
+
'onSelectionDragStop',
|
|
134
|
+
'onMoveStart',
|
|
135
|
+
'onMove',
|
|
136
|
+
'onMoveEnd',
|
|
137
|
+
'noPanClassName',
|
|
138
|
+
'nodeOrigin',
|
|
139
|
+
'autoPanOnConnect',
|
|
140
|
+
'autoPanOnNodeDrag',
|
|
141
|
+
'onError',
|
|
142
|
+
'connectionRadius',
|
|
143
|
+
'isValidConnection',
|
|
144
|
+
'selectNodesOnDrag',
|
|
145
|
+
'nodeDragThreshold',
|
|
146
|
+
'onBeforeDelete',
|
|
147
|
+
];
|
|
148
|
+
// rfId doesn't exist in ReactFlowProps, but it's one of the fields we want to update
|
|
149
|
+
const fieldsToTrack = [...reactFlowFieldsToTrack, 'rfId'];
|
|
150
|
+
const selector$o = (s) => ({
|
|
151
|
+
setNodes: s.setNodes,
|
|
152
|
+
setEdges: s.setEdges,
|
|
153
|
+
setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
|
|
154
|
+
setMinZoom: s.setMinZoom,
|
|
155
|
+
setMaxZoom: s.setMaxZoom,
|
|
156
|
+
setTranslateExtent: s.setTranslateExtent,
|
|
157
|
+
setNodeExtent: s.setNodeExtent,
|
|
158
|
+
reset: s.reset,
|
|
159
|
+
});
|
|
160
|
+
const StoreUpdater = (props) => {
|
|
161
|
+
const { setNodes, setEdges, setDefaultNodesAndEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset, } = useStore(selector$o, shallow);
|
|
162
|
+
const store = useStoreApi();
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
const edgesWithDefaults = props.defaultEdges?.map((e) => ({ ...e, ...props.defaultEdgeOptions }));
|
|
165
|
+
setDefaultNodesAndEdges(props.defaultNodes, edgesWithDefaults);
|
|
166
|
+
return () => {
|
|
167
|
+
reset();
|
|
168
|
+
};
|
|
169
|
+
}, []);
|
|
170
|
+
const previousFields = useRef({
|
|
171
|
+
// these are values that are also passed directly to other components
|
|
172
|
+
// than the StoreUpdater. We can reduce the number of setStore calls
|
|
173
|
+
// by setting the same values here as prev fields.
|
|
174
|
+
translateExtent: infiniteExtent,
|
|
175
|
+
nodeOrigin: initNodeOrigin,
|
|
176
|
+
minZoom: 0.5,
|
|
177
|
+
maxZoom: 2,
|
|
178
|
+
elementsSelectable: true,
|
|
179
|
+
noPanClassName: 'nopan',
|
|
180
|
+
rfId: '1',
|
|
181
|
+
});
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
for (const fieldName of fieldsToTrack) {
|
|
184
|
+
const fieldValue = props[fieldName];
|
|
185
|
+
const previousFieldValue = previousFields.current[fieldName];
|
|
186
|
+
if (fieldValue === previousFieldValue)
|
|
187
|
+
continue;
|
|
188
|
+
if (typeof props[fieldName] === 'undefined')
|
|
189
|
+
continue;
|
|
190
|
+
// Custom handling with dedicated setters for some fields
|
|
191
|
+
if (fieldName === 'nodes')
|
|
192
|
+
setNodes(fieldValue);
|
|
193
|
+
else if (fieldName === 'edges')
|
|
194
|
+
setEdges(fieldValue);
|
|
195
|
+
else if (fieldName === 'minZoom')
|
|
196
|
+
setMinZoom(fieldValue);
|
|
197
|
+
else if (fieldName === 'maxZoom')
|
|
198
|
+
setMaxZoom(fieldValue);
|
|
199
|
+
else if (fieldName === 'translateExtent')
|
|
200
|
+
setTranslateExtent(fieldValue);
|
|
201
|
+
else if (fieldName === 'nodeExtent')
|
|
202
|
+
setNodeExtent(fieldValue);
|
|
203
|
+
// Renamed fields
|
|
204
|
+
else if (fieldName === 'fitView')
|
|
205
|
+
store.setState({ fitViewOnInit: fieldValue });
|
|
206
|
+
else if (fieldName === 'fitViewOptions')
|
|
207
|
+
store.setState({ fitViewOnInitOptions: fieldValue });
|
|
208
|
+
// General case
|
|
209
|
+
else
|
|
210
|
+
store.setState({ [fieldName]: fieldValue });
|
|
211
|
+
}
|
|
212
|
+
previousFields.current = props;
|
|
213
|
+
},
|
|
214
|
+
// Only re-run the effect if one of the fields we track changes
|
|
215
|
+
fieldsToTrack.map((fieldName) => props[fieldName]));
|
|
216
|
+
return null;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const style = { display: 'none' };
|
|
220
|
+
const ariaLiveStyle = {
|
|
221
|
+
position: 'absolute',
|
|
222
|
+
width: 1,
|
|
223
|
+
height: 1,
|
|
224
|
+
margin: -1,
|
|
225
|
+
border: 0,
|
|
226
|
+
padding: 0,
|
|
227
|
+
overflow: 'hidden',
|
|
228
|
+
clip: 'rect(0px, 0px, 0px, 0px)',
|
|
229
|
+
clipPath: 'inset(100%)',
|
|
230
|
+
};
|
|
231
|
+
const ARIA_NODE_DESC_KEY = 'react-flow__node-desc';
|
|
232
|
+
const ARIA_EDGE_DESC_KEY = 'react-flow__edge-desc';
|
|
233
|
+
const ARIA_LIVE_MESSAGE = 'react-flow__aria-live';
|
|
234
|
+
const selector$n = (s) => s.ariaLiveMessage;
|
|
235
|
+
function AriaLiveMessage({ rfId }) {
|
|
236
|
+
const ariaLiveMessage = useStore(selector$n);
|
|
237
|
+
return (jsx("div", { id: `${ARIA_LIVE_MESSAGE}-${rfId}`, "aria-live": "assertive", "aria-atomic": "true", style: ariaLiveStyle, children: ariaLiveMessage }));
|
|
238
|
+
}
|
|
239
|
+
function A11yDescriptions({ rfId, disableKeyboardA11y }) {
|
|
240
|
+
return (jsxs(Fragment, { children: [jsxs("div", { id: `${ARIA_NODE_DESC_KEY}-${rfId}`, style: style, children: ["Press enter or space to select a node.", !disableKeyboardA11y && 'You can then use the arrow keys to move the node around.', " Press delete to remove it and escape to cancel.", ' '] }), jsx("div", { id: `${ARIA_EDGE_DESC_KEY}-${rfId}`, style: style, children: "Press enter or space to select an edge. You can then press delete to remove it or escape to cancel." }), !disableKeyboardA11y && jsx(AriaLiveMessage, { rfId: rfId })] }));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const defaultDoc = typeof document !== 'undefined' ? document : null;
|
|
244
|
+
/**
|
|
245
|
+
* Hook for handling key events.
|
|
246
|
+
*
|
|
247
|
+
* @public
|
|
248
|
+
* @param param.keyCode - The key code (string or array of strings) to use
|
|
249
|
+
* @param param.options - Options
|
|
250
|
+
* @returns boolean
|
|
251
|
+
*/
|
|
252
|
+
var useKeyPress = (
|
|
253
|
+
// the keycode can be a string 'a' or an array of strings ['a', 'a+d']
|
|
254
|
+
// a string means a single key 'a' or a combination when '+' is used 'a+d'
|
|
255
|
+
// an array means different possibilites. Explainer: ['a', 'd+s'] here the
|
|
256
|
+
// user can use the single key 'a' or the combination 'd' + 's'
|
|
257
|
+
keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true }) => {
|
|
258
|
+
const [keyPressed, setKeyPressed] = useState(false);
|
|
259
|
+
// we need to remember if a modifier key is pressed in order to track it
|
|
260
|
+
const modifierPressed = useRef(false);
|
|
261
|
+
// we need to remember the pressed keys in order to support combinations
|
|
262
|
+
const pressedKeys = useRef(new Set([]));
|
|
263
|
+
// keyCodes = array with single keys [['a']] or key combinations [['a', 's']]
|
|
264
|
+
// keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft']
|
|
265
|
+
// used to check if we store event.code or event.key. When the code is in the list of keysToWatch
|
|
266
|
+
// we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft"
|
|
267
|
+
// and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when
|
|
268
|
+
// we can't find it in the list of keysToWatch.
|
|
269
|
+
const [keyCodes, keysToWatch] = useMemo(() => {
|
|
270
|
+
if (keyCode !== null) {
|
|
271
|
+
const keyCodeArr = Array.isArray(keyCode) ? keyCode : [keyCode];
|
|
272
|
+
const keys = keyCodeArr.filter((kc) => typeof kc === 'string').map((kc) => kc.split('+'));
|
|
273
|
+
const keysFlat = keys.reduce((res, item) => res.concat(...item), []);
|
|
274
|
+
return [keys, keysFlat];
|
|
275
|
+
}
|
|
276
|
+
return [[], []];
|
|
277
|
+
}, [keyCode]);
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
const target = options?.target || defaultDoc;
|
|
280
|
+
if (keyCode !== null) {
|
|
281
|
+
const downHandler = (event) => {
|
|
282
|
+
modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey;
|
|
283
|
+
const preventAction = (!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) &&
|
|
284
|
+
isInputDOMNode(event);
|
|
285
|
+
if (preventAction) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
const keyOrCode = useKeyOrCode(event.code, keysToWatch);
|
|
289
|
+
pressedKeys.current.add(event[keyOrCode]);
|
|
290
|
+
if (isMatchingKey(keyCodes, pressedKeys.current, false)) {
|
|
291
|
+
event.preventDefault();
|
|
292
|
+
setKeyPressed(true);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const upHandler = (event) => {
|
|
296
|
+
const preventAction = (!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) &&
|
|
297
|
+
isInputDOMNode(event);
|
|
298
|
+
if (preventAction) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
const keyOrCode = useKeyOrCode(event.code, keysToWatch);
|
|
302
|
+
if (isMatchingKey(keyCodes, pressedKeys.current, true)) {
|
|
303
|
+
setKeyPressed(false);
|
|
304
|
+
pressedKeys.current.clear();
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
pressedKeys.current.delete(event[keyOrCode]);
|
|
308
|
+
}
|
|
309
|
+
modifierPressed.current = false;
|
|
310
|
+
};
|
|
311
|
+
const resetHandler = () => {
|
|
312
|
+
pressedKeys.current.clear();
|
|
313
|
+
setKeyPressed(false);
|
|
314
|
+
};
|
|
315
|
+
target?.addEventListener('keydown', downHandler);
|
|
316
|
+
target?.addEventListener('keyup', upHandler);
|
|
317
|
+
window.addEventListener('blur', resetHandler);
|
|
318
|
+
return () => {
|
|
319
|
+
target?.removeEventListener('keydown', downHandler);
|
|
320
|
+
target?.removeEventListener('keyup', upHandler);
|
|
321
|
+
window.removeEventListener('blur', resetHandler);
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}, [keyCode, setKeyPressed]);
|
|
325
|
+
return keyPressed;
|
|
326
|
+
};
|
|
327
|
+
// utils
|
|
328
|
+
function isMatchingKey(keyCodes, pressedKeys, isUp) {
|
|
329
|
+
return (keyCodes
|
|
330
|
+
// we only want to compare same sizes of keyCode definitions
|
|
331
|
+
// and pressed keys. When the user specified 'Meta' as a key somewhere
|
|
332
|
+
// this would also be truthy without this filter when user presses 'Meta' + 'r'
|
|
333
|
+
.filter((keys) => isUp || keys.length === pressedKeys.size)
|
|
334
|
+
// since we want to support multiple possibilities only one of the
|
|
335
|
+
// combinations need to be part of the pressed keys
|
|
336
|
+
.some((keys) => keys.every((k) => pressedKeys.has(k))));
|
|
337
|
+
}
|
|
338
|
+
function useKeyOrCode(eventCode, keysToWatch) {
|
|
339
|
+
return keysToWatch.includes(eventCode) ? 'code' : 'key';
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const selector$m = (s) => !!s.panZoom;
|
|
343
|
+
/**
|
|
344
|
+
* Hook for getting viewport helper functions.
|
|
345
|
+
*
|
|
346
|
+
* @internal
|
|
347
|
+
* @returns viewport helper functions
|
|
348
|
+
*/
|
|
349
|
+
const useViewportHelper = () => {
|
|
350
|
+
const store = useStoreApi();
|
|
351
|
+
const panZoomInitialized = useStore(selector$m);
|
|
352
|
+
const viewportHelperFunctions = useMemo(() => {
|
|
353
|
+
return {
|
|
354
|
+
zoomIn: (options) => store.getState().panZoom?.scaleBy(1.2, { duration: options?.duration }),
|
|
355
|
+
zoomOut: (options) => store.getState().panZoom?.scaleBy(1 / 1.2, { duration: options?.duration }),
|
|
356
|
+
zoomTo: (zoomLevel, options) => store.getState().panZoom?.scaleTo(zoomLevel, { duration: options?.duration }),
|
|
357
|
+
getZoom: () => store.getState().transform[2],
|
|
358
|
+
setViewport: (viewport, options) => {
|
|
359
|
+
const { transform: [tX, tY, tZoom], panZoom, } = store.getState();
|
|
360
|
+
panZoom?.setViewport({
|
|
361
|
+
x: viewport.x ?? tX,
|
|
362
|
+
y: viewport.y ?? tY,
|
|
363
|
+
zoom: viewport.zoom ?? tZoom,
|
|
364
|
+
}, { duration: options?.duration });
|
|
365
|
+
},
|
|
366
|
+
getViewport: () => {
|
|
367
|
+
const [x, y, zoom] = store.getState().transform;
|
|
368
|
+
return { x, y, zoom };
|
|
369
|
+
},
|
|
370
|
+
fitView: (options) => {
|
|
371
|
+
const { nodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
|
|
372
|
+
return panZoom
|
|
373
|
+
? fitView({
|
|
374
|
+
nodes,
|
|
375
|
+
width,
|
|
376
|
+
height,
|
|
377
|
+
nodeOrigin,
|
|
378
|
+
minZoom,
|
|
379
|
+
maxZoom,
|
|
380
|
+
panZoom,
|
|
381
|
+
}, options)
|
|
382
|
+
: false;
|
|
383
|
+
},
|
|
384
|
+
setCenter: (x, y, options) => {
|
|
385
|
+
const { width, height, maxZoom, panZoom } = store.getState();
|
|
386
|
+
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
|
|
387
|
+
const centerX = width / 2 - x * nextZoom;
|
|
388
|
+
const centerY = height / 2 - y * nextZoom;
|
|
389
|
+
panZoom?.setViewport({
|
|
390
|
+
x: centerX,
|
|
391
|
+
y: centerY,
|
|
392
|
+
zoom: nextZoom,
|
|
393
|
+
}, { duration: options?.duration });
|
|
394
|
+
},
|
|
395
|
+
fitBounds: (bounds, options) => {
|
|
396
|
+
const { width, height, minZoom, maxZoom, panZoom } = store.getState();
|
|
397
|
+
const viewport = getViewportForBounds(bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1);
|
|
398
|
+
panZoom?.setViewport(viewport, { duration: options?.duration });
|
|
399
|
+
},
|
|
400
|
+
screenToFlowPosition: (position) => {
|
|
401
|
+
const { transform, snapToGrid, snapGrid, domNode } = store.getState();
|
|
402
|
+
if (!domNode) {
|
|
403
|
+
return position;
|
|
404
|
+
}
|
|
405
|
+
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
|
406
|
+
const correctedPosition = {
|
|
407
|
+
x: position.x - domX,
|
|
408
|
+
y: position.y - domY,
|
|
409
|
+
};
|
|
410
|
+
return pointToRendererPoint(correctedPosition, transform, snapToGrid, snapGrid || [1, 1]);
|
|
411
|
+
},
|
|
412
|
+
flowToScreenPosition: (position) => {
|
|
413
|
+
const { transform, domNode } = store.getState();
|
|
414
|
+
if (!domNode) {
|
|
415
|
+
return position;
|
|
416
|
+
}
|
|
417
|
+
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
|
418
|
+
const rendererPosition = rendererPointToPoint(position, transform);
|
|
419
|
+
return {
|
|
420
|
+
x: rendererPosition.x + domX,
|
|
421
|
+
y: rendererPosition.y + domY,
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
viewportInitialized: panZoomInitialized,
|
|
425
|
+
};
|
|
426
|
+
}, [panZoomInitialized]);
|
|
427
|
+
return viewportHelperFunctions;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
function handleParentExpand(res, updateItem) {
|
|
431
|
+
const parent = res.find((e) => e.id === updateItem.parentNode);
|
|
432
|
+
if (parent) {
|
|
433
|
+
if (!parent.computed) {
|
|
434
|
+
parent.computed = {};
|
|
435
|
+
}
|
|
436
|
+
const extendWidth = updateItem.position.x + updateItem.computed.width - parent.computed.width;
|
|
437
|
+
const extendHeight = updateItem.position.y + updateItem.computed.height - parent.computed.height;
|
|
438
|
+
if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
|
|
439
|
+
parent.style = { ...parent.style } || {};
|
|
440
|
+
parent.style.width = parent.style.width ?? parent.computed.width;
|
|
441
|
+
parent.style.height = parent.style.height ?? parent.computed.height;
|
|
442
|
+
if (extendWidth > 0) {
|
|
443
|
+
parent.style.width += extendWidth;
|
|
444
|
+
}
|
|
445
|
+
if (extendHeight > 0) {
|
|
446
|
+
parent.style.height += extendHeight;
|
|
447
|
+
}
|
|
448
|
+
if (updateItem.position.x < 0) {
|
|
449
|
+
const xDiff = Math.abs(updateItem.position.x);
|
|
450
|
+
parent.position.x = parent.position.x - xDiff;
|
|
451
|
+
parent.style.width += xDiff;
|
|
452
|
+
updateItem.position.x = 0;
|
|
453
|
+
}
|
|
454
|
+
if (updateItem.position.y < 0) {
|
|
455
|
+
const yDiff = Math.abs(updateItem.position.y);
|
|
456
|
+
parent.position.y = parent.position.y - yDiff;
|
|
457
|
+
parent.style.height += yDiff;
|
|
458
|
+
updateItem.position.y = 0;
|
|
459
|
+
}
|
|
460
|
+
parent.computed.width = parent.style.width;
|
|
461
|
+
parent.computed.height = parent.style.height;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// This function applies changes to nodes or edges that are triggered by React Flow internally.
|
|
466
|
+
// When you drag a node for example, React Flow will send a position change update.
|
|
467
|
+
// This function then applies the changes and returns the updated elements.
|
|
468
|
+
function applyChanges(changes, elements) {
|
|
469
|
+
// we need this hack to handle the setNodes and setEdges function of the useReactFlow hook for controlled flows
|
|
470
|
+
if (changes.some((c) => c.type === 'reset')) {
|
|
471
|
+
return changes.filter((c) => c.type === 'reset').map((c) => c.item);
|
|
472
|
+
}
|
|
473
|
+
let remainingChanges = changes;
|
|
474
|
+
const updatedElements = [];
|
|
475
|
+
for (const item of elements) {
|
|
476
|
+
const nextChanges = [];
|
|
477
|
+
const _remainingChanges = [];
|
|
478
|
+
for (const c of remainingChanges) {
|
|
479
|
+
if (c.type === 'add') {
|
|
480
|
+
updatedElements.push(c.item);
|
|
481
|
+
}
|
|
482
|
+
else if (c.id === item.id) {
|
|
483
|
+
nextChanges.push(c);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
_remainingChanges.push(c);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
remainingChanges = _remainingChanges;
|
|
490
|
+
if (nextChanges.length === 0) {
|
|
491
|
+
updatedElements.push(item);
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const updateItem = { ...item };
|
|
495
|
+
for (const currentChange of nextChanges) {
|
|
496
|
+
if (currentChange) {
|
|
497
|
+
switch (currentChange.type) {
|
|
498
|
+
case 'select': {
|
|
499
|
+
updateItem.selected = currentChange.selected;
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
case 'position': {
|
|
503
|
+
if (typeof currentChange.position !== 'undefined') {
|
|
504
|
+
updateItem.position = currentChange.position;
|
|
505
|
+
}
|
|
506
|
+
if (typeof currentChange.positionAbsolute !== 'undefined') {
|
|
507
|
+
if (!updateItem.computed) {
|
|
508
|
+
updateItem.computed = {};
|
|
509
|
+
}
|
|
510
|
+
updateItem.computed.positionAbsolute = currentChange.positionAbsolute;
|
|
511
|
+
}
|
|
512
|
+
if (typeof currentChange.dragging !== 'undefined') {
|
|
513
|
+
updateItem.dragging = currentChange.dragging;
|
|
514
|
+
}
|
|
515
|
+
if (updateItem.expandParent) {
|
|
516
|
+
handleParentExpand(updatedElements, updateItem);
|
|
517
|
+
}
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
case 'dimensions': {
|
|
521
|
+
if (typeof currentChange.dimensions !== 'undefined') {
|
|
522
|
+
if (!updateItem.computed) {
|
|
523
|
+
updateItem.computed = {};
|
|
524
|
+
}
|
|
525
|
+
updateItem.computed.width = currentChange.dimensions.width;
|
|
526
|
+
updateItem.computed.height = currentChange.dimensions.height;
|
|
527
|
+
}
|
|
528
|
+
if (typeof currentChange.updateStyle !== 'undefined') {
|
|
529
|
+
updateItem.style = { ...(updateItem.style || {}), ...currentChange.dimensions };
|
|
530
|
+
}
|
|
531
|
+
if (typeof currentChange.resizing === 'boolean') {
|
|
532
|
+
updateItem.resizing = currentChange.resizing;
|
|
533
|
+
}
|
|
534
|
+
if (updateItem.expandParent) {
|
|
535
|
+
handleParentExpand(updatedElements, updateItem);
|
|
536
|
+
}
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
case 'remove': {
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
updatedElements.push(updateItem);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return updatedElements;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Drop in function that applies node changes to an array of nodes.
|
|
551
|
+
* @public
|
|
552
|
+
* @remarks Various events on the <ReactFlow /> component can produce an {@link NodeChange} that describes how to update the edges of your flow in some way.
|
|
553
|
+
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
|
554
|
+
* @param changes - Array of changes to apply
|
|
555
|
+
* @param nodes - Array of nodes to apply the changes to
|
|
556
|
+
* @returns Array of updated nodes
|
|
557
|
+
* @example
|
|
558
|
+
* const onNodesChange = useCallback(
|
|
559
|
+
(changes) => {
|
|
560
|
+
setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
|
|
561
|
+
},
|
|
562
|
+
[setNodes],
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
return (
|
|
566
|
+
<ReactFLow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
|
|
567
|
+
);
|
|
568
|
+
*/
|
|
569
|
+
function applyNodeChanges(changes, nodes) {
|
|
570
|
+
return applyChanges(changes, nodes);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Drop in function that applies edge changes to an array of edges.
|
|
574
|
+
* @public
|
|
575
|
+
* @remarks Various events on the <ReactFlow /> component can produce an {@link EdgeChange} that describes how to update the edges of your flow in some way.
|
|
576
|
+
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
|
577
|
+
* @param changes - Array of changes to apply
|
|
578
|
+
* @param edges - Array of edge to apply the changes to
|
|
579
|
+
* @returns Array of updated edges
|
|
580
|
+
* @example
|
|
581
|
+
* const onEdgesChange = useCallback(
|
|
582
|
+
(changes) => {
|
|
583
|
+
setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
|
|
584
|
+
},
|
|
585
|
+
[setEdges],
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
return (
|
|
589
|
+
<ReactFLow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
|
|
590
|
+
);
|
|
591
|
+
*/
|
|
592
|
+
function applyEdgeChanges(changes, edges) {
|
|
593
|
+
return applyChanges(changes, edges);
|
|
594
|
+
}
|
|
595
|
+
const createSelectionChange = (id, selected) => ({
|
|
596
|
+
id,
|
|
597
|
+
type: 'select',
|
|
598
|
+
selected,
|
|
599
|
+
});
|
|
600
|
+
function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false) {
|
|
601
|
+
const changes = [];
|
|
602
|
+
for (const item of items) {
|
|
603
|
+
const willBeSelected = selectedIds.has(item.id);
|
|
604
|
+
// we don't want to set all items to selected=false on the first selection
|
|
605
|
+
if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) {
|
|
606
|
+
if (mutateItem) {
|
|
607
|
+
// this hack is needed for nodes. When the user dragged a node, it's selected.
|
|
608
|
+
// When another node gets dragged, we need to deselect the previous one,
|
|
609
|
+
// in order to have only one selected node at a time - the onNodesChange callback comes too late here :/
|
|
610
|
+
item.selected = willBeSelected;
|
|
611
|
+
}
|
|
612
|
+
changes.push(createSelectionChange(item.id, willBeSelected));
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return changes;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Test whether an object is useable as a Node
|
|
620
|
+
* @public
|
|
621
|
+
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true
|
|
622
|
+
* @param element - The element to test
|
|
623
|
+
* @returns A boolean indicating whether the element is an Node
|
|
624
|
+
*/
|
|
625
|
+
const isNode = (isNodeBase);
|
|
626
|
+
/**
|
|
627
|
+
* Test whether an object is useable as an Edge
|
|
628
|
+
* @public
|
|
629
|
+
* @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true
|
|
630
|
+
* @param element - The element to test
|
|
631
|
+
* @returns A boolean indicating whether the element is an Edge
|
|
632
|
+
*/
|
|
633
|
+
const isEdge = (isEdgeBase);
|
|
634
|
+
/**
|
|
635
|
+
* Pass in a node, and get connected nodes where edge.source === node.id
|
|
636
|
+
* @public
|
|
637
|
+
* @param node - The node to get the connected nodes from
|
|
638
|
+
* @param nodes - The array of all nodes
|
|
639
|
+
* @param edges - The array of all edges
|
|
640
|
+
* @returns An array of nodes that are connected over eges where the source is the given node
|
|
641
|
+
*/
|
|
642
|
+
const getOutgoers = (getOutgoersBase);
|
|
643
|
+
/**
|
|
644
|
+
* Pass in a node, and get connected nodes where edge.target === node.id
|
|
645
|
+
* @public
|
|
646
|
+
* @param node - The node to get the connected nodes from
|
|
647
|
+
* @param nodes - The array of all nodes
|
|
648
|
+
* @param edges - The array of all edges
|
|
649
|
+
* @returns An array of nodes that are connected over eges where the target is the given node
|
|
650
|
+
*/
|
|
651
|
+
const getIncomers = (getIncomersBase);
|
|
652
|
+
/**
|
|
653
|
+
* This util is a convenience function to add a new Edge to an array of edges
|
|
654
|
+
* @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
|
|
655
|
+
* @public
|
|
656
|
+
* @param edgeParams - Either an Edge or a Connection you want to add
|
|
657
|
+
* @param edges - The array of all current edges
|
|
658
|
+
* @returns A new array of edges with the new edge added
|
|
659
|
+
*/
|
|
660
|
+
const addEdge = (addEdgeBase);
|
|
661
|
+
/**
|
|
662
|
+
* A handy utility to update an existing Edge with new properties
|
|
663
|
+
* @param oldEdge - The edge you want to update
|
|
664
|
+
* @param newConnection - The new connection you want to update the edge with
|
|
665
|
+
* @param edges - The array of all current edges
|
|
666
|
+
* @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id
|
|
667
|
+
* @returns the updated edges array
|
|
668
|
+
*/
|
|
669
|
+
const updateEdge = (updateEdgeBase);
|
|
670
|
+
/**
|
|
671
|
+
* Get all connecting edges for a given set of nodes
|
|
672
|
+
* @param nodes - Nodes you want to get the connected edges for
|
|
673
|
+
* @param edges - All edges
|
|
674
|
+
* @returns Array of edges that connect any of the given nodes with each other
|
|
675
|
+
*/
|
|
676
|
+
const getConnectedEdges = (getConnectedEdgesBase);
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Hook for accessing the ReactFlow instance.
|
|
680
|
+
*
|
|
681
|
+
* @public
|
|
682
|
+
* @returns ReactFlowInstance
|
|
683
|
+
*/
|
|
684
|
+
function useReactFlow() {
|
|
685
|
+
const viewportHelper = useViewportHelper();
|
|
686
|
+
const store = useStoreApi();
|
|
687
|
+
const getNodes = useCallback(() => {
|
|
688
|
+
return store.getState().nodes.map((n) => ({ ...n }));
|
|
689
|
+
}, []);
|
|
690
|
+
const getNode = useCallback((id) => {
|
|
691
|
+
return store.getState().nodeLookup.get(id);
|
|
692
|
+
}, []);
|
|
693
|
+
const getEdges = useCallback(() => {
|
|
694
|
+
const { edges = [] } = store.getState();
|
|
695
|
+
return edges.map((e) => ({ ...e }));
|
|
696
|
+
}, []);
|
|
697
|
+
const getEdge = useCallback((id) => {
|
|
698
|
+
const { edges = [] } = store.getState();
|
|
699
|
+
return edges.find((e) => e.id === id);
|
|
700
|
+
}, []);
|
|
701
|
+
const setNodes = useCallback((payload) => {
|
|
702
|
+
const { nodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
|
|
703
|
+
const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
|
|
704
|
+
if (hasDefaultNodes) {
|
|
705
|
+
setNodes(nextNodes);
|
|
706
|
+
}
|
|
707
|
+
else if (onNodesChange) {
|
|
708
|
+
const changes = nextNodes.length === 0
|
|
709
|
+
? nodes.map((node) => ({ type: 'remove', id: node.id }))
|
|
710
|
+
: nextNodes.map((node) => ({ item: node, type: 'reset' }));
|
|
711
|
+
onNodesChange(changes);
|
|
712
|
+
}
|
|
713
|
+
}, []);
|
|
714
|
+
const setEdges = useCallback((payload) => {
|
|
715
|
+
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
|
716
|
+
const nextEdges = typeof payload === 'function' ? payload(edges) : payload;
|
|
717
|
+
if (hasDefaultEdges) {
|
|
718
|
+
setEdges(nextEdges);
|
|
719
|
+
}
|
|
720
|
+
else if (onEdgesChange) {
|
|
721
|
+
const changes = nextEdges.length === 0
|
|
722
|
+
? edges.map((edge) => ({ type: 'remove', id: edge.id }))
|
|
723
|
+
: nextEdges.map((edge) => ({ item: edge, type: 'reset' }));
|
|
724
|
+
onEdgesChange(changes);
|
|
725
|
+
}
|
|
726
|
+
}, []);
|
|
727
|
+
const addNodes = useCallback((payload) => {
|
|
728
|
+
const nodes = Array.isArray(payload) ? payload : [payload];
|
|
729
|
+
const { nodes: currentNodes, hasDefaultNodes, onNodesChange, setNodes } = store.getState();
|
|
730
|
+
if (hasDefaultNodes) {
|
|
731
|
+
const nextNodes = [...currentNodes, ...nodes];
|
|
732
|
+
setNodes(nextNodes);
|
|
733
|
+
}
|
|
734
|
+
else if (onNodesChange) {
|
|
735
|
+
const changes = nodes.map((node) => ({ item: node, type: 'add' }));
|
|
736
|
+
onNodesChange(changes);
|
|
737
|
+
}
|
|
738
|
+
}, []);
|
|
739
|
+
const addEdges = useCallback((payload) => {
|
|
740
|
+
const nextEdges = Array.isArray(payload) ? payload : [payload];
|
|
741
|
+
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
|
742
|
+
if (hasDefaultEdges) {
|
|
743
|
+
setEdges([...edges, ...nextEdges]);
|
|
744
|
+
}
|
|
745
|
+
else if (onEdgesChange) {
|
|
746
|
+
const changes = nextEdges.map((edge) => ({ item: edge, type: 'add' }));
|
|
747
|
+
onEdgesChange(changes);
|
|
748
|
+
}
|
|
749
|
+
}, []);
|
|
750
|
+
const toObject = useCallback(() => {
|
|
751
|
+
const { nodes = [], edges = [], transform } = store.getState();
|
|
752
|
+
const [x, y, zoom] = transform;
|
|
753
|
+
return {
|
|
754
|
+
nodes: nodes.map((n) => ({ ...n })),
|
|
755
|
+
edges: edges.map((e) => ({ ...e })),
|
|
756
|
+
viewport: {
|
|
757
|
+
x,
|
|
758
|
+
y,
|
|
759
|
+
zoom,
|
|
760
|
+
},
|
|
761
|
+
};
|
|
762
|
+
}, []);
|
|
763
|
+
const deleteElements = useCallback(async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [], onBeforeDelete }) => {
|
|
764
|
+
const { nodes, edges, hasDefaultNodes, hasDefaultEdges, onNodesDelete, onEdgesDelete, onNodesChange, onEdgesChange, onDelete, } = store.getState();
|
|
765
|
+
const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
|
|
766
|
+
nodesToRemove,
|
|
767
|
+
edgesToRemove,
|
|
768
|
+
nodes,
|
|
769
|
+
edges,
|
|
770
|
+
onBeforeDelete,
|
|
771
|
+
});
|
|
772
|
+
const hasMatchingEdges = matchingEdges.length > 0;
|
|
773
|
+
const hasMatchingNodes = matchingNodes.length > 0;
|
|
774
|
+
if (hasMatchingEdges) {
|
|
775
|
+
if (hasDefaultEdges) {
|
|
776
|
+
store.setState({
|
|
777
|
+
edges: edges.filter((e) => !matchingEdges.some((mE) => mE.id === e.id)),
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
onEdgesDelete?.(matchingEdges);
|
|
781
|
+
onEdgesChange?.(matchingEdges.map((edge) => ({
|
|
782
|
+
id: edge.id,
|
|
783
|
+
type: 'remove',
|
|
784
|
+
})));
|
|
785
|
+
}
|
|
786
|
+
if (hasMatchingNodes) {
|
|
787
|
+
if (hasDefaultNodes) {
|
|
788
|
+
store.setState({
|
|
789
|
+
nodes: nodes.filter((n) => !matchingNodes.some((mN) => mN.id === n.id)),
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
onNodesDelete?.(matchingNodes);
|
|
793
|
+
onNodesChange?.(matchingNodes.map((node) => ({ id: node.id, type: 'remove' })));
|
|
794
|
+
}
|
|
795
|
+
if (hasMatchingNodes || hasMatchingEdges) {
|
|
796
|
+
onDelete?.({ nodes: matchingNodes, edges: matchingEdges });
|
|
797
|
+
}
|
|
798
|
+
return { deletedNodes: matchingNodes, deletedEdges: matchingEdges };
|
|
799
|
+
}, []);
|
|
800
|
+
const getNodeRect = useCallback((nodeOrRect) => {
|
|
801
|
+
const isRect = isRectObject(nodeOrRect);
|
|
802
|
+
const node = isRect ? null : store.getState().nodeLookup.get(nodeOrRect.id);
|
|
803
|
+
const nodeRect = isRect ? nodeOrRect : nodeToRect(node);
|
|
804
|
+
return [nodeRect, node, isRect];
|
|
805
|
+
}, []);
|
|
806
|
+
const getIntersectingNodes = useCallback((nodeOrRect, partially = true, nodes) => {
|
|
807
|
+
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
|
|
808
|
+
if (!nodeRect) {
|
|
809
|
+
return [];
|
|
810
|
+
}
|
|
811
|
+
return (nodes || store.getState().nodes).filter((n) => {
|
|
812
|
+
if (!isRect && (n.id === node.id || !n.computed?.positionAbsolute)) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
const currNodeRect = nodeToRect(n);
|
|
816
|
+
const overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
|
|
817
|
+
const partiallyVisible = partially && overlappingArea > 0;
|
|
818
|
+
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
819
|
+
});
|
|
820
|
+
}, []);
|
|
821
|
+
const isNodeIntersecting = useCallback((nodeOrRect, area, partially = true) => {
|
|
822
|
+
const [nodeRect] = getNodeRect(nodeOrRect);
|
|
823
|
+
if (!nodeRect) {
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
const overlappingArea = getOverlappingArea(nodeRect, area);
|
|
827
|
+
const partiallyVisible = partially && overlappingArea > 0;
|
|
828
|
+
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
829
|
+
}, []);
|
|
830
|
+
const updateNode = useCallback((id, nodeUpdate, options = { replace: true }) => {
|
|
831
|
+
setNodes((prevNodes) => prevNodes.map((node) => {
|
|
832
|
+
if (node.id === id) {
|
|
833
|
+
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node) : nodeUpdate;
|
|
834
|
+
return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode };
|
|
835
|
+
}
|
|
836
|
+
return node;
|
|
837
|
+
}));
|
|
838
|
+
}, [setNodes]);
|
|
839
|
+
const updateNodeData = useCallback((id, dataUpdate, options = { replace: false }) => {
|
|
840
|
+
updateNode(id, (node) => {
|
|
841
|
+
const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate;
|
|
842
|
+
return options.replace ? { ...node, data: nextData } : { ...node, data: { ...node.data, ...nextData } };
|
|
843
|
+
}, options);
|
|
844
|
+
}, [updateNode]);
|
|
845
|
+
return useMemo(() => {
|
|
846
|
+
return {
|
|
847
|
+
...viewportHelper,
|
|
848
|
+
getNodes,
|
|
849
|
+
getNode,
|
|
850
|
+
getEdges,
|
|
851
|
+
getEdge,
|
|
852
|
+
setNodes,
|
|
853
|
+
setEdges,
|
|
854
|
+
addNodes,
|
|
855
|
+
addEdges,
|
|
856
|
+
toObject,
|
|
857
|
+
deleteElements,
|
|
858
|
+
getIntersectingNodes,
|
|
859
|
+
isNodeIntersecting,
|
|
860
|
+
updateNode,
|
|
861
|
+
updateNodeData,
|
|
862
|
+
};
|
|
863
|
+
}, [
|
|
864
|
+
viewportHelper,
|
|
865
|
+
getNodes,
|
|
866
|
+
getNode,
|
|
867
|
+
getEdges,
|
|
868
|
+
getEdge,
|
|
869
|
+
setNodes,
|
|
870
|
+
setEdges,
|
|
871
|
+
addNodes,
|
|
872
|
+
addEdges,
|
|
873
|
+
toObject,
|
|
874
|
+
deleteElements,
|
|
875
|
+
getIntersectingNodes,
|
|
876
|
+
isNodeIntersecting,
|
|
877
|
+
updateNode,
|
|
878
|
+
updateNodeData,
|
|
879
|
+
]);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const selected = (item) => item.selected;
|
|
883
|
+
const deleteKeyOptions = { actInsideInputWithModifier: false };
|
|
884
|
+
/**
|
|
885
|
+
* Hook for handling global key events.
|
|
886
|
+
*
|
|
887
|
+
* @internal
|
|
888
|
+
*/
|
|
889
|
+
var useGlobalKeyHandler = ({ deleteKeyCode, multiSelectionKeyCode, }) => {
|
|
890
|
+
const store = useStoreApi();
|
|
891
|
+
const { deleteElements } = useReactFlow();
|
|
892
|
+
const deleteKeyPressed = useKeyPress(deleteKeyCode, deleteKeyOptions);
|
|
893
|
+
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
|
|
894
|
+
useEffect(() => {
|
|
895
|
+
if (deleteKeyPressed) {
|
|
896
|
+
const { edges, nodes, onBeforeDelete } = store.getState();
|
|
897
|
+
deleteElements({ nodes: nodes.filter(selected), edges: edges.filter(selected), onBeforeDelete });
|
|
898
|
+
store.setState({ nodesSelectionActive: false });
|
|
899
|
+
}
|
|
900
|
+
}, [deleteKeyPressed]);
|
|
901
|
+
useEffect(() => {
|
|
902
|
+
store.setState({ multiSelectionActive: multiSelectionKeyPressed });
|
|
903
|
+
}, [multiSelectionKeyPressed]);
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Hook for handling resize events.
|
|
908
|
+
*
|
|
909
|
+
* @internal
|
|
910
|
+
*/
|
|
911
|
+
function useResizeHandler(domNode) {
|
|
912
|
+
const store = useStoreApi();
|
|
913
|
+
useEffect(() => {
|
|
914
|
+
const updateDimensions = () => {
|
|
915
|
+
if (!domNode.current) {
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
const size = getDimensions(domNode.current);
|
|
919
|
+
if (size.height === 0 || size.width === 0) {
|
|
920
|
+
store.getState().onError?.('004', errorMessages['error004']());
|
|
921
|
+
}
|
|
922
|
+
store.setState({ width: size.width || 500, height: size.height || 500 });
|
|
923
|
+
};
|
|
924
|
+
if (domNode.current) {
|
|
925
|
+
updateDimensions();
|
|
926
|
+
window.addEventListener('resize', updateDimensions);
|
|
927
|
+
const resizeObserver = new ResizeObserver(() => updateDimensions());
|
|
928
|
+
resizeObserver.observe(domNode.current);
|
|
929
|
+
return () => {
|
|
930
|
+
window.removeEventListener('resize', updateDimensions);
|
|
931
|
+
if (resizeObserver && domNode.current) {
|
|
932
|
+
resizeObserver.unobserve(domNode.current);
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
}, []);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
const containerStyle = {
|
|
940
|
+
position: 'absolute',
|
|
941
|
+
width: '100%',
|
|
942
|
+
height: '100%',
|
|
943
|
+
top: 0,
|
|
944
|
+
left: 0,
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
const selector$l = (s) => ({
|
|
948
|
+
userSelectionActive: s.userSelectionActive,
|
|
949
|
+
lib: s.lib,
|
|
950
|
+
});
|
|
951
|
+
const ZoomPane = ({ onPaneContextMenu, zoomOnScroll = true, zoomOnPinch = true, panOnScroll = false, panOnScrollSpeed = 0.5, panOnScrollMode = PanOnScrollMode.Free, zoomOnDoubleClick = true, panOnDrag = true, defaultViewport, translateExtent, minZoom, maxZoom, zoomActivationKeyCode, preventScrolling = true, children, noWheelClassName, noPanClassName, onViewportChange, isControlledViewport, }) => {
|
|
952
|
+
const store = useStoreApi();
|
|
953
|
+
const zoomPane = useRef(null);
|
|
954
|
+
const { userSelectionActive, lib } = useStore(selector$l, shallow);
|
|
955
|
+
const zoomActivationKeyPressed = useKeyPress(zoomActivationKeyCode);
|
|
956
|
+
const panZoom = useRef();
|
|
957
|
+
useResizeHandler(zoomPane);
|
|
958
|
+
useEffect(() => {
|
|
959
|
+
if (zoomPane.current) {
|
|
960
|
+
panZoom.current = XYPanZoom({
|
|
961
|
+
domNode: zoomPane.current,
|
|
962
|
+
minZoom,
|
|
963
|
+
maxZoom,
|
|
964
|
+
translateExtent,
|
|
965
|
+
viewport: defaultViewport,
|
|
966
|
+
onTransformChange: (transform) => {
|
|
967
|
+
onViewportChange?.({ x: transform[0], y: transform[1], zoom: transform[2] });
|
|
968
|
+
if (!isControlledViewport) {
|
|
969
|
+
store.setState({ transform });
|
|
970
|
+
}
|
|
971
|
+
},
|
|
972
|
+
onDraggingChange: (paneDragging) => store.setState({ paneDragging }),
|
|
973
|
+
onPanZoomStart: (event, vp) => {
|
|
974
|
+
const { onViewportChangeStart, onMoveStart } = store.getState();
|
|
975
|
+
onMoveStart?.(event, vp);
|
|
976
|
+
onViewportChangeStart?.(vp);
|
|
977
|
+
},
|
|
978
|
+
onPanZoom: (event, vp) => {
|
|
979
|
+
const { onViewportChange, onMove } = store.getState();
|
|
980
|
+
onMove?.(event, vp);
|
|
981
|
+
onViewportChange?.(vp);
|
|
982
|
+
},
|
|
983
|
+
onPanZoomEnd: (event, vp) => {
|
|
984
|
+
const { onViewportChangeEnd, onMoveEnd } = store.getState();
|
|
985
|
+
onMoveEnd?.(event, vp);
|
|
986
|
+
onViewportChangeEnd?.(vp);
|
|
987
|
+
},
|
|
988
|
+
});
|
|
989
|
+
const { x, y, zoom } = panZoom.current.getViewport();
|
|
990
|
+
store.setState({
|
|
991
|
+
panZoom: panZoom.current,
|
|
992
|
+
transform: [x, y, zoom],
|
|
993
|
+
domNode: zoomPane.current.closest('.react-flow'),
|
|
994
|
+
});
|
|
995
|
+
return () => {
|
|
996
|
+
panZoom.current?.destroy();
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
}, []);
|
|
1000
|
+
useEffect(() => {
|
|
1001
|
+
panZoom.current?.update({
|
|
1002
|
+
onPaneContextMenu,
|
|
1003
|
+
zoomOnScroll,
|
|
1004
|
+
zoomOnPinch,
|
|
1005
|
+
panOnScroll,
|
|
1006
|
+
panOnScrollSpeed,
|
|
1007
|
+
panOnScrollMode,
|
|
1008
|
+
zoomOnDoubleClick,
|
|
1009
|
+
panOnDrag,
|
|
1010
|
+
zoomActivationKeyPressed,
|
|
1011
|
+
preventScrolling,
|
|
1012
|
+
noPanClassName,
|
|
1013
|
+
userSelectionActive,
|
|
1014
|
+
noWheelClassName,
|
|
1015
|
+
lib,
|
|
1016
|
+
});
|
|
1017
|
+
}, [
|
|
1018
|
+
onPaneContextMenu,
|
|
1019
|
+
zoomOnScroll,
|
|
1020
|
+
zoomOnPinch,
|
|
1021
|
+
panOnScroll,
|
|
1022
|
+
panOnScrollSpeed,
|
|
1023
|
+
panOnScrollMode,
|
|
1024
|
+
zoomOnDoubleClick,
|
|
1025
|
+
panOnDrag,
|
|
1026
|
+
zoomActivationKeyPressed,
|
|
1027
|
+
preventScrolling,
|
|
1028
|
+
noPanClassName,
|
|
1029
|
+
userSelectionActive,
|
|
1030
|
+
noWheelClassName,
|
|
1031
|
+
lib,
|
|
1032
|
+
]);
|
|
1033
|
+
return (jsx("div", { className: "react-flow__renderer", ref: zoomPane, style: containerStyle, children: children }));
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
const selector$k = (s) => ({
|
|
1037
|
+
userSelectionActive: s.userSelectionActive,
|
|
1038
|
+
userSelectionRect: s.userSelectionRect,
|
|
1039
|
+
});
|
|
1040
|
+
function UserSelection() {
|
|
1041
|
+
const { userSelectionActive, userSelectionRect } = useStore(selector$k, shallow);
|
|
1042
|
+
const isActive = userSelectionActive && userSelectionRect;
|
|
1043
|
+
if (!isActive) {
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
return (jsx("div", { className: "react-flow__selection react-flow__container", style: {
|
|
1047
|
+
width: userSelectionRect.width,
|
|
1048
|
+
height: userSelectionRect.height,
|
|
1049
|
+
transform: `translate(${userSelectionRect.x}px, ${userSelectionRect.y}px)`,
|
|
1050
|
+
} }));
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const wrapHandler = (handler, containerRef) => {
|
|
1054
|
+
return (event) => {
|
|
1055
|
+
if (event.target !== containerRef.current) {
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
handler?.(event);
|
|
1059
|
+
};
|
|
1060
|
+
};
|
|
1061
|
+
const selector$j = (s) => ({
|
|
1062
|
+
userSelectionActive: s.userSelectionActive,
|
|
1063
|
+
elementsSelectable: s.elementsSelectable,
|
|
1064
|
+
dragging: s.paneDragging,
|
|
1065
|
+
});
|
|
1066
|
+
const Pane = memo(({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSelectionStart, onSelectionEnd, onPaneClick, onPaneContextMenu, onPaneScroll, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, children, }) => {
|
|
1067
|
+
const container = useRef(null);
|
|
1068
|
+
const store = useStoreApi();
|
|
1069
|
+
const prevSelectedNodesCount = useRef(0);
|
|
1070
|
+
const prevSelectedEdgesCount = useRef(0);
|
|
1071
|
+
const containerBounds = useRef();
|
|
1072
|
+
const { userSelectionActive, elementsSelectable, dragging } = useStore(selector$j, shallow);
|
|
1073
|
+
const resetUserSelection = () => {
|
|
1074
|
+
store.setState({ userSelectionActive: false, userSelectionRect: null });
|
|
1075
|
+
prevSelectedNodesCount.current = 0;
|
|
1076
|
+
prevSelectedEdgesCount.current = 0;
|
|
1077
|
+
};
|
|
1078
|
+
const onClick = (event) => {
|
|
1079
|
+
onPaneClick?.(event);
|
|
1080
|
+
store.getState().resetSelectedElements();
|
|
1081
|
+
store.setState({ nodesSelectionActive: false });
|
|
1082
|
+
};
|
|
1083
|
+
const onContextMenu = (event) => {
|
|
1084
|
+
if (Array.isArray(panOnDrag) && panOnDrag?.includes(2)) {
|
|
1085
|
+
event.preventDefault();
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
onPaneContextMenu?.(event);
|
|
1089
|
+
};
|
|
1090
|
+
const onWheel = onPaneScroll ? (event) => onPaneScroll(event) : undefined;
|
|
1091
|
+
const onMouseDown = (event) => {
|
|
1092
|
+
const { resetSelectedElements, domNode } = store.getState();
|
|
1093
|
+
containerBounds.current = domNode?.getBoundingClientRect();
|
|
1094
|
+
if (!elementsSelectable ||
|
|
1095
|
+
!isSelecting ||
|
|
1096
|
+
event.button !== 0 ||
|
|
1097
|
+
event.target !== container.current ||
|
|
1098
|
+
!containerBounds.current) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current);
|
|
1102
|
+
resetSelectedElements();
|
|
1103
|
+
store.setState({
|
|
1104
|
+
userSelectionRect: {
|
|
1105
|
+
width: 0,
|
|
1106
|
+
height: 0,
|
|
1107
|
+
startX: x,
|
|
1108
|
+
startY: y,
|
|
1109
|
+
x,
|
|
1110
|
+
y,
|
|
1111
|
+
},
|
|
1112
|
+
});
|
|
1113
|
+
onSelectionStart?.(event);
|
|
1114
|
+
};
|
|
1115
|
+
const onMouseMove = (event) => {
|
|
1116
|
+
const { userSelectionRect, edges, transform, nodeOrigin, nodes, onNodesChange, onEdgesChange } = store.getState();
|
|
1117
|
+
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
store.setState({ userSelectionActive: true, nodesSelectionActive: false });
|
|
1121
|
+
const mousePos = getEventPosition(event.nativeEvent, containerBounds.current);
|
|
1122
|
+
const startX = userSelectionRect.startX ?? 0;
|
|
1123
|
+
const startY = userSelectionRect.startY ?? 0;
|
|
1124
|
+
const nextUserSelectRect = {
|
|
1125
|
+
...userSelectionRect,
|
|
1126
|
+
x: mousePos.x < startX ? mousePos.x : startX,
|
|
1127
|
+
y: mousePos.y < startY ? mousePos.y : startY,
|
|
1128
|
+
width: Math.abs(mousePos.x - startX),
|
|
1129
|
+
height: Math.abs(mousePos.y - startY),
|
|
1130
|
+
};
|
|
1131
|
+
const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, selectionMode === SelectionMode.Partial, true, nodeOrigin);
|
|
1132
|
+
const selectedEdgeIds = new Set();
|
|
1133
|
+
const selectedNodeIds = new Set();
|
|
1134
|
+
for (const selectedNode of selectedNodes) {
|
|
1135
|
+
selectedNodeIds.add(selectedNode.id);
|
|
1136
|
+
for (const edge of edges) {
|
|
1137
|
+
if (edge.source === selectedNode.id || edge.target === selectedNode.id) {
|
|
1138
|
+
selectedEdgeIds.add(edge.id);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
if (prevSelectedNodesCount.current !== selectedNodeIds.size) {
|
|
1143
|
+
prevSelectedNodesCount.current = selectedNodeIds.size;
|
|
1144
|
+
const changes = getSelectionChanges(nodes, selectedNodeIds, true);
|
|
1145
|
+
if (changes.length) {
|
|
1146
|
+
onNodesChange?.(changes);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
if (prevSelectedEdgesCount.current !== selectedEdgeIds.size) {
|
|
1150
|
+
prevSelectedEdgesCount.current = selectedEdgeIds.size;
|
|
1151
|
+
const changes = getSelectionChanges(edges, selectedEdgeIds);
|
|
1152
|
+
if (changes.length) {
|
|
1153
|
+
onEdgesChange?.(changes);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
store.setState({
|
|
1157
|
+
userSelectionRect: nextUserSelectRect,
|
|
1158
|
+
});
|
|
1159
|
+
};
|
|
1160
|
+
const onMouseUp = (event) => {
|
|
1161
|
+
if (event.button !== 0) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const { userSelectionRect } = store.getState();
|
|
1165
|
+
// We only want to trigger click functions when in selection mode if
|
|
1166
|
+
// the user did not move the mouse.
|
|
1167
|
+
if (!userSelectionActive && userSelectionRect && event.target === container.current) {
|
|
1168
|
+
onClick?.(event);
|
|
1169
|
+
}
|
|
1170
|
+
store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
|
|
1171
|
+
resetUserSelection();
|
|
1172
|
+
onSelectionEnd?.(event);
|
|
1173
|
+
};
|
|
1174
|
+
const onMouseLeave = (event) => {
|
|
1175
|
+
if (userSelectionActive) {
|
|
1176
|
+
store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
|
|
1177
|
+
onSelectionEnd?.(event);
|
|
1178
|
+
}
|
|
1179
|
+
resetUserSelection();
|
|
1180
|
+
};
|
|
1181
|
+
const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive);
|
|
1182
|
+
return (jsxs("div", { className: cc(['react-flow__pane', { dragging, selection: isSelecting }]), onClick: hasActiveSelection ? undefined : wrapHandler(onClick, container), onContextMenu: wrapHandler(onContextMenu, container), onWheel: wrapHandler(onWheel, container), onMouseEnter: hasActiveSelection ? undefined : onPaneMouseEnter, onMouseDown: hasActiveSelection ? onMouseDown : undefined, onMouseMove: hasActiveSelection ? onMouseMove : onPaneMouseMove, onMouseUp: hasActiveSelection ? onMouseUp : undefined, onMouseLeave: hasActiveSelection ? onMouseLeave : onPaneMouseLeave, ref: container, style: containerStyle, children: [children, jsx(UserSelection, {})] }));
|
|
1183
|
+
});
|
|
1184
|
+
Pane.displayName = 'Pane';
|
|
1185
|
+
|
|
1186
|
+
// this handler is called by
|
|
1187
|
+
// 1. the click handler when node is not draggable or selectNodesOnDrag = false
|
|
1188
|
+
// or
|
|
1189
|
+
// 2. the on drag start handler when node is draggable and selectNodesOnDrag = true
|
|
1190
|
+
function handleNodeClick({ id, store, unselect = false, nodeRef, }) {
|
|
1191
|
+
const { addSelectedNodes, unselectNodesAndEdges, multiSelectionActive, nodeLookup, onError } = store.getState();
|
|
1192
|
+
const node = nodeLookup.get(id);
|
|
1193
|
+
if (!node) {
|
|
1194
|
+
onError?.('012', errorMessages['error012'](id));
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
store.setState({ nodesSelectionActive: false });
|
|
1198
|
+
if (!node.selected) {
|
|
1199
|
+
addSelectedNodes([id]);
|
|
1200
|
+
}
|
|
1201
|
+
else if (unselect || (node.selected && multiSelectionActive)) {
|
|
1202
|
+
unselectNodesAndEdges({ nodes: [node], edges: [] });
|
|
1203
|
+
requestAnimationFrame(() => nodeRef?.current?.blur());
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* Hook for calling XYDrag helper from @xyflow/system.
|
|
1209
|
+
*
|
|
1210
|
+
* @internal
|
|
1211
|
+
*/
|
|
1212
|
+
function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, nodeId, isSelectable }) {
|
|
1213
|
+
const store = useStoreApi();
|
|
1214
|
+
const [dragging, setDragging] = useState(false);
|
|
1215
|
+
const xyDrag = useRef();
|
|
1216
|
+
useEffect(() => {
|
|
1217
|
+
if (nodeRef?.current) {
|
|
1218
|
+
xyDrag.current = XYDrag({
|
|
1219
|
+
domNode: nodeRef.current,
|
|
1220
|
+
getStoreItems: () => store.getState(),
|
|
1221
|
+
onNodeMouseDown: (id) => {
|
|
1222
|
+
handleNodeClick({
|
|
1223
|
+
id,
|
|
1224
|
+
store,
|
|
1225
|
+
nodeRef: nodeRef,
|
|
1226
|
+
});
|
|
1227
|
+
},
|
|
1228
|
+
onDragStart: () => {
|
|
1229
|
+
setDragging(true);
|
|
1230
|
+
},
|
|
1231
|
+
onDragStop: () => {
|
|
1232
|
+
setDragging(false);
|
|
1233
|
+
},
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
}, []);
|
|
1237
|
+
useEffect(() => {
|
|
1238
|
+
if (disabled) {
|
|
1239
|
+
xyDrag.current?.destroy();
|
|
1240
|
+
}
|
|
1241
|
+
else {
|
|
1242
|
+
xyDrag.current?.update({
|
|
1243
|
+
noDragClassName,
|
|
1244
|
+
handleSelector,
|
|
1245
|
+
domNode: nodeRef.current,
|
|
1246
|
+
isSelectable,
|
|
1247
|
+
nodeId,
|
|
1248
|
+
});
|
|
1249
|
+
return () => {
|
|
1250
|
+
xyDrag.current?.destroy();
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
}, [noDragClassName, handleSelector, disabled, isSelectable, nodeRef, nodeId]);
|
|
1254
|
+
return dragging;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
const selectedAndDraggable = (nodesDraggable) => (n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'));
|
|
1258
|
+
/**
|
|
1259
|
+
* Hook for updating node positions.
|
|
1260
|
+
*
|
|
1261
|
+
* @internal
|
|
1262
|
+
* @returns function for updating node positions
|
|
1263
|
+
*/
|
|
1264
|
+
function useUpdateNodePositions() {
|
|
1265
|
+
const store = useStoreApi();
|
|
1266
|
+
const updatePositions = useCallback((params) => {
|
|
1267
|
+
const { nodeExtent, nodes, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions } = store.getState();
|
|
1268
|
+
const selectedNodes = nodes.filter(selectedAndDraggable(nodesDraggable));
|
|
1269
|
+
// by default a node moves 5px on each key press, or 20px if shift is pressed
|
|
1270
|
+
// if snap grid is enabled, we use that for the velocity.
|
|
1271
|
+
const xVelo = snapToGrid ? snapGrid[0] : 5;
|
|
1272
|
+
const yVelo = snapToGrid ? snapGrid[1] : 5;
|
|
1273
|
+
const factor = params.isShiftPressed ? 4 : 1;
|
|
1274
|
+
const xDiff = params.x * xVelo * factor;
|
|
1275
|
+
const yDiff = params.y * yVelo * factor;
|
|
1276
|
+
const nodeUpdates = selectedNodes.map((node) => {
|
|
1277
|
+
if (node.computed?.positionAbsolute) {
|
|
1278
|
+
let nextPosition = {
|
|
1279
|
+
x: node.computed?.positionAbsolute.x + xDiff,
|
|
1280
|
+
y: node.computed?.positionAbsolute.y + yDiff,
|
|
1281
|
+
};
|
|
1282
|
+
if (snapToGrid) {
|
|
1283
|
+
nextPosition = snapPosition(nextPosition, snapGrid);
|
|
1284
|
+
}
|
|
1285
|
+
const { positionAbsolute, position } = calcNextPosition(node, nextPosition, nodes, nodeExtent, undefined, onError);
|
|
1286
|
+
node.position = position;
|
|
1287
|
+
if (!node.computed) {
|
|
1288
|
+
node.computed = {};
|
|
1289
|
+
}
|
|
1290
|
+
node.computed.positionAbsolute = positionAbsolute;
|
|
1291
|
+
}
|
|
1292
|
+
return node;
|
|
1293
|
+
});
|
|
1294
|
+
updateNodePositions(nodeUpdates, true, false);
|
|
1295
|
+
}, []);
|
|
1296
|
+
return updatePositions;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
const NodeIdContext = createContext(null);
|
|
1300
|
+
const Provider = NodeIdContext.Provider;
|
|
1301
|
+
NodeIdContext.Consumer;
|
|
1302
|
+
const useNodeId = () => {
|
|
1303
|
+
const nodeId = useContext(NodeIdContext);
|
|
1304
|
+
return nodeId;
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
const selector$i = (s) => ({
|
|
1308
|
+
connectionStartHandle: s.connectionStartHandle,
|
|
1309
|
+
connectOnClick: s.connectOnClick,
|
|
1310
|
+
noPanClassName: s.noPanClassName,
|
|
1311
|
+
});
|
|
1312
|
+
const connectingSelector = (nodeId, handleId, type) => (state) => {
|
|
1313
|
+
const { connectionStartHandle: startHandle, connectionEndHandle: endHandle, connectionClickStartHandle: clickHandle, } = state;
|
|
1314
|
+
return {
|
|
1315
|
+
connecting: (startHandle?.nodeId === nodeId && startHandle?.handleId === handleId && startHandle?.type === type) ||
|
|
1316
|
+
(endHandle?.nodeId === nodeId && endHandle?.handleId === handleId && endHandle?.type === type),
|
|
1317
|
+
clickConnecting: clickHandle?.nodeId === nodeId && clickHandle?.handleId === handleId && clickHandle?.type === type,
|
|
1318
|
+
};
|
|
1319
|
+
};
|
|
1320
|
+
const Handle = forwardRef(({ type = 'source', position = Position.Top, isValidConnection, isConnectable = true, isConnectableStart = true, isConnectableEnd = true, id, onConnect, children, className, onMouseDown, onTouchStart, ...rest }, ref) => {
|
|
1321
|
+
const handleId = id || null;
|
|
1322
|
+
const isTarget = type === 'target';
|
|
1323
|
+
const store = useStoreApi();
|
|
1324
|
+
const nodeId = useNodeId();
|
|
1325
|
+
const { connectOnClick, noPanClassName } = useStore(selector$i, shallow);
|
|
1326
|
+
const { connecting, clickConnecting } = useStore(connectingSelector(nodeId, handleId, type), shallow);
|
|
1327
|
+
if (!nodeId) {
|
|
1328
|
+
store.getState().onError?.('010', errorMessages['error010']());
|
|
1329
|
+
}
|
|
1330
|
+
const onConnectExtended = (params) => {
|
|
1331
|
+
const { defaultEdgeOptions, onConnect: onConnectAction, hasDefaultEdges } = store.getState();
|
|
1332
|
+
const edgeParams = {
|
|
1333
|
+
...defaultEdgeOptions,
|
|
1334
|
+
...params,
|
|
1335
|
+
};
|
|
1336
|
+
if (hasDefaultEdges) {
|
|
1337
|
+
const { edges, setEdges } = store.getState();
|
|
1338
|
+
setEdges(addEdge(edgeParams, edges));
|
|
1339
|
+
}
|
|
1340
|
+
onConnectAction?.(edgeParams);
|
|
1341
|
+
onConnect?.(edgeParams);
|
|
1342
|
+
};
|
|
1343
|
+
const onPointerDown = (event) => {
|
|
1344
|
+
if (!nodeId) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
const isMouseTriggered = isMouseEvent(event.nativeEvent);
|
|
1348
|
+
if (isConnectableStart &&
|
|
1349
|
+
((isMouseTriggered && event.button === 0) || !isMouseTriggered)) {
|
|
1350
|
+
const currentStore = store.getState();
|
|
1351
|
+
XYHandle.onPointerDown(event.nativeEvent, {
|
|
1352
|
+
autoPanOnConnect: currentStore.autoPanOnConnect,
|
|
1353
|
+
connectionMode: currentStore.connectionMode,
|
|
1354
|
+
connectionRadius: currentStore.connectionRadius,
|
|
1355
|
+
domNode: currentStore.domNode,
|
|
1356
|
+
nodes: currentStore.nodes,
|
|
1357
|
+
lib: currentStore.lib,
|
|
1358
|
+
isTarget,
|
|
1359
|
+
handleId,
|
|
1360
|
+
nodeId,
|
|
1361
|
+
panBy: currentStore.panBy,
|
|
1362
|
+
cancelConnection: currentStore.cancelConnection,
|
|
1363
|
+
onConnectStart: currentStore.onConnectStart,
|
|
1364
|
+
onConnectEnd: currentStore.onConnectEnd,
|
|
1365
|
+
updateConnection: currentStore.updateConnection,
|
|
1366
|
+
onConnect: onConnectExtended,
|
|
1367
|
+
isValidConnection: isValidConnection || currentStore.isValidConnection,
|
|
1368
|
+
getTransform: () => store.getState().transform,
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
if (isMouseTriggered) {
|
|
1372
|
+
onMouseDown?.(event);
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
onTouchStart?.(event);
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
const onClick = (event) => {
|
|
1379
|
+
const { onClickConnectStart, onClickConnectEnd, connectionClickStartHandle, connectionMode, isValidConnection: isValidConnectionStore, lib, } = store.getState();
|
|
1380
|
+
if (!nodeId || (!connectionClickStartHandle && !isConnectableStart)) {
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
if (!connectionClickStartHandle) {
|
|
1384
|
+
onClickConnectStart?.(event.nativeEvent, { nodeId, handleId, handleType: type });
|
|
1385
|
+
store.setState({ connectionClickStartHandle: { nodeId, type, handleId } });
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
const doc = getHostForElement(event.target);
|
|
1389
|
+
const isValidConnectionHandler = isValidConnection || isValidConnectionStore;
|
|
1390
|
+
const { connection, isValid } = XYHandle.isValid(event.nativeEvent, {
|
|
1391
|
+
handle: {
|
|
1392
|
+
nodeId,
|
|
1393
|
+
id: handleId,
|
|
1394
|
+
type,
|
|
1395
|
+
},
|
|
1396
|
+
connectionMode,
|
|
1397
|
+
fromNodeId: connectionClickStartHandle.nodeId,
|
|
1398
|
+
fromHandleId: connectionClickStartHandle.handleId || null,
|
|
1399
|
+
fromType: connectionClickStartHandle.type,
|
|
1400
|
+
isValidConnection: isValidConnectionHandler,
|
|
1401
|
+
doc,
|
|
1402
|
+
lib,
|
|
1403
|
+
});
|
|
1404
|
+
if (isValid && connection) {
|
|
1405
|
+
onConnectExtended(connection);
|
|
1406
|
+
}
|
|
1407
|
+
onClickConnectEnd?.(event);
|
|
1408
|
+
store.setState({ connectionClickStartHandle: null });
|
|
1409
|
+
};
|
|
1410
|
+
return (jsx("div", { "data-handleid": handleId, "data-nodeid": nodeId, "data-handlepos": position, "data-id": `${nodeId}-${handleId}-${type}`, className: cc([
|
|
1411
|
+
'react-flow__handle',
|
|
1412
|
+
`react-flow__handle-${position}`,
|
|
1413
|
+
'nodrag',
|
|
1414
|
+
noPanClassName,
|
|
1415
|
+
className,
|
|
1416
|
+
{
|
|
1417
|
+
source: !isTarget,
|
|
1418
|
+
target: isTarget,
|
|
1419
|
+
connectable: isConnectable,
|
|
1420
|
+
connectablestart: isConnectableStart,
|
|
1421
|
+
connectableend: isConnectableEnd,
|
|
1422
|
+
connecting: clickConnecting,
|
|
1423
|
+
// this class is used to style the handle when the user is connecting
|
|
1424
|
+
connectionindicator: isConnectable && ((isConnectableStart && !connecting) || (isConnectableEnd && connecting)),
|
|
1425
|
+
},
|
|
1426
|
+
]), onMouseDown: onPointerDown, onTouchStart: onPointerDown, onClick: connectOnClick ? onClick : undefined, ref: ref, ...rest, children: children }));
|
|
1427
|
+
});
|
|
1428
|
+
Handle.displayName = 'Handle';
|
|
1429
|
+
var Handle$1 = memo(Handle);
|
|
1430
|
+
|
|
1431
|
+
const InputNode = ({ data, isConnectable, sourcePosition = Position.Bottom }) => (jsxs(Fragment, { children: [data?.label, jsx(Handle$1, { type: "source", position: sourcePosition, isConnectable: isConnectable })] }));
|
|
1432
|
+
InputNode.displayName = 'InputNode';
|
|
1433
|
+
var InputNode$1 = memo(InputNode);
|
|
1434
|
+
|
|
1435
|
+
const DefaultNode = ({ data, isConnectable, targetPosition = Position.Top, sourcePosition = Position.Bottom, }) => {
|
|
1436
|
+
return (jsxs(Fragment, { children: [jsx(Handle$1, { type: "target", position: targetPosition, isConnectable: isConnectable }), data?.label, jsx(Handle$1, { type: "source", position: sourcePosition, isConnectable: isConnectable })] }));
|
|
1437
|
+
};
|
|
1438
|
+
DefaultNode.displayName = 'DefaultNode';
|
|
1439
|
+
var DefaultNode$1 = memo(DefaultNode);
|
|
1440
|
+
|
|
1441
|
+
const GroupNode = () => null;
|
|
1442
|
+
GroupNode.displayName = 'GroupNode';
|
|
1443
|
+
|
|
1444
|
+
const OutputNode = ({ data, isConnectable, targetPosition = Position.Top }) => (jsxs(Fragment, { children: [jsx(Handle$1, { type: "target", position: targetPosition, isConnectable: isConnectable }), data?.label] }));
|
|
1445
|
+
OutputNode.displayName = 'OutputNode';
|
|
1446
|
+
var OutputNode$1 = memo(OutputNode);
|
|
1447
|
+
|
|
1448
|
+
const arrowKeyDiffs = {
|
|
1449
|
+
ArrowUp: { x: 0, y: -1 },
|
|
1450
|
+
ArrowDown: { x: 0, y: 1 },
|
|
1451
|
+
ArrowLeft: { x: -1, y: 0 },
|
|
1452
|
+
ArrowRight: { x: 1, y: 0 },
|
|
1453
|
+
};
|
|
1454
|
+
const builtinNodeTypes = {
|
|
1455
|
+
input: InputNode$1,
|
|
1456
|
+
default: DefaultNode$1,
|
|
1457
|
+
output: OutputNode$1,
|
|
1458
|
+
group: GroupNode,
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
const selector$h = (s) => {
|
|
1462
|
+
const selectedNodes = s.nodes.filter((n) => n.selected);
|
|
1463
|
+
const { width, height, x, y } = getNodesBounds(selectedNodes, s.nodeOrigin);
|
|
1464
|
+
return {
|
|
1465
|
+
width,
|
|
1466
|
+
height,
|
|
1467
|
+
userSelectionActive: s.userSelectionActive,
|
|
1468
|
+
transformString: `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]}) translate(${x}px,${y}px)`,
|
|
1469
|
+
};
|
|
1470
|
+
};
|
|
1471
|
+
function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }) {
|
|
1472
|
+
const store = useStoreApi();
|
|
1473
|
+
const { width, height, transformString, userSelectionActive } = useStore(selector$h, shallow);
|
|
1474
|
+
const updatePositions = useUpdateNodePositions();
|
|
1475
|
+
const nodeRef = useRef(null);
|
|
1476
|
+
useEffect(() => {
|
|
1477
|
+
if (!disableKeyboardA11y) {
|
|
1478
|
+
nodeRef.current?.focus({
|
|
1479
|
+
preventScroll: true,
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
}, [disableKeyboardA11y]);
|
|
1483
|
+
useDrag({
|
|
1484
|
+
nodeRef,
|
|
1485
|
+
});
|
|
1486
|
+
if (userSelectionActive || !width || !height) {
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
const onContextMenu = onSelectionContextMenu
|
|
1490
|
+
? (event) => {
|
|
1491
|
+
const selectedNodes = store.getState().nodes.filter((n) => n.selected);
|
|
1492
|
+
onSelectionContextMenu(event, selectedNodes);
|
|
1493
|
+
}
|
|
1494
|
+
: undefined;
|
|
1495
|
+
const onKeyDown = (event) => {
|
|
1496
|
+
if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
|
1497
|
+
updatePositions({
|
|
1498
|
+
x: arrowKeyDiffs[event.key].x,
|
|
1499
|
+
y: arrowKeyDiffs[event.key].y,
|
|
1500
|
+
isShiftPressed: event.shiftKey,
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
return (jsx("div", { className: cc(['react-flow__nodesselection', 'react-flow__container', noPanClassName]), style: {
|
|
1505
|
+
transform: transformString,
|
|
1506
|
+
}, children: jsx("div", { ref: nodeRef, className: "react-flow__nodesselection-rect", onContextMenu: onContextMenu, tabIndex: disableKeyboardA11y ? undefined : -1, onKeyDown: disableKeyboardA11y ? undefined : onKeyDown, style: {
|
|
1507
|
+
width,
|
|
1508
|
+
height,
|
|
1509
|
+
} }) }));
|
|
1510
|
+
}
|
|
1511
|
+
var NodesSelection$1 = memo(NodesSelection);
|
|
1512
|
+
|
|
1513
|
+
const selector$g = (s) => s.nodesSelectionActive;
|
|
1514
|
+
const FlowRenderer = ({ 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, }) => {
|
|
1515
|
+
const nodesSelectionActive = useStore(selector$g);
|
|
1516
|
+
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
|
1517
|
+
const panActivationKeyPressed = useKeyPress(panActivationKeyCode);
|
|
1518
|
+
const panOnDrag = panActivationKeyPressed || _panOnDrag;
|
|
1519
|
+
const panOnScroll = panActivationKeyPressed || _panOnScroll;
|
|
1520
|
+
const isSelecting = selectionKeyPressed || (selectionOnDrag && panOnDrag !== true);
|
|
1521
|
+
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
|
1522
|
+
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$1, { onSelectionContextMenu: onSelectionContextMenu, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y }))] }) }));
|
|
1523
|
+
};
|
|
1524
|
+
FlowRenderer.displayName = 'FlowRenderer';
|
|
1525
|
+
var FlowRenderer$1 = memo(FlowRenderer);
|
|
1526
|
+
|
|
1527
|
+
const selector$f = (onlyRenderVisible) => (s) => {
|
|
1528
|
+
return onlyRenderVisible
|
|
1529
|
+
? getNodesInside(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map((node) => node.id)
|
|
1530
|
+
: Array.from(s.nodeLookup.keys());
|
|
1531
|
+
};
|
|
1532
|
+
/**
|
|
1533
|
+
* Hook for getting the visible node ids from the store.
|
|
1534
|
+
*
|
|
1535
|
+
* @internal
|
|
1536
|
+
* @param onlyRenderVisible
|
|
1537
|
+
* @returns array with visible node ids
|
|
1538
|
+
*/
|
|
1539
|
+
function useVisibleNodeIds(onlyRenderVisible) {
|
|
1540
|
+
const nodeIds = useStore(useCallback(selector$f(onlyRenderVisible), [onlyRenderVisible]), shallow);
|
|
1541
|
+
return nodeIds;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
const selector$e = (s) => s.updateNodeDimensions;
|
|
1545
|
+
function useResizeObserver() {
|
|
1546
|
+
const updateNodeDimensions = useStore(selector$e);
|
|
1547
|
+
const resizeObserverRef = useRef();
|
|
1548
|
+
const resizeObserver = useMemo(() => {
|
|
1549
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
const observer = new ResizeObserver((entries) => {
|
|
1553
|
+
const updates = new Map();
|
|
1554
|
+
entries.forEach((entry) => {
|
|
1555
|
+
const id = entry.target.getAttribute('data-id');
|
|
1556
|
+
updates.set(id, {
|
|
1557
|
+
id,
|
|
1558
|
+
nodeElement: entry.target,
|
|
1559
|
+
forceUpdate: true,
|
|
1560
|
+
});
|
|
1561
|
+
});
|
|
1562
|
+
updateNodeDimensions(updates);
|
|
1563
|
+
});
|
|
1564
|
+
resizeObserverRef.current = observer;
|
|
1565
|
+
return observer;
|
|
1566
|
+
}, []);
|
|
1567
|
+
useEffect(() => {
|
|
1568
|
+
return () => {
|
|
1569
|
+
resizeObserverRef?.current?.disconnect();
|
|
1570
|
+
};
|
|
1571
|
+
}, []);
|
|
1572
|
+
return resizeObserver;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
const NodeWrapper = ({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeExtent, nodeOrigin, onError, }) => {
|
|
1576
|
+
const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => {
|
|
1577
|
+
const node = s.nodeLookup.get(id);
|
|
1578
|
+
const positionAbsolute = nodeExtent
|
|
1579
|
+
? clampPosition(node.computed?.positionAbsolute, nodeExtent)
|
|
1580
|
+
: node.computed?.positionAbsolute || { x: 0, y: 0 };
|
|
1581
|
+
return {
|
|
1582
|
+
node,
|
|
1583
|
+
// we are mutating positionAbsolute, z and isParent attributes for sub flows
|
|
1584
|
+
// so we we need to force a re-render when some change
|
|
1585
|
+
positionAbsoluteX: positionAbsolute.x,
|
|
1586
|
+
positionAbsoluteY: positionAbsolute.y,
|
|
1587
|
+
zIndex: node[internalsSymbol]?.z ?? 0,
|
|
1588
|
+
isParent: !!node[internalsSymbol]?.isParent,
|
|
1589
|
+
};
|
|
1590
|
+
}, shallow);
|
|
1591
|
+
let nodeType = node.type || 'default';
|
|
1592
|
+
let NodeComponent = nodeTypes?.[nodeType] || builtinNodeTypes[nodeType];
|
|
1593
|
+
if (NodeComponent === undefined) {
|
|
1594
|
+
onError?.('003', errorMessages['error003'](nodeType));
|
|
1595
|
+
nodeType = 'default';
|
|
1596
|
+
NodeComponent = builtinNodeTypes.default;
|
|
1597
|
+
}
|
|
1598
|
+
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
|
|
1599
|
+
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
|
|
1600
|
+
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
|
1601
|
+
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
|
|
1602
|
+
const store = useStoreApi();
|
|
1603
|
+
const nodeRef = useRef(null);
|
|
1604
|
+
const prevSourcePosition = useRef(node.sourcePosition);
|
|
1605
|
+
const prevTargetPosition = useRef(node.targetPosition);
|
|
1606
|
+
const prevType = useRef(nodeType);
|
|
1607
|
+
const updatePositions = useUpdateNodePositions();
|
|
1608
|
+
useEffect(() => {
|
|
1609
|
+
if (nodeRef.current && !node.hidden) {
|
|
1610
|
+
const currNode = nodeRef.current;
|
|
1611
|
+
resizeObserver?.observe(currNode);
|
|
1612
|
+
return () => resizeObserver?.unobserve(currNode);
|
|
1613
|
+
}
|
|
1614
|
+
}, [node.hidden]);
|
|
1615
|
+
useEffect(() => {
|
|
1616
|
+
// when the user programmatically changes the source or handle position, we re-initialize the node
|
|
1617
|
+
const typeChanged = prevType.current !== nodeType;
|
|
1618
|
+
const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
|
|
1619
|
+
const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
|
|
1620
|
+
if (nodeRef.current && (typeChanged || sourcePosChanged || targetPosChanged)) {
|
|
1621
|
+
if (typeChanged) {
|
|
1622
|
+
prevType.current = nodeType;
|
|
1623
|
+
}
|
|
1624
|
+
if (sourcePosChanged) {
|
|
1625
|
+
prevSourcePosition.current = node.sourcePosition;
|
|
1626
|
+
}
|
|
1627
|
+
if (targetPosChanged) {
|
|
1628
|
+
prevTargetPosition.current = node.targetPosition;
|
|
1629
|
+
}
|
|
1630
|
+
store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, forceUpdate: true }]]));
|
|
1631
|
+
}
|
|
1632
|
+
}, [id, nodeType, node.sourcePosition, node.targetPosition]);
|
|
1633
|
+
const dragging = useDrag({
|
|
1634
|
+
nodeRef,
|
|
1635
|
+
disabled: node.hidden || !isDraggable,
|
|
1636
|
+
noDragClassName,
|
|
1637
|
+
handleSelector: node.dragHandle,
|
|
1638
|
+
nodeId: id,
|
|
1639
|
+
isSelectable,
|
|
1640
|
+
});
|
|
1641
|
+
if (node.hidden) {
|
|
1642
|
+
return null;
|
|
1643
|
+
}
|
|
1644
|
+
const width = node.width ?? undefined;
|
|
1645
|
+
const height = node.height ?? undefined;
|
|
1646
|
+
const computedWidth = node.computed?.width;
|
|
1647
|
+
const computedHeight = node.computed?.height;
|
|
1648
|
+
const positionAbsoluteOrigin = getPositionWithOrigin({
|
|
1649
|
+
x: positionAbsoluteX,
|
|
1650
|
+
y: positionAbsoluteY,
|
|
1651
|
+
width: computedWidth ?? width ?? 0,
|
|
1652
|
+
height: computedHeight ?? height ?? 0,
|
|
1653
|
+
origin: node.origin || nodeOrigin,
|
|
1654
|
+
});
|
|
1655
|
+
const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height);
|
|
1656
|
+
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
|
1657
|
+
const onMouseEnterHandler = onMouseEnter ? (event) => onMouseEnter(event, { ...node }) : undefined;
|
|
1658
|
+
const onMouseMoveHandler = onMouseMove ? (event) => onMouseMove(event, { ...node }) : undefined;
|
|
1659
|
+
const onMouseLeaveHandler = onMouseLeave ? (event) => onMouseLeave(event, { ...node }) : undefined;
|
|
1660
|
+
const onContextMenuHandler = onContextMenu ? (event) => onContextMenu(event, { ...node }) : undefined;
|
|
1661
|
+
const onDoubleClickHandler = onDoubleClick ? (event) => onDoubleClick(event, { ...node }) : undefined;
|
|
1662
|
+
const onSelectNodeHandler = (event) => {
|
|
1663
|
+
const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
|
|
1664
|
+
if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
|
|
1665
|
+
// this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
|
|
1666
|
+
// here we only need to call it when selectNodesOnDrag=false
|
|
1667
|
+
handleNodeClick({
|
|
1668
|
+
id,
|
|
1669
|
+
store,
|
|
1670
|
+
nodeRef,
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
if (onClick) {
|
|
1674
|
+
onClick(event, { ...node });
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
const onKeyDown = (event) => {
|
|
1678
|
+
if (isInputDOMNode(event.nativeEvent)) {
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
|
1682
|
+
const unselect = event.key === 'Escape';
|
|
1683
|
+
handleNodeClick({
|
|
1684
|
+
id,
|
|
1685
|
+
store,
|
|
1686
|
+
unselect,
|
|
1687
|
+
nodeRef,
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
else if (!disableKeyboardA11y &&
|
|
1691
|
+
isDraggable &&
|
|
1692
|
+
node.selected &&
|
|
1693
|
+
Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
|
1694
|
+
store.setState({
|
|
1695
|
+
ariaLiveMessage: `Moved selected node ${event.key
|
|
1696
|
+
.replace('Arrow', '')
|
|
1697
|
+
.toLowerCase()}. New position, x: ${~~positionAbsoluteX}, y: ${~~positionAbsoluteY}`,
|
|
1698
|
+
});
|
|
1699
|
+
updatePositions({
|
|
1700
|
+
x: arrowKeyDiffs[event.key].x,
|
|
1701
|
+
y: arrowKeyDiffs[event.key].y,
|
|
1702
|
+
isShiftPressed: event.shiftKey,
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
};
|
|
1706
|
+
return (jsx("div", { className: cc([
|
|
1707
|
+
'react-flow__node',
|
|
1708
|
+
`react-flow__node-${nodeType}`,
|
|
1709
|
+
{
|
|
1710
|
+
// this is overwritable by passing `nopan` as a class name
|
|
1711
|
+
[noPanClassName]: isDraggable,
|
|
1712
|
+
},
|
|
1713
|
+
node.className,
|
|
1714
|
+
{
|
|
1715
|
+
selected: node.selected,
|
|
1716
|
+
selectable: isSelectable,
|
|
1717
|
+
parent: isParent,
|
|
1718
|
+
dragging,
|
|
1719
|
+
},
|
|
1720
|
+
]), ref: nodeRef, style: {
|
|
1721
|
+
zIndex,
|
|
1722
|
+
transform: `translate(${positionAbsoluteOrigin.x}px,${positionAbsoluteOrigin.y}px)`,
|
|
1723
|
+
pointerEvents: hasPointerEvents ? 'all' : 'none',
|
|
1724
|
+
visibility: initialized ? 'visible' : 'hidden',
|
|
1725
|
+
width,
|
|
1726
|
+
height,
|
|
1727
|
+
...node.style,
|
|
1728
|
+
}, "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, width: computedWidth, height: computedHeight, positionAbsoluteX: positionAbsoluteX, positionAbsoluteY: positionAbsoluteY, selected: node.selected, isConnectable: isConnectable, sourcePosition: node.sourcePosition, targetPosition: node.targetPosition, dragging: dragging, dragHandle: node.dragHandle, zIndex: zIndex }) }) }));
|
|
1729
|
+
};
|
|
1730
|
+
NodeWrapper.displayName = 'NodeWrapper';
|
|
1731
|
+
var NodeWrapper$1 = memo(NodeWrapper);
|
|
1732
|
+
|
|
1733
|
+
const selector$d = (s) => ({
|
|
1734
|
+
nodesDraggable: s.nodesDraggable,
|
|
1735
|
+
nodesConnectable: s.nodesConnectable,
|
|
1736
|
+
nodesFocusable: s.nodesFocusable,
|
|
1737
|
+
elementsSelectable: s.elementsSelectable,
|
|
1738
|
+
onError: s.onError,
|
|
1739
|
+
});
|
|
1740
|
+
const NodeRenderer = (props) => {
|
|
1741
|
+
const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, onError } = useStore(selector$d, shallow);
|
|
1742
|
+
const nodeIds = useVisibleNodeIds(props.onlyRenderVisibleElements);
|
|
1743
|
+
const resizeObserver = useResizeObserver();
|
|
1744
|
+
return (jsx("div", { className: "react-flow__nodes", style: containerStyle, children: nodeIds.map((nodeId) => {
|
|
1745
|
+
return (
|
|
1746
|
+
// The split of responsibilities between NodeRenderer and
|
|
1747
|
+
// NodeComponentWrapper may appear weird. However, it’s designed to
|
|
1748
|
+
// minimize the cost of updates when individual nodes change.
|
|
1749
|
+
//
|
|
1750
|
+
// For example, when you’re dragging a single node, that node gets
|
|
1751
|
+
// updated multiple times per second. If `NodeRenderer` were to update
|
|
1752
|
+
// every time, it would have to re-run the `nodes.map()` loop every
|
|
1753
|
+
// time. This gets pricey with hundreds of nodes, especially if every
|
|
1754
|
+
// loop cycle does more than just rendering a JSX element!
|
|
1755
|
+
//
|
|
1756
|
+
// As a result of this choice, we took the following implementation
|
|
1757
|
+
// decisions:
|
|
1758
|
+
// - NodeRenderer subscribes *only* to node IDs – and therefore
|
|
1759
|
+
// rerender *only* when visible nodes are added or removed.
|
|
1760
|
+
// - NodeRenderer performs all operations the result of which can be
|
|
1761
|
+
// shared between nodes (such as creating the `ResizeObserver`
|
|
1762
|
+
// instance, or subscribing to `selector`). This means extra prop
|
|
1763
|
+
// drilling into `NodeComponentWrapper`, but it means we need to run
|
|
1764
|
+
// these operations only once – instead of once per node.
|
|
1765
|
+
// - Any operations that you’d normally write inside `nodes.map` are
|
|
1766
|
+
// moved into `NodeComponentWrapper`. This ensures they are
|
|
1767
|
+
// memorized – so if `NodeRenderer` *has* to rerender, it only
|
|
1768
|
+
// needs to regenerate the list of nodes, nothing else.
|
|
1769
|
+
jsx(NodeWrapper$1, { id: nodeId, nodeTypes: props.nodeTypes, nodeExtent: props.nodeExtent, nodeOrigin: props.nodeOrigin, onClick: props.onNodeClick, onMouseEnter: props.onNodeMouseEnter, onMouseMove: props.onNodeMouseMove, onMouseLeave: props.onNodeMouseLeave, onContextMenu: props.onNodeContextMenu, onDoubleClick: props.onNodeDoubleClick, noDragClassName: props.noDragClassName, noPanClassName: props.noPanClassName, rfId: props.rfId, disableKeyboardA11y: props.disableKeyboardA11y, resizeObserver: resizeObserver, nodesDraggable: nodesDraggable, nodesConnectable: nodesConnectable, nodesFocusable: nodesFocusable, elementsSelectable: elementsSelectable, onError: onError }, nodeId));
|
|
1770
|
+
}) }));
|
|
1771
|
+
};
|
|
1772
|
+
NodeRenderer.displayName = 'NodeRenderer';
|
|
1773
|
+
var NodeRenderer$1 = memo(NodeRenderer);
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* Hook for getting the visible edge ids from the store.
|
|
1777
|
+
*
|
|
1778
|
+
* @internal
|
|
1779
|
+
* @param onlyRenderVisible
|
|
1780
|
+
* @returns array with visible edge ids
|
|
1781
|
+
*/
|
|
1782
|
+
function useVisibleEdgeIds(onlyRenderVisible) {
|
|
1783
|
+
const edgeIds = useStore(useCallback((s) => {
|
|
1784
|
+
if (!onlyRenderVisible) {
|
|
1785
|
+
return s.edges.map((edge) => edge.id);
|
|
1786
|
+
}
|
|
1787
|
+
const visibleEdgeIds = [];
|
|
1788
|
+
if (s.width && s.height) {
|
|
1789
|
+
for (const edge of s.edges) {
|
|
1790
|
+
const sourceNode = s.nodeLookup.get(edge.source);
|
|
1791
|
+
const targetNode = s.nodeLookup.get(edge.target);
|
|
1792
|
+
if (sourceNode &&
|
|
1793
|
+
targetNode &&
|
|
1794
|
+
isEdgeVisible({
|
|
1795
|
+
sourceNode,
|
|
1796
|
+
targetNode,
|
|
1797
|
+
width: s.width,
|
|
1798
|
+
height: s.height,
|
|
1799
|
+
transform: s.transform,
|
|
1800
|
+
})) {
|
|
1801
|
+
visibleEdgeIds.push(edge.id);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
return visibleEdgeIds;
|
|
1806
|
+
}, [onlyRenderVisible]), shallow);
|
|
1807
|
+
return edgeIds;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
const ArrowSymbol = ({ color = 'none', strokeWidth = 1 }) => {
|
|
1811
|
+
return (jsx("polyline", { style: {
|
|
1812
|
+
stroke: color,
|
|
1813
|
+
strokeWidth,
|
|
1814
|
+
}, strokeLinecap: "round", strokeLinejoin: "round", fill: "none", points: "-5,-4 0,0 -5,4" }));
|
|
1815
|
+
};
|
|
1816
|
+
const ArrowClosedSymbol = ({ color = 'none', strokeWidth = 1 }) => {
|
|
1817
|
+
return (jsx("polyline", { style: {
|
|
1818
|
+
stroke: color,
|
|
1819
|
+
fill: color,
|
|
1820
|
+
strokeWidth,
|
|
1821
|
+
}, strokeLinecap: "round", strokeLinejoin: "round", points: "-5,-4 0,0 -5,4 -5,-4" }));
|
|
1822
|
+
};
|
|
1823
|
+
const MarkerSymbols = {
|
|
1824
|
+
[MarkerType.Arrow]: ArrowSymbol,
|
|
1825
|
+
[MarkerType.ArrowClosed]: ArrowClosedSymbol,
|
|
1826
|
+
};
|
|
1827
|
+
function useMarkerSymbol(type) {
|
|
1828
|
+
const store = useStoreApi();
|
|
1829
|
+
const symbol = useMemo(() => {
|
|
1830
|
+
const symbolExists = Object.prototype.hasOwnProperty.call(MarkerSymbols, type);
|
|
1831
|
+
if (!symbolExists) {
|
|
1832
|
+
store.getState().onError?.('009', errorMessages['error009'](type));
|
|
1833
|
+
return null;
|
|
1834
|
+
}
|
|
1835
|
+
return MarkerSymbols[type];
|
|
1836
|
+
}, [type]);
|
|
1837
|
+
return symbol;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
const Marker = ({ id, type, color, width = 12.5, height = 12.5, markerUnits = 'strokeWidth', strokeWidth, orient = 'auto-start-reverse', }) => {
|
|
1841
|
+
const Symbol = useMarkerSymbol(type);
|
|
1842
|
+
if (!Symbol) {
|
|
1843
|
+
return null;
|
|
1844
|
+
}
|
|
1845
|
+
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 }) }));
|
|
1846
|
+
};
|
|
1847
|
+
const markerSelector = ({ defaultColor, rfId }) => (s) => {
|
|
1848
|
+
const markers = createMarkerIds(s.edges, { id: rfId, defaultColor });
|
|
1849
|
+
return markers;
|
|
1850
|
+
};
|
|
1851
|
+
const markersEqual = (a, b) =>
|
|
1852
|
+
// the id includes all marker options, so we just need to look at that part of the marker
|
|
1853
|
+
!(a.length !== b.length || a.some((m, i) => m.id !== b[i].id));
|
|
1854
|
+
// when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
|
|
1855
|
+
// when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
|
|
1856
|
+
// that we can then use for creating our unique marker ids
|
|
1857
|
+
const MarkerDefinitions = ({ defaultColor, rfId }) => {
|
|
1858
|
+
const markers = useStore(useCallback(markerSelector({ defaultColor, rfId }), [defaultColor, rfId]), markersEqual);
|
|
1859
|
+
if (!markers.length) {
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1862
|
+
return (jsx("svg", { className: "react-flow__marker", children: jsx("defs", { children: markers.map((marker) => (jsx(Marker, { id: marker.id, type: marker.type, color: marker.color, width: marker.width, height: marker.height, markerUnits: marker.markerUnits, strokeWidth: marker.strokeWidth, orient: marker.orient }, marker.id))) }) }));
|
|
1863
|
+
};
|
|
1864
|
+
MarkerDefinitions.displayName = 'MarkerDefinitions';
|
|
1865
|
+
var MarkerDefinitions$1 = memo(MarkerDefinitions);
|
|
1866
|
+
|
|
1867
|
+
const EdgeText = ({ x, y, label, labelStyle = {}, labelShowBg = true, labelBgStyle = {}, labelBgPadding = [2, 4], labelBgBorderRadius = 2, children, className, ...rest }) => {
|
|
1868
|
+
const [edgeTextBbox, setEdgeTextBbox] = useState({ x: 1, y: 0, width: 0, height: 0 });
|
|
1869
|
+
const edgeTextClasses = cc(['react-flow__edge-textwrapper', className]);
|
|
1870
|
+
const onEdgeTextRefChange = useCallback((edgeRef) => {
|
|
1871
|
+
if (edgeRef === null)
|
|
1872
|
+
return;
|
|
1873
|
+
const textBbox = edgeRef.getBBox();
|
|
1874
|
+
setEdgeTextBbox({
|
|
1875
|
+
x: textBbox.x,
|
|
1876
|
+
y: textBbox.y,
|
|
1877
|
+
width: textBbox.width,
|
|
1878
|
+
height: textBbox.height,
|
|
1879
|
+
});
|
|
1880
|
+
}, []);
|
|
1881
|
+
if (typeof label === 'undefined' || !label) {
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
return (jsxs("g", { transform: `translate(${x - edgeTextBbox.width / 2} ${y - edgeTextBbox.height / 2})`, className: edgeTextClasses, visibility: edgeTextBbox.width ? 'visible' : 'hidden', ...rest, children: [labelShowBg && (jsx("rect", { width: edgeTextBbox.width + 2 * labelBgPadding[0], x: -labelBgPadding[0], y: -labelBgPadding[1], height: edgeTextBbox.height + 2 * labelBgPadding[1], className: "react-flow__edge-textbg", style: labelBgStyle, rx: labelBgBorderRadius, ry: labelBgBorderRadius })), jsx("text", { className: "react-flow__edge-text", y: edgeTextBbox.height / 2, dy: "0.3em", ref: onEdgeTextRefChange, style: labelStyle, children: label }), children] }));
|
|
1885
|
+
};
|
|
1886
|
+
var EdgeText$1 = memo(EdgeText);
|
|
1887
|
+
|
|
1888
|
+
const BaseEdge = ({ id, path, labelX, labelY, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, style, markerEnd, markerStart, className, interactionWidth = 20, }) => {
|
|
1889
|
+
return (jsxs(Fragment, { children: [jsx("path", { id: id, style: style, d: path, fill: "none", className: cc(['react-flow__edge-path', className]), markerEnd: markerEnd, markerStart: markerStart }), interactionWidth && (jsx("path", { d: path, fill: "none", strokeOpacity: 0, strokeWidth: interactionWidth, className: "react-flow__edge-interaction" })), label && isNumeric(labelX) && isNumeric(labelY) ? (jsx(EdgeText$1, { x: labelX, y: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius })) : null] }));
|
|
1890
|
+
};
|
|
1891
|
+
BaseEdge.displayName = 'BaseEdge';
|
|
1892
|
+
|
|
1893
|
+
function getControl({ pos, x1, y1, x2, y2 }) {
|
|
1894
|
+
if (pos === Position.Left || pos === Position.Right) {
|
|
1895
|
+
return [0.5 * (x1 + x2), y1];
|
|
1896
|
+
}
|
|
1897
|
+
return [x1, 0.5 * (y1 + y2)];
|
|
1898
|
+
}
|
|
1899
|
+
function getSimpleBezierPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, }) {
|
|
1900
|
+
const [sourceControlX, sourceControlY] = getControl({
|
|
1901
|
+
pos: sourcePosition,
|
|
1902
|
+
x1: sourceX,
|
|
1903
|
+
y1: sourceY,
|
|
1904
|
+
x2: targetX,
|
|
1905
|
+
y2: targetY,
|
|
1906
|
+
});
|
|
1907
|
+
const [targetControlX, targetControlY] = getControl({
|
|
1908
|
+
pos: targetPosition,
|
|
1909
|
+
x1: targetX,
|
|
1910
|
+
y1: targetY,
|
|
1911
|
+
x2: sourceX,
|
|
1912
|
+
y2: sourceY,
|
|
1913
|
+
});
|
|
1914
|
+
const [labelX, labelY, offsetX, offsetY] = getBezierEdgeCenter({
|
|
1915
|
+
sourceX,
|
|
1916
|
+
sourceY,
|
|
1917
|
+
targetX,
|
|
1918
|
+
targetY,
|
|
1919
|
+
sourceControlX,
|
|
1920
|
+
sourceControlY,
|
|
1921
|
+
targetControlX,
|
|
1922
|
+
targetControlY,
|
|
1923
|
+
});
|
|
1924
|
+
return [
|
|
1925
|
+
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
|
|
1926
|
+
labelX,
|
|
1927
|
+
labelY,
|
|
1928
|
+
offsetX,
|
|
1929
|
+
offsetY,
|
|
1930
|
+
];
|
|
1931
|
+
}
|
|
1932
|
+
function createSimpleBezierEdge(params) {
|
|
1933
|
+
// eslint-disable-next-line react/display-name
|
|
1934
|
+
return memo(({ id, sourceX, sourceY, targetX, targetY, sourcePosition = Position.Bottom, targetPosition = Position.Top, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, style, markerEnd, markerStart, interactionWidth, }) => {
|
|
1935
|
+
const [path, labelX, labelY] = getSimpleBezierPath({
|
|
1936
|
+
sourceX,
|
|
1937
|
+
sourceY,
|
|
1938
|
+
sourcePosition,
|
|
1939
|
+
targetX,
|
|
1940
|
+
targetY,
|
|
1941
|
+
targetPosition,
|
|
1942
|
+
});
|
|
1943
|
+
const _id = params.isInternal ? undefined : id;
|
|
1944
|
+
return (jsx(BaseEdge, { id: _id, path: path, labelX: labelX, labelY: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius, style: style, markerEnd: markerEnd, markerStart: markerStart, interactionWidth: interactionWidth }));
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
const SimpleBezierEdge = createSimpleBezierEdge({ isInternal: false });
|
|
1948
|
+
const SimpleBezierEdgeInternal = createSimpleBezierEdge({ isInternal: true });
|
|
1949
|
+
SimpleBezierEdge.displayName = 'SimpleBezierEdge';
|
|
1950
|
+
SimpleBezierEdgeInternal.displayName = 'SimpleBezierEdgeInternal';
|
|
1951
|
+
|
|
1952
|
+
function createSmoothStepEdge(params) {
|
|
1953
|
+
// eslint-disable-next-line react/display-name
|
|
1954
|
+
return memo(({ id, sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, style, sourcePosition = Position.Bottom, targetPosition = Position.Top, markerEnd, markerStart, pathOptions, interactionWidth, }) => {
|
|
1955
|
+
const [path, labelX, labelY] = getSmoothStepPath({
|
|
1956
|
+
sourceX,
|
|
1957
|
+
sourceY,
|
|
1958
|
+
sourcePosition,
|
|
1959
|
+
targetX,
|
|
1960
|
+
targetY,
|
|
1961
|
+
targetPosition,
|
|
1962
|
+
borderRadius: pathOptions?.borderRadius,
|
|
1963
|
+
offset: pathOptions?.offset,
|
|
1964
|
+
});
|
|
1965
|
+
const _id = params.isInternal ? undefined : id;
|
|
1966
|
+
return (jsx(BaseEdge, { id: _id, path: path, labelX: labelX, labelY: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius, style: style, markerEnd: markerEnd, markerStart: markerStart, interactionWidth: interactionWidth }));
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
const SmoothStepEdge = createSmoothStepEdge({ isInternal: false });
|
|
1970
|
+
const SmoothStepEdgeInternal = createSmoothStepEdge({ isInternal: true });
|
|
1971
|
+
SmoothStepEdge.displayName = 'SmoothStepEdge';
|
|
1972
|
+
SmoothStepEdgeInternal.displayName = 'SmoothStepEdgeInternal';
|
|
1973
|
+
|
|
1974
|
+
function createStepEdge(params) {
|
|
1975
|
+
// eslint-disable-next-line react/display-name
|
|
1976
|
+
return memo(({ id, ...props }) => {
|
|
1977
|
+
const _id = params.isInternal ? undefined : id;
|
|
1978
|
+
return (jsx(SmoothStepEdge, { ...props, id: _id, pathOptions: useMemo(() => ({ borderRadius: 0, offset: props.pathOptions?.offset }), [props.pathOptions?.offset]) }));
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
const StepEdge = createStepEdge({ isInternal: false });
|
|
1982
|
+
const StepEdgeInternal = createStepEdge({ isInternal: true });
|
|
1983
|
+
StepEdge.displayName = 'StepEdge';
|
|
1984
|
+
StepEdgeInternal.displayName = 'StepEdgeInternal';
|
|
1985
|
+
|
|
1986
|
+
function createStraightEdge(params) {
|
|
1987
|
+
// eslint-disable-next-line react/display-name
|
|
1988
|
+
return memo(({ id, sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, style, markerEnd, markerStart, interactionWidth, }) => {
|
|
1989
|
+
const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY });
|
|
1990
|
+
const _id = params.isInternal ? undefined : id;
|
|
1991
|
+
return (jsx(BaseEdge, { id: _id, path: path, labelX: labelX, labelY: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius, style: style, markerEnd: markerEnd, markerStart: markerStart, interactionWidth: interactionWidth }));
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
const StraightEdge = createStraightEdge({ isInternal: false });
|
|
1995
|
+
const StraightEdgeInternal = createStraightEdge({ isInternal: true });
|
|
1996
|
+
StraightEdge.displayName = 'StraightEdge';
|
|
1997
|
+
StraightEdgeInternal.displayName = 'StraightEdgeInternal';
|
|
1998
|
+
|
|
1999
|
+
function createBezierEdge(params) {
|
|
2000
|
+
// eslint-disable-next-line react/display-name
|
|
2001
|
+
return memo(({ id, sourceX, sourceY, targetX, targetY, sourcePosition = Position.Bottom, targetPosition = Position.Top, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, style, markerEnd, markerStart, pathOptions, interactionWidth, }) => {
|
|
2002
|
+
const [path, labelX, labelY] = getBezierPath({
|
|
2003
|
+
sourceX,
|
|
2004
|
+
sourceY,
|
|
2005
|
+
sourcePosition,
|
|
2006
|
+
targetX,
|
|
2007
|
+
targetY,
|
|
2008
|
+
targetPosition,
|
|
2009
|
+
curvature: pathOptions?.curvature,
|
|
2010
|
+
});
|
|
2011
|
+
const _id = params.isInternal ? undefined : id;
|
|
2012
|
+
return (jsx(BaseEdge, { id: _id, path: path, labelX: labelX, labelY: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius, style: style, markerEnd: markerEnd, markerStart: markerStart, interactionWidth: interactionWidth }));
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
const BezierEdge = createBezierEdge({ isInternal: false });
|
|
2016
|
+
const BezierEdgeInternal = createBezierEdge({ isInternal: true });
|
|
2017
|
+
BezierEdge.displayName = 'BezierEdge';
|
|
2018
|
+
BezierEdgeInternal.displayName = 'BezierEdgeInternal';
|
|
2019
|
+
|
|
2020
|
+
const builtinEdgeTypes = {
|
|
2021
|
+
default: BezierEdgeInternal,
|
|
2022
|
+
straight: StraightEdgeInternal,
|
|
2023
|
+
step: StepEdgeInternal,
|
|
2024
|
+
smoothstep: SmoothStepEdgeInternal,
|
|
2025
|
+
simplebezier: SimpleBezierEdgeInternal,
|
|
2026
|
+
};
|
|
2027
|
+
const nullPosition = {
|
|
2028
|
+
sourceX: null,
|
|
2029
|
+
sourceY: null,
|
|
2030
|
+
targetX: null,
|
|
2031
|
+
targetY: null,
|
|
2032
|
+
sourcePosition: null,
|
|
2033
|
+
targetPosition: null,
|
|
2034
|
+
};
|
|
2035
|
+
|
|
2036
|
+
const shiftX = (x, shift, position) => {
|
|
2037
|
+
if (position === Position.Left)
|
|
2038
|
+
return x - shift;
|
|
2039
|
+
if (position === Position.Right)
|
|
2040
|
+
return x + shift;
|
|
2041
|
+
return x;
|
|
2042
|
+
};
|
|
2043
|
+
const shiftY = (y, shift, position) => {
|
|
2044
|
+
if (position === Position.Top)
|
|
2045
|
+
return y - shift;
|
|
2046
|
+
if (position === Position.Bottom)
|
|
2047
|
+
return y + shift;
|
|
2048
|
+
return y;
|
|
2049
|
+
};
|
|
2050
|
+
const EdgeUpdaterClassName = 'react-flow__edgeupdater';
|
|
2051
|
+
const EdgeAnchor = ({ position, centerX, centerY, radius = 10, onMouseDown, onMouseEnter, onMouseOut, type, }) => (jsx("circle", { onMouseDown: onMouseDown, onMouseEnter: onMouseEnter, onMouseOut: onMouseOut, className: cc([EdgeUpdaterClassName, `${EdgeUpdaterClassName}-${type}`]), cx: shiftX(centerX, radius, position), cy: shiftY(centerY, radius, position), r: radius, stroke: "transparent", fill: "transparent" }));
|
|
2052
|
+
|
|
2053
|
+
function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleId, sourceHandleId, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, onEdgeUpdate, onEdgeUpdateStart, onEdgeUpdateEnd, setUpdating, setUpdateHover, }) {
|
|
2054
|
+
const store = useStoreApi();
|
|
2055
|
+
const handleEdgeUpdater = (event, isSourceHandle) => {
|
|
2056
|
+
// avoid triggering edge updater if mouse btn is not left
|
|
2057
|
+
if (event.button !== 0) {
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
const { autoPanOnConnect, domNode, isValidConnection, connectionMode, connectionRadius, lib, onConnectStart, onConnectEnd, cancelConnection, nodes, panBy, updateConnection, } = store.getState();
|
|
2061
|
+
const nodeId = isSourceHandle ? edge.target : edge.source;
|
|
2062
|
+
const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null;
|
|
2063
|
+
const handleType = isSourceHandle ? 'target' : 'source';
|
|
2064
|
+
const isTarget = isSourceHandle;
|
|
2065
|
+
setUpdating(true);
|
|
2066
|
+
onEdgeUpdateStart?.(event, edge, handleType);
|
|
2067
|
+
const _onEdgeUpdateEnd = (evt) => {
|
|
2068
|
+
setUpdating(false);
|
|
2069
|
+
onEdgeUpdateEnd?.(evt, edge, handleType);
|
|
2070
|
+
};
|
|
2071
|
+
const onConnectEdge = (connection) => onEdgeUpdate?.(edge, connection);
|
|
2072
|
+
XYHandle.onPointerDown(event.nativeEvent, {
|
|
2073
|
+
autoPanOnConnect,
|
|
2074
|
+
connectionMode,
|
|
2075
|
+
connectionRadius,
|
|
2076
|
+
domNode,
|
|
2077
|
+
handleId,
|
|
2078
|
+
nodeId,
|
|
2079
|
+
nodes,
|
|
2080
|
+
isTarget,
|
|
2081
|
+
edgeUpdaterType: handleType,
|
|
2082
|
+
lib,
|
|
2083
|
+
cancelConnection,
|
|
2084
|
+
panBy,
|
|
2085
|
+
isValidConnection,
|
|
2086
|
+
onConnect: onConnectEdge,
|
|
2087
|
+
onConnectStart,
|
|
2088
|
+
onConnectEnd,
|
|
2089
|
+
onEdgeUpdateEnd: _onEdgeUpdateEnd,
|
|
2090
|
+
updateConnection,
|
|
2091
|
+
getTransform: () => store.getState().transform,
|
|
2092
|
+
});
|
|
2093
|
+
};
|
|
2094
|
+
const onEdgeUpdaterSourceMouseDown = (event) => handleEdgeUpdater(event, true);
|
|
2095
|
+
const onEdgeUpdaterTargetMouseDown = (event) => handleEdgeUpdater(event, false);
|
|
2096
|
+
const onEdgeUpdaterMouseEnter = () => setUpdateHover(true);
|
|
2097
|
+
const onEdgeUpdaterMouseOut = () => setUpdateHover(false);
|
|
2098
|
+
return (jsxs(Fragment, { children: [(isUpdatable === 'source' || isUpdatable === true) && (jsx(EdgeAnchor, { position: sourcePosition, centerX: sourceX, centerY: sourceY, radius: edgeUpdaterRadius, onMouseDown: onEdgeUpdaterSourceMouseDown, onMouseEnter: onEdgeUpdaterMouseEnter, onMouseOut: onEdgeUpdaterMouseOut, type: "source" })), (isUpdatable === 'target' || isUpdatable === true) && (jsx(EdgeAnchor, { position: targetPosition, centerX: targetX, centerY: targetY, radius: edgeUpdaterRadius, onMouseDown: onEdgeUpdaterTargetMouseDown, onMouseEnter: onEdgeUpdaterMouseEnter, onMouseOut: onEdgeUpdaterMouseOut, type: "target" }))] }));
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, onClick, onDoubleClick, sourceHandleId, targetHandleId, onContextMenu, onMouseEnter, onMouseMove, onMouseLeave, edgeUpdaterRadius, onEdgeUpdate, onEdgeUpdateStart, onEdgeUpdateEnd, rfId, edgeTypes, noPanClassName, onError, }) {
|
|
2102
|
+
let edge = useStore((s) => s.edgeLookup.get(id));
|
|
2103
|
+
const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions);
|
|
2104
|
+
edge = defaultEdgeOptions ? { ...defaultEdgeOptions, ...edge } : edge;
|
|
2105
|
+
let edgeType = edge.type || 'default';
|
|
2106
|
+
let EdgeComponent = edgeTypes?.[edgeType] || builtinEdgeTypes[edgeType];
|
|
2107
|
+
if (EdgeComponent === undefined) {
|
|
2108
|
+
onError?.('011', errorMessages['error011'](edgeType));
|
|
2109
|
+
edgeType = 'default';
|
|
2110
|
+
EdgeComponent = builtinEdgeTypes.default;
|
|
2111
|
+
}
|
|
2112
|
+
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
|
2113
|
+
const isUpdatable = typeof onEdgeUpdate !== 'undefined' &&
|
|
2114
|
+
(edge.updatable || (edgesUpdatable && typeof edge.updatable === 'undefined'));
|
|
2115
|
+
const isSelectable = !!(edge.selectable || (elementsSelectable && typeof edge.selectable === 'undefined'));
|
|
2116
|
+
const edgeRef = useRef(null);
|
|
2117
|
+
const [updateHover, setUpdateHover] = useState(false);
|
|
2118
|
+
const [updating, setUpdating] = useState(false);
|
|
2119
|
+
const store = useStoreApi();
|
|
2120
|
+
const { zIndex, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition } = useStore(useCallback((store) => {
|
|
2121
|
+
const sourceNode = store.nodeLookup.get(edge.source);
|
|
2122
|
+
const targetNode = store.nodeLookup.get(edge.target);
|
|
2123
|
+
if (!sourceNode || !targetNode) {
|
|
2124
|
+
return {
|
|
2125
|
+
zIndex: edge.zIndex,
|
|
2126
|
+
...nullPosition,
|
|
2127
|
+
};
|
|
2128
|
+
}
|
|
2129
|
+
const edgePosition = getEdgePosition({
|
|
2130
|
+
id,
|
|
2131
|
+
sourceNode,
|
|
2132
|
+
targetNode,
|
|
2133
|
+
sourceHandle: sourceHandleId || null,
|
|
2134
|
+
targetHandle: targetHandleId || null,
|
|
2135
|
+
connectionMode: store.connectionMode,
|
|
2136
|
+
onError,
|
|
2137
|
+
});
|
|
2138
|
+
const zIndex = getElevatedEdgeZIndex({
|
|
2139
|
+
selected: edge.selected,
|
|
2140
|
+
zIndex: edge.zIndex,
|
|
2141
|
+
sourceNode,
|
|
2142
|
+
targetNode,
|
|
2143
|
+
elevateOnSelect: store.elevateEdgesOnSelect,
|
|
2144
|
+
});
|
|
2145
|
+
return {
|
|
2146
|
+
zIndex,
|
|
2147
|
+
...(edgePosition || nullPosition),
|
|
2148
|
+
};
|
|
2149
|
+
}, [edge.source, edge.target, edge.selected, edge.zIndex]), shallow);
|
|
2150
|
+
const markerStartUrl = useMemo(() => (edge.markerStart ? `url(#${getMarkerId(edge.markerStart, rfId)})` : undefined), [edge.markerStart, rfId]);
|
|
2151
|
+
const markerEndUrl = useMemo(() => (edge.markerEnd ? `url(#${getMarkerId(edge.markerEnd, rfId)})` : undefined), [edge.markerEnd, rfId]);
|
|
2152
|
+
if (edge.hidden || !sourceX || !sourceY || !targetX || !targetY) {
|
|
2153
|
+
return null;
|
|
2154
|
+
}
|
|
2155
|
+
const onEdgeClick = (event) => {
|
|
2156
|
+
const { addSelectedEdges, unselectNodesAndEdges, multiSelectionActive } = store.getState();
|
|
2157
|
+
if (isSelectable) {
|
|
2158
|
+
store.setState({ nodesSelectionActive: false });
|
|
2159
|
+
if (edge.selected && multiSelectionActive) {
|
|
2160
|
+
unselectNodesAndEdges({ nodes: [], edges: [edge] });
|
|
2161
|
+
edgeRef.current?.blur();
|
|
2162
|
+
}
|
|
2163
|
+
else {
|
|
2164
|
+
addSelectedEdges([id]);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
if (onClick) {
|
|
2168
|
+
onClick(event, edge);
|
|
2169
|
+
}
|
|
2170
|
+
};
|
|
2171
|
+
const onEdgeDoubleClick = onDoubleClick
|
|
2172
|
+
? (event) => {
|
|
2173
|
+
onDoubleClick(event, { ...edge });
|
|
2174
|
+
}
|
|
2175
|
+
: undefined;
|
|
2176
|
+
const onEdgeContextMenu = onContextMenu
|
|
2177
|
+
? (event) => {
|
|
2178
|
+
onContextMenu(event, { ...edge });
|
|
2179
|
+
}
|
|
2180
|
+
: undefined;
|
|
2181
|
+
const onEdgeMouseEnter = onMouseEnter
|
|
2182
|
+
? (event) => {
|
|
2183
|
+
onMouseEnter(event, { ...edge });
|
|
2184
|
+
}
|
|
2185
|
+
: undefined;
|
|
2186
|
+
const onEdgeMouseMove = onMouseMove
|
|
2187
|
+
? (event) => {
|
|
2188
|
+
onMouseMove(event, { ...edge });
|
|
2189
|
+
}
|
|
2190
|
+
: undefined;
|
|
2191
|
+
const onEdgeMouseLeave = onMouseLeave
|
|
2192
|
+
? (event) => {
|
|
2193
|
+
onMouseLeave(event, { ...edge });
|
|
2194
|
+
}
|
|
2195
|
+
: undefined;
|
|
2196
|
+
const onKeyDown = (event) => {
|
|
2197
|
+
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
|
2198
|
+
const { unselectNodesAndEdges, addSelectedEdges } = store.getState();
|
|
2199
|
+
const unselect = event.key === 'Escape';
|
|
2200
|
+
if (unselect) {
|
|
2201
|
+
edgeRef.current?.blur();
|
|
2202
|
+
unselectNodesAndEdges({ edges: [edge] });
|
|
2203
|
+
}
|
|
2204
|
+
else {
|
|
2205
|
+
addSelectedEdges([id]);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
return (jsx("svg", { style: { zIndex }, children: jsxs("g", { className: cc([
|
|
2210
|
+
'react-flow__edge',
|
|
2211
|
+
`react-flow__edge-${edgeType}`,
|
|
2212
|
+
edge.className,
|
|
2213
|
+
noPanClassName,
|
|
2214
|
+
{
|
|
2215
|
+
selected: edge.selected,
|
|
2216
|
+
animated: edge.animated,
|
|
2217
|
+
inactive: !isSelectable && !onClick,
|
|
2218
|
+
updating: updateHover,
|
|
2219
|
+
},
|
|
2220
|
+
]), 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: sourceHandleId, targetHandleId: targetHandleId, 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: sourceHandleId, targetHandleId: targetHandleId }))] }) }));
|
|
2221
|
+
}
|
|
2222
|
+
EdgeWrapper.displayName = 'EdgeWrapper';
|
|
2223
|
+
var EdgeWrapper$1 = memo(EdgeWrapper);
|
|
2224
|
+
|
|
2225
|
+
const selector$c = (s) => ({
|
|
2226
|
+
width: s.width,
|
|
2227
|
+
height: s.height,
|
|
2228
|
+
edgesFocusable: s.edgesFocusable,
|
|
2229
|
+
edgesUpdatable: s.edgesUpdatable,
|
|
2230
|
+
elementsSelectable: s.elementsSelectable,
|
|
2231
|
+
connectionMode: s.connectionMode,
|
|
2232
|
+
onError: s.onError,
|
|
2233
|
+
});
|
|
2234
|
+
const EdgeRenderer = ({ defaultMarkerColor, onlyRenderVisibleElements, rfId, edgeTypes, noPanClassName, onEdgeUpdate, onEdgeContextMenu, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, onEdgeClick, edgeUpdaterRadius, onEdgeDoubleClick, onEdgeUpdateStart, onEdgeUpdateEnd, children, }) => {
|
|
2235
|
+
const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector$c, shallow);
|
|
2236
|
+
const edgeIds = useVisibleEdgeIds(onlyRenderVisibleElements);
|
|
2237
|
+
return (jsxs("div", { className: "react-flow__edges", children: [jsx(MarkerDefinitions$1, { defaultColor: defaultMarkerColor, rfId: rfId }), edgeIds.map((id) => {
|
|
2238
|
+
return (jsx(EdgeWrapper$1, { id: id, edgesFocusable: edgesFocusable, edgesUpdatable: edgesUpdatable, elementsSelectable: elementsSelectable, noPanClassName: noPanClassName, onEdgeUpdate: onEdgeUpdate, onContextMenu: onEdgeContextMenu, onMouseEnter: onEdgeMouseEnter, onMouseMove: onEdgeMouseMove, onMouseLeave: onEdgeMouseLeave, onClick: onEdgeClick, edgeUpdaterRadius: edgeUpdaterRadius, onDoubleClick: onEdgeDoubleClick, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, rfId: rfId, onError: onError, edgeTypes: edgeTypes }, id));
|
|
2239
|
+
}), children] }));
|
|
2240
|
+
};
|
|
2241
|
+
EdgeRenderer.displayName = 'EdgeRenderer';
|
|
2242
|
+
var EdgeRenderer$1 = memo(EdgeRenderer);
|
|
2243
|
+
|
|
2244
|
+
const selector$b = (s) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
|
|
2245
|
+
function Viewport({ children }) {
|
|
2246
|
+
const transform = useStore(selector$b);
|
|
2247
|
+
return (jsx("div", { className: "react-flow__viewport xyflow__viewport react-flow__container", style: { transform }, children: children }));
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/**
|
|
2251
|
+
* Hook for calling onInit handler.
|
|
2252
|
+
*
|
|
2253
|
+
* @internal
|
|
2254
|
+
*/
|
|
2255
|
+
function useOnInitHandler(onInit) {
|
|
2256
|
+
const rfInstance = useReactFlow();
|
|
2257
|
+
const isInitialized = useRef(false);
|
|
2258
|
+
useEffect(() => {
|
|
2259
|
+
if (!isInitialized.current && rfInstance.viewportInitialized && onInit) {
|
|
2260
|
+
setTimeout(() => onInit(rfInstance), 1);
|
|
2261
|
+
isInitialized.current = true;
|
|
2262
|
+
}
|
|
2263
|
+
}, [onInit, rfInstance.viewportInitialized]);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
const selector$a = (state) => state.panZoom?.syncViewport;
|
|
2267
|
+
/**
|
|
2268
|
+
* Hook for syncing the viewport with the panzoom instance.
|
|
2269
|
+
*
|
|
2270
|
+
* @internal
|
|
2271
|
+
* @param viewport
|
|
2272
|
+
*/
|
|
2273
|
+
function useViewportSync(viewport) {
|
|
2274
|
+
const syncViewport = useStore(selector$a);
|
|
2275
|
+
const store = useStoreApi();
|
|
2276
|
+
useEffect(() => {
|
|
2277
|
+
if (viewport) {
|
|
2278
|
+
syncViewport?.(viewport);
|
|
2279
|
+
store.setState({ transform: [viewport.x, viewport.y, viewport.zoom] });
|
|
2280
|
+
}
|
|
2281
|
+
}, [viewport, syncViewport]);
|
|
2282
|
+
return null;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
const oppositePosition = {
|
|
2286
|
+
[Position.Left]: Position.Right,
|
|
2287
|
+
[Position.Right]: Position.Left,
|
|
2288
|
+
[Position.Top]: Position.Bottom,
|
|
2289
|
+
[Position.Bottom]: Position.Top,
|
|
2290
|
+
};
|
|
2291
|
+
const ConnectionLine = ({ nodeId, handleType, style, type = ConnectionLineType.Bezier, CustomComponent, connectionStatus, }) => {
|
|
2292
|
+
const { fromNode, handleId, toX, toY, connectionMode } = useStore(useCallback((s) => ({
|
|
2293
|
+
fromNode: s.nodeLookup.get(nodeId),
|
|
2294
|
+
handleId: s.connectionStartHandle?.handleId,
|
|
2295
|
+
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
|
2296
|
+
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
|
2297
|
+
connectionMode: s.connectionMode,
|
|
2298
|
+
}), [nodeId]), shallow);
|
|
2299
|
+
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
|
|
2300
|
+
let handleBounds = fromHandleBounds?.[handleType];
|
|
2301
|
+
if (connectionMode === ConnectionMode.Loose) {
|
|
2302
|
+
handleBounds = handleBounds ? handleBounds : fromHandleBounds?.[handleType === 'source' ? 'target' : 'source'];
|
|
2303
|
+
}
|
|
2304
|
+
if (!fromNode || !handleBounds) {
|
|
2305
|
+
return null;
|
|
2306
|
+
}
|
|
2307
|
+
const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
|
|
2308
|
+
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.computed?.width ?? 0) / 2;
|
|
2309
|
+
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.computed?.height ?? 0;
|
|
2310
|
+
const fromX = (fromNode.computed?.positionAbsolute?.x ?? 0) + fromHandleX;
|
|
2311
|
+
const fromY = (fromNode.computed?.positionAbsolute?.y ?? 0) + fromHandleY;
|
|
2312
|
+
const fromPosition = fromHandle?.position;
|
|
2313
|
+
const toPosition = fromPosition ? oppositePosition[fromPosition] : null;
|
|
2314
|
+
if (!fromPosition || !toPosition) {
|
|
2315
|
+
return null;
|
|
2316
|
+
}
|
|
2317
|
+
if (CustomComponent) {
|
|
2318
|
+
return (jsx(CustomComponent, { connectionLineType: type, connectionLineStyle: style, fromNode: fromNode, fromHandle: fromHandle, fromX: fromX, fromY: fromY, toX: toX, toY: toY, fromPosition: fromPosition, toPosition: toPosition, connectionStatus: connectionStatus }));
|
|
2319
|
+
}
|
|
2320
|
+
let dAttr = '';
|
|
2321
|
+
const pathParams = {
|
|
2322
|
+
sourceX: fromX,
|
|
2323
|
+
sourceY: fromY,
|
|
2324
|
+
sourcePosition: fromPosition,
|
|
2325
|
+
targetX: toX,
|
|
2326
|
+
targetY: toY,
|
|
2327
|
+
targetPosition: toPosition,
|
|
2328
|
+
};
|
|
2329
|
+
if (type === ConnectionLineType.Bezier) {
|
|
2330
|
+
// we assume the destination position is opposite to the source position
|
|
2331
|
+
[dAttr] = getBezierPath(pathParams);
|
|
2332
|
+
}
|
|
2333
|
+
else if (type === ConnectionLineType.Step) {
|
|
2334
|
+
[dAttr] = getSmoothStepPath({
|
|
2335
|
+
...pathParams,
|
|
2336
|
+
borderRadius: 0,
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
else if (type === ConnectionLineType.SmoothStep) {
|
|
2340
|
+
[dAttr] = getSmoothStepPath(pathParams);
|
|
2341
|
+
}
|
|
2342
|
+
else if (type === ConnectionLineType.SimpleBezier) {
|
|
2343
|
+
[dAttr] = getSimpleBezierPath(pathParams);
|
|
2344
|
+
}
|
|
2345
|
+
else {
|
|
2346
|
+
dAttr = `M${fromX},${fromY} ${toX},${toY}`;
|
|
2347
|
+
}
|
|
2348
|
+
return jsx("path", { d: dAttr, fill: "none", className: "react-flow__connection-path", style: style });
|
|
2349
|
+
};
|
|
2350
|
+
ConnectionLine.displayName = 'ConnectionLine';
|
|
2351
|
+
const selector$9 = (s) => ({
|
|
2352
|
+
nodeId: s.connectionStartHandle?.nodeId,
|
|
2353
|
+
handleType: s.connectionStartHandle?.type,
|
|
2354
|
+
nodesConnectable: s.nodesConnectable,
|
|
2355
|
+
connectionStatus: s.connectionStatus,
|
|
2356
|
+
width: s.width,
|
|
2357
|
+
height: s.height,
|
|
2358
|
+
});
|
|
2359
|
+
function ConnectionLineWrapper({ containerStyle, style, type, component }) {
|
|
2360
|
+
const { nodeId, handleType, nodesConnectable, width, height, connectionStatus } = useStore(selector$9, shallow);
|
|
2361
|
+
const isValid = !!(nodeId && handleType && width && nodesConnectable);
|
|
2362
|
+
if (!isValid) {
|
|
2363
|
+
return null;
|
|
2364
|
+
}
|
|
2365
|
+
return (jsx("svg", { style: containerStyle, width: width, height: height, className: "react-flow__edges react-flow__connectionline react-flow__container", children: jsx("g", { className: cc(['react-flow__connection', connectionStatus]), children: jsx(ConnectionLine, { nodeId: nodeId, handleType: handleType, style: style, type: type, CustomComponent: component, connectionStatus: connectionStatus }) }) }));
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
const emptyTypes = {};
|
|
2369
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2370
|
+
function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes = emptyTypes) {
|
|
2371
|
+
const updateCount = useRef(0);
|
|
2372
|
+
const store = useStoreApi();
|
|
2373
|
+
useEffect(() => {
|
|
2374
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2375
|
+
if (updateCount.current > 1) {
|
|
2376
|
+
store.getState().onError?.('002', errorMessages['error002']());
|
|
2377
|
+
}
|
|
2378
|
+
updateCount.current += 1;
|
|
2379
|
+
}
|
|
2380
|
+
}, [nodeOrEdgeTypes]);
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
const GraphView = ({ nodeTypes, edgeTypes, onInit, onNodeClick, onEdgeClick, onNodeDoubleClick, onEdgeDoubleClick, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, onNodeContextMenu, onSelectionContextMenu, onSelectionStart, onSelectionEnd, connectionLineType, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, selectionKeyCode, selectionOnDrag, selectionMode, multiSelectionKeyCode, panActivationKeyCode, zoomActivationKeyCode, deleteKeyCode, onlyRenderVisibleElements, elementsSelectable, defaultViewport, translateExtent, minZoom, maxZoom, preventScrolling, defaultMarkerColor, zoomOnScroll, zoomOnPinch, panOnScroll, panOnScrollSpeed, panOnScrollMode, zoomOnDoubleClick, panOnDrag, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneScroll, onPaneContextMenu, onEdgeUpdate, onEdgeContextMenu, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, edgeUpdaterRadius, onEdgeUpdateStart, onEdgeUpdateEnd, noDragClassName, noWheelClassName, noPanClassName, disableKeyboardA11y, nodeOrigin, nodeExtent, rfId, viewport, onViewportChange, }) => {
|
|
2384
|
+
useNodeOrEdgeTypesWarning(nodeTypes);
|
|
2385
|
+
useNodeOrEdgeTypesWarning(edgeTypes);
|
|
2386
|
+
useOnInitHandler(onInit);
|
|
2387
|
+
useViewportSync(viewport);
|
|
2388
|
+
return (jsx(FlowRenderer$1, { onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneContextMenu: onPaneContextMenu, onPaneScroll: onPaneScroll, deleteKeyCode: deleteKeyCode, selectionKeyCode: selectionKeyCode, selectionOnDrag: selectionOnDrag, selectionMode: selectionMode, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, multiSelectionKeyCode: multiSelectionKeyCode, panActivationKeyCode: panActivationKeyCode, zoomActivationKeyCode: zoomActivationKeyCode, elementsSelectable: elementsSelectable, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, zoomOnDoubleClick: zoomOnDoubleClick, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, panOnDrag: panOnDrag, defaultViewport: defaultViewport, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, onSelectionContextMenu: onSelectionContextMenu, preventScrolling: preventScrolling, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y, onViewportChange: onViewportChange, isControlledViewport: !!viewport, children: jsxs(Viewport, { children: [jsx(EdgeRenderer$1, { edgeTypes: edgeTypes, onEdgeClick: onEdgeClick, onEdgeDoubleClick: onEdgeDoubleClick, onEdgeUpdate: onEdgeUpdate, onlyRenderVisibleElements: onlyRenderVisibleElements, onEdgeContextMenu: onEdgeContextMenu, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, edgeUpdaterRadius: edgeUpdaterRadius, defaultMarkerColor: defaultMarkerColor, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y, rfId: rfId, children: jsx(ConnectionLineWrapper, { style: connectionLineStyle, type: connectionLineType, component: connectionLineComponent, containerStyle: connectionLineContainerStyle }) }), jsx("div", { className: "react-flow__edgelabel-renderer" }), jsx("div", { className: "react-flow__viewport-portal" }), jsx(NodeRenderer$1, { nodeTypes: nodeTypes, onNodeClick: onNodeClick, onNodeDoubleClick: onNodeDoubleClick, onNodeMouseEnter: onNodeMouseEnter, onNodeMouseMove: onNodeMouseMove, onNodeMouseLeave: onNodeMouseLeave, onNodeContextMenu: onNodeContextMenu, onlyRenderVisibleElements: onlyRenderVisibleElements, noPanClassName: noPanClassName, noDragClassName: noDragClassName, disableKeyboardA11y: disableKeyboardA11y, nodeOrigin: nodeOrigin, nodeExtent: nodeExtent, rfId: rfId })] }) }));
|
|
2389
|
+
};
|
|
2390
|
+
GraphView.displayName = 'GraphView';
|
|
2391
|
+
var GraphView$1 = memo(GraphView);
|
|
2392
|
+
|
|
2393
|
+
function handleControlledSelectionChange(changes, items) {
|
|
2394
|
+
return items.map((item) => {
|
|
2395
|
+
const change = changes.find((change) => change.id === item.id);
|
|
2396
|
+
if (change) {
|
|
2397
|
+
item.selected = change.selected;
|
|
2398
|
+
}
|
|
2399
|
+
return item;
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get, set }) {
|
|
2403
|
+
const { nodes, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get();
|
|
2404
|
+
if (changedNodes?.length) {
|
|
2405
|
+
if (hasDefaultNodes) {
|
|
2406
|
+
set({ nodes: handleControlledSelectionChange(changedNodes, nodes) });
|
|
2407
|
+
}
|
|
2408
|
+
onNodesChange?.(changedNodes);
|
|
2409
|
+
}
|
|
2410
|
+
if (changedEdges?.length) {
|
|
2411
|
+
if (hasDefaultEdges) {
|
|
2412
|
+
set({ edges: handleControlledSelectionChange(changedEdges, edges) });
|
|
2413
|
+
}
|
|
2414
|
+
onEdgesChange?.(changedEdges);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {}) => {
|
|
2419
|
+
const nodeLookup = new Map();
|
|
2420
|
+
const connectionLookup = new Map();
|
|
2421
|
+
const edgeLookup = new Map();
|
|
2422
|
+
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
|
2423
|
+
const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
|
2424
|
+
nodeOrigin: [0, 0],
|
|
2425
|
+
elevateNodesOnSelect: false,
|
|
2426
|
+
});
|
|
2427
|
+
let transform = [0, 0, 1];
|
|
2428
|
+
if (fitView && width && height) {
|
|
2429
|
+
const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height);
|
|
2430
|
+
const bounds = getNodesBounds(nodesWithDimensions, [0, 0]);
|
|
2431
|
+
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
|
2432
|
+
transform = [x, y, zoom];
|
|
2433
|
+
}
|
|
2434
|
+
return {
|
|
2435
|
+
rfId: '1',
|
|
2436
|
+
width: 0,
|
|
2437
|
+
height: 0,
|
|
2438
|
+
transform,
|
|
2439
|
+
nodes: nextNodes,
|
|
2440
|
+
nodeLookup,
|
|
2441
|
+
edges,
|
|
2442
|
+
edgeLookup,
|
|
2443
|
+
connectionLookup,
|
|
2444
|
+
onNodesChange: null,
|
|
2445
|
+
onEdgesChange: null,
|
|
2446
|
+
hasDefaultNodes: false,
|
|
2447
|
+
hasDefaultEdges: false,
|
|
2448
|
+
panZoom: null,
|
|
2449
|
+
minZoom: 0.5,
|
|
2450
|
+
maxZoom: 2,
|
|
2451
|
+
translateExtent: infiniteExtent,
|
|
2452
|
+
nodeExtent: infiniteExtent,
|
|
2453
|
+
nodesSelectionActive: false,
|
|
2454
|
+
userSelectionActive: false,
|
|
2455
|
+
userSelectionRect: null,
|
|
2456
|
+
connectionPosition: { x: 0, y: 0 },
|
|
2457
|
+
connectionStatus: null,
|
|
2458
|
+
connectionMode: ConnectionMode.Strict,
|
|
2459
|
+
domNode: null,
|
|
2460
|
+
paneDragging: false,
|
|
2461
|
+
noPanClassName: 'nopan',
|
|
2462
|
+
nodeOrigin: [0, 0],
|
|
2463
|
+
nodeDragThreshold: 1,
|
|
2464
|
+
snapGrid: [15, 15],
|
|
2465
|
+
snapToGrid: false,
|
|
2466
|
+
nodesDraggable: true,
|
|
2467
|
+
nodesConnectable: true,
|
|
2468
|
+
nodesFocusable: true,
|
|
2469
|
+
edgesFocusable: true,
|
|
2470
|
+
edgesUpdatable: true,
|
|
2471
|
+
elementsSelectable: true,
|
|
2472
|
+
elevateNodesOnSelect: true,
|
|
2473
|
+
elevateEdgesOnSelect: false,
|
|
2474
|
+
fitViewOnInit: false,
|
|
2475
|
+
fitViewDone: false,
|
|
2476
|
+
fitViewOnInitOptions: undefined,
|
|
2477
|
+
selectNodesOnDrag: true,
|
|
2478
|
+
multiSelectionActive: false,
|
|
2479
|
+
connectionStartHandle: null,
|
|
2480
|
+
connectionEndHandle: null,
|
|
2481
|
+
connectionClickStartHandle: null,
|
|
2482
|
+
connectOnClick: true,
|
|
2483
|
+
ariaLiveMessage: '',
|
|
2484
|
+
autoPanOnConnect: true,
|
|
2485
|
+
autoPanOnNodeDrag: true,
|
|
2486
|
+
connectionRadius: 20,
|
|
2487
|
+
onError: () => null,
|
|
2488
|
+
isValidConnection: undefined,
|
|
2489
|
+
onSelectionChangeHandlers: [],
|
|
2490
|
+
lib: 'react',
|
|
2491
|
+
};
|
|
2492
|
+
};
|
|
2493
|
+
|
|
2494
|
+
const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) => createWithEqualityFn((set, get) => ({
|
|
2495
|
+
...getInitialState({ nodes, edges, width, height, fitView: fitView$1 }),
|
|
2496
|
+
setNodes: (nodes) => {
|
|
2497
|
+
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2498
|
+
// setNodes() is called exclusively in response to user actions:
|
|
2499
|
+
// - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
|
2500
|
+
// - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
|
|
2501
|
+
//
|
|
2502
|
+
// When this happens, we take the note objects passed by the user and extend them with fields
|
|
2503
|
+
// relevant for internal React Flow operations.
|
|
2504
|
+
// TODO: consider updating the types to reflect the distinction between user-provided nodes and internal nodes.
|
|
2505
|
+
const nodesWithInternalData = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
|
2506
|
+
set({ nodes: nodesWithInternalData });
|
|
2507
|
+
},
|
|
2508
|
+
setEdges: (edges) => {
|
|
2509
|
+
const { connectionLookup, edgeLookup } = get();
|
|
2510
|
+
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
|
2511
|
+
set({ edges });
|
|
2512
|
+
},
|
|
2513
|
+
// when the user works with an uncontrolled flow,
|
|
2514
|
+
// we set a flag `hasDefaultNodes` / `hasDefaultEdges`
|
|
2515
|
+
setDefaultNodesAndEdges: (nodes, edges) => {
|
|
2516
|
+
const hasDefaultNodes = typeof nodes !== 'undefined';
|
|
2517
|
+
const hasDefaultEdges = typeof edges !== 'undefined';
|
|
2518
|
+
const nextState = {
|
|
2519
|
+
hasDefaultNodes,
|
|
2520
|
+
hasDefaultEdges,
|
|
2521
|
+
};
|
|
2522
|
+
if (hasDefaultNodes) {
|
|
2523
|
+
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2524
|
+
nextState.nodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
|
2525
|
+
nodeOrigin,
|
|
2526
|
+
elevateNodesOnSelect,
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
if (hasDefaultEdges) {
|
|
2530
|
+
const { connectionLookup, edgeLookup } = get();
|
|
2531
|
+
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
|
2532
|
+
nextState.edges = edges;
|
|
2533
|
+
}
|
|
2534
|
+
set(nextState);
|
|
2535
|
+
},
|
|
2536
|
+
// Every node gets registerd at a ResizeObserver. Whenever a node
|
|
2537
|
+
// changes its dimensions, this function is called to measure the
|
|
2538
|
+
// new dimensions and update the nodes.
|
|
2539
|
+
updateNodeDimensions: (updates) => {
|
|
2540
|
+
const { onNodesChange, fitView, nodes, nodeLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, } = get();
|
|
2541
|
+
const changes = [];
|
|
2542
|
+
const updatedNodes = updateNodeDimensions(updates, nodes, nodeLookup, domNode, nodeOrigin, (id, dimensions) => {
|
|
2543
|
+
changes.push({
|
|
2544
|
+
id: id,
|
|
2545
|
+
type: 'dimensions',
|
|
2546
|
+
dimensions,
|
|
2547
|
+
});
|
|
2548
|
+
});
|
|
2549
|
+
if (!updatedNodes) {
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
const nextNodes = updateAbsolutePositions(updatedNodes, nodeLookup, nodeOrigin);
|
|
2553
|
+
// we call fitView once initially after all dimensions are set
|
|
2554
|
+
let nextFitViewDone = fitViewDone;
|
|
2555
|
+
if (!fitViewDone && fitViewOnInit) {
|
|
2556
|
+
nextFitViewDone = fitView(nextNodes, {
|
|
2557
|
+
...fitViewOnInitOptions,
|
|
2558
|
+
nodes: fitViewOnInitOptions?.nodes || nextNodes,
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
// here we are cirmumventing the onNodesChange handler
|
|
2562
|
+
// in order to be able to display nodes even if the user
|
|
2563
|
+
// has not provided an onNodesChange handler.
|
|
2564
|
+
// Nodes are only rendered if they have a width and height
|
|
2565
|
+
// attribute which they get from this handler.
|
|
2566
|
+
set({ nodes: nextNodes, fitViewDone: nextFitViewDone });
|
|
2567
|
+
if (changes?.length > 0) {
|
|
2568
|
+
onNodesChange?.(changes);
|
|
2569
|
+
}
|
|
2570
|
+
},
|
|
2571
|
+
updateNodePositions: (nodeDragItems, positionChanged = true, dragging = false) => {
|
|
2572
|
+
const changes = nodeDragItems.map((node) => {
|
|
2573
|
+
const change = {
|
|
2574
|
+
id: node.id,
|
|
2575
|
+
type: 'position',
|
|
2576
|
+
dragging,
|
|
2577
|
+
};
|
|
2578
|
+
if (positionChanged) {
|
|
2579
|
+
change.positionAbsolute = node.computed?.positionAbsolute;
|
|
2580
|
+
change.position = node.position;
|
|
2581
|
+
}
|
|
2582
|
+
return change;
|
|
2583
|
+
});
|
|
2584
|
+
get().triggerNodeChanges(changes);
|
|
2585
|
+
},
|
|
2586
|
+
triggerNodeChanges: (changes) => {
|
|
2587
|
+
const { onNodesChange, nodeLookup, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2588
|
+
if (changes?.length) {
|
|
2589
|
+
if (hasDefaultNodes) {
|
|
2590
|
+
const updatedNodes = applyNodeChanges(changes, nodes);
|
|
2591
|
+
const nextNodes = adoptUserProvidedNodes(updatedNodes, nodeLookup, {
|
|
2592
|
+
nodeOrigin,
|
|
2593
|
+
elevateNodesOnSelect,
|
|
2594
|
+
});
|
|
2595
|
+
set({ nodes: nextNodes });
|
|
2596
|
+
}
|
|
2597
|
+
onNodesChange?.(changes);
|
|
2598
|
+
}
|
|
2599
|
+
},
|
|
2600
|
+
addSelectedNodes: (selectedNodeIds) => {
|
|
2601
|
+
const { multiSelectionActive, edges, nodes } = get();
|
|
2602
|
+
let changedNodes;
|
|
2603
|
+
let changedEdges = null;
|
|
2604
|
+
if (multiSelectionActive) {
|
|
2605
|
+
changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
|
|
2606
|
+
}
|
|
2607
|
+
else {
|
|
2608
|
+
changedNodes = getSelectionChanges(nodes, new Set([...selectedNodeIds]), true);
|
|
2609
|
+
changedEdges = getSelectionChanges(edges);
|
|
2610
|
+
}
|
|
2611
|
+
updateNodesAndEdgesSelections({
|
|
2612
|
+
changedNodes,
|
|
2613
|
+
changedEdges,
|
|
2614
|
+
get,
|
|
2615
|
+
set,
|
|
2616
|
+
});
|
|
2617
|
+
},
|
|
2618
|
+
addSelectedEdges: (selectedEdgeIds) => {
|
|
2619
|
+
const { multiSelectionActive, edges, nodes } = get();
|
|
2620
|
+
let changedEdges;
|
|
2621
|
+
let changedNodes = null;
|
|
2622
|
+
if (multiSelectionActive) {
|
|
2623
|
+
changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
|
|
2624
|
+
}
|
|
2625
|
+
else {
|
|
2626
|
+
changedEdges = getSelectionChanges(edges, new Set([...selectedEdgeIds]));
|
|
2627
|
+
changedNodes = getSelectionChanges(nodes, new Set(), true);
|
|
2628
|
+
}
|
|
2629
|
+
updateNodesAndEdgesSelections({
|
|
2630
|
+
changedNodes,
|
|
2631
|
+
changedEdges,
|
|
2632
|
+
get,
|
|
2633
|
+
set,
|
|
2634
|
+
});
|
|
2635
|
+
},
|
|
2636
|
+
unselectNodesAndEdges: ({ nodes, edges } = {}) => {
|
|
2637
|
+
const { edges: storeEdges, nodes: storeNodes } = get();
|
|
2638
|
+
const nodesToUnselect = nodes ? nodes : storeNodes;
|
|
2639
|
+
const edgesToUnselect = edges ? edges : storeEdges;
|
|
2640
|
+
const changedNodes = nodesToUnselect.map((n) => {
|
|
2641
|
+
n.selected = false;
|
|
2642
|
+
return createSelectionChange(n.id, false);
|
|
2643
|
+
});
|
|
2644
|
+
const changedEdges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false));
|
|
2645
|
+
updateNodesAndEdgesSelections({
|
|
2646
|
+
changedNodes,
|
|
2647
|
+
changedEdges,
|
|
2648
|
+
get,
|
|
2649
|
+
set,
|
|
2650
|
+
});
|
|
2651
|
+
},
|
|
2652
|
+
setMinZoom: (minZoom) => {
|
|
2653
|
+
const { panZoom, maxZoom } = get();
|
|
2654
|
+
panZoom?.setScaleExtent([minZoom, maxZoom]);
|
|
2655
|
+
set({ minZoom });
|
|
2656
|
+
},
|
|
2657
|
+
setMaxZoom: (maxZoom) => {
|
|
2658
|
+
const { panZoom, minZoom } = get();
|
|
2659
|
+
panZoom?.setScaleExtent([minZoom, maxZoom]);
|
|
2660
|
+
set({ maxZoom });
|
|
2661
|
+
},
|
|
2662
|
+
setTranslateExtent: (translateExtent) => {
|
|
2663
|
+
get().panZoom?.setTranslateExtent(translateExtent);
|
|
2664
|
+
set({ translateExtent });
|
|
2665
|
+
},
|
|
2666
|
+
resetSelectedElements: () => {
|
|
2667
|
+
const { edges, nodes } = get();
|
|
2668
|
+
const nodesToUnselect = nodes
|
|
2669
|
+
.filter((e) => e.selected)
|
|
2670
|
+
.map((n) => createSelectionChange(n.id, false));
|
|
2671
|
+
const edgesToUnselect = edges
|
|
2672
|
+
.filter((e) => e.selected)
|
|
2673
|
+
.map((e) => createSelectionChange(e.id, false));
|
|
2674
|
+
updateNodesAndEdgesSelections({
|
|
2675
|
+
changedNodes: nodesToUnselect,
|
|
2676
|
+
changedEdges: edgesToUnselect,
|
|
2677
|
+
get,
|
|
2678
|
+
set,
|
|
2679
|
+
});
|
|
2680
|
+
},
|
|
2681
|
+
setNodeExtent: (nodeExtent) => {
|
|
2682
|
+
const { nodes } = get();
|
|
2683
|
+
set({
|
|
2684
|
+
nodeExtent,
|
|
2685
|
+
nodes: nodes.map((node) => {
|
|
2686
|
+
const positionAbsolute = clampPosition(node.position, nodeExtent);
|
|
2687
|
+
return {
|
|
2688
|
+
...node,
|
|
2689
|
+
computed: {
|
|
2690
|
+
...node.computed,
|
|
2691
|
+
positionAbsolute,
|
|
2692
|
+
},
|
|
2693
|
+
};
|
|
2694
|
+
}),
|
|
2695
|
+
});
|
|
2696
|
+
},
|
|
2697
|
+
panBy: (delta) => {
|
|
2698
|
+
const { transform, width, height, panZoom, translateExtent } = get();
|
|
2699
|
+
return panBy({ delta, panZoom, transform, translateExtent, width, height });
|
|
2700
|
+
},
|
|
2701
|
+
fitView: (nodes, options) => {
|
|
2702
|
+
const { panZoom, width, height, minZoom, maxZoom, nodeOrigin } = get();
|
|
2703
|
+
if (!panZoom) {
|
|
2704
|
+
return false;
|
|
2705
|
+
}
|
|
2706
|
+
return fitView({
|
|
2707
|
+
nodes,
|
|
2708
|
+
width,
|
|
2709
|
+
height,
|
|
2710
|
+
panZoom,
|
|
2711
|
+
minZoom,
|
|
2712
|
+
maxZoom,
|
|
2713
|
+
nodeOrigin,
|
|
2714
|
+
}, options);
|
|
2715
|
+
},
|
|
2716
|
+
cancelConnection: () => set({
|
|
2717
|
+
connectionStatus: null,
|
|
2718
|
+
connectionStartHandle: null,
|
|
2719
|
+
connectionEndHandle: null,
|
|
2720
|
+
}),
|
|
2721
|
+
updateConnection: (params) => {
|
|
2722
|
+
const { connectionStatus, connectionStartHandle, connectionEndHandle, connectionPosition } = get();
|
|
2723
|
+
const currentConnection = {
|
|
2724
|
+
connectionPosition: params.connectionPosition ?? connectionPosition,
|
|
2725
|
+
connectionStatus: params.connectionStatus ?? connectionStatus,
|
|
2726
|
+
connectionStartHandle: params.connectionStartHandle ?? connectionStartHandle,
|
|
2727
|
+
connectionEndHandle: params.connectionEndHandle ?? connectionEndHandle,
|
|
2728
|
+
};
|
|
2729
|
+
set(currentConnection);
|
|
2730
|
+
},
|
|
2731
|
+
reset: () => {
|
|
2732
|
+
// @todo: what should we do about this? Do we still need it?
|
|
2733
|
+
// if you are on a SPA with multiple flows, we want to make sure that the store gets resetted
|
|
2734
|
+
// when you switch pages. Does this reset solves this? Currently it always gets called. This
|
|
2735
|
+
// leads to an emtpy nodes array at the beginning.
|
|
2736
|
+
// set({ ...getInitialState() });
|
|
2737
|
+
},
|
|
2738
|
+
}), Object.is);
|
|
2739
|
+
|
|
2740
|
+
function ReactFlowProvider({ children, initialNodes, initialEdges, initialWidth, initialHeight, fitView, }) {
|
|
2741
|
+
const storeRef = useRef(null);
|
|
2742
|
+
if (!storeRef.current) {
|
|
2743
|
+
storeRef.current = createRFStore({
|
|
2744
|
+
nodes: initialNodes,
|
|
2745
|
+
edges: initialEdges,
|
|
2746
|
+
width: initialWidth,
|
|
2747
|
+
height: initialHeight,
|
|
2748
|
+
fitView,
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
return jsx(Provider$1, { value: storeRef.current, children: children });
|
|
2752
|
+
}
|
|
2753
|
+
ReactFlowProvider.displayName = 'ReactFlowProvider';
|
|
2754
|
+
|
|
2755
|
+
function Wrapper({ children, nodes, edges, width, height, fitView, }) {
|
|
2756
|
+
const isWrapped = useContext(StoreContext);
|
|
2757
|
+
if (isWrapped) {
|
|
2758
|
+
// we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
|
|
2759
|
+
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
|
2760
|
+
return jsx(Fragment, { children: children });
|
|
2761
|
+
}
|
|
2762
|
+
return (jsx(ReactFlowProvider, { initialNodes: nodes, initialEdges: edges, initialWidth: width, initialHeight: height, fitView: fitView, children: children }));
|
|
2763
|
+
}
|
|
2764
|
+
Wrapper.displayName = 'ReactFlowWrapper';
|
|
2765
|
+
|
|
2766
|
+
function getMediaQuery() {
|
|
2767
|
+
if (typeof window === 'undefined' || !window.matchMedia) {
|
|
2768
|
+
return null;
|
|
2769
|
+
}
|
|
2770
|
+
return window.matchMedia('(prefers-color-scheme: dark)');
|
|
2771
|
+
}
|
|
2772
|
+
/**
|
|
2773
|
+
* Hook for receiving the current color mode class 'dark' or 'light'.
|
|
2774
|
+
*
|
|
2775
|
+
* @internal
|
|
2776
|
+
* @param colorMode - The color mode to use ('dark', 'light' or 'system')
|
|
2777
|
+
*/
|
|
2778
|
+
function useColorModeClass(colorMode) {
|
|
2779
|
+
const [colorModeClass, setColorModeClass] = useState(colorMode === 'system' ? null : colorMode);
|
|
2780
|
+
useEffect(() => {
|
|
2781
|
+
if (colorMode !== 'system') {
|
|
2782
|
+
setColorModeClass(colorMode);
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
const mediaQuery = getMediaQuery();
|
|
2786
|
+
const updateColorModeClass = () => setColorModeClass(mediaQuery?.matches ? 'dark' : 'light');
|
|
2787
|
+
updateColorModeClass();
|
|
2788
|
+
mediaQuery?.addEventListener('change', updateColorModeClass);
|
|
2789
|
+
return () => {
|
|
2790
|
+
mediaQuery?.removeEventListener('change', updateColorModeClass);
|
|
2791
|
+
};
|
|
2792
|
+
}, [colorMode]);
|
|
2793
|
+
return colorModeClass !== null ? colorModeClass : getMediaQuery()?.matches ? 'dark' : 'light';
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
const initNodeOrigin = [0, 0];
|
|
2797
|
+
const initDefaultViewport = { x: 0, y: 0, zoom: 1 };
|
|
2798
|
+
const wrapperStyle = {
|
|
2799
|
+
width: '100%',
|
|
2800
|
+
height: '100%',
|
|
2801
|
+
overflow: 'hidden',
|
|
2802
|
+
position: 'relative',
|
|
2803
|
+
zIndex: 0,
|
|
2804
|
+
};
|
|
2805
|
+
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 = initNodeOrigin, edgesFocusable, edgesUpdatable, elementsSelectable = true, defaultViewport = initDefaultViewport, 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) => {
|
|
2806
|
+
const rfId = id || '1';
|
|
2807
|
+
const colorModeClassName = useColorModeClass(colorMode);
|
|
2808
|
+
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$1, { 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(Wrapper$1, { onSelectionChange: onSelectionChange }), children, jsx(Attribution, { proOptions: proOptions, position: attributionPosition }), jsx(A11yDescriptions, { rfId: rfId, disableKeyboardA11y: disableKeyboardA11y })] }) }));
|
|
2809
|
+
});
|
|
2810
|
+
ReactFlow.displayName = 'ReactFlow';
|
|
2811
|
+
|
|
2812
|
+
const selector$8 = (s) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
|
|
2813
|
+
function EdgeLabelRenderer({ children }) {
|
|
2814
|
+
const edgeLabelRenderer = useStore(selector$8);
|
|
2815
|
+
if (!edgeLabelRenderer) {
|
|
2816
|
+
return null;
|
|
2817
|
+
}
|
|
2818
|
+
return createPortal(children, edgeLabelRenderer);
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
const selector$7 = (s) => s.domNode?.querySelector('.react-flow__viewport-portal');
|
|
2822
|
+
function ViewportPortal({ children }) {
|
|
2823
|
+
const viewPortalDiv = useStore(selector$7);
|
|
2824
|
+
if (!viewPortalDiv) {
|
|
2825
|
+
return null;
|
|
2826
|
+
}
|
|
2827
|
+
return createPortal(children, viewPortalDiv);
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
/**
|
|
2831
|
+
* Hook for updating node internals.
|
|
2832
|
+
*
|
|
2833
|
+
* @public
|
|
2834
|
+
* @returns function for updating node internals
|
|
2835
|
+
*/
|
|
2836
|
+
function useUpdateNodeInternals() {
|
|
2837
|
+
const store = useStoreApi();
|
|
2838
|
+
return useCallback((id) => {
|
|
2839
|
+
const { domNode, updateNodeDimensions } = store.getState();
|
|
2840
|
+
const updateIds = Array.isArray(id) ? id : [id];
|
|
2841
|
+
const updates = new Map();
|
|
2842
|
+
updateIds.forEach((updateId) => {
|
|
2843
|
+
const nodeElement = domNode?.querySelector(`.react-flow__node[data-id="${updateId}"]`);
|
|
2844
|
+
if (nodeElement) {
|
|
2845
|
+
updates.set(updateId, { id: updateId, nodeElement, forceUpdate: true });
|
|
2846
|
+
}
|
|
2847
|
+
});
|
|
2848
|
+
requestAnimationFrame(() => updateNodeDimensions(updates));
|
|
2849
|
+
}, []);
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
const nodesSelector = (state) => state.nodes;
|
|
2853
|
+
/**
|
|
2854
|
+
* Hook for getting the current nodes from the store.
|
|
2855
|
+
*
|
|
2856
|
+
* @public
|
|
2857
|
+
* @returns An array of nodes
|
|
2858
|
+
*/
|
|
2859
|
+
function useNodes() {
|
|
2860
|
+
const nodes = useStore(nodesSelector, shallow);
|
|
2861
|
+
return nodes;
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
const edgesSelector = (state) => state.edges;
|
|
2865
|
+
/**
|
|
2866
|
+
* Hook for getting the current edges from the store.
|
|
2867
|
+
*
|
|
2868
|
+
* @public
|
|
2869
|
+
* @returns An array of edges
|
|
2870
|
+
*/
|
|
2871
|
+
function useEdges() {
|
|
2872
|
+
const edges = useStore(edgesSelector, shallow);
|
|
2873
|
+
return edges;
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
const viewportSelector = (state) => ({
|
|
2877
|
+
x: state.transform[0],
|
|
2878
|
+
y: state.transform[1],
|
|
2879
|
+
zoom: state.transform[2],
|
|
2880
|
+
});
|
|
2881
|
+
/**
|
|
2882
|
+
* Hook for getting the current viewport from the store.
|
|
2883
|
+
*
|
|
2884
|
+
* @public
|
|
2885
|
+
* @returns The current viewport
|
|
2886
|
+
*/
|
|
2887
|
+
function useViewport() {
|
|
2888
|
+
const viewport = useStore(viewportSelector, shallow);
|
|
2889
|
+
return viewport;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
/**
|
|
2893
|
+
* Hook for managing the state of nodes - should only be used for prototyping / simple use cases.
|
|
2894
|
+
*
|
|
2895
|
+
* @public
|
|
2896
|
+
* @param initialNodes
|
|
2897
|
+
* @returns an array [nodes, setNodes, onNodesChange]
|
|
2898
|
+
*/
|
|
2899
|
+
function useNodesState(initialNodes) {
|
|
2900
|
+
const [nodes, setNodes] = useState(initialNodes);
|
|
2901
|
+
const onNodesChange = useCallback((changes) => setNodes((nds) => applyNodeChanges(changes, nds)), []);
|
|
2902
|
+
return [nodes, setNodes, onNodesChange];
|
|
2903
|
+
}
|
|
2904
|
+
/**
|
|
2905
|
+
* Hook for managing the state of edges - should only be used for prototyping / simple use cases.
|
|
2906
|
+
*
|
|
2907
|
+
* @public
|
|
2908
|
+
* @param initialEdges
|
|
2909
|
+
* @returns an array [edges, setEdges, onEdgesChange]
|
|
2910
|
+
*/
|
|
2911
|
+
function useEdgesState(initialEdges) {
|
|
2912
|
+
const [edges, setEdges] = useState(initialEdges);
|
|
2913
|
+
const onEdgesChange = useCallback((changes) => setEdges((eds) => applyEdgeChanges(changes, eds)), []);
|
|
2914
|
+
return [edges, setEdges, onEdgesChange];
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
/**
|
|
2918
|
+
* Hook for registering an onViewportChange handler.
|
|
2919
|
+
*
|
|
2920
|
+
* @public
|
|
2921
|
+
* @param params.onStart - gets called when the viewport starts changing
|
|
2922
|
+
* @param params.onChange - gets called when the viewport changes
|
|
2923
|
+
* @param params.onEnd - gets called when the viewport stops changing
|
|
2924
|
+
*/
|
|
2925
|
+
function useOnViewportChange({ onStart, onChange, onEnd }) {
|
|
2926
|
+
const store = useStoreApi();
|
|
2927
|
+
useEffect(() => {
|
|
2928
|
+
store.setState({ onViewportChangeStart: onStart });
|
|
2929
|
+
}, [onStart]);
|
|
2930
|
+
useEffect(() => {
|
|
2931
|
+
store.setState({ onViewportChange: onChange });
|
|
2932
|
+
}, [onChange]);
|
|
2933
|
+
useEffect(() => {
|
|
2934
|
+
store.setState({ onViewportChangeEnd: onEnd });
|
|
2935
|
+
}, [onEnd]);
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
/**
|
|
2939
|
+
* Hook for registering an onSelectionChange handler.
|
|
2940
|
+
*
|
|
2941
|
+
* @public
|
|
2942
|
+
* @params params.onChange - The handler to register
|
|
2943
|
+
*/
|
|
2944
|
+
function useOnSelectionChange({ onChange }) {
|
|
2945
|
+
const store = useStoreApi();
|
|
2946
|
+
useEffect(() => {
|
|
2947
|
+
const nextOnSelectionChangeHandlers = [...store.getState().onSelectionChangeHandlers, onChange];
|
|
2948
|
+
store.setState({ onSelectionChangeHandlers: nextOnSelectionChangeHandlers });
|
|
2949
|
+
return () => {
|
|
2950
|
+
const nextHandlers = store.getState().onSelectionChangeHandlers.filter((fn) => fn !== onChange);
|
|
2951
|
+
store.setState({ onSelectionChangeHandlers: nextHandlers });
|
|
2952
|
+
};
|
|
2953
|
+
}, [onChange]);
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
const selector$6 = (options) => (s) => {
|
|
2957
|
+
if (s.nodes.length === 0) {
|
|
2958
|
+
return false;
|
|
2959
|
+
}
|
|
2960
|
+
return s.nodes
|
|
2961
|
+
.filter((n) => (options.includeHiddenNodes ? true : !n.hidden))
|
|
2962
|
+
.every((n) => n[internalsSymbol]?.handleBounds !== undefined);
|
|
2963
|
+
};
|
|
2964
|
+
const defaultOptions = {
|
|
2965
|
+
includeHiddenNodes: false,
|
|
2966
|
+
};
|
|
2967
|
+
/**
|
|
2968
|
+
* Hook which returns true when all nodes are initialized.
|
|
2969
|
+
*
|
|
2970
|
+
* @public
|
|
2971
|
+
* @param options.includeHiddenNodes - defaults to false
|
|
2972
|
+
* @returns boolean indicating whether all nodes are initialized
|
|
2973
|
+
*/
|
|
2974
|
+
function useNodesInitialized(options = defaultOptions) {
|
|
2975
|
+
const initialized = useStore(selector$6(options));
|
|
2976
|
+
return initialized;
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
/**
|
|
2980
|
+
* Hook to check if a <Handle /> is connected to another <Handle /> and get the connections.
|
|
2981
|
+
*
|
|
2982
|
+
* @public
|
|
2983
|
+
* @param param.type - handle type 'source' or 'target'
|
|
2984
|
+
* @param param.nodeId - node id - if not provided, the node id from the NodeIdContext is used
|
|
2985
|
+
* @param param.id - the handle id (this is only needed if the node has multiple handles of the same type)
|
|
2986
|
+
* @param param.onConnect - gets called when a connection is established
|
|
2987
|
+
* @param param.onDisconnect - gets called when a connection is removed
|
|
2988
|
+
* @returns an array with connections
|
|
2989
|
+
*/
|
|
2990
|
+
function useHandleConnections({ type, id = null, nodeId, onConnect, onDisconnect, }) {
|
|
2991
|
+
const _nodeId = useNodeId();
|
|
2992
|
+
const prevConnections = useRef(null);
|
|
2993
|
+
const currentNodeId = nodeId || _nodeId;
|
|
2994
|
+
const connections = useStore((state) => state.connectionLookup.get(`${currentNodeId}-${type}-${id}`), areConnectionMapsEqual);
|
|
2995
|
+
useEffect(() => {
|
|
2996
|
+
// @todo dicuss if onConnect/onDisconnect should be called when the component mounts/unmounts
|
|
2997
|
+
if (prevConnections.current && prevConnections.current !== connections) {
|
|
2998
|
+
const _connections = connections ?? new Map();
|
|
2999
|
+
handleConnectionChange(prevConnections.current, _connections, onDisconnect);
|
|
3000
|
+
handleConnectionChange(_connections, prevConnections.current, onConnect);
|
|
3001
|
+
}
|
|
3002
|
+
prevConnections.current = connections ?? new Map();
|
|
3003
|
+
}, [connections, onConnect, onDisconnect]);
|
|
3004
|
+
return useMemo(() => Array.from(connections?.values() ?? []), [connections]);
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3008
|
+
function useNodesData(nodeIds) {
|
|
3009
|
+
const nodesData = useStore(useCallback((s) => {
|
|
3010
|
+
if (!Array.isArray(nodeIds)) {
|
|
3011
|
+
return s.nodeLookup.get(nodeIds)?.data || null;
|
|
3012
|
+
}
|
|
3013
|
+
const data = [];
|
|
3014
|
+
for (const nodeId of nodeIds) {
|
|
3015
|
+
const nodeData = s.nodeLookup.get(nodeId)?.data;
|
|
3016
|
+
if (nodeData) {
|
|
3017
|
+
data.push(nodeData);
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
return data;
|
|
3021
|
+
}, [nodeIds]), shallow);
|
|
3022
|
+
return nodesData;
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
const selector$5 = (s) => ({
|
|
3026
|
+
startHandle: s.connectionStartHandle,
|
|
3027
|
+
endHandle: s.connectionEndHandle,
|
|
3028
|
+
status: s.connectionStatus,
|
|
3029
|
+
position: s.connectionStartHandle ? s.connectionPosition : null,
|
|
3030
|
+
});
|
|
3031
|
+
/**
|
|
3032
|
+
* Hook for accessing the ongoing connection.
|
|
3033
|
+
*
|
|
3034
|
+
* @public
|
|
3035
|
+
* @returns ongoing connection: startHandle, endHandle, status, position
|
|
3036
|
+
*/
|
|
3037
|
+
function useConnection() {
|
|
3038
|
+
const ongoingConnection = useStore(selector$5, shallow);
|
|
3039
|
+
return ongoingConnection;
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
var BackgroundVariant;
|
|
3043
|
+
(function (BackgroundVariant) {
|
|
3044
|
+
BackgroundVariant["Lines"] = "lines";
|
|
3045
|
+
BackgroundVariant["Dots"] = "dots";
|
|
3046
|
+
BackgroundVariant["Cross"] = "cross";
|
|
3047
|
+
})(BackgroundVariant || (BackgroundVariant = {}));
|
|
3048
|
+
|
|
3049
|
+
function LinePattern({ dimensions, lineWidth, variant, className }) {
|
|
3050
|
+
return (jsx("path", { strokeWidth: lineWidth, d: `M${dimensions[0] / 2} 0 V${dimensions[1]} M0 ${dimensions[1] / 2} H${dimensions[0]}`, className: cc(['react-flow__background-pattern', variant, className]) }));
|
|
3051
|
+
}
|
|
3052
|
+
function DotPattern({ radius, className }) {
|
|
3053
|
+
return (jsx("circle", { cx: radius, cy: radius, r: radius, className: cc(['react-flow__background-pattern', 'dots', className]) }));
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
const defaultSize = {
|
|
3057
|
+
[BackgroundVariant.Dots]: 1,
|
|
3058
|
+
[BackgroundVariant.Lines]: 1,
|
|
3059
|
+
[BackgroundVariant.Cross]: 6,
|
|
3060
|
+
};
|
|
3061
|
+
const selector$4 = (s) => ({ transform: s.transform, patternId: `pattern-${s.rfId}` });
|
|
3062
|
+
function Background({ id, variant = BackgroundVariant.Dots,
|
|
3063
|
+
// only used for dots and cross
|
|
3064
|
+
gap = 20,
|
|
3065
|
+
// only used for lines and cross
|
|
3066
|
+
size, lineWidth = 1, offset = 2, color, bgColor, style, className, patternClassName, }) {
|
|
3067
|
+
const ref = useRef(null);
|
|
3068
|
+
const { transform, patternId } = useStore(selector$4, shallow);
|
|
3069
|
+
const patternSize = size || defaultSize[variant];
|
|
3070
|
+
const isDots = variant === BackgroundVariant.Dots;
|
|
3071
|
+
const isCross = variant === BackgroundVariant.Cross;
|
|
3072
|
+
const gapXY = Array.isArray(gap) ? gap : [gap, gap];
|
|
3073
|
+
const scaledGap = [gapXY[0] * transform[2] || 1, gapXY[1] * transform[2] || 1];
|
|
3074
|
+
const scaledSize = patternSize * transform[2];
|
|
3075
|
+
const patternDimensions = isCross ? [scaledSize, scaledSize] : scaledGap;
|
|
3076
|
+
const patternOffset = isDots
|
|
3077
|
+
? [scaledSize / offset, scaledSize / offset]
|
|
3078
|
+
: [patternDimensions[0] / offset, patternDimensions[1] / offset];
|
|
3079
|
+
const _patternId = `${patternId}${id ? id : ''}`;
|
|
3080
|
+
return (jsxs("svg", { className: cc(['react-flow__background', className]), style: {
|
|
3081
|
+
...style,
|
|
3082
|
+
...containerStyle,
|
|
3083
|
+
'--xy-background-color-props': bgColor,
|
|
3084
|
+
'--xy-background-pattern-color-props': color,
|
|
3085
|
+
}, ref: ref, "data-testid": "rf__background", children: [jsx("pattern", { id: _patternId, x: transform[0] % scaledGap[0], y: transform[1] % scaledGap[1], width: scaledGap[0], height: scaledGap[1], patternUnits: "userSpaceOnUse", patternTransform: `translate(-${patternOffset[0]},-${patternOffset[1]})`, children: isDots ? (jsx(DotPattern, { radius: scaledSize / offset, className: patternClassName })) : (jsx(LinePattern, { dimensions: patternDimensions, lineWidth: lineWidth, variant: variant, className: patternClassName })) }), jsx("rect", { x: "0", y: "0", width: "100%", height: "100%", fill: `url(#${_patternId})` })] }));
|
|
3086
|
+
}
|
|
3087
|
+
Background.displayName = 'Background';
|
|
3088
|
+
var Background$1 = memo(Background);
|
|
3089
|
+
|
|
3090
|
+
function PlusIcon() {
|
|
3091
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 32 32", children: jsx("path", { d: "M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z" }) }));
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
function MinusIcon() {
|
|
3095
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 32 5", children: jsx("path", { d: "M0 0h32v4.2H0z" }) }));
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
function FitViewIcon() {
|
|
3099
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 32 30", children: jsx("path", { d: "M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z" }) }));
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
function LockIcon() {
|
|
3103
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 25 32", children: jsx("path", { d: "M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z" }) }));
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
function UnlockIcon() {
|
|
3107
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 25 32", children: jsx("path", { d: "M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z" }) }));
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
const ControlButton = ({ children, className, ...rest }) => (jsx("button", { type: "button", className: cc(['react-flow__controls-button', className]), ...rest, children: children }));
|
|
3111
|
+
ControlButton.displayName = 'ControlButton';
|
|
3112
|
+
|
|
3113
|
+
const selector$3 = (s) => ({
|
|
3114
|
+
isInteractive: s.nodesDraggable || s.nodesConnectable || s.elementsSelectable,
|
|
3115
|
+
minZoomReached: s.transform[2] <= s.minZoom,
|
|
3116
|
+
maxZoomReached: s.transform[2] >= s.maxZoom,
|
|
3117
|
+
});
|
|
3118
|
+
const Controls = ({ style, showZoom = true, showFitView = true, showInteractive = true, fitViewOptions, onZoomIn, onZoomOut, onFitView, onInteractiveChange, className, children, position = 'bottom-left', }) => {
|
|
3119
|
+
const store = useStoreApi();
|
|
3120
|
+
const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector$3, shallow);
|
|
3121
|
+
const { zoomIn, zoomOut, fitView } = useReactFlow();
|
|
3122
|
+
const onZoomInHandler = () => {
|
|
3123
|
+
zoomIn();
|
|
3124
|
+
onZoomIn?.();
|
|
3125
|
+
};
|
|
3126
|
+
const onZoomOutHandler = () => {
|
|
3127
|
+
zoomOut();
|
|
3128
|
+
onZoomOut?.();
|
|
3129
|
+
};
|
|
3130
|
+
const onFitViewHandler = () => {
|
|
3131
|
+
fitView(fitViewOptions);
|
|
3132
|
+
onFitView?.();
|
|
3133
|
+
};
|
|
3134
|
+
const onToggleInteractivity = () => {
|
|
3135
|
+
store.setState({
|
|
3136
|
+
nodesDraggable: !isInteractive,
|
|
3137
|
+
nodesConnectable: !isInteractive,
|
|
3138
|
+
elementsSelectable: !isInteractive,
|
|
3139
|
+
});
|
|
3140
|
+
onInteractiveChange?.(!isInteractive);
|
|
3141
|
+
};
|
|
3142
|
+
return (jsxs(Panel, { className: cc(['react-flow__controls', className]), position: position, style: style, "data-testid": "rf__controls", children: [showZoom && (jsxs(Fragment, { children: [jsx(ControlButton, { onClick: onZoomInHandler, className: "react-flow__controls-zoomin", title: "zoom in", "aria-label": "zoom in", disabled: maxZoomReached, children: jsx(PlusIcon, {}) }), jsx(ControlButton, { onClick: onZoomOutHandler, className: "react-flow__controls-zoomout", title: "zoom out", "aria-label": "zoom out", disabled: minZoomReached, children: jsx(MinusIcon, {}) })] })), showFitView && (jsx(ControlButton, { className: "react-flow__controls-fitview", onClick: onFitViewHandler, title: "fit view", "aria-label": "fit view", children: jsx(FitViewIcon, {}) })), showInteractive && (jsx(ControlButton, { className: "react-flow__controls-interactive", onClick: onToggleInteractivity, title: "toggle interactivity", "aria-label": "toggle interactivity", children: isInteractive ? jsx(UnlockIcon, {}) : jsx(LockIcon, {}) })), children] }));
|
|
3143
|
+
};
|
|
3144
|
+
Controls.displayName = 'Controls';
|
|
3145
|
+
var Controls$1 = memo(Controls);
|
|
3146
|
+
|
|
3147
|
+
function MiniMapNode({ id, x, y, width, height, style, color, strokeColor, strokeWidth, className, borderRadius, shapeRendering, selected, onClick, }) {
|
|
3148
|
+
const { background, backgroundColor } = style || {};
|
|
3149
|
+
const fill = (color || background || backgroundColor);
|
|
3150
|
+
return (jsx("rect", { className: cc(['react-flow__minimap-node', { selected }, className]), x: x, y: y, rx: borderRadius, ry: borderRadius, width: width, height: height, style: {
|
|
3151
|
+
fill,
|
|
3152
|
+
stroke: strokeColor,
|
|
3153
|
+
strokeWidth,
|
|
3154
|
+
}, shapeRendering: shapeRendering, onClick: onClick ? (event) => onClick(event, id) : undefined }));
|
|
3155
|
+
}
|
|
3156
|
+
var MiniMapNode$1 = memo(MiniMapNode);
|
|
3157
|
+
|
|
3158
|
+
const selector$2 = (s) => s.nodeOrigin;
|
|
3159
|
+
const selectorNodeIds = (s) => s.nodes.map((node) => node.id);
|
|
3160
|
+
const getAttrFunction = (func) => (func instanceof Function ? func : () => func);
|
|
3161
|
+
function MiniMapNodes({ nodeStrokeColor, nodeColor, nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth,
|
|
3162
|
+
// We need to rename the prop to be `CapitalCase` so that JSX will render it as
|
|
3163
|
+
// a component properly.
|
|
3164
|
+
nodeComponent: NodeComponent = MiniMapNode$1, onClick, }) {
|
|
3165
|
+
const nodeIds = useStore(selectorNodeIds, shallow);
|
|
3166
|
+
const nodeOrigin = useStore(selector$2);
|
|
3167
|
+
const nodeColorFunc = getAttrFunction(nodeColor);
|
|
3168
|
+
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
|
3169
|
+
const nodeClassNameFunc = getAttrFunction(nodeClassName);
|
|
3170
|
+
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
|
|
3171
|
+
return (jsx(Fragment, { children: nodeIds.map((nodeId) => (
|
|
3172
|
+
// The split of responsibilities between MiniMapNodes and
|
|
3173
|
+
// NodeComponentWrapper may appear weird. However, it’s designed to
|
|
3174
|
+
// minimize the cost of updates when individual nodes change.
|
|
3175
|
+
//
|
|
3176
|
+
// For more details, see a similar commit in `NodeRenderer/index.tsx`.
|
|
3177
|
+
jsx(NodeComponentWrapper, { id: nodeId, nodeOrigin: nodeOrigin, nodeColorFunc: nodeColorFunc, nodeStrokeColorFunc: nodeStrokeColorFunc, nodeClassNameFunc: nodeClassNameFunc, nodeBorderRadius: nodeBorderRadius, nodeStrokeWidth: nodeStrokeWidth, NodeComponent: NodeComponent, onClick: onClick, shapeRendering: shapeRendering }, nodeId))) }));
|
|
3178
|
+
}
|
|
3179
|
+
const NodeComponentWrapper = memo(function NodeComponentWrapper({ id, nodeOrigin, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, nodeBorderRadius, nodeStrokeWidth, shapeRendering, NodeComponent, onClick, }) {
|
|
3180
|
+
const { node, x, y } = useStore((s) => {
|
|
3181
|
+
const node = s.nodeLookup.get(id);
|
|
3182
|
+
const { x, y } = getNodePositionWithOrigin(node, node?.origin || nodeOrigin).positionAbsolute;
|
|
3183
|
+
return {
|
|
3184
|
+
node,
|
|
3185
|
+
x,
|
|
3186
|
+
y,
|
|
3187
|
+
};
|
|
3188
|
+
}, shallow);
|
|
3189
|
+
if (!node || node.hidden || !(node.computed?.width || node.width) || !(node.computed?.height || node.height)) {
|
|
3190
|
+
return null;
|
|
3191
|
+
}
|
|
3192
|
+
return (jsx(NodeComponent, { x: x, y: y, width: node.computed?.width ?? node.width ?? 0, height: node.computed?.height ?? node.height ?? 0, style: node.style, selected: !!node.selected, className: nodeClassNameFunc(node), color: nodeColorFunc(node), borderRadius: nodeBorderRadius, strokeColor: nodeStrokeColorFunc(node), strokeWidth: nodeStrokeWidth, shapeRendering: shapeRendering, onClick: onClick, id: node.id }));
|
|
3193
|
+
});
|
|
3194
|
+
var MiniMapNodes$1 = memo(MiniMapNodes);
|
|
3195
|
+
|
|
3196
|
+
const defaultWidth = 200;
|
|
3197
|
+
const defaultHeight = 150;
|
|
3198
|
+
const selector$1 = (s) => {
|
|
3199
|
+
const viewBB = {
|
|
3200
|
+
x: -s.transform[0] / s.transform[2],
|
|
3201
|
+
y: -s.transform[1] / s.transform[2],
|
|
3202
|
+
width: s.width / s.transform[2],
|
|
3203
|
+
height: s.height / s.transform[2],
|
|
3204
|
+
};
|
|
3205
|
+
return {
|
|
3206
|
+
viewBB,
|
|
3207
|
+
boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getNodesBounds(s.nodes, s.nodeOrigin), viewBB) : viewBB,
|
|
3208
|
+
rfId: s.rfId,
|
|
3209
|
+
nodeOrigin: s.nodeOrigin,
|
|
3210
|
+
panZoom: s.panZoom,
|
|
3211
|
+
translateExtent: s.translateExtent,
|
|
3212
|
+
flowWidth: s.width,
|
|
3213
|
+
flowHeight: s.height,
|
|
3214
|
+
};
|
|
3215
|
+
};
|
|
3216
|
+
const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
|
|
3217
|
+
function MiniMap({ style, className, nodeStrokeColor, nodeColor, nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth,
|
|
3218
|
+
// We need to rename the prop to be `CapitalCase` so that JSX will render it as
|
|
3219
|
+
// a component properly.
|
|
3220
|
+
nodeComponent, maskColor, maskStrokeColor = 'none', maskStrokeWidth = 1, position = 'bottom-right', onClick, onNodeClick, pannable = false, zoomable = false, ariaLabel = 'React Flow mini map', inversePan, zoomStep = 10, offsetScale = 5, }) {
|
|
3221
|
+
const store = useStoreApi();
|
|
3222
|
+
const svg = useRef(null);
|
|
3223
|
+
const { boundingRect, viewBB, rfId, panZoom, translateExtent, flowWidth, flowHeight } = useStore(selector$1, shallow);
|
|
3224
|
+
const elementWidth = style?.width ?? defaultWidth;
|
|
3225
|
+
const elementHeight = style?.height ?? defaultHeight;
|
|
3226
|
+
const scaledWidth = boundingRect.width / elementWidth;
|
|
3227
|
+
const scaledHeight = boundingRect.height / elementHeight;
|
|
3228
|
+
const viewScale = Math.max(scaledWidth, scaledHeight);
|
|
3229
|
+
const viewWidth = viewScale * elementWidth;
|
|
3230
|
+
const viewHeight = viewScale * elementHeight;
|
|
3231
|
+
const offset = offsetScale * viewScale;
|
|
3232
|
+
const x = boundingRect.x - (viewWidth - boundingRect.width) / 2 - offset;
|
|
3233
|
+
const y = boundingRect.y - (viewHeight - boundingRect.height) / 2 - offset;
|
|
3234
|
+
const width = viewWidth + offset * 2;
|
|
3235
|
+
const height = viewHeight + offset * 2;
|
|
3236
|
+
const labelledBy = `${ARIA_LABEL_KEY}-${rfId}`;
|
|
3237
|
+
const viewScaleRef = useRef(0);
|
|
3238
|
+
const minimapInstance = useRef();
|
|
3239
|
+
viewScaleRef.current = viewScale;
|
|
3240
|
+
useEffect(() => {
|
|
3241
|
+
if (svg.current && panZoom) {
|
|
3242
|
+
minimapInstance.current = XYMinimap({
|
|
3243
|
+
domNode: svg.current,
|
|
3244
|
+
panZoom,
|
|
3245
|
+
getTransform: () => store.getState().transform,
|
|
3246
|
+
getViewScale: () => viewScaleRef.current,
|
|
3247
|
+
});
|
|
3248
|
+
return () => {
|
|
3249
|
+
minimapInstance.current?.destroy();
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
}, [panZoom]);
|
|
3253
|
+
useEffect(() => {
|
|
3254
|
+
minimapInstance.current?.update({
|
|
3255
|
+
translateExtent,
|
|
3256
|
+
width: flowWidth,
|
|
3257
|
+
height: flowHeight,
|
|
3258
|
+
inversePan,
|
|
3259
|
+
pannable,
|
|
3260
|
+
zoomStep,
|
|
3261
|
+
zoomable,
|
|
3262
|
+
});
|
|
3263
|
+
}, [pannable, zoomable, inversePan, zoomStep, translateExtent, flowWidth, flowHeight]);
|
|
3264
|
+
const onSvgClick = onClick
|
|
3265
|
+
? (event) => {
|
|
3266
|
+
const [x, y] = minimapInstance.current?.pointer(event) || [0, 0];
|
|
3267
|
+
onClick(event, { x, y });
|
|
3268
|
+
}
|
|
3269
|
+
: undefined;
|
|
3270
|
+
const onSvgNodeClick = onNodeClick
|
|
3271
|
+
? useCallback((event, nodeId) => {
|
|
3272
|
+
const node = store.getState().nodeLookup.get(nodeId);
|
|
3273
|
+
onNodeClick(event, node);
|
|
3274
|
+
}, [])
|
|
3275
|
+
: undefined;
|
|
3276
|
+
return (jsx(Panel, { position: position, style: {
|
|
3277
|
+
...style,
|
|
3278
|
+
'--xy-minimap-mask-color-props': typeof maskColor === 'string' ? maskColor : undefined,
|
|
3279
|
+
'--xy-minimap-node-background-color-props': typeof nodeColor === 'string' ? nodeColor : undefined,
|
|
3280
|
+
'--xy-minimap-node-stroke-color-props': typeof nodeStrokeColor === 'string' ? nodeStrokeColor : undefined,
|
|
3281
|
+
'--xy-minimap-node-stroke-width-props': typeof nodeStrokeWidth === 'string' ? nodeStrokeWidth : undefined,
|
|
3282
|
+
}, 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
|
|
3283
|
+
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`, fillRule: "evenodd", stroke: maskStrokeColor, strokeWidth: maskStrokeWidth, pointerEvents: "none" })] }) }));
|
|
3284
|
+
}
|
|
3285
|
+
MiniMap.displayName = 'MiniMap';
|
|
3286
|
+
var MiniMap$1 = memo(MiniMap);
|
|
3287
|
+
|
|
3288
|
+
var ResizeControlVariant;
|
|
3289
|
+
(function (ResizeControlVariant) {
|
|
3290
|
+
ResizeControlVariant["Line"] = "line";
|
|
3291
|
+
ResizeControlVariant["Handle"] = "handle";
|
|
3292
|
+
})(ResizeControlVariant || (ResizeControlVariant = {}));
|
|
3293
|
+
|
|
3294
|
+
// returns an array of two numbers (0, 1 or -1) representing the direction of the resize
|
|
3295
|
+
// 0 = no change, 1 = increase, -1 = decrease
|
|
3296
|
+
function getDirection({ width, prevWidth, height, prevHeight, invertX, invertY }) {
|
|
3297
|
+
const deltaWidth = width - prevWidth;
|
|
3298
|
+
const deltaHeight = height - prevHeight;
|
|
3299
|
+
const direction = [deltaWidth > 0 ? 1 : deltaWidth < 0 ? -1 : 0, deltaHeight > 0 ? 1 : deltaHeight < 0 ? -1 : 0];
|
|
3300
|
+
if (deltaWidth && invertX) {
|
|
3301
|
+
direction[0] = direction[0] * -1;
|
|
3302
|
+
}
|
|
3303
|
+
if (deltaHeight && invertY) {
|
|
3304
|
+
direction[1] = direction[1] * -1;
|
|
3305
|
+
}
|
|
3306
|
+
return direction;
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
const initPrevValues = { width: 0, height: 0, x: 0, y: 0 };
|
|
3310
|
+
const initStartValues = {
|
|
3311
|
+
...initPrevValues,
|
|
3312
|
+
pointerX: 0,
|
|
3313
|
+
pointerY: 0,
|
|
3314
|
+
aspectRatio: 1,
|
|
3315
|
+
};
|
|
3316
|
+
function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle, className, style = {}, children, color, minWidth = 10, minHeight = 10, maxWidth = Number.MAX_VALUE, maxHeight = Number.MAX_VALUE, keepAspectRatio = false, shouldResize, onResizeStart, onResize, onResizeEnd, }) {
|
|
3317
|
+
const contextNodeId = useNodeId();
|
|
3318
|
+
const id = typeof nodeId === 'string' ? nodeId : contextNodeId;
|
|
3319
|
+
const store = useStoreApi();
|
|
3320
|
+
const resizeControlRef = useRef(null);
|
|
3321
|
+
const startValues = useRef(initStartValues);
|
|
3322
|
+
const prevValues = useRef(initPrevValues);
|
|
3323
|
+
const defaultPosition = variant === ResizeControlVariant.Line ? 'right' : 'bottom-right';
|
|
3324
|
+
const controlPosition = position ?? defaultPosition;
|
|
3325
|
+
useEffect(() => {
|
|
3326
|
+
if (!resizeControlRef.current || !id) {
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
const selection = select(resizeControlRef.current);
|
|
3330
|
+
const enableX = controlPosition.includes('right') || controlPosition.includes('left');
|
|
3331
|
+
const enableY = controlPosition.includes('bottom') || controlPosition.includes('top');
|
|
3332
|
+
const invertX = controlPosition.includes('left');
|
|
3333
|
+
const invertY = controlPosition.includes('top');
|
|
3334
|
+
const dragHandler = drag()
|
|
3335
|
+
.on('start', (event) => {
|
|
3336
|
+
const { nodeLookup, transform, snapGrid, snapToGrid } = store.getState();
|
|
3337
|
+
const node = nodeLookup.get(id);
|
|
3338
|
+
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
|
3339
|
+
prevValues.current = {
|
|
3340
|
+
width: node?.computed?.width ?? 0,
|
|
3341
|
+
height: node?.computed?.height ?? 0,
|
|
3342
|
+
x: node?.position.x ?? 0,
|
|
3343
|
+
y: node?.position.y ?? 0,
|
|
3344
|
+
};
|
|
3345
|
+
startValues.current = {
|
|
3346
|
+
...prevValues.current,
|
|
3347
|
+
pointerX: xSnapped,
|
|
3348
|
+
pointerY: ySnapped,
|
|
3349
|
+
aspectRatio: prevValues.current.width / prevValues.current.height,
|
|
3350
|
+
};
|
|
3351
|
+
onResizeStart?.(event, { ...prevValues.current });
|
|
3352
|
+
})
|
|
3353
|
+
.on('drag', (event) => {
|
|
3354
|
+
const { nodeLookup, transform, snapGrid, snapToGrid, triggerNodeChanges } = store.getState();
|
|
3355
|
+
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
|
3356
|
+
const node = nodeLookup.get(id);
|
|
3357
|
+
if (node) {
|
|
3358
|
+
const changes = [];
|
|
3359
|
+
const { pointerX: startX, pointerY: startY, width: startWidth, height: startHeight, x: startNodeX, y: startNodeY, aspectRatio, } = startValues.current;
|
|
3360
|
+
const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues.current;
|
|
3361
|
+
const distX = Math.floor(enableX ? xSnapped - startX : 0);
|
|
3362
|
+
const distY = Math.floor(enableY ? ySnapped - startY : 0);
|
|
3363
|
+
let width = clamp(startWidth + (invertX ? -distX : distX), minWidth, maxWidth);
|
|
3364
|
+
let height = clamp(startHeight + (invertY ? -distY : distY), minHeight, maxHeight);
|
|
3365
|
+
if (keepAspectRatio) {
|
|
3366
|
+
const nextAspectRatio = width / height;
|
|
3367
|
+
const isDiagonal = enableX && enableY;
|
|
3368
|
+
const isHorizontal = enableX && !enableY;
|
|
3369
|
+
const isVertical = enableY && !enableX;
|
|
3370
|
+
width = (nextAspectRatio <= aspectRatio && isDiagonal) || isVertical ? height * aspectRatio : width;
|
|
3371
|
+
height = (nextAspectRatio > aspectRatio && isDiagonal) || isHorizontal ? width / aspectRatio : height;
|
|
3372
|
+
if (width >= maxWidth) {
|
|
3373
|
+
width = maxWidth;
|
|
3374
|
+
height = maxWidth / aspectRatio;
|
|
3375
|
+
}
|
|
3376
|
+
else if (width <= minWidth) {
|
|
3377
|
+
width = minWidth;
|
|
3378
|
+
height = minWidth / aspectRatio;
|
|
3379
|
+
}
|
|
3380
|
+
if (height >= maxHeight) {
|
|
3381
|
+
height = maxHeight;
|
|
3382
|
+
width = maxHeight * aspectRatio;
|
|
3383
|
+
}
|
|
3384
|
+
else if (height <= minHeight) {
|
|
3385
|
+
height = minHeight;
|
|
3386
|
+
width = minHeight * aspectRatio;
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
const isWidthChange = width !== prevWidth;
|
|
3390
|
+
const isHeightChange = height !== prevHeight;
|
|
3391
|
+
if (invertX || invertY) {
|
|
3392
|
+
const x = invertX ? startNodeX - (width - startWidth) : startNodeX;
|
|
3393
|
+
const y = invertY ? startNodeY - (height - startHeight) : startNodeY;
|
|
3394
|
+
// only transform the node if the width or height changes
|
|
3395
|
+
const isXPosChange = x !== prevX && isWidthChange;
|
|
3396
|
+
const isYPosChange = y !== prevY && isHeightChange;
|
|
3397
|
+
if (isXPosChange || isYPosChange) {
|
|
3398
|
+
const positionChange = {
|
|
3399
|
+
id: node.id,
|
|
3400
|
+
type: 'position',
|
|
3401
|
+
position: {
|
|
3402
|
+
x: isXPosChange ? x : prevX,
|
|
3403
|
+
y: isYPosChange ? y : prevY,
|
|
3404
|
+
},
|
|
3405
|
+
};
|
|
3406
|
+
changes.push(positionChange);
|
|
3407
|
+
prevValues.current.x = positionChange.position.x;
|
|
3408
|
+
prevValues.current.y = positionChange.position.y;
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
if (isWidthChange || isHeightChange) {
|
|
3412
|
+
const dimensionChange = {
|
|
3413
|
+
id: id,
|
|
3414
|
+
type: 'dimensions',
|
|
3415
|
+
updateStyle: true,
|
|
3416
|
+
resizing: true,
|
|
3417
|
+
dimensions: {
|
|
3418
|
+
width: width,
|
|
3419
|
+
height: height,
|
|
3420
|
+
},
|
|
3421
|
+
};
|
|
3422
|
+
changes.push(dimensionChange);
|
|
3423
|
+
prevValues.current.width = width;
|
|
3424
|
+
prevValues.current.height = height;
|
|
3425
|
+
}
|
|
3426
|
+
if (changes.length === 0) {
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
const direction = getDirection({
|
|
3430
|
+
width: prevValues.current.width,
|
|
3431
|
+
prevWidth,
|
|
3432
|
+
height: prevValues.current.height,
|
|
3433
|
+
prevHeight,
|
|
3434
|
+
invertX,
|
|
3435
|
+
invertY,
|
|
3436
|
+
});
|
|
3437
|
+
const nextValues = { ...prevValues.current, direction };
|
|
3438
|
+
const callResize = shouldResize?.(event, nextValues);
|
|
3439
|
+
if (callResize === false) {
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
onResize?.(event, nextValues);
|
|
3443
|
+
triggerNodeChanges(changes);
|
|
3444
|
+
}
|
|
3445
|
+
})
|
|
3446
|
+
.on('end', (event) => {
|
|
3447
|
+
const dimensionChange = {
|
|
3448
|
+
id: id,
|
|
3449
|
+
type: 'dimensions',
|
|
3450
|
+
resizing: false,
|
|
3451
|
+
};
|
|
3452
|
+
onResizeEnd?.(event, { ...prevValues.current });
|
|
3453
|
+
store.getState().triggerNodeChanges([dimensionChange]);
|
|
3454
|
+
});
|
|
3455
|
+
selection.call(dragHandler);
|
|
3456
|
+
return () => {
|
|
3457
|
+
selection.on('.drag', null);
|
|
3458
|
+
};
|
|
3459
|
+
}, [
|
|
3460
|
+
id,
|
|
3461
|
+
controlPosition,
|
|
3462
|
+
minWidth,
|
|
3463
|
+
minHeight,
|
|
3464
|
+
maxWidth,
|
|
3465
|
+
maxHeight,
|
|
3466
|
+
keepAspectRatio,
|
|
3467
|
+
onResizeStart,
|
|
3468
|
+
onResize,
|
|
3469
|
+
onResizeEnd,
|
|
3470
|
+
]);
|
|
3471
|
+
const positionClassNames = controlPosition.split('-');
|
|
3472
|
+
const colorStyleProp = variant === ResizeControlVariant.Line ? 'borderColor' : 'backgroundColor';
|
|
3473
|
+
const controlStyle = color ? { ...style, [colorStyleProp]: color } : style;
|
|
3474
|
+
return (jsx("div", { className: cc(['react-flow__resize-control', 'nodrag', ...positionClassNames, variant, className]), ref: resizeControlRef, style: controlStyle, children: children }));
|
|
3475
|
+
}
|
|
3476
|
+
var ResizeControl$1 = memo(ResizeControl);
|
|
3477
|
+
|
|
3478
|
+
const handleControls = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
|
3479
|
+
const lineControls = ['top', 'right', 'bottom', 'left'];
|
|
3480
|
+
function NodeResizer({ nodeId, isVisible = true, handleClassName, handleStyle, lineClassName, lineStyle, color, minWidth = 10, minHeight = 10, maxWidth = Number.MAX_VALUE, maxHeight = Number.MAX_VALUE, keepAspectRatio = false, shouldResize, onResizeStart, onResize, onResizeEnd, }) {
|
|
3481
|
+
if (!isVisible) {
|
|
3482
|
+
return null;
|
|
3483
|
+
}
|
|
3484
|
+
return (jsxs(Fragment, { children: [lineControls.map((c) => (jsx(ResizeControl$1, { className: lineClassName, style: lineStyle, nodeId: nodeId, position: c, variant: ResizeControlVariant.Line, color: color, minWidth: minWidth, minHeight: minHeight, maxWidth: maxWidth, maxHeight: maxHeight, onResizeStart: onResizeStart, keepAspectRatio: keepAspectRatio, shouldResize: shouldResize, onResize: onResize, onResizeEnd: onResizeEnd }, c))), handleControls.map((c) => (jsx(ResizeControl$1, { className: handleClassName, style: handleStyle, nodeId: nodeId, position: c, color: color, minWidth: minWidth, minHeight: minHeight, maxWidth: maxWidth, maxHeight: maxHeight, onResizeStart: onResizeStart, keepAspectRatio: keepAspectRatio, shouldResize: shouldResize, onResize: onResize, onResizeEnd: onResizeEnd }, c)))] }));
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
const selector = (state) => state.domNode?.querySelector('.react-flow__renderer');
|
|
3488
|
+
function NodeToolbarPortal({ children }) {
|
|
3489
|
+
const wrapperRef = useStore(selector);
|
|
3490
|
+
if (!wrapperRef) {
|
|
3491
|
+
return null;
|
|
3492
|
+
}
|
|
3493
|
+
return createPortal(children, wrapperRef);
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
const nodeEqualityFn = (a, b) => a?.computed?.positionAbsolute?.x !== b?.computed?.positionAbsolute?.x ||
|
|
3497
|
+
a?.computed?.positionAbsolute?.y !== b?.computed?.positionAbsolute?.y ||
|
|
3498
|
+
a?.computed?.width !== b?.computed?.width ||
|
|
3499
|
+
a?.computed?.height !== b?.computed?.height ||
|
|
3500
|
+
a?.selected !== b?.selected ||
|
|
3501
|
+
a?.[internalsSymbol]?.z !== b?.[internalsSymbol]?.z;
|
|
3502
|
+
const nodesEqualityFn = (a, b) => {
|
|
3503
|
+
if (a.length !== b.length) {
|
|
3504
|
+
return false;
|
|
3505
|
+
}
|
|
3506
|
+
return !a.some((node, i) => nodeEqualityFn(node, b[i]));
|
|
3507
|
+
};
|
|
3508
|
+
const storeSelector = (state) => ({
|
|
3509
|
+
viewport: {
|
|
3510
|
+
x: state.transform[0],
|
|
3511
|
+
y: state.transform[1],
|
|
3512
|
+
zoom: state.transform[2],
|
|
3513
|
+
},
|
|
3514
|
+
nodeOrigin: state.nodeOrigin,
|
|
3515
|
+
selectedNodesCount: state.nodes.filter((node) => node.selected).length,
|
|
3516
|
+
});
|
|
3517
|
+
function NodeToolbar({ nodeId, children, className, style, isVisible, position = Position.Top, offset = 10, align = 'center', ...rest }) {
|
|
3518
|
+
const contextNodeId = useNodeId();
|
|
3519
|
+
const nodesSelector = useCallback((state) => {
|
|
3520
|
+
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ''];
|
|
3521
|
+
return nodeIds.reduce((acc, id) => {
|
|
3522
|
+
const node = state.nodeLookup.get(id);
|
|
3523
|
+
if (node) {
|
|
3524
|
+
acc.push(node);
|
|
3525
|
+
}
|
|
3526
|
+
return acc;
|
|
3527
|
+
}, []);
|
|
3528
|
+
}, [nodeId, contextNodeId]);
|
|
3529
|
+
const nodes = useStore(nodesSelector, nodesEqualityFn);
|
|
3530
|
+
const { viewport, nodeOrigin, selectedNodesCount } = useStore(storeSelector, shallow);
|
|
3531
|
+
// if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
|
|
3532
|
+
const isActive = typeof isVisible === 'boolean' ? isVisible : nodes.length === 1 && nodes[0].selected && selectedNodesCount === 1;
|
|
3533
|
+
if (!isActive || !nodes.length) {
|
|
3534
|
+
return null;
|
|
3535
|
+
}
|
|
3536
|
+
const nodeRect = getNodesBounds(nodes, nodeOrigin);
|
|
3537
|
+
const zIndex = Math.max(...nodes.map((node) => (node[internalsSymbol]?.z || 1) + 1));
|
|
3538
|
+
const wrapperStyle = {
|
|
3539
|
+
position: 'absolute',
|
|
3540
|
+
transform: getNodeToolbarTransform(nodeRect, viewport, position, offset, align),
|
|
3541
|
+
zIndex,
|
|
3542
|
+
...style,
|
|
3543
|
+
};
|
|
3544
|
+
return (jsx(NodeToolbarPortal, { children: jsx("div", { style: wrapperStyle, className: cc(['react-flow__node-toolbar', className]), ...rest, "data-id": nodes.reduce((acc, node) => `${acc}${node.id} `, '').trim(), children: children }) }));
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
export { Background$1 as Background, BackgroundVariant, BaseEdge, BezierEdge, ControlButton, Controls$1 as Controls, EdgeLabelRenderer, EdgeText$1 as EdgeText, Handle$1 as Handle, MiniMap$1 as MiniMap, ResizeControl$1 as NodeResizeControl, NodeResizer, NodeToolbar, Panel, ReactFlow, ReactFlowProvider, ResizeControlVariant, SimpleBezierEdge, SmoothStepEdge, StepEdge, StraightEdge, ViewportPortal, addEdge, applyEdgeChanges, applyNodeChanges, getConnectedEdges, getIncomers, getOutgoers, getSimpleBezierPath, handleParentExpand, isEdge, isNode, updateEdge, useConnection, useEdges, useEdgesState, useHandleConnections, useKeyPress, useNodeId, useNodes, useNodesData, useNodesInitialized, useNodesState, useOnSelectionChange, useOnViewportChange, useReactFlow, useStore, useStoreApi, useUpdateNodeInternals, useViewport };
|