Bug 1511710 - The timeline should let you zoom. r=bhackett

Tags:

Bug #: 1511710

Differential Revision: https://phabricator.services.mozilla.com/D13643
This commit is contained in:
Jason Laster 2018-12-02 08:02:20 -05:00
parent 966d77bf1d
commit ae132b3430
2 changed files with 172 additions and 22 deletions

View File

@ -483,6 +483,7 @@
background: #fff;
margin: 4px 10px 4px 0;
border: 1px solid #bfc9d2;
overflow: hidden;
}
.webreplay-player .progress {
@ -497,7 +498,7 @@
background: var(--purple-50);
width: 1px;
height: 100%;
right: 0;
right: -0.5px;
opacity: 0.4;
display: block;
content: "";
@ -629,3 +630,55 @@
background: #d0021b;
opacity: 0.3;
}
.webreplay-player .tick {
position: absolute;
height: 100%;
transition-duration: var(--progress-bar-transition);
}
.webreplay-player .tick::before,
.webreplay-player .tick::after {
height: 1.5px;
width: 1px;
right: 0;
position: absolute;
content: "";
display: block;
}
.webreplay-player .recording .tick::before,
.webreplay-player .recording .tick::after {
background: #d0021b;
}
.webreplay-player .tick.future::before,
.webreplay-player .tick.future::after {
background: #bfc9d2;
}
.webreplay-player .tick::before,
.webreplay-player .tick::after {
background: var(--blue-50);
}
.webreplay-player .tick::after {
bottom: 0;
}
.webreplay-player .tick::before {
top: 0;
}
.webreplay-player #overlay:hover .tick {
opacity: 1;
}
.webreplay-player #overlay .tick {
opacity: 0.5;
}
.webreplay-player #overlay .tick:hover ~ .tick {
opacity: 0.5;
}

View File

