main
js 143 lines 4.01 KB
Raw
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 import typeof {SyntheticMouseEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
21
22 type Props = {
23 data: ItemData,
24 index: number,
25 style: Object,
26 ...
27 };
28
29 function CommitFlamegraphListItem({data, index, style}: Props): React.Node {
30 const {
31 chartData,
32 currentSearchMatchID,
33 matchedFiberIDs,
34 onElementMouseEnter,
35 onElementMouseLeave,
36 scaleX,
37 searchRegExp,
38 selectedChartNode,
39 selectedChartNodeIndex,
40 selectFiber,
41 width,
42 } = data;
43 const {renderPathNodes, maxSelfDuration, rows} = chartData;
44
45 const {lineHeight} = useContext(SettingsContext);
46
47 const handleClick = useCallback(
48 (event: SyntheticMouseEvent, id: number, name: string) => {
49 event.stopPropagation();
50 selectFiber(id, name);
51 },
52 [selectFiber],
53 );
54
55 const handleMouseEnter = (nodeData: ChartNodeType) => {
56 const {id, name} = nodeData;
57 onElementMouseEnter({id, name});
58 };
59
60 const handleMouseLeave = () => {
61 onElementMouseLeave();
62 };
63
64 // List items are absolutely positioned using the CSS "top" attribute.
65 // The "left" value will always be 0.
66 // Since height is fixed, and width is based on the node's duration,
67 // We can ignore those values as well.
68 const top = parseInt(style.top, 10);
69
70 const row = rows[index];
71
72 const selectedNodeOffset = scaleX(
73 selectedChartNode !== null ? selectedChartNode.offset : 0,
74 width,
75 );
76
77 return (
78 <Fragment>
79 {row.map(chartNode => {
80 const {
81 didRender,
82 id,
83 label,
84 name,
85 offset,
86 selfDuration,
87 treeBaseDuration,
88 } = chartNode;
89
90 const nodeOffset = scaleX(offset, width);
91 const nodeWidth = scaleX(treeBaseDuration, width);
92
93 // Filter out nodes that are too small to see or click.
94 // This also helps render large trees faster.
95 if (nodeWidth < barWidthThreshold) {
96 return null;
97 }
98
99 // Filter out nodes that are outside of the horizontal window.
100 if (
101 nodeOffset + nodeWidth < selectedNodeOffset ||
102 nodeOffset > selectedNodeOffset + width
103 ) {
104 return null;
105 }
106
107 let color = 'url(#didNotRenderPattern)';
108 let textColor = 'var(--color-commit-did-not-render-pattern-text)';
109 if (didRender) {
110 color = getGradientColor(selfDuration / maxSelfDuration);
111 textColor = 'var(--color-commit-gradient-text)';
112 } else if (renderPathNodes.has(id)) {
113 color = 'var(--color-commit-did-not-render-fill)';
114 textColor = 'var(--color-commit-did-not-render-fill-text)';
115 }
116
117 return (
118 <ChartNode
119 color={color}
120 height={lineHeight}
121 isCurrentSearchMatch={id === currentSearchMatchID}
122 isDimmed={index < selectedChartNodeIndex}
123 isSearchMatch={matchedFiberIDs.has(id)}
124 key={id}
125 label={label}
126 onClick={event => handleClick(event, id, name)}
127 onMouseEnter={() => handleMouseEnter(chartNode)}
128 onMouseLeave={handleMouseLeave}
129 searchRegExp={searchRegExp}
130 textStyle={{color: textColor}}
131 width={nodeWidth}
132 x={nodeOffset - selectedNodeOffset}
133 y={top}
134 />
135 );
136 })}
137 </Fragment>
138 );
139 }
140
141 export default memo(CommitFlamegraphListItem, areEqual) as component(
142 ...props: Props
143 );