@samitouri / QOS-React / commits / 39223239bb

Select DOM nodes on hover

Dan Abramov committed Apr 8, 2019 at 14:26 UTC 39223239bbcea68f801d2d3607c5326099e22e91
5 files changed +47 -7
src/backend/agent.js
+15 -4
@@ -63,6 +63,10 @@ export default class Agent extends EventEmitter {
63 this._bridge = bridge;
64
65 bridge.addListener('captureScreenshot', this.captureScreenshot);
66 + bridge.addListener(
67 + 'clearHighlightedElementInDOM',
68 + this.clearHighlightedElementInDOM
69 + );
70 bridge.addListener('exportProfilingSummary', this.exportProfilingSummary);
71 bridge.addListener('getCommitDetails', this.getCommitDetails);
72 bridge.addListener('getFiberCommits', this.getFiberCommits);
@@ -216,14 +220,22 @@ export default class Agent extends EventEmitter {
220 }
221 };
222
223 + clearHighlightedElementInDOM = () => {
224 + hideOverlay();
225 + };
226 +
227 highlightElementInDOM = ({
228 displayName,
229 id,
230 + isSticky,
231 rendererID,
232 + scrollIntoView,
233 }: {
234 displayName: string,
235 id: number,
236 + isSticky: boolean,
237 rendererID: number,
238 + scrollIntoView: boolean,
239 }) => {
240 const renderer = this._rendererInterfaces[rendererID];
241 if (renderer == null) {
@@ -236,13 +248,12 @@ export default class Agent extends EventEmitter {
248 }
249
250 if (node != null) {
239 - if (typeof node.scrollIntoView === 'function') {
251 + if (scrollIntoView && typeof node.scrollIntoView === 'function') {
252 // If the node isn't visible show it before highlighting it.
253 // We may want to reconsider this; it might be a little disruptive.
254 node.scrollIntoView({ block: 'nearest', inline: 'nearest' });
255 }
244 -
245 - showOverlay(((node: any): HTMLElement), displayName);
256 + showOverlay(((node: any): HTMLElement), displayName, isSticky);
257 } else {
258 hideOverlay();
259 }
@@ -466,6 +477,6 @@ export default class Agent extends EventEmitter {
477
478 // Don't pass the name explicitly.
479 // It will be inferred from DOM tag and Fiber owner.
469 - showOverlay(target);
480 + showOverlay(target, null, true);
481 };
482 }
src/backend/views/Highlighter.js
+5 -2
@@ -18,7 +18,8 @@ export function hideOverlay() {
18
19 export function showOverlay(
20 element: HTMLElement | null,
21 - componentName: string = ''
21 + componentName: string | null,
22 + isSticky: boolean
23 ) {
24 if (timeoutID !== null) {
25 clearTimeout(timeoutID);
@@ -34,5 +35,7 @@ export function showOverlay(
35
36 overlay.inspect(element, componentName);
37
37 - timeoutID = setTimeout(hideOverlay, SHOW_DURATION);
38 + if (!isSticky) {
39 + timeoutID = setTimeout(hideOverlay, SHOW_DURATION);
40 + }
41 }
src/devtools/views/Components/Element.js
+18
@@ -11,6 +11,7 @@ import React, {
11 import { ElementTypeClass, ElementTypeFunction } from 'src/devtools/types';
12 import { createRegExp } from '../utils';
13 import { TreeContext } from './TreeContext';
14 +import { BridgeContext, StoreContext } from '../context';
15
16 import type { Element } from './types';
17
@@ -31,6 +32,9 @@ export default function ElementView({ index, style, data }: Props) {
32 selectedElementID,
33 selectElementByID,
34 } = useContext(TreeContext);
35 + const bridge = useContext(BridgeContext);
36 + const store = useContext(StoreContext);
37 +
38 const element = getElementAtIndex(index);
39
40 const id = element === null ? null : element.id;
@@ -82,6 +86,19 @@ export default function ElementView({ index, style, data }: Props) {
86 [id, selectElementByID]
87 );
88
89 + const rendererID = store.getRendererIDForElement(element.id) || null;
90 + const handleMouseEnter = useCallback(() => {
91 + if (rendererID !== null) {
92 + bridge.send('highlightElementInDOM', {
93 + displayName: element.displayName,
94 + id: element.id,
95 + rendererID,
96 + scrollIntoView: false,
97 + isSticky: true,
98 + });
99 + }
100 + }, [bridge, element, rendererID]);
101 +
102 // Handle elements that are removed from the tree while an async render is in progress.
103 if (element == null) {
104 console.warn(`<ElementView> Could not find element at index ${index}`);
@@ -100,6 +117,7 @@ export default function ElementView({ index, style, data }: Props) {
117 return (
118 <div
119 className={isSelected ? styles.SelectedElement : styles.Element}
120 + onMouseEnter={handleMouseEnter}
121 onMouseDown={handleMouseDown}
122 onDoubleClick={handleDoubleClick}
123 style={{
src/devtools/views/Components/SelectedElement.js
+2
@@ -47,6 +47,8 @@ export default function SelectedElement(_: Props) {
47 displayName: element.displayName,
48 id: selectedElementID,
49 rendererID,
50 + scrollIntoView: true,
51 + isSticky: false,
52 });
53 }
54 }
src/devtools/views/Components/Tree.js
+7 -1
@@ -12,6 +12,7 @@ import AutoSizer from 'react-virtualized-auto-sizer';
12 import { FixedSizeList } from 'react-window';
13 import { TreeContext } from './TreeContext';
14 import { SettingsContext } from '../Settings/SettingsContext';
15 +import { BridgeContext } from '../context';
16 import Element from './Element';
17 import InspectHostNodesToggle from './InspectHostNodesToggle';
18 import OwnersStack from './OwnersStack';
@@ -32,6 +33,7 @@ export default function Tree(props: Props) {
33 selectParentElementInTree,
34 selectPreviousElementInTree,
35 } = useContext(TreeContext);
36 + const bridge = useContext(BridgeContext);
37 const listRef = useRef<FixedSizeList<any> | null>(null);
38 const treeRef = useRef<HTMLDivElement | null>(null);
39
@@ -112,13 +114,17 @@ export default function Tree(props: Props) {
114 [baseDepth, numElements, getElementAtIndex, lastScrolledIDRef]
115 );
116
117 + const handleMouseLeave = useCallback(() => {
118 + bridge.send('clearHighlightedElementInDOM');
119 + }, [bridge]);
120 +
121 return (
122 <div className={styles.Tree} ref={treeRef}>
123 <div className={styles.SearchInput}>
124 {ownerStack.length > 0 ? <OwnersStack /> : <SearchInput />}
125 <InspectHostNodesToggle />
126 </div>
121 - <div className={styles.AutoSizerWrapper}>
127 + <div className={styles.AutoSizerWrapper} onMouseLeave={handleMouseLeave}>
128 <AutoSizer>
129 {({ height, width }) => (
130 <FixedSizeList