Skip to content

Commit 30fca45

Browse files
authored
fix: apply initial horizontal offset on tree mount (facebook#34088)
When the element is pre-selected and the Tree component is mounted, right now we are only applying initial vertical offset, but not the horizontal one. Because of this, if the DOM element was selected on Elements panel and then user opens Components panel for the first time of the browser DevTools session, depending on the element's depth, it could be hidden. Similarly to vertical offset, apply horizontal one, but via ref setter. ### Before: https://github.com/user-attachments/assets/0ab3cca9-93c1-4e9e-8d23-88330d438912 ### After: https://github.com/user-attachments/assets/10de153a-1e55-4cf7-b1ff-4cc7cb35ba10
1 parent c499adf commit 30fca45

File tree

1 file changed

+46
-7
lines changed
  • packages/react-devtools-shared/src/devtools/views/Components

1 file changed

+46
-7
lines changed

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

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
2424
import Icon from '../Icon';
2525
import {SettingsContext} from '../Settings/SettingsContext';
2626
import {BridgeContext, StoreContext, OptionsContext} from '../context';
27-
import Element from './Element';
27+
import ComponentsTreeElement from './Element';
2828
import InspectHostNodesToggle from './InspectHostNodesToggle';
2929
import OwnersStack from './OwnersStack';
3030
import ComponentSearchInput from './ComponentSearchInput';
@@ -93,8 +93,47 @@ export default function Tree(): React.Node {
9393

9494
const treeRef = useRef<HTMLDivElement | null>(null);
9595
const focusTargetRef = useRef<HTMLDivElement | null>(null);
96-
const listRef = useRef(null);
97-
const listDOMElementRef = useRef(null);
96+
const listDOMElementRef = useRef<Element | null>(null);
97+
const setListDOMElementRef = useCallback((listDOMElement: Element) => {
98+
listDOMElementRef.current = listDOMElement;
99+
100+
// Controls the initial horizontal offset of the Tree if the element was pre-selected. For example, via Elements panel in browser DevTools.
101+
// Initial vertical offset is controlled via initialScrollOffset prop of the FixedSizeList component.
102+
if (
103+
!componentsPanelVisible ||
104+
inspectedElementIndex == null ||
105+
listDOMElement == null
106+
) {
107+
return;
108+
}
109+
110+
const element = store.getElementAtIndex(inspectedElementIndex);
111+
if (element == null) {
112+
return;
113+
}
114+
115+
const viewportLeft = listDOMElement.scrollLeft;
116+
const viewportRight = viewportLeft + listDOMElement.clientWidth;
117+
const elementLeft = calculateElementOffset(element.depth);
118+
// Because of virtualization, this element might not be rendered yet; we can't look up its width.
119+
// Assuming that it may take up to the half of the viewport.
120+
const elementRight = elementLeft + listDOMElement.clientWidth / 2;
121+
122+
const isElementFullyVisible =
123+
elementLeft >= viewportLeft && elementRight <= viewportRight;
124+
125+
if (!isElementFullyVisible) {
126+
const horizontalDelta =
127+
Math.min(0, elementLeft - viewportLeft) +
128+
Math.max(0, elementRight - viewportRight);
129+
130+
// $FlowExpectedError[incompatible-call] Flow doesn't support instant as an option for behavior.
131+
listDOMElement.scrollBy({
132+
left: horizontalDelta,
133+
behavior: 'instant',
134+
});
135+
}
136+
}, []);
98137

99138
useEffect(() => {
100139
if (!componentsPanelVisible || inspectedElementIndex == null) {
@@ -118,7 +157,7 @@ export default function Tree(): React.Node {
118157
}
119158
const elementLeft = calculateElementOffset(element.depth);
120159
// Because of virtualization, this element might not be rendered yet; we can't look up its width.
121-
// Assuming that it may take up to the half of the vieport.
160+
// Assuming that it may take up to the half of the viewport.
122161
const elementRight = elementLeft + listDOMElement.clientWidth / 2;
123162
const elementTop = inspectedElementIndex * lineHeight;
124163
const elementBottom = elementTop + lineHeight;
@@ -137,6 +176,7 @@ export default function Tree(): React.Node {
137176
Math.min(0, elementLeft - viewportLeft) +
138177
Math.max(0, elementRight - viewportRight);
139178

179+
// $FlowExpectedError[incompatible-call] Flow doesn't support instant as an option for behavior.
140180
listDOMElement.scrollBy({
141181
top: verticalDelta,
142182
left: horizontalDelta,
@@ -471,11 +511,10 @@ export default function Tree(): React.Node {
471511
itemData={itemData}
472512
itemKey={itemKey}
473513
itemSize={lineHeight}
474-
ref={listRef}
475-
outerRef={listDOMElementRef}
514+
outerRef={setListDOMElementRef}
476515
overscanCount={10}
477516
width={width}>
478-
{Element}
517+
{ComponentsTreeElement}
479518
</FixedSizeList>
480519
)}
481520
</AutoSizer>

0 commit comments

Comments
 (0)