1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import * as React from 'react';
    
  11. import {Fragment, memo, useCallback, useContext} from 'react';
    
  12. import {areEqual} from 'react-window';
    
  13. import {barWidthThreshold} from './constants';
    
  14. import {getGradientColor} from './utils';
    
  15. import ChartNode from './ChartNode';
    
  16. import {SettingsContext} from '../Settings/SettingsContext';
    
  17. 
    
  18. import type {ChartNode as ChartNodeType} from './FlamegraphChartBuilder';
    
  19. import type {ItemData} from './CommitFlamegraph';
    
  20. 
    
  21. type Props = {
    
  22.   data: ItemData,
    
  23.   index: number,
    
  24.   style: Object,
    
  25.   ...
    
  26. };
    
  27. 
    
  28. function CommitFlamegraphListItem({data, index, style}: Props): React.Node {
    
  29.   const {
    
  30.     chartData,
    
  31.     onElementMouseEnter,
    
  32.     onElementMouseLeave,
    
  33.     scaleX,
    
  34.     selectedChartNode,
    
  35.     selectedChartNodeIndex,
    
  36.     selectFiber,
    
  37.     width,
    
  38.   } = data;
    
  39.   const {renderPathNodes, maxSelfDuration, rows} = chartData;
    
  40. 
    
  41.   const {lineHeight} = useContext(SettingsContext);
    
  42. 
    
  43.   const handleClick = useCallback(
    
  44.     (event: SyntheticMouseEvent<EventTarget>, id: number, name: string) => {
    
  45.       event.stopPropagation();
    
  46.       selectFiber(id, name);
    
  47.     },
    
  48.     [selectFiber],
    
  49.   );
    
  50. 
    
  51.   const handleMouseEnter = (nodeData: ChartNodeType) => {
    
  52.     const {id, name} = nodeData;
    
  53.     onElementMouseEnter({id, name});
    
  54.   };
    
  55. 
    
  56.   const handleMouseLeave = () => {
    
  57.     onElementMouseLeave();
    
  58.   };
    
  59. 
    
  60.   // List items are absolutely positioned using the CSS "top" attribute.
    
  61.   // The "left" value will always be 0.
    
  62.   // Since height is fixed, and width is based on the node's duration,
    
  63.   // We can ignore those values as well.
    
  64.   const top = parseInt(style.top, 10);
    
  65. 
    
  66.   const row = rows[index];
    
  67. 
    
  68.   const selectedNodeOffset = scaleX(
    
  69.     selectedChartNode !== null ? selectedChartNode.offset : 0,
    
  70.     width,
    
  71.   );
    
  72. 
    
  73.   return (
    
  74.     <Fragment>
    
  75.       {row.map(chartNode => {
    
  76.         const {
    
  77.           didRender,
    
  78.           id,
    
  79.           label,
    
  80.           name,
    
  81.           offset,
    
  82.           selfDuration,
    
  83.           treeBaseDuration,
    
  84.         } = chartNode;
    
  85. 
    
  86.         const nodeOffset = scaleX(offset, width);
    
  87.         const nodeWidth = scaleX(treeBaseDuration, width);
    
  88. 
    
  89.         // Filter out nodes that are too small to see or click.
    
  90.         // This also helps render large trees faster.
    
  91.         if (nodeWidth < barWidthThreshold) {
    
  92.           return null;
    
  93.         }
    
  94. 
    
  95.         // Filter out nodes that are outside of the horizontal window.
    
  96.         if (
    
  97.           nodeOffset + nodeWidth < selectedNodeOffset ||
    
  98.           nodeOffset > selectedNodeOffset + width
    
  99.         ) {
    
  100.           return null;
    
  101.         }
    
  102. 
    
  103.         let color = 'url(#didNotRenderPattern)';
    
  104.         let textColor = 'var(--color-commit-did-not-render-pattern-text)';
    
  105.         if (didRender) {
    
  106.           color = getGradientColor(selfDuration / maxSelfDuration);
    
  107.           textColor = 'var(--color-commit-gradient-text)';
    
  108.         } else if (renderPathNodes.has(id)) {
    
  109.           color = 'var(--color-commit-did-not-render-fill)';
    
  110.           textColor = 'var(--color-commit-did-not-render-fill-text)';
    
  111.         }
    
  112. 
    
  113.         return (
    
  114.           <ChartNode
    
  115.             color={color}
    
  116.             height={lineHeight}
    
  117.             isDimmed={index < selectedChartNodeIndex}
    
  118.             key={id}
    
  119.             label={label}
    
  120.             onClick={event => handleClick(event, id, name)}
    
  121.             onMouseEnter={() => handleMouseEnter(chartNode)}
    
  122.             onMouseLeave={handleMouseLeave}
    
  123.             textStyle={{color: textColor}}
    
  124.             width={nodeWidth}
    
  125.             x={nodeOffset - selectedNodeOffset}
    
  126.             y={top}
    
  127.           />
    
  128.         );
    
  129.       })}
    
  130.     </Fragment>
    
  131.   );
    
  132. }
    
  133. 
    
  134. export default (memo(
    
  135.   CommitFlamegraphListItem,
    
  136.   areEqual,
    
  137. ): React.ComponentType<Props>);