Skip to content

Commit 6207743

Browse files
bl00mberbvaughn
andauthored
[DevTools] Allow to continue dragging when leaving profiler picker (facebook#18852)
Co-authored-by: Brian Vaughn <brian.david.vaughn@gmail.com>
1 parent df14b5b commit 6207743

File tree

2 files changed

+78
-40
lines changed

2 files changed

+78
-40
lines changed

packages/react-devtools-shared/src/devtools/views/Profiler/SnapshotCommitList.js

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import * as React from 'react';
11-
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
11+
import {useEffect, useMemo, useRef, useState} from 'react';
1212
import AutoSizer from 'react-virtualized-auto-sizer';
1313
import {FixedSizeList} from 'react-window';
1414
import SnapshotCommitListItem from './SnapshotCommitListItem';
@@ -20,11 +20,11 @@ export type ItemData = {|
2020
commitDurations: Array<number>,
2121
commitTimes: Array<number>,
2222
filteredCommitIndices: Array<number>,
23-
isMouseDown: boolean,
2423
maxDuration: number,
2524
selectedCommitIndex: number | null,
2625
selectedFilteredCommitIndex: number | null,
2726
selectCommitIndex: (index: number) => void,
27+
startCommitDrag: (newDragState: DragState) => void,
2828
|};
2929

3030
type Props = {|
@@ -73,6 +73,12 @@ type ListProps = {|
7373
width: number,
7474
|};
7575

76+
type DragState = {
77+
commitIndex: number,
78+
left: number,
79+
sizeIncrement: number,
80+
};
81+
7682
function List({
7783
commitDurations,
7884
selectedCommitIndex,
@@ -97,28 +103,6 @@ function List({
97103
}
98104
}, [listRef, selectedFilteredCommitIndex]);
99105

100-
// When the mouse is down, dragging over a commit should auto-select it.
101-
// This provides a nice way for users to swipe across a range of commits to compare them.
102-
const [isMouseDown, setIsMouseDown] = useState(false);
103-
const handleMouseDown = useCallback(() => {
104-
setIsMouseDown(true);
105-
}, []);
106-
const handleMouseUp = useCallback(() => {
107-
setIsMouseDown(false);
108-
}, []);
109-
useEffect(() => {
110-
if (divRef.current === null) {
111-
return () => {};
112-
}
113-
114-
// It's important to listen to the ownerDocument to support the browser extension.
115-
// Here we use portals to render individual tabs (e.g. Profiler),
116-
// and the root document might belong to a different window.
117-
const ownerDocument = divRef.current.ownerDocument;
118-
ownerDocument.addEventListener('mouseup', handleMouseUp);
119-
return () => ownerDocument.removeEventListener('mouseup', handleMouseUp);
120-
}, [divRef, handleMouseUp]);
121-
122106
const itemSize = useMemo(
123107
() => Math.max(minBarWidth, width / filteredCommitIndices.length),
124108
[filteredCommitIndices, width],
@@ -128,23 +112,76 @@ function List({
128112
[commitDurations],
129113
);
130114

115+
const maxCommitIndex = filteredCommitIndices.length - 1;
116+
117+
const [dragState, setDragState] = useState<DragState | null>(null);
118+
119+
const handleDragCommit = ({buttons, pageX}: any) => {
120+
if (buttons === 0) {
121+
setDragState(null);
122+
return;
123+
}
124+
125+
if (dragState !== null) {
126+
const {commitIndex, left, sizeIncrement} = dragState;
127+
128+
let newCommitIndex = commitIndex;
129+
let newCommitLeft = left;
130+
131+
if (pageX < newCommitLeft) {
132+
while (pageX < newCommitLeft) {
133+
newCommitLeft -= sizeIncrement;
134+
newCommitIndex -= 1;
135+
}
136+
} else {
137+
let newCommitRectRight = newCommitLeft + sizeIncrement;
138+
while (pageX > newCommitRectRight) {
139+
newCommitRectRight += sizeIncrement;
140+
newCommitIndex += 1;
141+
}
142+
}
143+
144+
if (newCommitIndex < 0) {
145+
newCommitIndex = 0;
146+
} else if (newCommitIndex > maxCommitIndex) {
147+
newCommitIndex = maxCommitIndex;
148+
}
149+
150+
selectCommitIndex(newCommitIndex);
151+
}
152+
};
153+
154+
useEffect(() => {
155+
if (dragState === null) {
156+
return;
157+
}
158+
159+
const element = divRef.current;
160+
if (element !== null) {
161+
const ownerDocument = element.ownerDocument;
162+
ownerDocument.addEventListener('mousemove', handleDragCommit);
163+
return () => {
164+
ownerDocument.removeEventListener('mousemove', handleDragCommit);
165+
};
166+
}
167+
}, [dragState]);
168+
131169
// Pass required contextual data down to the ListItem renderer.
132170
const itemData = useMemo<ItemData>(
133171
() => ({
134172
commitDurations,
135173
commitTimes,
136174
filteredCommitIndices,
137-
isMouseDown,
138175
maxDuration,
139176
selectedCommitIndex,
140177
selectedFilteredCommitIndex,
141178
selectCommitIndex,
179+
startCommitDrag: setDragState,
142180
}),
143181
[
144182
commitDurations,
145183
commitTimes,
146184
filteredCommitIndices,
147-
isMouseDown,
148185
maxDuration,
149186
selectedCommitIndex,
150187
selectedFilteredCommitIndex,
@@ -153,11 +190,7 @@ function List({
153190
);
154191

155192
return (
156-
<div
157-
onMouseDown={handleMouseDown}
158-
onMouseUp={handleMouseUp}
159-
ref={divRef}
160-
style={{height, width}}>
193+
<div ref={divRef} style={{height, width}}>
161194
<FixedSizeList
162195
className={styles.List}
163196
layout="horizontal"

packages/react-devtools-shared/src/devtools/views/Profiler/SnapshotCommitListItem.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import * as React from 'react';
11-
import {memo, useCallback} from 'react';
11+
import {memo} from 'react';
1212
import {areEqual} from 'react-window';
1313
import {getGradientColor, formatDuration, formatTime} from './utils';
1414

@@ -28,22 +28,17 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
2828
commitDurations,
2929
commitTimes,
3030
filteredCommitIndices,
31-
isMouseDown,
3231
maxDuration,
3332
selectedCommitIndex,
3433
selectCommitIndex,
34+
startCommitDrag,
3535
} = itemData;
3636

3737
index = filteredCommitIndices[index];
3838

3939
const commitDuration = commitDurations[index];
4040
const commitTime = commitTimes[index];
4141

42-
const handleClick = useCallback(() => selectCommitIndex(index), [
43-
index,
44-
selectCommitIndex,
45-
]);
46-
4742
// Guard against commits with duration 0
4843
const percentage =
4944
Math.min(1, Math.max(0, commitDuration / maxDuration)) || 0;
@@ -52,11 +47,21 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
5247
// Leave a 1px gap between snapshots
5348
const width = parseFloat(style.width) - 1;
5449

50+
const handleMouseDown = ({buttons, target}: any) => {
51+
if (buttons === 1) {
52+
selectCommitIndex(index);
53+
startCommitDrag({
54+
commitIndex: index,
55+
left: target.getBoundingClientRect().left,
56+
sizeIncrement: parseFloat(style.width),
57+
});
58+
}
59+
};
60+
5561
return (
5662
<div
5763
className={styles.Outer}
58-
onClick={handleClick}
59-
onMouseEnter={isMouseDown ? handleClick : null}
64+
onMouseDown={handleMouseDown}
6065
style={{
6166
...style,
6267
width,

0 commit comments

Comments
 (0)