import React, { FC, useCallback, useEffect, useRef } from 'react'; import { GraphEdge, GraphNode } from './utils'; interface OwnProps { nodes: GraphNode[]; edges: GraphEdge[]; direction?: 'UD' | 'DU' | 'LR' | 'RL'; onDoubleClick?: (node: string) => void; width?: string; height?: string; } interface ConnectedProps {} interface DispatchProps {} export type Props = OwnProps & ConnectedProps & DispatchProps; export const NetworkGraph: FC = ({ nodes, edges, direction, width, height, onDoubleClick }) => { const network = useRef(null); const ref = useRef(null); const onNodeDoubleClick = useCallback( (params: { nodes: string[] }) => { if (onDoubleClick) { onDoubleClick(params.nodes[0]); } }, [onDoubleClick] ); useEffect(() => { const createNetwork = async () => { // @ts-ignore no types yet for visjs-network const visJs = await import(/* webpackChunkName: "visjs-network" */ 'visjs-network'); const data = { nodes: toVisNetworkNodes(visJs, nodes), edges: toVisNetworkEdges(visJs, edges), }; const options = { width: '100%', height: '100%', autoResize: true, layout: { improvedLayout: true, hierarchical: { enabled: true, direction: direction ?? 'DU', sortMethod: 'directed', }, }, interaction: { navigationButtons: true, dragNodes: false, }, }; network.current = new visJs.Network(ref.current, data, options); network.current?.on('doubleClick', onNodeDoubleClick); }; createNetwork(); return () => { // unsubscribe event handlers if (network.current) { network.current.off('doubleClick'); } }; }, [direction, edges, nodes, onNodeDoubleClick]); return (
); }; function toVisNetworkNodes(visJs: any, nodes: GraphNode[]): any[] { const nodesWithStyle: any[] = nodes.map((node) => ({ ...node, shape: 'box', })); return new visJs.DataSet(nodesWithStyle); } function toVisNetworkEdges(visJs: any, edges: GraphEdge[]): any[] { const edgesWithStyle: any[] = edges.map((edge) => ({ ...edge, arrows: 'to', dashes: true })); return new visJs.DataSet(edgesWithStyle); }