@samitouri / QOS-React-2 / commits / 097d0386b1

Track search navigation in DOM

Dan Abramov committed Apr 12, 2019 at 18:59 UTC 097d0386b133f779f4c3622d2a745d64e1e32fc8
3 files changed +57 -24
src/devtools/views/Components/Element.js
+2 -2
@@ -44,7 +44,7 @@ export default function ElementView({ data, index, style }: Props) {
44 const {
45 lastScrolledIDRef,
46 treeFocused,
47 - isNavigatingWithKeyboard,
47 + isUsingKeyboardOrSearch,
48 onElementMouseEnter,
49 } = data;
50 const id = element === null ? null : element.id;
@@ -122,7 +122,7 @@ export default function ElementView({ data, index, style }: Props) {
122 className = treeFocused
123 ? styles.SelectedElement
124 : styles.InactiveSelectedElement;
125 - } else if (isHovered && !isNavigatingWithKeyboard) {
125 + } else if (isHovered && !isUsingKeyboardOrSearch) {
126 className = styles.HoveredElement;
127 }
128
src/devtools/views/Components/SearchInput.js
+24 -8
@@ -8,9 +8,12 @@ import Icon from '../Icon';
8
9 import styles from './SearchInput.css';
10
11 -type Props = {||};
11 +type Props = {|
12 + onSearchInteraction: () => void,
13 +|};
14
15 export default function SearchInput(props: Props) {
16 + const { onSearchInteraction } = props;
17 const {
18 goToNextSearchResult,
19 goToPreviousSearchResult,
@@ -25,13 +28,17 @@ export default function SearchInput(props: Props) {
28 const inputRef = useRef<HTMLInputElement | null>(null);
29
30 const handleTextChange = useCallback(
28 - ({ currentTarget }) => setSearchText(currentTarget.value),
29 - [setSearchText]
31 + ({ currentTarget }) => {
32 + setSearchText(currentTarget.value);
33 + onSearchInteraction();
34 + },
35 + [setSearchText, onSearchInteraction]
36 );
37
38 const resetSearch = useCallback(() => {
39 setSearchText('');
34 - }, [setSearchText]);
40 + onSearchInteraction();
41 + }, [setSearchText, onSearchInteraction]);
42
43 const handleKeyDown = useCallback(
44 event => {
@@ -39,26 +46,29 @@ export default function SearchInput(props: Props) {
46 switch (event.key) {
47 case 'ArrowDown':
48 selectNextElementInTree();
49 + onSearchInteraction();
50 event.preventDefault();
51 break;
52 case 'ArrowUp':
53 selectPreviousElementInTree();
54 + onSearchInteraction();
55 event.preventDefault();
56 break;
57 default:
58 break;
59 }
60 },
52 - [selectNextElementInTree, selectPreviousElementInTree]
61 + [selectNextElementInTree, selectPreviousElementInTree, onSearchInteraction]
62 );
63
64 const handleInputKeyPress = useCallback(
65 ({ key }) => {
66 if (key === 'Enter') {
67 goToNextSearchResult();
68 + onSearchInteraction();
69 }
70 },
61 - [goToNextSearchResult]
71 + [goToNextSearchResult, onSearchInteraction]
72 );
73
74 // Auto-focus search input
@@ -109,7 +119,10 @@ export default function SearchInput(props: Props) {
119 <Button
120 className={styles.IconButton}
121 disabled={!searchText}
112 - onClick={goToPreviousSearchResult}
122 + onClick={() => {
123 + goToPreviousSearchResult();
124 + onSearchInteraction();
125 + }}
126 title="Scroll to previous search result"
127 >
128 <ButtonIcon type="up" />
@@ -117,7 +130,10 @@ export default function SearchInput(props: Props) {
130 <Button
131 className={styles.IconButton}
132 disabled={!searchText}
120 - onClick={goToNextSearchResult}
133 + onClick={() => {
134 + goToNextSearchResult();
135 + onSearchInteraction();
136 + }}
137 title="Scroll to next search result"
138 >
139 <ButtonIcon type="down" />
src/devtools/views/Components/Tree.js
+31 -14
@@ -27,7 +27,7 @@ export type ItemData = {|
27 baseDepth: number,
28 numElements: number,
29 getElementAtIndex: (index: number) => Element | null,
30 - isNavigatingWithKeyboard: boolean,
30 + isUsingKeyboardOrSearch: boolean,
31 lastScrolledIDRef: { current: number | null },
32 onElementMouseEnter: (id: number) => void,
33 treeFocused: boolean,
@@ -52,9 +52,7 @@ export default function Tree(props: Props) {
52 } = useContext(TreeContext);
53 const bridge = useContext(BridgeContext);
54 const store = useContext(StoreContext);
55 - const [isNavigatingWithKeyboard, setIsNavigatingWithKeyboard] = useState(
56 - false
57 - );
55 + const [isUsingKeyboardOrSearch, setIsUsingKeyboardOrSearch] = useState(false);
56 // $FlowFixMe https://github.com/facebook/flow/issues/7341
57 const listRef = useRef<FixedSizeList<ItemData> | null>(null);
58 const treeRef = useRef<HTMLDivElement | null>(null);
@@ -146,7 +144,7 @@ export default function Tree(props: Props) {
144 default:
145 return;
146 }
149 - setIsNavigatingWithKeyboard(true);
147 + setIsUsingKeyboardOrSearch(true);
148 };
149
150 // It's important to listen to the ownerDocument to support the browser extension.
@@ -213,33 +211,48 @@ export default function Tree(props: Props) {
211 // If we switch the selected element while using the keyboard,
212 // start highlighting it in the DOM instead of the last hovered node.
213 useEffect(() => {
216 - if (isNavigatingWithKeyboard && selectedElementID !== null) {
217 - highlightElementInDOM(selectedElementID);
214 + if (isUsingKeyboardOrSearch) {
215 + if (selectedElementID !== null) {
216 + highlightElementInDOM(selectedElementID);
217 + } else {
218 + bridge.send('clearHighlightedElementInDOM');
219 + }
220 }
219 - }, [isNavigatingWithKeyboard, highlightElementInDOM, selectedElementID]);
221 + }, [
222 + bridge,
223 + isUsingKeyboardOrSearch,
224 + highlightElementInDOM,
225 + selectedElementID,
226 + ]);
227
228 // Highlight last hovered element.
229 const handleElementMouseEnter = useCallback(
230 id => {
231 // Ignore hover while we're navigating with keyboard.
232 // This avoids flicker from the hovered nodes under the mouse.
226 - if (!isNavigatingWithKeyboard) {
233 + if (!isUsingKeyboardOrSearch) {
234 highlightElementInDOM(id);
235 }
236 },
230 - [isNavigatingWithKeyboard, highlightElementInDOM]
237 + [isUsingKeyboardOrSearch, highlightElementInDOM]
238 );
239
240 const handleMouseMove = useCallback(() => {
241 // We started using the mouse again.
242 // This will enable hover styles in individual rows.
236 - setIsNavigatingWithKeyboard(false);
243 + setIsUsingKeyboardOrSearch(false);
244 }, []);
245
246 const handleMouseLeave = useCallback(() => {
247 bridge.send('clearHighlightedElementInDOM');
248 }, [bridge]);
249
250 + const handleSearchInteraction = useCallback(() => {
251 + // We started navigating using search.
252 + // This will disable hover styles and start highlighting DOM elements.
253 + setIsUsingKeyboardOrSearch(true);
254 + }, []);
255 +
256 // Let react-window know to re-render any time the underlying tree data changes.
257 // This includes the owner context, since it controls a filtered view of the tree.
258 const itemData = useMemo<ItemData>(
@@ -247,7 +260,7 @@ export default function Tree(props: Props) {
260 baseDepth,
261 numElements,
262 getElementAtIndex,
250 - isNavigatingWithKeyboard,
263 + isUsingKeyboardOrSearch,
264 onElementMouseEnter: handleElementMouseEnter,
265 lastScrolledIDRef,
266 treeFocused,
@@ -256,7 +269,7 @@ export default function Tree(props: Props) {
269 baseDepth,
270 numElements,
271 getElementAtIndex,
259 - isNavigatingWithKeyboard,
272 + isUsingKeyboardOrSearch,
273 handleElementMouseEnter,
274 lastScrolledIDRef,
275 treeFocused,
@@ -266,7 +279,11 @@ export default function Tree(props: Props) {
279 return (
280 <div className={styles.Tree} ref={treeRef}>
281 <div className={styles.SearchInput}>
269 - {ownerStack.length > 0 ? <OwnersStack /> : <SearchInput />}
282 + {ownerStack.length > 0 ? (
283 + <OwnersStack />
284 + ) : (
285 + <SearchInput onSearchInteraction={handleSearchInteraction} />
286 + )}
287 <InspectHostNodesToggle />
288 </div>
289 <div