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  }