Skip to content

Commit

Permalink
Add a tooltip component.
Browse files Browse the repository at this point in the history
This tooltip is a little weird, as it uses ReactDOM's render method to
insert a DOM component outside of the normal DOM position. This is
needed because tooltips need to be outside of the stacking context
of the parent elements.
  • Loading branch information
gregtatum committed Jun 9, 2017
1 parent ff636b7 commit 86294f6
Show file tree
Hide file tree
Showing 12 changed files with 423 additions and 62 deletions.
17 changes: 17 additions & 0 deletions res/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ body, #root, .profileViewer {
max-height: 100%;
}

body {
/* Stolen from the light theme at devtools/client/themes/variables.css */
--theme-highlight-green: #2cbb0f;
--theme-highlight-blue: #0088cc;
--theme-highlight-bluegrey: #0072ab;
--theme-highlight-purple: #5b5fff;
--theme-highlight-lightorange: #d97e00;
--theme-highlight-orange: #f13c00;
--theme-highlight-red: #ed2655;
--theme-highlight-pink: #b82ee5;
--theme-highlight-gray: #b4babf;/* except for this one, I made this one darker */
}

#root {
z-index: 0;
}

.treeView {
display: flex;
flex-flow: column nowrap;
Expand Down
47 changes: 24 additions & 23 deletions src/common/types/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,26 +80,26 @@ export type ProfilerMarkerTracing = {
endTime: Milliseconds, // Same as cpuend
stack?: Thread,
interval: "start" | "end",
} & (
{ category?: string } |
{
category: "Paint",
name: "RefreshDriverTick" |
"FireScrollEvent" |
"Scripts" |
"Styles" |
"Reflow" |
"DispatchSynthMouseMove" |
"DisplayList" |
"LayerBuilding" |
"Rasterize" |
"ForwardTransaction" |
"NotifyDidPaint" |
"LayerTransaction" |
"Composite",
}
// TODO - Add more markers.
);
}

export type PaintProfilerMarkerTracing = ProfilerMarkerTracing & {
category: "Paint",
name: "RefreshDriverTick" |
"FireScrollEvent" |
"Scripts" |
"Styles" |
"Reflow" |
"DispatchSynthMouseMove" |
"DisplayList" |
"LayerBuilding" |
"Rasterize" |
"ForwardTransaction" |
"NotifyDidPaint" |
"LayerTransaction" |
"Composite",
}

// TODO - Add more markers.

/**
* The payload for the UserTimings API. These are added through performance.measure()
Expand All @@ -119,8 +119,8 @@ export type UserTimingMarkerPayload = {
*/
export type MarkerPayload =
GPUMarkerPayload |
ProfilerMarkerTracing |
UserTimingMarkerPayload |
PaintProfilerMarkerTracing |
null;

/**
Expand Down Expand Up @@ -176,8 +176,9 @@ export type ResourceTable = {
addonId: any[], // TODO
icon: any[], // TODO
length: number,
lib: IndexIntoLibs[],
name: IndexIntoStringTable[],
lib: Array<IndexIntoLibs|null>,
name: Array<IndexIntoStringTable|-1>,
host: Array<IndexIntoStringTable|null>,
type: resourceTypeEnum[],
}

Expand Down
8 changes: 8 additions & 0 deletions src/content/components/FlameChartCanvas.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@
top: 0;
left: 0;
}

/**
* This tooltip is multi-line and more intense, so add additional whitespace to make it
* more readable.
*/
.flameChartCanvasTooltip {
padding: 5px;
}
71 changes: 64 additions & 7 deletions src/content/components/FlameChartCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ type Props = {
getCategory: GetCategory,
getLabel: GetLabel,
updateProfileSelection: ProfileSelection => Action,
isDragging: boolean,
isRowExpanded: boolean,
};

