Mirror of https://github.com/roostorg/osprey github.com/roostorg/osprey
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 136 lines 3.7 kB view raw
1import { memo, MutableRefObject, ReactElement, useEffect, useRef } from 'react'; 2import * as ReactDOM from 'react-dom/client'; 3import cytoscape, { Css, NodeSingular } from 'cytoscape'; 4import dagre, { DagreLayoutOptions } from 'cytoscape-dagre'; 5import popper from 'cytoscape-popper'; 6import tippy from 'tippy.js'; 7 8import { HierarchicalGraphOptions } from '../../types/RulesVisualizerTypes'; 9 10import 'tippy.js/dist/tippy.css'; 11import styles from './HierarchicalGraph.module.css'; 12 13const defaultNodeStyle: Css.Node = { 14 'text-wrap': 'wrap', 15 'text-valign': 'bottom', 16 color: '#3c3c40', 17}; 18 19const defaultEdgeStyle: Css.Edge = { 20 width: 1, 21 'target-arrow-shape': 'triangle', 22 'line-color': '#9dbaea', 23 'target-arrow-color': '#9dbaea', 24 'curve-style': 'bezier', 25}; 26 27const defaultlayoutOptions: DagreLayoutOptions = { 28 name: 'dagre', 29 fit: true, 30 avoidOverlap: true, 31 nodeDimensionsIncludeLabels: true, 32 rankDir: 'LR', 33}; 34 35cytoscape.use(dagre); 36cytoscape.use(popper); 37 38const HierarchicalGraph = ({ 39 elements, 40 nodeStyle = {}, 41 edgeStyle = {}, 42 layoutOptions = {}, 43 onLoad = () => {}, 44 ToolTip, 45}: HierarchicalGraphOptions) => { 46 const containerRef = useRef(null); 47 const toolTipRef = useRef(null); 48 const tooltipRootRef = useRef<ReactDOM.Root | null>(null); 49 50 useEffect(() => { 51 const cy = cytoscape({ 52 container: containerRef.current, 53 elements, 54 style: [ 55 { 56 selector: 'node', 57 style: { ...defaultNodeStyle, ...nodeStyle }, 58 }, 59 { 60 selector: 'edge', 61 style: { ...defaultEdgeStyle, ...edgeStyle }, 62 }, 63 ], 64 layout: { ...defaultlayoutOptions, ...layoutOptions }, 65 maxZoom: 10, 66 autoungrabify: true, 67 }); 68 onLoad(cy); 69 70 let tip: any; 71 if (ToolTip) { 72 cy.nodes().bind('mouseover', (event) => { 73 tip = renderToolTipWithTippy(event.target as NodeSingular, ToolTip, containerRef, toolTipRef, tooltipRootRef); 74 }); 75 cy.nodes().bind('mouseout', () => { 76 if (tip) { 77 tip.destroy(); 78 tooltipRootRef.current?.unmount(); 79 tooltipRootRef.current = null; 80 } 81 }); 82 } 83 84 return () => { 85 if (tip) { 86 tip.destroy(); 87 tooltipRootRef.current?.unmount(); 88 tooltipRootRef.current = null; 89 } 90 cy.nodes().unbind('mouseover'); 91 cy.nodes().unbind('mouseout'); 92 cy.destroy(); 93 }; 94 }, [elements, nodeStyle, edgeStyle, layoutOptions, onLoad, ToolTip]); 95 96 return ( 97 <> 98 <div ref={containerRef} className={styles.cyContainer} /> 99 <div ref={toolTipRef} /> 100 </> 101 ); 102}; 103 104function renderToolTipWithTippy( 105 node: NodeSingular, 106 ToolTip: ({ node }: { node: NodeSingular }) => ReactElement, 107 containerRef: MutableRefObject<null>, 108 toolTipRef: MutableRefObject<null>, 109 tooltipRootRef: MutableRefObject<ReactDOM.Root | null> 110) { 111 const popperRef = node.popperRef(); 112 if (toolTipRef.current) { 113 tooltipRootRef.current = ReactDOM.createRoot(toolTipRef.current); 114 tooltipRootRef.current.render(<ToolTip node={node} />); 115 } 116 if (containerRef.current && toolTipRef.current) { 117 const tip: any = tippy(containerRef.current, { 118 getReferenceClientRect: popperRef.getBoundingClientRect, 119 content: toolTipRef.current, 120 placement: 'bottom', 121 arrow: true, 122 }); 123 tip.show(); 124 return tip; 125 } 126} 127 128export default memo(HierarchicalGraph, (prevProps, nextProps) => { 129 return ( 130 JSON.stringify(prevProps.elements) == JSON.stringify(nextProps.elements) && 131 prevProps.nodeStyle == nextProps.nodeStyle && 132 prevProps.edgeStyle == nextProps.edgeStyle && 133 prevProps.layoutOptions == nextProps.layoutOptions && 134 prevProps.ToolTip == nextProps.ToolTip 135 ); 136});