resources/perf.webkit.org/public/v3/pages/dashboard-page.js (192 lines of code) (raw):

class DashboardPage extends PageWithHeading { constructor(name, table, toolbar) { console.assert(toolbar instanceof DashboardToolbar); super(name, toolbar); this._table = table; this._charts = []; this._needsTableConstruction = true; this._needsStatusUpdate = true; this._statusViews = []; this._startTime = Date.now() - 60 * 24 * 3600 * 1000; this._endTime = Date.now(); this._tableGroups = null; } routeName() { return `dashboard/${this._name}`; } serializeState() { return {numberOfDays: this.toolbar().numberOfDays()}; } updateFromSerializedState(state, isOpen) { if (!isOpen || state.numberOfDays) this.toolbar().setNumberOfDays(state.numberOfDays); var startTime = this.toolbar().startTime(); var endTime = this.toolbar().endTime(); for (var chart of this._charts) chart.setDomain(startTime, endTime); this._needsTableConstruction = true; if (!isOpen) this.enqueueToRender(); } open(state) { if (!this._tableGroups) { var columnCount = 0; var tableGroups = []; for (var row of this._table) { if (!row.some(function (cell) { return cell instanceof Array; })) { tableGroups.push([]); row = [''].concat(row); } tableGroups[tableGroups.length - 1].push(row); columnCount = Math.max(columnCount, row.length); } for (var group of tableGroups) { for (var row of group) { for (var i = 0; i < row.length; i++) { if (row[i] instanceof Array) row[i] = this._createChartForCell(row[i]); } while (row.length < columnCount) row.push([]); } } this._tableGroups = tableGroups; } super.open(state); } render() { super.render(); console.assert(this._tableGroups); var element = ComponentBase.createElement; var link = ComponentBase.createLink; if (this._needsTableConstruction) { var tree = []; var router = this.router(); var startTime = this.toolbar().startTime(); for (var group of this._tableGroups) { tree.push(element('thead', element('tr', group[0].map(function (cell, cellIndex) { if (!cellIndex) return element('th', {class: 'heading-column'}); return element('td', cell.content || cell); })))); tree.push(element('tbody', group.slice(1).map(function (row) { return element('tr', row.map(function (cell, cellIndex) { if (!cellIndex) return element('th', element('span', {class: 'vertical-label'}, cell)); if (!cell.chart) return element('td', cell); var url = router.url('charts', ChartsPage.createStateForDashboardItem(cell.platform.id(), cell.metric.id(), startTime)); return element('td', [cell.statusView, link(cell.chart.element(), cell.label, url)]); })); }))); } this.renderReplace(this.content().querySelector('.dashboard-table'), tree); this._needsTableConstruction = false; } for (var chart of this._charts) chart.enqueueToRender(); if (this._needsStatusUpdate) { for (var statusView of this._statusViews) statusView.enqueueToRender(); this._needsStatusUpdate = false; } } _createChartForCell(cell) { console.assert(this.router()); var platformId = cell[0]; var metricId = cell[1]; if (!platformId || !metricId) return ''; var result = ChartStyles.resolveConfiguration(platformId, metricId); if (result.error) return result.error; var options = ChartStyles.dashboardOptions(result.metric.makeFormatter(3)); let chart = new TimeSeriesChart(ChartStyles.createSourceList(result.platform, result.metric, false, false, true), options); chart.listenToAction('dataChange', () => this._fetchedData()) this._charts.push(chart); var statusView = new DashboardChartStatusView(result.metric, chart); this._statusViews.push(statusView); return { chart: chart, statusView: statusView, platform: result.platform, metric: result.metric, label: result.metric.fullName() + ' on ' + result.platform.label() }; } _fetchedData() { if (this._needsStatusUpdate) return; this._needsStatusUpdate = true; setTimeout(() => { this.enqueueToRender(); }, 10); } static htmlTemplate() { return `<section class="page-with-heading"><table class="dashboard-table"></table></section>`; } static cssTemplate() { return ` .dashboard-table { table-layout: fixed; } .dashboard-table td, .dashboard-table th { border: none; text-align: center; } .dashboard-table th, .dashboard-table thead td { color: #f96; font-weight: inherit; font-size: 1.1rem; text-align: center; padding: 0.2rem 0.4rem; } .dashboard-table th { height: 10rem; width: 2rem; position: relative; } .dashboard-table .heading-column { width: 2rem; height: 1rem; } .dashboard-table th .vertical-label { position: absolute; left: 0; right: 0; display: block; -webkit-transform: rotate(-90deg) translate(-50%, 0); -webkit-transform-origin: 0 0; transform: rotate(-90deg) translate(-50%, 0); transform-origin: 0 0; width: 10rem; height: 2rem; line-height: 1.8rem; } table.dashboard-table { width: 100%; height: 100%; border: 0; } .dashboard-table td time-series-chart { height: 10rem; } .dashboard-table td > chart-status-view { display: block; width: 100%; } .dashboard-table td *:first-child { margin: 0 0 0.2rem 0; } `; } }