Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(294)

Unified Diff: tracing/tracing/ui/base/chart_base.html

Issue 2162963002: [polymer] Merge of master into polymer10-migration (Closed) Base URL: git@github.com:catapult-project/catapult.git@polymer10-migration
Patch Set: Merge polymer10-migration int polymer10-merge Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tracing/tracing/ui/base/bar_chart_test.html ('k') | tracing/tracing/ui/base/chart_base_2d.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tracing/tracing/ui/base/chart_base.html
diff --git a/tracing/tracing/ui/base/chart_base.html b/tracing/tracing/ui/base/chart_base.html
index 8c8c9d60d53c907b8792fccceec1c2a386c84b27..e793d83841c148ede38380d1d10d4e142adc4540 100644
--- a/tracing/tracing/ui/base/chart_base.html
+++ b/tracing/tracing/ui/base/chart_base.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+ <!DOCTYPE html>
<!--
Copyright (c) 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
@@ -6,16 +6,127 @@ found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/color_scheme.html">
+<link rel="import" href="/tracing/ui/analysis/analysis_link.html">
<link rel="import" href="/tracing/ui/base/d3.html">
<link rel="import" href="/tracing/ui/base/ui.html">
+<dom-module id="tr-ui-b-chart-legend-key">
+ <template>
+ <style>
+ #checkbox {
+ margin: 0;
+ visibility: hidden;
+ vertical-align: text-top;
+ }
+ #label, #link {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ display: inline-block;
+ }
+ </style>
+
+ <input type=checkbox id="checkbox" checked>
+ <tr-ui-a-analysis-link id="link"></tr-ui-a-analysis-link>
+ <label id="label"></label>
+ </template>
+</dom-module>
+<script>
+'use strict';
+Polymer({
+ is: 'tr-ui-b-chart-legend-key',
+
+ ready: function() {
+ this.$.checkbox.addEventListener(
+ 'change', this.onCheckboxChange_.bind(this));
+ },
+
+ /**
+ * Dispatch an event when the checkbox is toggled.
+ * The checkbox is visible when optional is set to true.
+ */
+ onCheckboxChange_: function() {
+ tr.b.dispatchSimpleEvent(this, tr.ui.b.DataSeriesEnableChangeEventType,
+ true, false,
+ {key: Polymer.dom(this).textContent, enabled: this.enabled});
+ },
+
+ set textContent(t) {
+ Polymer.dom(this.$.label).textContent = t;
+ Polymer.dom(this.$.link).textContent = t;
+ this.updateContents_();
+ },
+
+ set width(w) {
+ w -= 20; // reserve 20px for the checkbox
+ this.$.link.style.width = w + 'px';
+ this.$.label.style.width = w + 'px';
+ },
+
+ get textContent() {
+ return Polymer.dom(this.$.label).textContent;
+ },
+
+ /**
+ * When a legend-key is "optional", then its checkbox is visible to allow
+ * the user to enable/disable the data series for the key.
+ * See ChartBase.customizeOptionalSeries().
+ *
+ * @param {boolean} optional
+ */
+ set optional(optional) {
+ this.$.checkbox.style.visibility = optional ? 'visible' : 'hidden';
+ },
+
+ get optional() {
+ return this.$.checkbox.style.visibility === 'visible';
+ },
+
+ set enabled(enabled) {
+ this.$.checkbox.checked = enabled ? 'checked' : '';
+ },
+
+ get enabled() {
+ return this.$.checkbox.checked;
+ },
+
+ set color(c) {
+ this.$.label.style.color = c;
+ this.$.link.color = c;
+ },
+
+ /**
+ * When target is defined, label is hidden and link is shown.
+ * When the link is clicked, then a RequestSelectionChangeEvent is
+ * dispatched containing the target.
+ * When target is undefined, label is shown and link is hidden, so that the
+ * link is not clickable.
+ * See ChartBase.customizeLegendTargets().
+ */
+ set target(target) {
+ this.$.link.setSelectionAndContent(
+ target, Polymer.dom(this.$.label).textContent);
+ this.updateContents_();
+ },
+
+ get target() {
+ return this.$.link.selection;
+ },
+
+ updateContents_: function() {
+ this.$.link.style.display = this.target ? '' : 'none';
+ this.$.label.style.display = this.target ? 'none' : '';
+ this.$.label.htmlFor = this.optional ? 'checkbox' : '';
+ }
+});
+</script>
+
<style>
* /deep/ .chart-base #title {
font-size: 16pt;
}
* /deep/ .chart-base {
- font-size: 12pt;
-webkit-user-select: none;
cursor: default;
}
@@ -26,6 +137,10 @@ found in the LICENSE file.
shape-rendering: crispEdges;
stroke: #000;
}
+
+ * /deep/ .chart-base .legend body {
+ margin: 0;
+ }
</style>
<template id="chart-base-template">
@@ -42,6 +157,8 @@ found in the LICENSE file.
'use strict';
tr.exportTo('tr.ui.b', function() {
+ var DataSeriesEnableChangeEventType = 'data-series-enabled-change';
+
var THIS_DOC = document.currentScript.ownerDocument;
var svgNS = 'http://www.w3.org/2000/svg';
@@ -54,6 +171,47 @@ tr.exportTo('tr.ui.b', function() {
return ColorScheme.colorsAsStrings[id];
}
+ function DataSeries(key) {
+ this.key_ = key;
+ this.target_ = undefined;
+ this.optional_ = false;
+ this.enabled_ = true;
+ }
+
+ DataSeries.prototype = {
+ get key() {
+ return this.key_;
+ },
+
+ get optional() {
+ return this.optional_;
+ },
+
+ set optional(optional) {
+ this.optional_ = optional;
+ },
+
+ get enabled() {
+ return this.enabled_;
+ },
+
+ set enabled(enabled) {
+ // If the caller is disabling a data series, but it wasn't optional, then
+ // force it to be optional.
+ if (!this.optional && !enabled)
+ this.optional = true;
+ this.enabled_ = enabled;
+ },
+
+ get target() {
+ return this.target_;
+ },
+
+ set target(t) {
+ this.target_ = t;
+ }
+ };
+
/**
* A virtual base class for basic charts that provides X and Y axes, if
* needed, a title, and legend.
@@ -63,23 +221,32 @@ tr.exportTo('tr.ui.b', function() {
var ChartBase = tr.ui.b.define('svg', undefined, svgNS);
ChartBase.prototype = {
- __proto__: HTMLDivElement.prototype,
+ __proto__: HTMLUnknownElement.prototype,
+
+ getDataSeries: function(key) {
+ if (!this.seriesByKey_.has(key))
+ this.seriesByKey_.set(key, new DataSeries(key));
+ return this.seriesByKey_.get(key);
+ },
decorate: function() {
Polymer.dom(this).classList.add('chart-base');
this.chartTitle_ = undefined;
- this.seriesKeys_ = undefined;
+ this.seriesByKey_ = new Map();
this.width_ = 400;
this.height_ = 300;
+ this.margin = {top: 20, right: 72, bottom: 30, left: 50};
+ this.hideLegend_ = false;
// This should use tr.ui.b.instantiateTemplate. However, creating
// svg-namespaced elements inside a template isn't possible. Thus, this
// hack.
- var template = Polymer.dom(THIS_DOC)
- .querySelector('#chart-base-template');
+ var template =
+ Polymer.dom(THIS_DOC).querySelector('#chart-base-template');
var svgEl = Polymer.dom(template.content).querySelector('svg');
- for (var i = 0; i < svgEl.children.length; i++)
- Polymer.dom(this).appendChild(svgEl.children[i].cloneNode(true));
+ for (var i = 0; i < Polymer.dom(svgEl).children.length; i++)
+ Polymer.dom(this).appendChild(
+ Polymer.dom(svgEl.children[i]).cloneNode(true));
// svg likes to take over width & height properties for some reason. This
// works around it.
@@ -103,6 +270,26 @@ tr.exportTo('tr.ui.b', function() {
this.updateContents_();
}
});
+ this.addEventListener(DataSeriesEnableChangeEventType,
+ this.onDataSeriesEnableChange_.bind(this));
+ },
+
+ get hideLegend() {
+ return this.hideLegend_;
+ },
+
+ set hideLegend(h) {
+ this.hideLegend_ = h;
+ this.updateContents_();
+ },
+
+ isSeriesEnabled: function(key) {
+ return this.getDataSeries(key).enabled;
+ },
+
+ onDataSeriesEnableChange_: function(event) {
+ this.getDataSeries(event.key).enabled = event.enabled;
+ this.updateContents_();
},
get chartTitle() {
@@ -110,10 +297,19 @@ tr.exportTo('tr.ui.b', function() {
},
set chartTitle(chartTitle) {
+ if (chartTitle && !this.chartTitle_)
+ this.margin.top += this.titleMarginPx;
+ else if (this.chartTitle_ && !chartTitle)
+ this.margin.top -= this.titleMarginPx;
+
this.chartTitle_ = chartTitle;
this.updateContents_();
},
+ get titleMarginPx() {
+ return 20;
+ },
+
get chartAreaElement() {
return Polymer.dom(this).querySelector('#chart-area');
},
@@ -124,27 +320,64 @@ tr.exportTo('tr.ui.b', function() {
this.updateContents_();
},
- getMargin_: function() {
- var margin = {top: 20, right: 20, bottom: 30, left: 50};
- if (this.chartTitle_)
- margin.top += 20;
- return margin;
+ get chartAreaSize() {
+ return {
+ width: this.width_ - this.margin.left - this.margin.right,
+ height: this.height_ - this.margin.top - this.margin.bottom
+ };
},
- get margin() {
- return this.getMargin_();
+ /**
+ * Legend keys can be clickable links instead of plain text.
+ * When a legend key link is clicked, a RequestSelectionChangeEvent is
+ * dispatched containing arbitrary data. ChartBase calls that arbitrary data
+ * the "target" of the legend key link.
+ * In order to turn the legend key for the 'foo' data series into a
+ * clickable link, call customizeLegendTargets({foo: target}). When the user
+ * clicks on the legend key link for 'foo', then a
+ * RequestSelectionChangeEvent will be dispatched, and its |selection| field
+ * will be the |target| value for the 'foo' key in |delta|.
+ *
+ * @param {!Object} delta
+ */
+ customizeLegendTargets: function(delta) {
+ tr.b.iterItems(delta, function(key, value) {
+ this.getDataSeries(key).target = value;
+ }, this);
},
- get chartAreaSize() {
- var margin = this.margin;
- return {
- width: this.width_ - margin.left - margin.right,
- height: this.height_ - margin.top - margin.bottom
- };
+ /**
+ * Optional data series can be enabled and disabled using checkboxes.
+ * In order to allow the user to enable/disabled the 'foo' data series,
+ * call customizeOptionalSeries({foo: true}). This will show a checkbox
+ * next to the 'foo' legend key. When the user toggles the checkbox, then a
+ * DataSeriesEnableChangeEvent will be dispatched with its |key| = 'foo'.
+ * ChartBase listens for that event and updates |isSeriesEnabled('foo')| to
+ * reflect the state of that checkbox, and calls updateContents_().
+ * Subclasses are responsible for implementing updateContents_() in order to
+ * hiding disabled data series -- see BarChart.
+ *
+ * @param {!Object} delta
+ */
+ customizeOptionalSeries: function(delta) {
+ tr.b.iterItems(delta, function(key, value) {
+ this.getDataSeries(key).optional = value;
+ }, this);
},
- getLegendKeys_: function() {
- throw new Error('Not implemented');
+ /**
+ * Data series can be enabled and disabled.
+ * See customizeOptionalSeries() in order to allow the user to
+ * enable/disable data series manually. Callers may call
+ * customizeEnabledSeries({foo: false}) in order to automatically
+ * disable the 'foo' data series, for example.
+ *
+ * @param {!Object} delta
+ */
+ customizeEnabledSeries: function(delta) {
+ tr.b.iterItems(delta, function(key, value) {
+ this.getDataSeries(key).enabled = value;
+ }, this);
},
updateScales_: function() {
@@ -152,15 +385,13 @@ tr.exportTo('tr.ui.b', function() {
},
updateContents_: function() {
- var margin = this.margin;
-
var thisSel = d3.select(this);
thisSel.attr('width', this.width_);
thisSel.attr('height', this.height_);
var chartAreaSel = d3.select(this.chartAreaElement);
chartAreaSel.attr('transform',
- 'translate(' + margin.left + ',' + margin.top + ')');
+ 'translate(' + this.margin.left + ',' + this.margin.top + ')');
this.updateScales_();
this.updateTitle_(chartAreaSel);
@@ -182,45 +413,37 @@ tr.exportTo('tr.ui.b', function() {
.text(this.chartTitle_);
},
- // TODO(charliea): We should change updateLegend_ so that it ellipsizes the
- // series names after a certain point. Otherwise, the series names start
- // dipping below the x-axis and continue on outside of the viewport.
updateLegend_: function() {
- var keys = this.getLegendKeys_();
- if (keys === undefined)
- return;
-
var chartAreaSel = d3.select(this.chartAreaElement);
- var chartAreaSize = this.chartAreaSize;
+ chartAreaSel.selectAll('.legend').remove();
+ if (this.hideLegend)
+ return;
- var legendEntriesSel = chartAreaSel.selectAll('.legend')
- .data(keys.slice().reverse());
+ var series = [...this.seriesByKey_.values()].reverse();
+ var legendEntriesSel = chartAreaSel.selectAll('.legend').data(series);
+ var width = this.margin.right - 2;
legendEntriesSel.enter()
- .append('g')
+ .append('foreignObject')
.attr('class', 'legend')
- .attr('transform', function(d, i) {
- return 'translate(0,' + i * 20 + ')';
+ .attr('x', this.chartAreaSize.width + 2)
+ .attr('width', width)
+ .attr('height', 18)
+ .attr('transform', function(series, i) {
+ return 'translate(0,' + i * 18 + ')';
})
- .append('text').text(function(key) {
- return key;
- });
+ .append('xhtml:body')
+ .append('tr-ui-b-chart-legend-key')
+ .property('color', function(series) {
+ var selected = this.currentHighlightedLegendKey === series.key;
+ return getColorOfKey(series.key, selected);
+ }.bind(this))
+ .property('width', width)
+ .property('target', function(series) { return series.target; })
+ .property('optional', function(series) { return series.optional; })
+ .property('enabled', function(series) { return series.enabled; })
+ .text(function(series) { return series.key; });
legendEntriesSel.exit().remove();
-
- legendEntriesSel.attr('x', chartAreaSize.width - 18)
- .attr('width', 18)
- .attr('height', 18)
- .style('fill', function(key) {
- var selected = this.currentHighlightedLegendKey === key;
- return getColorOfKey(key, selected);
- }.bind(this));
-
- legendEntriesSel.selectAll('text')
- .attr('x', chartAreaSize.width - 24)
- .attr('y', 9)
- .attr('dy', '.35em')
- .style('text-anchor', 'end')
- .text(function(d) { return d; });
},
get highlightedLegendKey() {
@@ -271,6 +494,7 @@ tr.exportTo('tr.ui.b', function() {
};
return {
+ DataSeriesEnableChangeEventType: DataSeriesEnableChangeEventType,
getColorOfKey: getColorOfKey,
ChartBase: ChartBase
};
« no previous file with comments | « tracing/tracing/ui/base/bar_chart_test.html ('k') | tracing/tracing/ui/base/chart_base_2d.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698