diff --git a/packages/react-devtools-shared/src/devtools/views/Components/ComponentSearchInput.js b/packages/react-devtools-shared/src/devtools/views/Components/ComponentSearchInput.js
new file mode 100644
index 0000000000000..3180a7ebb0702
--- /dev/null
+++ b/packages/react-devtools-shared/src/devtools/views/Components/ComponentSearchInput.js
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import * as React from 'react';
+import {useContext} from 'react';
+import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
+
+import SearchInput from '../SearchInput';
+
+type Props = {||};
+
+export default function ComponentSearchInput(props: Props) {
+ const {searchIndex, searchResults, searchText} = useContext(TreeStateContext);
+ const dispatch = useContext(TreeDispatcherContext);
+
+ const search = text => dispatch({type: 'SET_SEARCH_TEXT', payload: text});
+ const goToNextResult = () => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'});
+ const goToPreviousResult = () =>
+ dispatch({type: 'GO_TO_PREVIOUS_SEARCH_RESULT'});
+
+ return (
+
+ );
+}
diff --git a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js
index 061ea131a9bf3..3126bfc8f0d3f 100644
--- a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js
+++ b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js
@@ -27,7 +27,7 @@ import {BridgeContext, StoreContext, OptionsContext} from '../context';
import Element from './Element';
import InspectHostNodesToggle from './InspectHostNodesToggle';
import OwnersStack from './OwnersStack';
-import SearchInput from './SearchInput';
+import ComponentSearchInput from './ComponentSearchInput';
import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
import SelectedTreeHighlight from './SelectedTreeHighlight';
import TreeFocusedContext from './TreeFocusedContext';
@@ -343,7 +343,7 @@ export default function Tree(props: Props) {
)}
}>
- {ownerID !== null ? : }
+ {ownerID !== null ? : }
{showInlineWarningsAndErrors &&
ownerID === null &&
diff --git a/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js b/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js
index 93e680bc751af..f6854468dc6eb 100644
--- a/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js
+++ b/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js
@@ -829,7 +829,7 @@ type Props = {|
defaultSelectedElementIndex?: ?number,
|};
-// TODO Remove TreeContextController wrapper element once global ConsearchText.write API exists.
+// TODO Remove TreeContextController wrapper element once global Context.write API exists.
function TreeContextController({
children,
defaultInspectedElementID,
diff --git a/packages/react-devtools-shared/src/devtools/views/Profiler/ClearProfilingDataButton.js b/packages/react-devtools-shared/src/devtools/views/Profiler/ClearProfilingDataButton.js
index f57b4ba8e1731..8fbebfeba86dc 100644
--- a/packages/react-devtools-shared/src/devtools/views/Profiler/ClearProfilingDataButton.js
+++ b/packages/react-devtools-shared/src/devtools/views/Profiler/ClearProfilingDataButton.js
@@ -20,19 +20,19 @@ export default function ClearProfilingDataButton() {
const {didRecordCommits, isProfiling, selectedTabID} = useContext(
ProfilerContext,
);
- const {clearTimelineData, timelineData} = useContext(TimelineContext);
+ const {file, setFile} = useContext(TimelineContext);
const {profilerStore} = store;
let doesHaveData = false;
if (selectedTabID === 'timeline') {
- doesHaveData = timelineData !== null;
+ doesHaveData = file !== null;
} else {
doesHaveData = didRecordCommits;
}
const clear = () => {
if (selectedTabID === 'timeline') {
- clearTimelineData();
+ setFile(null);
} else {
profilerStore.clear();
}
diff --git a/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.css b/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.css
index 6763564e52a29..12089b71fdc6a 100644
--- a/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.css
+++ b/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.css
@@ -115,3 +115,9 @@
.Link {
color: var(--color-button);
}
+
+.TimlineSearchInputContainer {
+ flex: 1 1;
+ display: flex;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js b/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js
index 0bd6b0375e076..fbe8c4ac20c03 100644
--- a/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js
+++ b/packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js
@@ -28,6 +28,7 @@ import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views
import {SettingsModalContextController} from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext';
import portaledContent from '../portaledContent';
import {StoreContext} from '../context';
+import {TimelineContext} from 'react-devtools-timeline/src/TimelineContext';
import styles from './Profiler.css';
@@ -43,19 +44,19 @@ function Profiler(_: {||}) {
supportsProfiling,
} = useContext(ProfilerContext);
+ const {searchInputContainerRef} = useContext(TimelineContext);
+
const {supportsTimeline} = useContext(StoreContext);
- let isLegacyProfilerSelected = false;
+ const isLegacyProfilerSelected = selectedTabID !== 'timeline';
let view = null;
if (didRecordCommits || selectedTabID === 'timeline') {
switch (selectedTabID) {
case 'flame-chart':
- isLegacyProfilerSelected = true;
view = ;
break;
case 'ranked-chart':
- isLegacyProfilerSelected = true;
view = ;
break;
case 'timeline':
@@ -121,6 +122,12 @@ function Profiler(_: {||}) {
/>
+ {!isLegacyProfilerSelected && (
+
+ )}
{isLegacyProfilerSelected && didRecordCommits && (
diff --git a/packages/react-devtools-shared/src/devtools/views/Profiler/ProfilingImportExportButtons.js b/packages/react-devtools-shared/src/devtools/views/Profiler/ProfilingImportExportButtons.js
index 5b53e04410df0..afa3b91f2a12e 100644
--- a/packages/react-devtools-shared/src/devtools/views/Profiler/ProfilingImportExportButtons.js
+++ b/packages/react-devtools-shared/src/devtools/views/Profiler/ProfilingImportExportButtons.js
@@ -29,7 +29,7 @@ export default function ProfilingImportExportButtons() {
const {isProfiling, profilingData, rootID, selectedTabID} = useContext(
ProfilerContext,
);
- const {importTimelineData} = useContext(TimelineContext);
+ const {setFile} = useContext(TimelineContext);
const store = useContext(StoreContext);
const {profilerStore} = store;
@@ -111,7 +111,8 @@ export default function ProfilingImportExportButtons() {
const importTimelineDataWrapper = event => {
const input = inputRef.current;
if (input !== null && input.files.length > 0) {
- importTimelineData(input.files[0]);
+ const file = input.files[0];
+ setFile(file);
}
};
diff --git a/packages/react-devtools-shared/src/devtools/views/Components/SearchInput.css b/packages/react-devtools-shared/src/devtools/views/SearchInput.css
similarity index 100%
rename from packages/react-devtools-shared/src/devtools/views/Components/SearchInput.css
rename to packages/react-devtools-shared/src/devtools/views/SearchInput.css
diff --git a/packages/react-devtools-shared/src/devtools/views/Components/SearchInput.js b/packages/react-devtools-shared/src/devtools/views/SearchInput.js
similarity index 61%
rename from packages/react-devtools-shared/src/devtools/views/Components/SearchInput.js
rename to packages/react-devtools-shared/src/devtools/views/SearchInput.js
index 736d39fc75244..5edb915793cb1 100644
--- a/packages/react-devtools-shared/src/devtools/views/Components/SearchInput.js
+++ b/packages/react-devtools-shared/src/devtools/views/SearchInput.js
@@ -8,44 +8,48 @@
*/
import * as React from 'react';
-import {useCallback, useContext, useEffect, useRef} from 'react';
-import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
-import Button from '../Button';
-import ButtonIcon from '../ButtonIcon';
-import Icon from '../Icon';
+import {useEffect, useRef} from 'react';
+import Button from './Button';
+import ButtonIcon from './ButtonIcon';
+import Icon from './Icon';
import styles from './SearchInput.css';
-type Props = {||};
-
-export default function SearchInput(props: Props) {
- const {searchIndex, searchResults, searchText} = useContext(TreeStateContext);
- const dispatch = useContext(TreeDispatcherContext);
+type Props = {|
+ goToNextResult: () => void,
+ goToPreviousResult: () => void,
+ placeholder: string,
+ search: (text: string) => void,
+ searchIndex: number,
+ searchResultsCount: number,
+ searchText: string,
+|};
+export default function SearchInput({
+ goToNextResult,
+ goToPreviousResult,
+ placeholder,
+ search,
+ searchIndex,
+ searchResultsCount,
+ searchText,
+}: Props) {
const inputRef = useRef(null);
- const handleTextChange = useCallback(
- ({currentTarget}) =>
- dispatch({type: 'SET_SEARCH_TEXT', payload: currentTarget.value}),
- [dispatch],
- );
- const resetSearch = useCallback(
- () => dispatch({type: 'SET_SEARCH_TEXT', payload: ''}),
- [dispatch],
- );
+ const resetSearch = () => search('');
- const handleInputKeyPress = useCallback(
- ({key, shiftKey}) => {
- if (key === 'Enter') {
- if (shiftKey) {
- dispatch({type: 'GO_TO_PREVIOUS_SEARCH_RESULT'});
- } else {
- dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'});
- }
+ const handleChange = ({currentTarget}) => {
+ search(currentTarget.value);
+ };
+ const handleKeyPress = ({key, shiftKey}) => {
+ if (key === 'Enter') {
+ if (shiftKey) {
+ goToPreviousResult();
+ } else {
+ goToNextResult();
}
- },
- [dispatch],
- );
+ }
+ };
// Auto-focus search input
useEffect(() => {
@@ -53,7 +57,7 @@ export default function SearchInput(props: Props) {
return () => {};
}
- const handleWindowKey = (event: KeyboardEvent) => {
+ const handleKeyDown = (event: KeyboardEvent) => {
const {key, metaKey} = event;
if (key === 'f' && metaKey) {
if (inputRef.current !== null) {
@@ -68,9 +72,9 @@ export default function SearchInput(props: Props) {
// Here we use portals to render individual tabs (e.g. Profiler),
// and the root document might belong to a different window.
const ownerDocument = inputRef.current.ownerDocument;
- ownerDocument.addEventListener('keydown', handleWindowKey);
+ ownerDocument.addEventListener('keydown', handleKeyDown);
- return () => ownerDocument.removeEventListener('keydown', handleWindowKey);
+ return () => ownerDocument.removeEventListener('keydown', handleKeyDown);
}, [inputRef]);
return (
@@ -78,23 +82,23 @@ export default function SearchInput(props: Props) {
{!!searchText && (
- {Math.min(searchIndex + 1, searchResults.length)} |{' '}
- {searchResults.length}
+ {Math.min(searchIndex + 1, searchResultsCount)} |{' '}
+ {searchResultsCount}