import React, { useEffect, useRef, memo, CSSProperties } from 'react'; import classnames from 'classnames'; import { ResizeObserver } from 'resize-observer'; import { useStoreState, useStoreActions } from '../../store/hooks'; import NodeRenderer from '../NodeRenderer'; import EdgeRenderer from '../EdgeRenderer'; import UserSelection from '../../components/UserSelection'; import NodesSelection from '../../components/NodesSelection'; import useKeyPress from '../../hooks/useKeyPress'; import useD3Zoom from '../../hooks/useD3Zoom'; import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler'; import useElementUpdater from '../../hooks/useElementUpdater'; import { getDimensions } from '../../utils'; import { fitView, zoomIn, zoomOut, project, getElements } from '../../utils/graph'; import { Elements, NodeTypesType, EdgeTypesType, OnLoadFunc, Node, Edge, Connection, ConnectionLineType, } from '../../types'; export interface GraphViewProps { elements: Elements; onElementClick?: (element: Node | Edge) => void; onElementsRemove?: (elements: Elements) => void; onNodeDragStart?: (node: Node) => void; onNodeDragStop?: (node: Node) => void; onConnect?: (connection: Connection | Edge) => void; onLoad?: OnLoadFunc; onMove?: () => void; selectionKeyCode: number; nodeTypes: NodeTypesType; edgeTypes: EdgeTypesType; connectionLineType: ConnectionLineType; connectionLineStyle?: CSSProperties; deleteKeyCode: number; snapToGrid: boolean; snapGrid: [number, number]; onlyRenderVisibleNodes: boolean; isInteractive: boolean; selectNodesOnDrag: boolean; minZoom: number; maxZoom: number; defaultZoom: number; } const GraphView = memo( ({ nodeTypes, edgeTypes, onMove, onLoad, onElementClick, onNodeDragStart, onNodeDragStop, connectionLineType, connectionLineStyle, selectionKeyCode, onElementsRemove, deleteKeyCode, elements, onConnect, snapToGrid, snapGrid, onlyRenderVisibleNodes, isInteractive, selectNodesOnDrag, minZoom, maxZoom, defaultZoom, }: GraphViewProps) => { const zoomPane = useRef(null); const rendererNode = useRef(null); const width = useStoreState((s) => s.width); const height = useStoreState((s) => s.height); const d3Initialised = useStoreState((s) => s.d3Initialised); const nodesSelectionActive = useStoreState((s) => s.nodesSelectionActive); const updateSize = useStoreActions((actions) => actions.updateSize); const setNodesSelection = useStoreActions((actions) => actions.setNodesSelection); const setOnConnect = useStoreActions((a) => a.setOnConnect); const setSnapGrid = useStoreActions((actions) => actions.setSnapGrid); const setInteractive = useStoreActions((actions) => actions.setInteractive); const updateTransform = useStoreActions((actions) => actions.updateTransform); const setMinMaxZoom = useStoreActions((actions) => actions.setMinMaxZoom); const selectionKeyPressed = useKeyPress(selectionKeyCode); const rendererClasses = classnames('react-flow__renderer', { 'is-interactive': isInteractive }); const onZoomPaneClick = () => setNodesSelection({ isActive: false }); const updateDimensions = () => { if (!rendererNode.current) { return; } const size = getDimensions(rendererNode.current); if (size.height === 0 || size.width === 0) { throw new Error('The React Flow parent container needs a width and a height to render the graph.'); } updateSize(size); }; useEffect(() => { let resizeObserver: ResizeObserver; updateDimensions(); window.onresize = updateDimensions; if (onConnect) { setOnConnect(onConnect); } if (defaultZoom !== 1) { updateTransform({ x: 0, y: 0, k: defaultZoom }); } if (rendererNode.current) { resizeObserver = new ResizeObserver((entries) => { for (let _ of entries) { updateDimensions(); } }); resizeObserver.observe(rendererNode.current); } return () => { window.onresize = null; if (resizeObserver && rendererNode.current) { resizeObserver.unobserve(rendererNode.current!); } }; }, []); useD3Zoom({ zoomPane, onMove, selectionKeyPressed }); useEffect(() => { if (d3Initialised && onLoad) { onLoad({ fitView, zoomIn, zoomOut, project, getElements, }); } }, [d3Initialised, onLoad]); useEffect(() => { setSnapGrid({ snapToGrid, snapGrid }); }, [snapToGrid]); useEffect(() => { setInteractive(isInteractive); }, [isInteractive]); useEffect(() => { setMinMaxZoom({ minZoom, maxZoom }); }, [minZoom, maxZoom]); useGlobalKeyHandler({ onElementsRemove, deleteKeyCode }); useElementUpdater(elements); return (
{selectionKeyPressed && } {nodesSelectionActive && }
); } ); GraphView.displayName = 'GraphView'; export default GraphView;