github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/packages/pyroscope-flamegraph/src/FlameGraph/FlameGraphComponent/Highlight.tsx (about) 1 import { Maybe } from 'true-myth'; 2 import React from 'react'; 3 import { DeepReadonly } from 'ts-essentials'; 4 import styles from './Highlight.module.css'; 5 6 export interface HighlightProps { 7 // probably the same as the bar height 8 barHeight: number; 9 zoom: Maybe<DeepReadonly<{ i: number; j: number }>>; 10 xyToHighlightData: ( 11 x: number, 12 y: number 13 ) => Maybe<{ 14 left: number; 15 top: number; 16 width: number; 17 }>; 18 19 canvasRef: React.RefObject<HTMLCanvasElement>; 20 } 21 export default function Highlight(props: HighlightProps) { 22 const { canvasRef, barHeight, xyToHighlightData, zoom } = props; 23 const [style, setStyle] = React.useState<React.CSSProperties>({ 24 height: '0px', 25 visibility: 'hidden', 26 }); 27 28 React.useEffect(() => { 29 // stops highlighting every time a node is zoomed or unzoomed 30 // then, when a mouse move event is detected, 31 // listeners are triggered and highlighting becomes visible again 32 setStyle({ 33 height: '0px', 34 visibility: 'hidden', 35 }); 36 }, [zoom]); 37 38 const onMouseMove = (e: MouseEvent) => { 39 const opt = xyToHighlightData(e.offsetX, e.offsetY); 40 41 if (opt.isJust) { 42 const data = opt.value; 43 44 setStyle({ 45 visibility: 'visible', 46 height: `${barHeight}px`, 47 ...data, 48 }); 49 } else { 50 // it doesn't map to a valid xy 51 // so it means we are hovering out 52 onMouseOut(); 53 } 54 }; 55 56 const onMouseOut = () => { 57 setStyle({ 58 ...style, 59 visibility: 'hidden', 60 }); 61 }; 62 63 React.useEffect( 64 () => { 65 // use closure to "cache" the current canvas reference 66 // so that when cleaning up, it points to a valid canvas 67 // (otherwise it would be null) 68 const canvasEl = canvasRef.current; 69 if (!canvasEl) { 70 return () => {}; 71 } 72 73 // watch for mouse events on the bar 74 canvasEl.addEventListener('mousemove', onMouseMove); 75 canvasEl.addEventListener('mouseout', onMouseOut); 76 77 return () => { 78 canvasEl.removeEventListener('mousemove', onMouseMove); 79 canvasEl.removeEventListener('mouseout', onMouseOut); 80 }; 81 }, 82 83 // refresh callback functions when they change 84 [canvasRef.current, onMouseMove, onMouseOut] 85 ); 86 87 return ( 88 <div 89 className={styles.highlight} 90 style={style} 91 data-testid="flamegraph-highlight" 92 /> 93 ); 94 }