@ -7,7 +7,7 @@ const { Component } = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {sortBy} = require("devtools/client/shared/vendor/lodash");
const { sortBy, range } = require("devtools/client/shared/vendor/lodash");
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper(
@ -104,8 +104,11 @@ class WebReplayPlayer extends Component {
paused: false,
messages: [],
highlightedMessage: null,
start: 0,
end: 1,
};
this.overlayWidth = 0;
this.overlayWidth = 1;
this.onClickProgressBar = this.onClickProgressBar.bind(this);
}
componentDidMount() {
@ -159,6 +162,19 @@ class WebReplayPlayer extends Component {
return this.state.seeking;
}
getTickSize() {
const {start, end} = this.state;
const minSize = 10;
if (!start && !end) {
return minSize;
}
const maxSize = this.overlayWidth / 10;
const ratio = end - start;
return ((1 - ratio) * maxSize) + minSize;
}
onPaused(_, packet) {
if (packet && packet.recordingEndpoint) {
const { executionPoint, recordingEndpoint } = packet;
@ -204,9 +220,13 @@ class WebReplayPlayer extends Component {
const {
messages: { visibleMessages, messagesById },
} = consoleState;
const messages = visibleMessages.map(id => messagesById.get(id));
if (visibleMessages != this.state.visibleMessages) {
const messages = sortBy(
visibleMessages.map(id => messagesById.get(id)),
message => getMessageProgress(message)
);
this.setState({ messages, visibleMessages });
}
}
@ -223,6 +243,27 @@ class WebReplayPlayer extends Component {
return null;
}
onClickProgressBar(e) {
if (!e.altKey) {
return;
}
const {start, end} = this.state;
const direction = e.shiftKey ? "end" : "start";
const { left, width } = e.currentTarget.getBoundingClientRect();
const clickLeft = e.clientX;
const clickPosition = (clickLeft - left) / width;
const position = ((end - start) * clickPosition) + start;
this.setTimelinePosition({ position, direction });
}
setTimelinePosition({ position, direction }) {
this.setState({[direction]: position});
}
scrollToMessage() {
const {closestMessage} = this.state;
@ -358,7 +399,7 @@ class WebReplayPlayer extends Component {
updateOverlayWidth() {
const el = ReactDOM.findDOMNode(this).querySelector(".progressBar");
return el.clientWidth;
return el ? el.clientWidth : 1;
}
// calculate pixel distance from two points
@ -374,12 +415,48 @@ class WebReplayPlayer extends Component {
return (percent * this.overlayWidth) / 100;
}
getPercent(executionPoint) {
const {recordingEndpoint} = this.state;
if (!recordingEndpoint) {
return 100;
}
if (!executionPoint) {
return 0;
}
const ratio = executionPoint.progress / recordingEndpoint.progress;
return ratio * 100;
}
getVisiblePercent(executionPoint) {
const {start, end} = this.state;
const position = this.getPercent(executionPoint) / 100;
if (position < start || position > end) {
return -1;
}
return ((position - start) / (end - start)) * 100;
}
getVisibleOffset(point) {
const percent = this.getVisiblePercent(point);
return (percent * this.overlayWidth) / 100;
}
renderMessage(message, index) {
const { messages, executionPoint, highlightedMessage } = this.state;
const offset = this.getOffset(message.executionPoint);
const offset = this.getVisibleOffset(message.executionPoint);
const previousMessage = messages[index - 1];
if (offset < 0) {
return null;
}
// Check to see if two messages overlay each other on the timeline
const isOverlayed =
previousMessage &&
@ -402,35 +479,50 @@ class WebReplayPlayer extends Component {
highlighted: isHighlighted,
}),
style: {
left: `${offset - markerWidth / 2}px`,
left: `${Math.max(offset - markerWidth/2, 0)}px`,
zIndex: `${index + 100}`,
},
title: getFormatStr("jumpMessage", index + 1),
onClick: () => this.seek(message.executionPoint),
onClick: (e) => {
e.preventDefault();
e.stopPropagation();
this.seek(message.executionPoint);
},
});
}
renderMessages() {
const messages = this.state.messages;
return messages.map((message, index) => this.renderMessage(message, index));
return messages
.map((message, index) => this.renderMessage(message, index));
}
getPercent(executionPoint) {
if (!this.state.recordingEndpoint) {
return 100;
}
renderTicks() {
const tickSize = this.getTickSize();
const ticks = Math.round((this.overlayWidth) / tickSize);
return range(ticks).map((value, index) => this.renderTick(index));
}
if (!executionPoint) {
return 0;
}
renderTick(index) {
const { executionPoint } = this.state;
const tickSize = this.getTickSize();
const offset = Math.round(this.getOffset(executionPoint));
const position = index * tickSize;
const isFuture = position > offset;
const ratio =
executionPoint.progress / this.state.recordingEndpoint.progress;
return ratio * 100;
return dom.span({
className: classname("tick", {
future: isFuture,
}),
style: {
left: `${position}px`,
width: `${tickSize}px`,
},
});
}
render() {
const percent = this.getPercent(this.state.executionPoint);
const percent = this.getVisiblePercent(this.state.executionPoint);
const recording = this.isRecording();
return div(
{ className: "webreplay-player" },
@ -443,7 +535,11 @@ class WebReplayPlayer extends Component {
{ className: "overlay-container " },
div({ className: "commands" }, ...this.renderCommands()),
div(
{ className: "progressBar" },
{
className: "progressBar",
onClick: this.onClickProgressBar,
onDoubleClick: () => this.setState({ start: 0, end: 1 }),
},
div({
className: "progress",
style: { width: `${percent}%` },
@ -456,7 +552,8 @@ class WebReplayPlayer extends Component {
className: "progress-line end",
style: { left: `${percent}%`, width: `${100 - percent}%` },
}),
...this.renderMessages()
...this.renderMessages(),
...this.renderTicks()
)
)
)