Select DOM nodes on hover

This commit is contained in:
Dan Abramov
2019-04-08 17:58:12 +01:00
parent cb164eb9fb
commit 39223239bb
5 changed files with 47 additions and 7 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,
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);
};
}
+5 -2
View File
@@ -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);
}
}
+18
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,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(`<ElementView> Could not find element at index ${index}`);
@@ -100,6 +117,7 @@ export default function ElementView({ index, style, data }: Props) {
return (
<div
className={isSelected ? styles.SelectedElement : styles.Element}
onMouseEnter={handleMouseEnter}
onMouseDown={handleMouseDown}
onDoubleClick={handleDoubleClick}
style={{
@@ -47,6 +47,8 @@ export default function SelectedElement(_: Props) {
displayName: element.displayName,
id: selectedElementID,
rendererID,
scrollIntoView: true,
isSticky: false,
});
}
}
+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