1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 tvcm.require('tvcm.range');
8 tvcm.require('tvcm.ui.d3');
9 tvcm.require('tvcm.ui.chart_base');
11 tvcm.requireStylesheet('tvcm.ui.bar_chart');
13 tvcm.exportTo('tvcm.ui', function() {
14 var ChartBase = tvcm.ui.ChartBase;
15 var getColorOfKey = tvcm.ui.getColorOfKey;
20 var BarChart = tvcm.ui.define('bar-chart', ChartBase);
22 BarChart.prototype = {
23 __proto__: ChartBase.prototype,
25 decorate: function() {
26 ChartBase.prototype.decorate.call(this);
27 this.classList.add('bar-chart');
29 this.xScale_ = undefined;
30 this.xSubScale_ = undefined;
31 this.yScale_ = undefined;
33 this.data_ = undefined;
34 this.xLabelValues_ = undefined;
35 this.xLabelKey_ = undefined;
36 this.seriesKeys_ = undefined;
44 return this.xLabelKey_;
47 setDataAndXLabelKey: function(data, xLabelKey) {
48 if (data !== undefined) {
49 // Figure out what the series keys are. E.g. for {label: 'a', value1: 3,
50 // value2: 4} compute ['value1', 'value2'].
52 d3.keys(data[0]).forEach(function(k) {
58 // Figure out the x labels in the data set. E.g. from
59 // [{label: 'a', ...}, {label: 'b', ...}]
60 // we would commpute ['a', 'y'].
61 var xLabelValues = [];
62 var seenXLabelValues = {};
63 data.forEach(function(d) {
64 var xLabelValue = d[xLabelKey];
65 if (seenXLabelValues[xLabelValue])
66 throw new Error('Label ' + xLabelValue + ' has been used already');
67 xLabelValues.push(xLabelValue);
68 seenXLabelValues[xLabelValue] = true;
70 this.xLabelKey_ = xLabelKey;
71 this.seriesKeys_ = seriesKeys;
72 this.xLabelValues_ = xLabelValues;
74 this.xLabelKey_ = undefined;
75 this.seriesKeys_ = undefined;
76 this.xLabelValues_ = undefined;
79 this.updateContents_();
82 getLegendKeys_: function() {
83 if (this.seriesKeys_ &&
84 this.seriesKeys_.length > 1)
85 return this.seriesKeys_.slice();
89 updateScales_: function(width, height) {
90 if (this.data_ === undefined) {
91 this.xScale_ = undefined;
92 this.xSubScale_ = undefined;
93 this.yScale_ = undefined;
97 // xScale maps x labels to a position in the overall timeline.
98 this.xScale_ = d3.scale.ordinal();
99 this.xScale_.rangeRoundBands([0, width], .1);
100 this.xScale_.domain(this.xLabelValues_);
102 // xSubScale maps an individual series to a position within its group
104 this.xSubScale_ = d3.scale.ordinal();
105 this.xSubScale_.domain(this.seriesKeys_)
106 .rangeRoundBands([0, this.xScale_.rangeBand()]);
108 // Regular mapping of values to the full chart height.
109 var yRange = new tvcm.Range();
110 this.data_.forEach(function(d) {
111 this.seriesKeys_.forEach(function(k) {
112 yRange.addValue(d[k]);
115 this.yScale_ = d3.scale.linear();
116 this.yScale_.range([height, 0]);
118 this.yScale_.domain([yRange.min, yRange.max]);
121 updateContents_: function() {
122 ChartBase.prototype.updateContents_.call(this);
126 var width = this.chartAreaSize.width;
127 var height = this.chartAreaSize.height;
129 var chartAreaSel = d3.select(this.chartAreaElement);
131 // An index-group has the rects from the same array index in the source
133 var indexGroupSel = chartAreaSel.selectAll('.index-group')
135 indexGroupSel.enter().append('g')
136 .attr('class', '.index-group')
137 .attr('transform', function(d) {
138 var k = d[this.xLabelKey_];
139 return 'translate(' + this.xScale_(k) + ',0)';
141 indexGroupSel.exit().remove();
143 // Within an index group, create a rect for each actual value.
144 var rectsSel = indexGroupSel.selectAll('rect')
146 // 'd' is an index in the original array. We want to extract out the
147 // actual values from it from this.seriesKeys_. This we turn into
148 // {name: seriesKey, value: d[seriesKey]} objects that then get
149 // data-bound to each rect.
151 for (var i = 0; i < this.seriesKeys_.length; i++) {
152 var k = this.seriesKeys_[i];
153 values.push({name: k,
159 rectsSel.enter().append('rect')
160 .attr('width', this.xSubScale_.rangeBand())
161 .attr('x', function(d) {
162 return this.xSubScale_(d.name);
164 .attr('y', function(d) {
165 return this.yScale_(d.value);
167 .attr('height', function(d) {
168 return height - this.yScale_(d.value);
170 .style('fill', function(d) {
171 return getColorOfKey(d.name);