[DevTools] Resign Timeline Profiler Sidebar (#24816)
This PR: * Redesigned the sidebar to resemble the flamegraph profiler sidebar and added title and timestamp to the sidebar * Added ability to copy the component stack (for places where you're unable to link to source) https://user-images.githubusercontent.com/2735514/176564897-5301d6d4-429a-4ea3-86cd-74427cff4ce6.mov
This commit is contained in:
parent
1974d08c93
commit
4e1fcfa771
|
@ -5,6 +5,7 @@
|
|||
padding: 0;
|
||||
border-radius: 0.25rem;
|
||||
flex: 0 0 auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ButtonContent {
|
||||
display: inline-flex;
|
||||
|
|
|
@ -20,18 +20,15 @@
|
|||
}
|
||||
|
||||
.ListItem {
|
||||
margin: 0;
|
||||
flex: 1 1;
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.Label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
[data-source="true"]:hover .Label > .Button {
|
||||
background-color: var(--color-background-hover);
|
||||
flex: 1 1;
|
||||
}
|
||||
|
||||
.Value {
|
||||
|
@ -39,32 +36,31 @@
|
|||
font-size: var(--font-size-monospace-normal);
|
||||
}
|
||||
|
||||
.NothingSelected {
|
||||
display: flex;
|
||||
.Row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.UnclickableSource,
|
||||
.ClickableSource {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-sans-normal);
|
||||
}
|
||||
|
||||
.UnclickableSource {
|
||||
color: var(--color-dim);
|
||||
}
|
||||
|
||||
.Button {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
||||
max-width: 95%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
.ClickableSource {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
[data-source="true"] .Button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.Button > span {
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.Source {
|
||||
.ClickableSource:focus,
|
||||
.ClickableSource:hover {
|
||||
background-color: var(--color-background-hover);
|
||||
}
|
||||
|
|
|
@ -15,18 +15,25 @@ import ButtonIcon from '../ButtonIcon';
|
|||
import ViewSourceContext from '../Components/ViewSourceContext';
|
||||
import {useContext} from 'react';
|
||||
import {TimelineContext} from 'react-devtools-timeline/src/TimelineContext';
|
||||
import {
|
||||
formatTimestamp,
|
||||
getSchedulingEventLabel,
|
||||
} from 'react-devtools-timeline/src/utils/formatting';
|
||||
import {stackToComponentSources} from 'react-devtools-shared/src/devtools/utils';
|
||||
import {copy} from 'clipboard-js';
|
||||
|
||||
import styles from './SidebarEventInfo.css';
|
||||
|
||||
export type Props = {||};
|
||||
|
||||
function SchedulingEventInfo({eventInfo}: {eventInfo: SchedulingEvent}) {
|
||||
const {viewUrlSourceFunction} = useContext(ViewSourceContext);
|
||||
type SchedulingEventProps = {|
|
||||
eventInfo: SchedulingEvent,
|
||||
|};
|
||||
|
||||
const componentStack = eventInfo.componentStack
|
||||
? stackToComponentSources(eventInfo.componentStack)
|
||||
: null;
|
||||
function SchedulingEventInfo({eventInfo}: SchedulingEventProps) {
|
||||
const {viewUrlSourceFunction} = useContext(ViewSourceContext);
|
||||
const {componentName, timestamp} = eventInfo;
|
||||
const componentStack = eventInfo.componentStack || null;
|
||||
|
||||
const viewSource = source => {
|
||||
if (viewUrlSourceFunction != null && source != null) {
|
||||
|
@ -35,45 +42,60 @@ function SchedulingEventInfo({eventInfo}: {eventInfo: SchedulingEvent}) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={styles.Content} tabIndex={0}>
|
||||
{componentStack ? (
|
||||
<ol className={styles.List}>
|
||||
{componentStack.map(([displayName, source], index) => {
|
||||
const hasSource = source != null;
|
||||
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
className={styles.ListItem}
|
||||
data-source={hasSource}>
|
||||
<label className={styles.Label}>
|
||||
<Button
|
||||
className={styles.Button}
|
||||
onClick={() => viewSource(source)}>
|
||||
{displayName}
|
||||
</Button>
|
||||
{hasSource && (
|
||||
<ButtonIcon className={styles.Source} type="view-source" />
|
||||
)}
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
) : null}
|
||||
</div>
|
||||
<>
|
||||
<div className={styles.Toolbar}>
|
||||
{componentName} {getSchedulingEventLabel(eventInfo)}
|
||||
</div>
|
||||
<div className={styles.Content} tabIndex={0}>
|
||||
<ul className={styles.List}>
|
||||
<li className={styles.ListItem}>
|
||||
<label className={styles.Label}>Timestamp</label>:{' '}
|
||||
<span className={styles.Value}>{formatTimestamp(timestamp)}</span>
|
||||
</li>
|
||||
{componentStack && (
|
||||
<li className={styles.ListItem}>
|
||||
<div className={styles.Row}>
|
||||
<label className={styles.Label}>Rendered by</label>
|
||||
<Button
|
||||
onClick={() => copy(componentStack)}
|
||||
title="Copy component stack to clipboard">
|
||||
<ButtonIcon type="copy" />
|
||||
</Button>
|
||||
</div>
|
||||
<ul className={styles.List}>
|
||||
{stackToComponentSources(componentStack).map(
|
||||
([displayName, source], index) => {
|
||||
return (
|
||||
<li key={index}>
|
||||
<Button
|
||||
className={
|
||||
source
|
||||
? styles.ClickableSource
|
||||
: styles.UnclickableSource
|
||||
}
|
||||
disabled={!source}
|
||||
onClick={() => viewSource(source)}>
|
||||
{displayName}
|
||||
</Button>
|
||||
</li>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SidebarEventInfo(_: Props) {
|
||||
const {selectedEvent} = useContext(TimelineContext);
|
||||
// (TODO) Refactor in next PR so this supports multiple types of events
|
||||
return selectedEvent ? (
|
||||
<>
|
||||
<div className={styles.Toolbar}>Event Component Tree</div>
|
||||
{selectedEvent.schedulingEvent ? (
|
||||
<SchedulingEventInfo eventInfo={selectedEvent.schedulingEvent} />
|
||||
) : null}
|
||||
</>
|
||||
) : null;
|
||||
if (selectedEvent && selectedEvent.schedulingEvent) {
|
||||
return <SchedulingEventInfo eventInfo={selectedEvent.schedulingEvent} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,12 @@ import type {
|
|||
} from './types';
|
||||
|
||||
import * as React from 'react';
|
||||
import {formatDuration, formatTimestamp, trimString} from './utils/formatting';
|
||||
import {
|
||||
formatDuration,
|
||||
formatTimestamp,
|
||||
trimString,
|
||||
getSchedulingEventLabel,
|
||||
} from './utils/formatting';
|
||||
import {getBatchRange} from './utils/getBatchRange';
|
||||
import useSmartTooltip from './utils/useSmartTooltip';
|
||||
import styles from './EventTooltip.css';
|
||||
|
@ -40,19 +45,6 @@ type Props = {|
|
|||
width: number,
|
||||
|};
|
||||
|
||||
function getSchedulingEventLabel(event: SchedulingEvent): string | null {
|
||||
switch (event.type) {
|
||||
case 'schedule-render':
|
||||
return 'render scheduled';
|
||||
case 'schedule-state-update':
|
||||
return 'state update scheduled';
|
||||
case 'schedule-force-update':
|
||||
return 'force update scheduled';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getReactMeasureLabel(type): string | null {
|
||||
switch (type) {
|
||||
case 'commit':
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {SchedulingEvent} from '../types';
|
||||
|
||||
import prettyMilliseconds from 'pretty-ms';
|
||||
|
||||
export function formatTimestamp(ms: number) {
|
||||
|
@ -28,3 +30,16 @@ export function trimString(string: string, length: number): string {
|
|||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
export function getSchedulingEventLabel(event: SchedulingEvent): string | null {
|
||||
switch (event.type) {
|
||||
case 'schedule-render':
|
||||
return 'render scheduled';
|
||||
case 'schedule-state-update':
|
||||
return 'state update scheduled';
|
||||
case 'schedule-force-update':
|
||||
return 'force update scheduled';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue