From 39223239bbcea68f801d2d3607c5326099e22e91 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 8 Apr 2019 14:26:16 +0100 Subject: [PATCH] Select DOM nodes on hover --- src/backend/agent.js | 19 +++++++++++++++---- src/backend/views/Highlighter.js | 7 +++++-- src/devtools/views/Components/Element.js | 18 ++++++++++++++++++ .../views/Components/SelectedElement.js | 2 ++ src/devtools/views/Components/Tree.js | 8 +++++++- 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/backend/agent.js b/src/backend/agent.js index 3bde89b953..5e2f332b85 100644 --- a/src/backend/agent.js +++ b/src/backend/agent.js @@ -63,6 +63,10 @@ export default class Agent extends EventEmitter { this._bridge = bridge; bridge.addListener('captureScreenshot', this.captureScreenshot); + bridge.addListener( + 'clearHighlightedElementInDOM', + this.clearHighlightedElementInDOM + ); bridge.addListener('exportProfilingSummary', this.exportProfilingSummary); bridge.addListener('getCommitDetails', this.getCommitDetails); bridge.addListener('getFiberCommits', this.getFiberCommits); @@ -216,14 +220,22 @@ export default class Agent extends EventEmitter { } }; + clearHighlightedElementInDOM = () => { + hideOverlay(); + }; + highlightElementInDOM = ({ displayName, id, + isSticky, rendererID, + scrollIntoView, }: { displayName: string, id: number, + isSticky: boolean, rendererID: number, + scrollIntoView: boolean, }) => { const renderer = this._rendererInterfaces[rendererID]; if (renderer == null) { @@ -236,13 +248,12 @@ export default class Agent extends EventEmitter { } if (node != null) { - if (typeof node.scrollIntoView === 'function') { + if (scrollIntoView && typeof node.scrollIntoView === 'function') { // If the node isn't visible show it before highlighting it. // We may want to reconsider this; it might be a little disruptive. node.scrollIntoView({ block: 'nearest', inline: 'nearest' }); } - - showOverlay(((node: any): HTMLElement), displayName); + showOverlay(((node: any): HTMLElement), displayName, isSticky); } else { hideOverlay(); } @@ -466,6 +477,6 @@ export default class Agent extends EventEmitter { // Don't pass the name explicitly. // It will be inferred from DOM tag and Fiber owner. - showOverlay(target); + showOverlay(target, null, true); }; } diff --git a/src/backend/views/Highlighter.js b/src/backend/views/Highlighter.js index fc98d4a134..8b1c17676a 100644 --- a/src/backend/views/Highlighter.js +++ b/src/backend/views/Highlighter.js @@ -18,7 +18,8 @@ export function hideOverlay() { export function showOverlay( element: HTMLElement | null, - componentName: string = '' + componentName: string | null, + isSticky: boolean ) { if (timeoutID !== null) { clearTimeout(timeoutID); @@ -34,5 +35,7 @@ export function showOverlay( overlay.inspect(element, componentName); - timeoutID = setTimeout(hideOverlay, SHOW_DURATION); + if (!isSticky) { + timeoutID = setTimeout(hideOverlay, SHOW_DURATION); + } } diff --git a/src/devtools/views/Components/Element.js b/src/devtools/views/Components/Element.js index 14acf8ae2f..f10284cc91 100644 --- a/src/devtools/views/Components/Element.js +++ b/src/devtools/views/Components/Element.js @@ -11,6 +11,7 @@ import React, { import { ElementTypeClass, ElementTypeFunction } from 'src/devtools/types'; import { createRegExp } from '../utils'; import { TreeContext } from './TreeContext'; +import { BridgeContext, StoreContext } from '../context'; import type { Element } from './types'; @@ -31,6 +32,9 @@ export default function ElementView({ index, style, data }: Props) { selectedElementID, selectElementByID, } = useContext(TreeContext); + const bridge = useContext(BridgeContext); + const store = useContext(StoreContext); + const element = getElementAtIndex(index); const id = element === null ? null : element.id; @@ -82,6 +86,19 @@ export default function ElementView({ index, style, data }: Props) { [id, selectElementByID] ); + const rendererID = store.getRendererIDForElement(element.id) || null; + const handleMouseEnter = useCallback(() => { + if (rendererID !== null) { + bridge.send('highlightElementInDOM', { + displayName: element.displayName, + id: element.id, + rendererID, + scrollIntoView: false, + isSticky: true, + }); + } + }, [bridge, element, rendererID]); + // Handle elements that are removed from the tree while an async render is in progress. if (element == null) { console.warn(` Could not find element at index ${index}`); @@ -100,6 +117,7 @@ export default function ElementView({ index, style, data }: Props) { return (
| null>(null); const treeRef = useRef(null); @@ -112,13 +114,17 @@ export default function Tree(props: Props) { [baseDepth, numElements, getElementAtIndex, lastScrolledIDRef] ); + const handleMouseLeave = useCallback(() => { + bridge.send('clearHighlightedElementInDOM'); + }, [bridge]); + return (
{ownerStack.length > 0 ? : }
-
+
{({ height, width }) => (