@samitouri / QOS-React-2 / commits / 7785a5263e

[DevTools] - Highlight rendered by elements on hover. (#18479)

* [DevTools] - Highlight rendered by elements on hover. * Fixed formatting issue. * DevTools - Extracted highlight logic to custom hook. Added highlight support for rendered by elements. * Removed unnecessary padding style * Removed unnecessary wrapper function. Co-authored-by: Brian Vaughn <brian.david.vaughn@gmail.com>

Hristo Kanchev committed Apr 3, 2020 at 19:49 UTC 7785a5263e0bbbdcb997a84a840c9c039fcf6e5d
4 files changed +74 -32
packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.css
+14 -4
@@ -82,7 +82,6 @@
82 .Owner {
83 border-radius: 0.25rem;
84 padding: 0.125rem 0.25rem;
85 - cursor: pointer;
85 background: none;
86 border: none;
87 display: block;
@@ -107,12 +106,23 @@
106 }
107
108 .OwnerButton {
109 + cursor: pointer;
110 + width: 100%;
111 + padding: 0;
112 +}
113 +
114 +.OwnerContent {
115 display: flex;
116 align-items: center;
112 - margin-left: 0.5rem;
113 - padding: 0;
117 + padding-left: 1rem;
118 + width: 100%;
119 + border-radius: 0.25rem;
120 +}
121 +
122 +.OwnerContent:hover {
123 + background-color: var(--color-background-hover);
124 }
125
126 .ContextMenuIcon {
127 margin-right: 0.5rem;
118 -}
\ No newline at end of file
128 +}
packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.js
+19 -6
@@ -26,6 +26,7 @@ import ViewElementSourceContext from './ViewElementSourceContext';
26 import NativeStyleEditor from './NativeStyleEditor';
27 import Toggle from '../Toggle';
28 import Badge from './Badge';
29 +import {useHighlightNativeElement} from '../hooks';
30 import {
31 ComponentFilterElementType,
32 ElementTypeClass,
@@ -522,6 +523,10 @@ function OwnerView({
523 type,
524 }: OwnerViewProps) {
525 const dispatch = useContext(TreeDispatcherContext);
526 + const {
527 + highlightNativeElement,
528 + clearHighlightNativeElement,
529 + } = useHighlightNativeElement();
530
531 const handleClick = useCallback(
532 () =>
@@ -532,18 +537,26 @@ function OwnerView({
537 [dispatch, id],
538 );
539
540 + const onMouseEnter = () => highlightNativeElement(id);
541 +
542 + const onMouseLeave = clearHighlightNativeElement;
543 +
544 return (
545 <Button
546 key={id}
547 className={styles.OwnerButton}
548 disabled={!isInStore}
540 - onClick={handleClick}>
541 - <span
542 - className={`${styles.Owner} ${isInStore ? '' : styles.NotInStore}`}
543 - title={displayName}>
544 - {displayName}
549 + onClick={handleClick}
550 + onMouseEnter={onMouseEnter}
551 + onMouseLeave={onMouseLeave}>
552 + <span className={styles.OwnerContent}>
553 + <span
554 + className={`${styles.Owner} ${isInStore ? '' : styles.NotInStore}`}
555 + title={displayName}>
556 + {displayName}
557 + </span>
558 + <Badge hocDisplayNames={hocDisplayNames} type={type} />
559 </span>
546 - <Badge hocDisplayNames={hocDisplayNames} type={type} />
560 </Button>
561 );
562 }
packages/react-devtools-shared/src/devtools/views/Components/Tree.js
+7 -22
@@ -30,6 +30,7 @@ import SearchInput from './SearchInput';
30 import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
31 import SelectedTreeHighlight from './SelectedTreeHighlight';
32 import TreeFocusedContext from './TreeFocusedContext';
33 +import {useHighlightNativeElement} from '../hooks';
34
35 import styles from './Tree.css';
36
@@ -61,6 +62,10 @@ export default function Tree(props: Props) {
62 const [isNavigatingWithKeyboard, setIsNavigatingWithKeyboard] = useState(
63 false,
64 );
65 + const {
66 + highlightNativeElement,
67 + clearHighlightNativeElement,
68 + } = useHighlightNativeElement();
69 const treeRef = useRef<HTMLDivElement | null>(null);
70 const focusTargetRef = useRef<HTMLDivElement | null>(null);
71
@@ -205,24 +210,6 @@ export default function Tree(props: Props) {
210 [dispatch, selectedElementID],
211 );
212
208 - const highlightNativeElement = useCallback(
209 - (id: number) => {
210 - const element = store.getElementByID(id);
211 - const rendererID = store.getRendererIDForElement(id);
212 - if (element !== null && rendererID !== null) {
213 - bridge.send('highlightNativeElement', {
214 - displayName: element.displayName,
215 - hideAfterTimeout: false,
216 - id,
217 - openNativeElementsPanel: false,
218 - rendererID,
219 - scrollIntoView: false,
220 - });
221 - }
222 - },
223 - [store, bridge],
224 - );
225 -
213 // If we switch the selected element while using the keyboard,
214 // start highlighting it in the DOM instead of the last hovered node.
215 const searchRef = useRef({searchIndex, searchResults});
@@ -240,7 +227,7 @@ export default function Tree(props: Props) {
227 if (selectedElementID !== null) {
228 highlightNativeElement(selectedElementID);
229 } else {
243 - bridge.send('clearNativeElementHighlight');
230 + clearHighlightNativeElement();
231 }
232 }
233 }, [
@@ -270,9 +257,7 @@ export default function Tree(props: Props) {
257 setIsNavigatingWithKeyboard(false);
258 }, []);
259
273 - const handleMouseLeave = useCallback(() => {
274 - bridge.send('clearNativeElementHighlight');
275 - }, [bridge]);
260 + const handleMouseLeave = clearHighlightNativeElement;
261
262 // Let react-window know to re-render any time the underlying tree data changes.
263 // This includes the owner context, since it controls a filtered view of the tree.
packages/react-devtools-shared/src/devtools/views/hooks.js
+34
@@ -14,11 +14,13 @@ import {
14 useLayoutEffect,
15 useReducer,
16 useState,
17 + useContext,
18 } from 'react';
19 import {
20 localStorageGetItem,
21 localStorageSetItem,
22 } from 'react-devtools-shared/src/storage';
23 +import {StoreContext, BridgeContext} from './context';
24 import {sanitizeForParse, smartParse, smartStringify} from '../utils';
25
26 type ACTION_RESET = {|
@@ -301,3 +303,35 @@ export function useSubscription<Value>({
303
304 return state.value;
305 }
306 +
307 +export function useHighlightNativeElement() {
308 + const bridge = useContext(BridgeContext);
309 + const store = useContext(StoreContext);
310 +
311 + const highlightNativeElement = useCallback(
312 + (id: number) => {
313 + const element = store.getElementByID(id);
314 + const rendererID = store.getRendererIDForElement(id);
315 + if (element !== null && rendererID !== null) {
316 + bridge.send('highlightNativeElement', {
317 + displayName: element.displayName,
318 + hideAfterTimeout: false,
319 + id,
320 + openNativeElementsPanel: false,
321 + rendererID,
322 + scrollIntoView: false,
323 + });
324 + }
325 + },
326 + [store, bridge],
327 + );
328 +
329 + const clearHighlightNativeElement = useCallback(() => {
330 + bridge.send('clearNativeElementHighlight');
331 + }, [bridge]);
332 +
333 + return {
334 + highlightNativeElement,
335 + clearHighlightNativeElement,
336 + };
337 +}