Skip to content

Commit

Permalink
Highlight current search result in Timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn committed Nov 21, 2021
1 parent 68f39c6 commit c38e017
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 11 deletions.
13 changes: 7 additions & 6 deletions packages/react-devtools-timeline/src/CanvasPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,9 @@ function AutoSizedCanvas({
const {searchIndex, searchResults} = useContext(TimelineSearchContext);

useLayoutEffect(() => {
if (searchResults.length === 0) {
return;
}

const componentMeasure = searchResults[searchIndex];
if (componentMeasure !== null) {
const componentMeasure =
searchResults.length > 0 ? searchResults[searchIndex] : null;
if (componentMeasure != null) {
const scrollState = moveStateToRange({
state: viewState.horizontalScrollState,
rangeStart: componentMeasure.timestamp,
Expand All @@ -191,8 +188,11 @@ function AutoSizedCanvas({
});

viewState.updateHorizontalScrollState(scrollState);
viewState.updateSearchResult({componentMeasure});

surfaceRef.current.displayIfNeeded();
} else {
viewState.updateSearchResult(null);
}
}, [searchIndex, searchResults, viewState]);

Expand Down Expand Up @@ -360,6 +360,7 @@ function AutoSizedCanvas({
surface,
defaultFrame,
data,
viewState,
);
componentMeasuresViewRef.current = componentMeasuresView;
componentMeasuresViewWrapper = createViewHelper(
Expand Down
23 changes: 21 additions & 2 deletions packages/react-devtools-timeline/src/TimelineContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
import * as React from 'react';
import {createContext, useMemo, useRef, useState} from 'react';

import type {HorizontalScrollStateChangeCallback, ViewState} from './types';
import type {
HorizontalScrollStateChangeCallback,
SearchResult,
SearchResultStateChangeCallback,
ViewState,
} from './types';
import type {RefObject} from 'shared/ReactTypes';

export type Context = {|
Expand All @@ -34,17 +39,22 @@ function TimelineContextController({children}: Props) {
// Recreate view state any time new profiling data is imported.
const viewState = useMemo<ViewState>(() => {
const horizontalScrollStateChangeCallbacks: Set<HorizontalScrollStateChangeCallback> = new Set();
const searchResultStateChangeCallbacks: Set<SearchResultStateChangeCallback> = new Set();

const horizontalScrollState = {
offset: 0,
length: 0,
};

return {
const state: ViewState = {
horizontalScrollState,
onHorizontalScrollStateChange: callback => {
horizontalScrollStateChangeCallbacks.add(callback);
},
onSearchResultStateChange: callback => {
searchResultStateChangeCallbacks.add(callback);
},
searchResult: null,
updateHorizontalScrollState: scrollState => {
if (
horizontalScrollState.offset === scrollState.offset &&
Expand All @@ -60,8 +70,17 @@ function TimelineContextController({children}: Props) {
callback(scrollState);
});
},
updateSearchResult: (searchResult: SearchResult | null) => {
state.searchResult = searchResult;

searchResultStateChangeCallbacks.forEach(callback => {
callback(searchResult);
});
},
viewToMutableViewStateMap: new Map(),
};

return state;
}, [file]);

const value = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
* @flow
*/

import type {ReactComponentMeasure, ReactProfilerData} from '../types';
import type {
ReactComponentMeasure,
ReactProfilerData,
SearchResult,
ViewState,
} from '../types';
import type {
Interaction,
IntrinsicSize,
Expand All @@ -31,20 +36,28 @@ import {
rectIntersectsRect,
intersectionOfRects,
} from '../view-base';
import {COLORS, NATIVE_EVENT_HEIGHT, BORDER_SIZE} from './constants';
import {BORDER_SIZE, COLORS, NATIVE_EVENT_HEIGHT} from './constants';

const ROW_WITH_BORDER_HEIGHT = NATIVE_EVENT_HEIGHT + BORDER_SIZE;

export class ComponentMeasuresView extends View {
_hoveredComponentMeasure: ReactComponentMeasure | null = null;
_intrinsicSize: IntrinsicSize;
_profilerData: ReactProfilerData;
_searchResult: ReactComponentMeasure | null = null;

onHover: ((event: ReactComponentMeasure | null) => void) | null = null;

constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) {
constructor(
surface: Surface,
frame: Rect,
profilerData: ReactProfilerData,
viewState: ViewState,
) {
super(surface, frame);

viewState.onSearchResultStateChange(this._handleSearchResultStateChange);

this._profilerData = profilerData;

this._intrinsicSize = {
Expand Down Expand Up @@ -74,6 +87,7 @@ export class ComponentMeasuresView extends View {
componentMeasure: ReactComponentMeasure,
scaleFactor: number,
showHoverHighlight: boolean,
showSearchResultBorder: boolean,
): boolean {
const {frame} = this;
const {
Expand Down Expand Up @@ -150,6 +164,11 @@ export class ComponentMeasuresView extends View {
break;
}
}

if (showSearchResultBorder) {
context.fillStyle = COLORS.SEARCH_RESULT_FILL;
}

context.fillRect(
drawableRect.origin.x,
drawableRect.origin.y,
Expand All @@ -171,6 +190,7 @@ export class ComponentMeasuresView extends View {
frame,
_profilerData: {componentMeasures},
_hoveredComponentMeasure,
_searchResult,
visibleArea,
} = this;

Expand All @@ -197,6 +217,7 @@ export class ComponentMeasuresView extends View {
componentMeasure,
scaleFactor,
componentMeasure === _hoveredComponentMeasure,
componentMeasure === _searchResult,
) || didDrawMeasure;
});

Expand Down Expand Up @@ -256,6 +277,11 @@ export class ComponentMeasuresView extends View {
onHover(null);
}

_handleSearchResultStateChange = (searchResult: SearchResult | null) => {
this._searchResult =
searchResult === null ? null : searchResult.componentMeasure;
};

handleInteraction(interaction: Interaction, viewRefs: ViewRefs) {
switch (interaction.type) {
case 'mousemove':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export let COLORS = {
SCROLL_CARET: '',
SCRUBBER_BACKGROUND: '',
SCRUBBER_BORDER: '',
SEARCH_RESULT_FILL: '',
TEXT_COLOR: '',
TEXT_DIM_COLOR: '',
TIME_MARKER_LABEL: '',
Expand Down Expand Up @@ -235,6 +236,9 @@ export function updateColorsToMatchTheme(element: Element): boolean {
SCRUBBER_BACKGROUND: computedStyle.getPropertyValue(
'--color-timeline-react-suspense-rejected',
),
SEARCH_RESULT_FILL: computedStyle.getPropertyValue(
'--color-timeline-react-suspense-rejected',
),
SCRUBBER_BORDER: computedStyle.getPropertyValue(
'--color-timeline-text-color',
),
Expand Down
13 changes: 13 additions & 0 deletions packages/react-devtools-timeline/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,18 @@ export type FlamechartStackLayer = FlamechartStackFrame[];

export type Flamechart = FlamechartStackLayer[];

export type SearchResult = {|
componentMeasure: ReactComponentMeasure | null,
|};

export type HorizontalScrollStateChangeCallback = (
scrollState: ScrollState,
) => void;

export type SearchResultStateChangeCallback = (
searchResult: SearchResult | null,
) => void;

// Imperative view state that corresponds to profiler data.
// This state lives outside of React's lifecycle
// and should be erased/reset whenever new profiler data is loaded.
Expand All @@ -179,7 +187,12 @@ export type ViewState = {|
onHorizontalScrollStateChange: (
callback: HorizontalScrollStateChangeCallback,
) => void,
onSearchResultStateChange: (
callback: SearchResultStateChangeCallback,
) => void,
searchResult: SearchResult | null,
updateHorizontalScrollState: (scrollState: ScrollState) => void,
updateSearchResult: (searchResult: SearchResult | null) => void,
viewToMutableViewStateMap: Map<string, mixed>,
|};

Expand Down

0 comments on commit c38e017

Please sign in to comment.