Merge pull request #92 from gaearon/highlight-on-hover

Select DOM nodes on hover
This commit is contained in:
Brian Vaughn
2019-04-08 10:23:56 -07:00
committed by GitHub
5 changed files with 50 additions and 8 deletions
+15 -4
View File
@@ -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,
hideAfterTimeout,
id,
rendererID,
scrollIntoView,
}: {
displayName: string,
hideAfterTimeout: boolean,
id: number,
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, hideAfterTimeout);
} 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, false);
};
}
+5 -2
View File
@@ -18,7 +18,8 @@ export function hideOverlay() {
export function showOverlay(
element: HTMLElement | null,
componentName: string = ''
componentName: string | null,
hideAfterTimeout: boolean
) {
if (timeoutID !== null) {
clearTimeout(timeoutID);
@@ -34,5 +35,7 @@ export function showOverlay(
overlay.inspect(element, componentName);
timeoutID = setTimeout(hideOverlay, SHOW_DURATION);
if (hideAfterTimeout) {
timeoutID = setTimeout(hideOverlay, SHOW_DURATION);
}
}
+20
View File
@@ -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,21 @@ export default function ElementView({ index, style, data }: Props) {
[id, selectElementByID]
);
const rendererID = id !== null ? store.getRendererIDForElement(id) : null;
// Individual elements don't have a corresponding leave handler.
// Instead, it's implemented on the tree level.
const handleMouseEnter = useCallback(() => {
if (element !== null && id !== null && rendererID !== null) {
bridge.send('highlightElementInDOM', {
displayName: element.displayName,
hideAfterTimeout: false,
id,
rendererID,
scrollIntoView: false,
});
}
}, [bridge, element, id, rendererID]);
// Handle elements that are removed from the tree while an async render is in progress.
if (element == null) {
console.warn(`<ElementView> Could not find element at index ${index}`);
@@ -100,6 +119,7 @@ export default function ElementView({ index, style, data }: Props) {
return (
<div
className={isSelected ? styles.SelectedElement : styles.Element}
onMouseEnter={handleMouseEnter}
onMouseDown={handleMouseDown}
onDoubleClick={handleDoubleClick}
style={{
@@ -45,8 +45,10 @@ export default function SelectedElement(_: Props) {
if (rendererID !== null) {
bridge.send('highlightElementInDOM', {
displayName: element.displayName,
hideAfterTimeout: true,
id: selectedElementID,
rendererID,
scrollIntoView: true,
});
}
}
@@ -269,7 +271,7 @@ function useInspectedElement(id: number | null): InspectedElement | null {
return () => {};
}
const rendererID = store.getRendererIDForElement(id) || null;
const rendererID = store.getRendererIDForElement(id);
// Update the $r variable.
bridge.send('selectElement', { id, rendererID });
+7 -1
View File
@@ -12,6 +12,7 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import { FixedSizeList } from 'react-window';
import { TreeContext } from './TreeContext';
import { SettingsContext } from '../Settings/SettingsContext';
import { BridgeContext } from '../context';
import Element from './Element';
import InspectHostNodesToggle from './InspectHostNodesToggle';
import OwnersStack from './OwnersStack';
@@ -32,6 +33,7 @@ export default function Tree(props: Props) {
selectParentElementInTree,
selectPreviousElementInTree,
} = useContext(TreeContext);
const bridge = useContext(BridgeContext);
const listRef = useRef<FixedSizeList<any> | null>(null);
const treeRef = useRef<HTMLDivElement | null>(null);
@@ -112,13 +114,17 @@ export default function Tree(props: Props) {
[baseDepth, numElements, getElementAtIndex, lastScrolledIDRef]
);
const handleMouseLeave = useCallback(() => {
bridge.send('clearHighlightedElementInDOM');
}, [bridge]);
return (
<div className={styles.Tree} ref={treeRef}>
<div className={styles.SearchInput}>
{ownerStack.length > 0 ? <OwnersStack /> : <SearchInput />}
<InspectHostNodesToggle />
</div>
<div className={styles.AutoSizerWrapper}>
<div className={styles.AutoSizerWrapper} onMouseLeave={handleMouseLeave}>
<AutoSizer>
{({ height, width }) => (
<FixedSizeList