main
js 184 lines 4.99 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, useContext} from 'react';
12 import Button from '../Button';
13 import ButtonIcon from '../ButtonIcon';
14 import {ProfilerContext} from './ProfilerContext';
15 import SnapshotCommitList from './SnapshotCommitList';
16 import {maxBarWidth} from './constants';
17 import {StoreContext} from '../context';
18
19 import styles from './SnapshotSelector.css';
20
21 export type Props = {};
22
23 export default function SnapshotSelector(_: Props): React.Node {
24 const {
25 rootID,
26 selectedCommitIndex,
27 selectCommitIndex,
28 selectPrevCommitIndex,
29 selectNextCommitIndex,
30 filteredCommitIndices,
31 selectedFilteredCommitIndex,
32 } = useContext(ProfilerContext);
33
34 const {profilerStore} = useContext(StoreContext);
35 const {commitData} = profilerStore.getDataForRoot(rootID as any as number);
36
37 const totalDurations: Array<number> = [];
38 const commitTimes: Array<number> = [];
39 commitData.forEach(commitDatum => {
40 totalDurations.push(
41 commitDatum.duration +
42 (commitDatum.effectDuration || 0) +
43 (commitDatum.passiveEffectDuration || 0),
44 );
45 commitTimes.push(commitDatum.timestamp);
46 });
47
48 const numFilteredCommits = filteredCommitIndices.length;
49
50 let label = null;
51 if (numFilteredCommits > 0) {
52 // $FlowFixMe[missing-local-annot]
53 const handleCommitInputChange = event => {
54 const value = parseInt(event.currentTarget.value, 10);
55 if (!isNaN(value)) {
56 const filteredIndex = Math.min(
57 Math.max(value - 1, 0),
58
59 // Snashots are shown to the user as 1-based
60 // but the indices within the profiler data array ar 0-based.
61 numFilteredCommits - 1,
62 );
63 selectCommitIndex(filteredCommitIndices[filteredIndex]);
64 }
65 };
66
67 // $FlowFixMe[missing-local-annot]
68 const handleClick = event => {
69 event.currentTarget.select();
70 };
71
72 // $FlowFixMe[missing-local-annot]
73 const handleKeyDown = event => {
74 switch (event.key) {
75 case 'ArrowDown':
76 selectPrevCommitIndex();
77 event.stopPropagation();
78 break;
79 case 'ArrowUp':
80 selectNextCommitIndex();
81 event.stopPropagation();
82 break;
83 default:
84 break;
85 }
86 };
87
88 const input = (
89 <input
90 className={styles.Input}
91 data-testname="SnapshotSelector-Input"
92 type="text"
93 inputMode="numeric"
94 pattern="[0-9]*"
95 value={
96 // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
97 selectedFilteredCommitIndex + 1
98 }
99 size={`${numFilteredCommits}`.length}
100 onChange={handleCommitInputChange}
101 onClick={handleClick}
102 onKeyDown={handleKeyDown}
103 />
104 );
105
106 label = (
107 <Fragment>
108 {input} / {numFilteredCommits}
109 </Fragment>
110 );
111 }
112
113 // $FlowFixMe[missing-local-annot]
114 const handleKeyDown = event => {
115 switch (event.key) {
116 case 'ArrowLeft':
117 selectPrevCommitIndex();
118 event.stopPropagation();
119 break;
120 case 'ArrowRight':
121 selectNextCommitIndex();
122 event.stopPropagation();
123 break;
124 default:
125 break;
126 }
127 };
128
129 if (commitData.length === 0) {
130 return null;
131 }
132
133 return (
134 <Fragment>
135 <span
136 className={styles.IndexLabel}
137 data-testname="SnapshotSelector-Label">
138 {label}
139 </span>
140 <Button
141 className={styles.Button}
142 data-testname="SnapshotSelector-PreviousButton"
143 disabled={numFilteredCommits === 0}
144 onClick={selectPrevCommitIndex}
145 title="Select previous commit ←">
146 <ButtonIcon type="previous" />
147 </Button>
148 <div
149 className={styles.Commits}
150 onKeyDown={handleKeyDown}
151 style={{
152 flex: numFilteredCommits > 0 ? '1 1 auto' : '0 0 auto',
153 maxWidth:
154 numFilteredCommits > 0
155 ? numFilteredCommits * maxBarWidth
156 : undefined,
157 }}
158 tabIndex={0}>
159 {numFilteredCommits > 0 && (
160 <SnapshotCommitList
161 commitData={commitData}
162 commitTimes={commitTimes}
163 filteredCommitIndices={filteredCommitIndices}
164 selectedCommitIndex={selectedCommitIndex}
165 selectedFilteredCommitIndex={selectedFilteredCommitIndex}
166 selectCommitIndex={selectCommitIndex}
167 totalDurations={totalDurations}
168 />
169 )}
170 {numFilteredCommits === 0 && (
171 <div className={styles.NoCommits}>No commits</div>
172 )}
173 </div>
174 <Button
175 className={styles.Button}
176 data-testname="SnapshotSelector-NextButton"
177 disabled={numFilteredCommits === 0}
178 onClick={selectNextCommitIndex}
179 title="Select next commit →">
180 <ButtonIcon type="next" />
181 </Button>
182 </Fragment>
183 );
184 }