type HoveredStackTiming = {
Expand Down Expand Up @@ -156,12 +158,14 @@ class FlameChartCanvas extends PureComponent {

_getHoveredStackInfo(
{depth, stackTableIndex}: HoveredStackTiming
): string {
const { thread, getLabel, stackTimingByDepth } = this.props;
const label = getLabel(thread, stackTimingByDepth[depth].stack[stackTableIndex]);
): React$Element<*> {
const {
thread, getLabel, getCategory, stackTimingByDepth, isRowExpanded,
} = this.props;
const stackTiming = stackTimingByDepth[depth];

const duration = stackTimingByDepth[depth].end[stackTableIndex] -
stackTimingByDepth[depth].start[stackTableIndex];
const duration = stackTiming.end[stackTableIndex] -
stackTiming.start[stackTableIndex];
let durationString;
if (duration >= 10) {
durationString = duration.toFixed(0);
Expand All @@ -173,7 +177,59 @@ class FlameChartCanvas extends PureComponent {
durationString = duration.toFixed(3);
}

return `${durationString}ms - ${label}`;
const stackIndex = stackTiming.stack[stackTableIndex];
const frameIndex = thread.stackTable.frame[stackIndex];
const label = getLabel(thread, stackIndex);
const category = getCategory(thread, frameIndex);
const funcIndex = thread.frameTable.func[frameIndex];

let resourceOrFileName = null;
// Only show resources or filenames if the chart is expanded, as collapsed stacks
// would show incorrect details about a group of stacks.
if (isRowExpanded) {
// Only JavaScript functions have a filename.
const fileNameIndex = thread.funcTable.fileName[funcIndex];
if (fileNameIndex !== null) {
resourceOrFileName = (
<div className='tooltipOneLine tooltipDetails'>
<div className='tooltipLabel'>File:</div>
{thread.stringTable.getString(fileNameIndex)}
</div>
);
} else {
const resourceIndex = thread.funcTable.resource[funcIndex];
if (resourceIndex !== -1) {
const resourceNameIndex = thread.resourceTable.name[resourceIndex];
if (resourceNameIndex !== -1) {
resourceOrFileName = (
<div className='tooltipOneLine tooltipDetails'>
<div className='tooltipLabel'>Resource:</div>
{thread.stringTable.getString(resourceNameIndex)}
</div>
);
}
}
}
}

return (
<div className='flameChartCanvasTooltip'>
<div className='tooltipOneLine tooltipHeader'>
<div className='tooltipTiming'>
{durationString}ms
</div>
<div className='tooltipName'>
{label}
</div>
</div>
<div className='tooltipOneLine tooltipDetails'>
<div className='tooltipLabel'>Category:</div>
<div className='tooltipSwatch' style={{backgroundColor: category.color}} />
{category.name}
</div>
{resourceOrFileName}
</div>
);
}

_onDoubleClickStack(hoveredItem: HoveredStackTiming | null) {
Expand Down Expand Up @@ -220,11 +276,12 @@ class FlameChartCanvas extends PureComponent {


render() {
const { containerWidth, containerHeight } = this.props;
const { containerWidth, containerHeight, isDragging } = this.props;

return <TimelineCanvas className='flameChartCanvas'
containerWidth={containerWidth}
containerHeight={containerHeight}
isDragging={isDragging}
onDoubleClickItem={this._onDoubleClickStack}
getHoveredItemInfo={this._getHoveredStackInfo}
drawCanvas={this._drawCanvas}
Expand Down
41 changes: 33 additions & 8 deletions src/content/components/IntervalMarkerOverview.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React, { PureComponent } from 'react';
import classNames from 'classnames';
import { timeCode } from '../../common/time-code';
import { withSize } from '../with-size';
import Tooltip from './Tooltip';
import type { Milliseconds, CssPixels } from '../../common/types/units';
import type { TracingMarker } from '../../common/types/profile-derived';

Expand All @@ -23,6 +24,7 @@ type Props = {
onSelect: any,
styles: any,
isSelected: boolean,
isModifyingSelection: boolean,
overlayFills: {
HOVERED: string,
PRESSED: string,
Expand All @@ -36,14 +38,21 @@ class IntervalMarkerOverview extends PureComponent {
state: {
hoveredItem: TracingMarker|null,
mouseDownItem: TracingMarker|null,
mouseX: CssPixels,
mouseY: CssPixels,
}

_canvas: HTMLCanvasElement|null
_requestedAnimationFrame: bool|null

constructor(props: Props) {
super(props);
this.state = { hoveredItem: null, mouseDownItem: null };
this.state = {
hoveredItem: null,
mouseDownItem: null,
mouseX: 0,
mouseY: 0,
};
(this: any)._onMouseDown = this._onMouseDown.bind(this);
(this: any)._onMouseMove = this._onMouseMove.bind(this);
(this: any)._onMouseUp = this._onMouseUp.bind(this);
Expand Down Expand Up @@ -100,11 +109,16 @@ class IntervalMarkerOverview extends PureComponent {
return null;
}

_onMouseMove(e) {
const hoveredItem = this._hitTest(e);
_onMouseMove(event: SyntheticMouseEvent) {
const hoveredItem = this._hitTest(event);
if (this.state.hoveredItem !== hoveredItem) {
this.setState({ hoveredItem });
}

this.setState({
mouseX: event.pageX,
mouseY: event.pageY,
});
}

_onMouseDown(e) {
Expand Down Expand Up @@ -142,9 +156,11 @@ class IntervalMarkerOverview extends PureComponent {

render() {
this._scheduleDraw();
const { className, isSelected } = this.props;
const { mouseDownItem, hoveredItem } = this.state;
const title = !mouseDownItem && hoveredItem ? hoveredItem.title : null;
const { className, isSelected, isModifyingSelection } = this.props;
const { mouseDownItem, hoveredItem, mouseX, mouseY } = this.state;
const tooltipContents = !isModifyingSelection && !mouseDownItem && hoveredItem
? hoveredItem.title
: null;
const canvasClassName = className.split(' ').map(name => `${name}Canvas`).join(' ');

return (
Expand All @@ -154,8 +170,17 @@ class IntervalMarkerOverview extends PureComponent {
onMouseDown={this._onMouseDown}
onMouseMove={this._onMouseMove}
onMouseUp={this._onMouseUp}
onMouseOut={this._onMouseOut}
title={title}/>
onMouseOut={this._onMouseOut}/>
{
tooltipContents
? <Tooltip mouseX={mouseX}
mouseY={mouseY}
offsetParent={this._takeCanvasRef}
boundedAtBottom={false}>
{tooltipContents}
</Tooltip>
: null
}
</div>
);
}
Expand Down
Loading

0 comments on commit 86294f6

Please sign in to comment.