fune/devtools/client/netmonitor/components/request-list-header.js
J. Ryan Stinnett c26a6274cc Bug 1326100 - Restore notable inline comments in DevTools. r=tromey
MozReview-Commit-ID: 6cLDkoCt0al

--HG--
extra : rebase_source : 26e95a9320998085675a1f14bfa4442f4bbc38dc
2017-03-20 16:22:55 -05:00

198 lines
6.3 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
const { div, button } = DOM;
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { L10N } = require("../utils/l10n");
const { getWaterfallScale } = require("../selectors/index");
const Actions = require("../actions/index");
const WaterfallBackground = require("../waterfall-background");
const { getFormattedTime } = require("../utils/format-utils");
const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5; // ms
const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60; // px
const HEADERS = [
{ name: "status", label: "status3" },
{ name: "method" },
{ name: "file", boxName: "icon-and-file" },
{ name: "domain", boxName: "security-and-domain" },
{ name: "cause" },
{ name: "type" },
{ name: "transferred" },
{ name: "size" },
{ name: "waterfall" }
];
/**
* Render the request list header with sorting arrows for columns.
* Displays tick marks in the waterfall column header.
* Also draws the waterfall background canvas and updates it when needed.
*/
const RequestListHeader = createClass({
displayName: "RequestListHeader",
propTypes: {
sort: PropTypes.object,
scale: PropTypes.number,
waterfallWidth: PropTypes.number,
onHeaderClick: PropTypes.func.isRequired,
resizeWaterfall: PropTypes.func.isRequired,
},
componentDidMount() {
// Create the object that takes care of drawing the waterfall canvas background
this.background = new WaterfallBackground(document);
this.background.draw(this.props);
this.resizeWaterfall();
window.addEventListener("resize", this.resizeWaterfall);
},
componentDidUpdate() {
this.background.draw(this.props);
},
componentWillUnmount() {
this.background.destroy();
this.background = null;
window.removeEventListener("resize", this.resizeWaterfall);
},
resizeWaterfall() {
// Measure its width and update the 'waterfallWidth' property in the store.
// The 'waterfallWidth' will be further updated on every window resize.
setNamedTimeout("resize-events", 50, () => {
const { width } = this.refs.header.getBoundingClientRect();
this.props.resizeWaterfall(width);
});
},
render() {
const { sort, scale, waterfallWidth, onHeaderClick } = this.props;
return div(
{ className: "devtools-toolbar requests-list-toolbar" },
div({ className: "toolbar-labels" },
HEADERS.map(header => {
const name = header.name;
const boxName = header.boxName || name;
const label = L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
let sorted, sortedTitle;
const active = sort.type == name ? true : undefined;
if (active) {
sorted = sort.ascending ? "ascending" : "descending";
sortedTitle = L10N.getStr(sort.ascending
? "networkMenu.sortedAsc"
: "networkMenu.sortedDesc");
}
return div(
{
id: `requests-list-${boxName}-header-box`,
className: `requests-list-header requests-list-${boxName}`,
key: name,
ref: "header",
// Used to style the next column.
"data-active": active,
},
button(
{
id: `requests-list-${name}-button`,
className: `requests-list-header-button requests-list-${name}`,
"data-sorted": sorted,
title: sortedTitle,
onClick: () => onHeaderClick(name),
},
name == "waterfall" ? WaterfallLabel(waterfallWidth, scale, label)
: div({ className: "button-text" }, label),
div({ className: "button-icon" })
)
);
})
)
);
}
});
/**
* Build the waterfall header - timing tick marks with the right spacing
*/
function waterfallDivisionLabels(waterfallWidth, scale) {
let labels = [];
// Build new millisecond tick labels...
let timingStep = REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE;
let scaledStep = scale * timingStep;
// Ignore any divisions that would end up being too close to each other.
while (scaledStep < REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN) {
scaledStep *= 2;
}
// Insert one label for each division on the current scale.
for (let x = 0; x < waterfallWidth; x += scaledStep) {
let millisecondTime = x / scale;
let divisionScale = "millisecond";
// If the division is greater than 1 minute.
if (millisecondTime > 60000) {
divisionScale = "minute";
} else if (millisecondTime > 1000) {
// If the division is greater than 1 second.
divisionScale = "second";
}
let width = (x + scaledStep | 0) - (x | 0);
// Adjust the first marker for the borders
if (x == 0) {
width -= 2;
}
// Last marker doesn't need a width specified at all
if (x + scaledStep >= waterfallWidth) {
width = undefined;
}
labels.push(div(
{
key: labels.length,
className: "requests-list-timings-division",
"data-division-scale": divisionScale,
style: { width }
},
getFormattedTime(millisecondTime)
));
}
return labels;
}
function WaterfallLabel(waterfallWidth, scale, label) {
let className = "button-text requests-list-waterfall-label-wrapper";
if (waterfallWidth != null && scale != null) {
label = waterfallDivisionLabels(waterfallWidth, scale);
className += " requests-list-waterfall-visible";
}
return div({ className }, label);
}
module.exports = connect(
state => ({
sort: state.sort,
scale: getWaterfallScale(state),
waterfallWidth: state.ui.waterfallWidth,
firstRequestStartedMillis: state.requests.firstStartedMillis,
timingMarkers: state.timingMarkers,
}),
dispatch => ({
onHeaderClick: type => dispatch(Actions.sortBy(type)),
resizeWaterfall: width => dispatch(Actions.resizeWaterfall(width)),
})
)(RequestListHeader);