Skip to content

Commit 85ca13d

Browse files
committed
[DevTools] More robust resize handling
1 parent 7ca1810 commit 85ca13d

File tree

4 files changed

+67
-68
lines changed

4 files changed

+67
-68
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ module.exports = {
608608
symbol: 'readonly',
609609
SyntheticEvent: 'readonly',
610610
SyntheticMouseEvent: 'readonly',
611+
SyntheticPointerEvent: 'readonly',
611612
Thenable: 'readonly',
612613
TimeoutID: 'readonly',
613614
WheelEventHandler: 'readonly',

packages/react-devtools-shared/src/devtools/views/Components/Components.css

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
.TreeWrapper {
1818
flex: 0 0 var(--horizontal-resize-percentage);
19-
overflow: auto;
2019
}
2120

2221
.InspectedElementWrapper {
@@ -32,7 +31,14 @@
3231

3332
.ResizeBar {
3433
position: absolute;
35-
left: -2px;
34+
/*
35+
* moving the bar out of its bounding box might cause its hitbox to overlap
36+
* with another scrollbar creating disorienting UX where you both resize and scroll
37+
* at the same time.
38+
* If you adjust this value, double check that starting resize right on this edge
39+
* doesn't also cause scroll
40+
*/
41+
left: 1px;
3642
width: 5px;
3743
height: 100%;
3844
cursor: ew-resize;
@@ -52,7 +58,7 @@
5258
}
5359

5460
.ResizeBar {
55-
top: -2px;
61+
top: 1px;
5662
left: 0;
5763
width: 100%;
5864
height: 5px;

packages/react-devtools-shared/src/devtools/views/Components/Components.js

Lines changed: 56 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ type Orientation = 'horizontal' | 'vertical';
2929

3030
type ResizeActionType =
3131
| 'ACTION_SET_DID_MOUNT'
32-
| 'ACTION_SET_IS_RESIZING'
3332
| 'ACTION_SET_HORIZONTAL_PERCENTAGE'
3433
| 'ACTION_SET_VERTICAL_PERCENTAGE';
3534

@@ -40,7 +39,6 @@ type ResizeAction = {
4039

4140
type ResizeState = {
4241
horizontalPercentage: number,
43-
isResizing: boolean,
4442
verticalPercentage: number,
4543
};
4644

@@ -81,82 +79,81 @@ function Components(_: {}) {
8179
return () => clearTimeout(timeoutID);
8280
}, [horizontalPercentage, verticalPercentage]);
8381

84-
const {isResizing} = state;
82+
const onResizeStart = (event: SyntheticPointerEvent<HTMLElement>) => {
83+
const element = event.currentTarget;
84+
element.setPointerCapture(event.pointerId);
85+
};
86+
87+
const onResizeEnd = (event: SyntheticPointerEvent<HTMLElement>) => {
88+
const element = event.currentTarget;
89+
element.releasePointerCapture(event.pointerId);
90+
};
91+
92+
const onResize = (event: SyntheticPointerEvent<HTMLElement>) => {
93+
const element = event.currentTarget;
94+
const isResizing = element.hasPointerCapture(event.pointerId);
95+
if (!isResizing) {
96+
return;
97+
}
8598

86-
const onResizeStart = () =>
87-
dispatch({type: 'ACTION_SET_IS_RESIZING', payload: true});
99+
const resizeElement = resizeElementRef.current;
100+
const wrapperElement = wrapperElementRef.current;
88101

89-
let onResize;
90-
let onResizeEnd;
91-
if (isResizing) {
92-
onResizeEnd = () =>
93-
dispatch({type: 'ACTION_SET_IS_RESIZING', payload: false});
102+
if (wrapperElement === null || resizeElement === null) {
103+
return;
104+
}
94105

95-
// $FlowFixMe[missing-local-annot]
96-
onResize = event => {
97-
const resizeElement = resizeElementRef.current;
98-
const wrapperElement = wrapperElementRef.current;
106+
event.preventDefault();
99107

100-
if (!isResizing || wrapperElement === null || resizeElement === null) {
101-
return;
102-
}
108+
const orientation = getOrientation(wrapperElement);
103109

104-
event.preventDefault();
110+
const {height, width, left, top} = wrapperElement.getBoundingClientRect();
105111

106-
const orientation = getOrientation(wrapperElement);
112+
const currentMousePosition =
113+
orientation === 'horizontal' ? event.clientX - left : event.clientY - top;
107114

108-
const {height, width, left, top} = wrapperElement.getBoundingClientRect();
115+
const boundaryMin = MINIMUM_SIZE;
116+
const boundaryMax =
117+
orientation === 'horizontal'
118+
? width - MINIMUM_SIZE
119+
: height - MINIMUM_SIZE;
109120

110-
const currentMousePosition =
111-
orientation === 'horizontal'
112-
? event.clientX - left
113-
: event.clientY - top;
121+
const isMousePositionInBounds =
122+
currentMousePosition > boundaryMin && currentMousePosition < boundaryMax;
114123

115-
const boundaryMin = MINIMUM_SIZE;
116-
const boundaryMax =
124+
if (isMousePositionInBounds) {
125+
const resizedElementDimension =
126+
orientation === 'horizontal' ? width : height;
127+
const actionType =
117128
orientation === 'horizontal'
118-
? width - MINIMUM_SIZE
119-
: height - MINIMUM_SIZE;
120-
121-
const isMousePositionInBounds =
122-
currentMousePosition > boundaryMin &&
123-
currentMousePosition < boundaryMax;
124-
125-
if (isMousePositionInBounds) {
126-
const resizedElementDimension =
127-
orientation === 'horizontal' ? width : height;
128-
const actionType =
129-
orientation === 'horizontal'
130-
? 'ACTION_SET_HORIZONTAL_PERCENTAGE'
131-
: 'ACTION_SET_VERTICAL_PERCENTAGE';
132-
const percentage =
133-
(currentMousePosition / resizedElementDimension) * 100;
134-
135-
setResizeCSSVariable(resizeElement, orientation, percentage);
136-
137-
dispatch({
138-
type: actionType,
139-
payload: currentMousePosition / resizedElementDimension,
140-
});
141-
}
142-
};
143-
}
129+
? 'ACTION_SET_HORIZONTAL_PERCENTAGE'
130+
: 'ACTION_SET_VERTICAL_PERCENTAGE';
131+
const percentage = (currentMousePosition / resizedElementDimension) * 100;
132+
133+
setResizeCSSVariable(resizeElement, orientation, percentage);
134+
135+
dispatch({
136+
type: actionType,
137+
payload: currentMousePosition / resizedElementDimension,
138+
});
139+
}
140+
};
144141

145142
return (
146143
<SettingsModalContextController>
147144
<OwnersListContextController>
148-
<div
149-
ref={wrapperElementRef}
150-
className={styles.Components}
151-
onMouseMove={onResize}
152-
onMouseLeave={onResizeEnd}
153-
onMouseUp={onResizeEnd}>
145+
<div ref={wrapperElementRef} className={styles.Components}>
154146
<Fragment>
155147
<div ref={resizeElementRef} className={styles.TreeWrapper}>
156148
<Tree />
157149
</div>
158150
<div className={styles.ResizeBarWrapper}>
159-
<div onMouseDown={onResizeStart} className={styles.ResizeBar} />
151+
<div
152+
onPointerDown={onResizeStart}
153+
onPointerMove={onResize}
154+
onPointerUp={onResizeEnd}
155+
className={styles.ResizeBar}
156+
/>
160157
</div>
161158
<div className={styles.InspectedElementWrapper}>
162159
<NativeStyleContextController>
@@ -193,18 +190,12 @@ function initResizeState(): ResizeState {
193190

194191
return {
195192
horizontalPercentage,
196-
isResizing: false,
197193
verticalPercentage,
198194
};
199195
}
200196

201197
function resizeReducer(state: ResizeState, action: ResizeAction): ResizeState {
202198
switch (action.type) {
203-
case 'ACTION_SET_IS_RESIZING':
204-
return {
205-
...state,
206-
isResizing: action.payload,
207-
};
208199
case 'ACTION_SET_HORIZONTAL_PERCENTAGE':
209200
return {
210201
...state,

packages/react-devtools-shared/src/devtools/views/Components/Tree.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
font-family: var(--font-family-monospace);
3939
font-size: var(--font-size-monospace-normal);
4040
line-height: var(--line-height-data);
41+
user-select: none;
4142
}
4243

4344
.VRule {

0 commit comments

Comments
 (0)