NetworkGraph.tsx 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import React, { FC, useCallback, useEffect, useRef } from 'react';
  2. import { GraphEdge, GraphNode } from './utils';
  3. interface OwnProps {
  4. nodes: GraphNode[];
  5. edges: GraphEdge[];
  6. direction?: 'UD' | 'DU' | 'LR' | 'RL';
  7. onDoubleClick?: (node: string) => void;
  8. width?: string;
  9. height?: string;
  10. }
  11. interface ConnectedProps {}
  12. interface DispatchProps {}
  13. export type Props = OwnProps & ConnectedProps & DispatchProps;
  14. export const NetworkGraph: FC<Props> = ({ nodes, edges, direction, width, height, onDoubleClick }) => {
  15. const network = useRef<any>(null);
  16. const ref = useRef(null);
  17. const onNodeDoubleClick = useCallback(
  18. (params: { nodes: string[] }) => {
  19. if (onDoubleClick) {
  20. onDoubleClick(params.nodes[0]);
  21. }
  22. },
  23. [onDoubleClick]
  24. );
  25. useEffect(() => {
  26. const createNetwork = async () => {
  27. // @ts-ignore no types yet for visjs-network
  28. const visJs = await import(/* webpackChunkName: "visjs-network" */ 'visjs-network');
  29. const data = {
  30. nodes: toVisNetworkNodes(visJs, nodes),
  31. edges: toVisNetworkEdges(visJs, edges),
  32. };
  33. const options = {
  34. width: '100%',
  35. height: '100%',
  36. autoResize: true,
  37. layout: {
  38. improvedLayout: true,
  39. hierarchical: {
  40. enabled: true,
  41. direction: direction ?? 'DU',
  42. sortMethod: 'directed',
  43. },
  44. },
  45. interaction: {
  46. navigationButtons: true,
  47. dragNodes: false,
  48. },
  49. };
  50. network.current = new visJs.Network(ref.current, data, options);
  51. network.current?.on('doubleClick', onNodeDoubleClick);
  52. };
  53. createNetwork();
  54. return () => {
  55. // unsubscribe event handlers
  56. if (network.current) {
  57. network.current.off('doubleClick');
  58. }
  59. };
  60. }, [direction, edges, nodes, onNodeDoubleClick]);
  61. return (
  62. <div>
  63. <div ref={ref} style={{ width: width ?? '100%', height: height ?? '60vh' }} />
  64. </div>
  65. );
  66. };
  67. function toVisNetworkNodes(visJs: any, nodes: GraphNode[]): any[] {
  68. const nodesWithStyle: any[] = nodes.map((node) => ({
  69. ...node,
  70. shape: 'box',
  71. }));
  72. return new visJs.DataSet(nodesWithStyle);
  73. }
  74. function toVisNetworkEdges(visJs: any, edges: GraphEdge[]): any[] {
  75. const edgesWithStyle: any[] = edges.map((edge) => ({ ...edge, arrows: 'to', dashes: true }));
  76. return new visJs.DataSet(edgesWithStyle);
  77. }