| OLD | NEW |
| 1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
| 2 <!-- | 2 <!-- |
| 3 Copyright (c) 2014 The Chromium Authors. All rights reserved. | 3 Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| 4 Use of this source code is governed by a BSD-style license that can be | 4 Use of this source code is governed by a BSD-style license that can be |
| 5 found in the LICENSE file. | 5 found in the LICENSE file. |
| 6 --> | 6 --> |
| 7 | 7 |
| 8 <link rel="import" href="/tracing/base/color_scheme.html"> | 8 <link rel="import" href="/tracing/base/color_scheme.html"> |
| 9 <link rel="import" href="/tracing/ui/analysis/analysis_link.html"> |
| 9 <link rel="import" href="/tracing/ui/base/d3.html"> | 10 <link rel="import" href="/tracing/ui/base/d3.html"> |
| 10 <link rel="import" href="/tracing/ui/base/ui.html"> | 11 <link rel="import" href="/tracing/ui/base/ui.html"> |
| 11 | 12 |
| 13 <dom-module id="tr-ui-b-chart-legend-key"> |
| 14 <template> |
| 15 <style> |
| 16 #checkbox { |
| 17 margin: 0; |
| 18 visibility: hidden; |
| 19 vertical-align: text-top; |
| 20 } |
| 21 #label, #link { |
| 22 white-space: nowrap; |
| 23 text-overflow: ellipsis; |
| 24 overflow: hidden; |
| 25 display: inline-block; |
| 26 } |
| 27 </style> |
| 28 |
| 29 <input type=checkbox id="checkbox" checked> |
| 30 <tr-ui-a-analysis-link id="link"></tr-ui-a-analysis-link> |
| 31 <label id="label"></label> |
| 32 </template> |
| 33 </dom-module> |
| 34 <script> |
| 35 'use strict'; |
| 36 Polymer({ |
| 37 is: 'tr-ui-b-chart-legend-key', |
| 38 |
| 39 ready: function() { |
| 40 this.$.checkbox.addEventListener( |
| 41 'change', this.onCheckboxChange_.bind(this)); |
| 42 }, |
| 43 |
| 44 /** |
| 45 * Dispatch an event when the checkbox is toggled. |
| 46 * The checkbox is visible when optional is set to true. |
| 47 */ |
| 48 onCheckboxChange_: function() { |
| 49 tr.b.dispatchSimpleEvent(this, tr.ui.b.DataSeriesEnableChangeEventType, |
| 50 true, false, |
| 51 {key: Polymer.dom(this).textContent, enabled: this.enabled}); |
| 52 }, |
| 53 |
| 54 set textContent(t) { |
| 55 Polymer.dom(this.$.label).textContent = t; |
| 56 Polymer.dom(this.$.link).textContent = t; |
| 57 this.updateContents_(); |
| 58 }, |
| 59 |
| 60 set width(w) { |
| 61 w -= 20; // reserve 20px for the checkbox |
| 62 this.$.link.style.width = w + 'px'; |
| 63 this.$.label.style.width = w + 'px'; |
| 64 }, |
| 65 |
| 66 get textContent() { |
| 67 return Polymer.dom(this.$.label).textContent; |
| 68 }, |
| 69 |
| 70 /** |
| 71 * When a legend-key is "optional", then its checkbox is visible to allow |
| 72 * the user to enable/disable the data series for the key. |
| 73 * See ChartBase.customizeOptionalSeries(). |
| 74 * |
| 75 * @param {boolean} optional |
| 76 */ |
| 77 set optional(optional) { |
| 78 this.$.checkbox.style.visibility = optional ? 'visible' : 'hidden'; |
| 79 }, |
| 80 |
| 81 get optional() { |
| 82 return this.$.checkbox.style.visibility === 'visible'; |
| 83 }, |
| 84 |
| 85 set enabled(enabled) { |
| 86 this.$.checkbox.checked = enabled ? 'checked' : ''; |
| 87 }, |
| 88 |
| 89 get enabled() { |
| 90 return this.$.checkbox.checked; |
| 91 }, |
| 92 |
| 93 set color(c) { |
| 94 this.$.label.style.color = c; |
| 95 this.$.link.color = c; |
| 96 }, |
| 97 |
| 98 /** |
| 99 * When target is defined, label is hidden and link is shown. |
| 100 * When the link is clicked, then a RequestSelectionChangeEvent is |
| 101 * dispatched containing the target. |
| 102 * When target is undefined, label is shown and link is hidden, so that the |
| 103 * link is not clickable. |
| 104 * See ChartBase.customizeLegendTargets(). |
| 105 */ |
| 106 set target(target) { |
| 107 this.$.link.setSelectionAndContent( |
| 108 target, Polymer.dom(this.$.label).textContent); |
| 109 this.updateContents_(); |
| 110 }, |
| 111 |
| 112 get target() { |
| 113 return this.$.link.selection; |
| 114 }, |
| 115 |
| 116 updateContents_: function() { |
| 117 this.$.link.style.display = this.target ? '' : 'none'; |
| 118 this.$.label.style.display = this.target ? 'none' : ''; |
| 119 this.$.label.htmlFor = this.optional ? 'checkbox' : ''; |
| 120 } |
| 121 }); |
| 122 </script> |
| 123 |
| 12 <style> | 124 <style> |
| 13 * /deep/ .chart-base #title { | 125 * /deep/ .chart-base #title { |
| 14 font-size: 16pt; | 126 font-size: 16pt; |
| 15 } | 127 } |
| 16 | 128 |
| 17 * /deep/ .chart-base { | 129 * /deep/ .chart-base { |
| 18 font-size: 12pt; | |
| 19 -webkit-user-select: none; | 130 -webkit-user-select: none; |
| 20 cursor: default; | 131 cursor: default; |
| 21 } | 132 } |
| 22 | 133 |
| 23 * /deep/ .chart-base .axis path, | 134 * /deep/ .chart-base .axis path, |
| 24 * /deep/ .chart-base .axis line { | 135 * /deep/ .chart-base .axis line { |
| 25 fill: none; | 136 fill: none; |
| 26 shape-rendering: crispEdges; | 137 shape-rendering: crispEdges; |
| 27 stroke: #000; | 138 stroke: #000; |
| 28 } | 139 } |
| 140 |
| 141 * /deep/ .chart-base .legend body { |
| 142 margin: 0; |
| 143 } |
| 29 </style> | 144 </style> |
| 30 | 145 |
| 31 <template id="chart-base-template"> | 146 <template id="chart-base-template"> |
| 32 <svg> <!-- svg tag is dropped by ChartBase.decorate. --> | 147 <svg> <!-- svg tag is dropped by ChartBase.decorate. --> |
| 33 <g xmlns="http://www.w3.org/2000/svg" id="chart-area"> | 148 <g xmlns="http://www.w3.org/2000/svg" id="chart-area"> |
| 34 <g class="x axis"></g> | 149 <g class="x axis"></g> |
| 35 <g class="y axis"></g> | 150 <g class="y axis"></g> |
| 36 <text id="title"></text> | 151 <text id="title"></text> |
| 37 </g> | 152 </g> |
| 38 </svg> | 153 </svg> |
| 39 </template> | 154 </template> |
| 40 | 155 |
| 41 <script> | 156 <script> |
| 42 'use strict'; | 157 'use strict'; |
| 43 | 158 |
| 44 tr.exportTo('tr.ui.b', function() { | 159 tr.exportTo('tr.ui.b', function() { |
| 160 var DataSeriesEnableChangeEventType = 'data-series-enabled-change'; |
| 161 |
| 45 var THIS_DOC = document.currentScript.ownerDocument; | 162 var THIS_DOC = document.currentScript.ownerDocument; |
| 46 | 163 |
| 47 var svgNS = 'http://www.w3.org/2000/svg'; | 164 var svgNS = 'http://www.w3.org/2000/svg'; |
| 48 var ColorScheme = tr.b.ColorScheme; | 165 var ColorScheme = tr.b.ColorScheme; |
| 49 | 166 |
| 50 function getColorOfKey(key, selected) { | 167 function getColorOfKey(key, selected) { |
| 51 var id = ColorScheme.getColorIdForGeneralPurposeString(key); | 168 var id = ColorScheme.getColorIdForGeneralPurposeString(key); |
| 52 if (selected) | 169 if (selected) |
| 53 id += ColorScheme.properties.brightenedOffsets[0]; | 170 id += ColorScheme.properties.brightenedOffsets[0]; |
| 54 return ColorScheme.colorsAsStrings[id]; | 171 return ColorScheme.colorsAsStrings[id]; |
| 55 } | 172 } |
| 56 | 173 |
| 174 function DataSeries(key) { |
| 175 this.key_ = key; |
| 176 this.target_ = undefined; |
| 177 this.optional_ = false; |
| 178 this.enabled_ = true; |
| 179 } |
| 180 |
| 181 DataSeries.prototype = { |
| 182 get key() { |
| 183 return this.key_; |
| 184 }, |
| 185 |
| 186 get optional() { |
| 187 return this.optional_; |
| 188 }, |
| 189 |
| 190 set optional(optional) { |
| 191 this.optional_ = optional; |
| 192 }, |
| 193 |
| 194 get enabled() { |
| 195 return this.enabled_; |
| 196 }, |
| 197 |
| 198 set enabled(enabled) { |
| 199 // If the caller is disabling a data series, but it wasn't optional, then |
| 200 // force it to be optional. |
| 201 if (!this.optional && !enabled) |
| 202 this.optional = true; |
| 203 this.enabled_ = enabled; |
| 204 }, |
| 205 |
| 206 get target() { |
| 207 return this.target_; |
| 208 }, |
| 209 |
| 210 set target(t) { |
| 211 this.target_ = t; |
| 212 } |
| 213 }; |
| 214 |
| 57 /** | 215 /** |
| 58 * A virtual base class for basic charts that provides X and Y axes, if | 216 * A virtual base class for basic charts that provides X and Y axes, if |
| 59 * needed, a title, and legend. | 217 * needed, a title, and legend. |
| 60 * | 218 * |
| 61 * @constructor | 219 * @constructor |
| 62 */ | 220 */ |
| 63 var ChartBase = tr.ui.b.define('svg', undefined, svgNS); | 221 var ChartBase = tr.ui.b.define('svg', undefined, svgNS); |
| 64 | 222 |
| 65 ChartBase.prototype = { | 223 ChartBase.prototype = { |
| 66 __proto__: HTMLDivElement.prototype, | 224 __proto__: HTMLUnknownElement.prototype, |
| 225 |
| 226 getDataSeries: function(key) { |
| 227 if (!this.seriesByKey_.has(key)) |
| 228 this.seriesByKey_.set(key, new DataSeries(key)); |
| 229 return this.seriesByKey_.get(key); |
| 230 }, |
| 67 | 231 |
| 68 decorate: function() { | 232 decorate: function() { |
| 69 Polymer.dom(this).classList.add('chart-base'); | 233 Polymer.dom(this).classList.add('chart-base'); |
| 70 this.chartTitle_ = undefined; | 234 this.chartTitle_ = undefined; |
| 71 this.seriesKeys_ = undefined; | 235 this.seriesByKey_ = new Map(); |
| 72 this.width_ = 400; | 236 this.width_ = 400; |
| 73 this.height_ = 300; | 237 this.height_ = 300; |
| 238 this.margin = {top: 20, right: 72, bottom: 30, left: 50}; |
| 239 this.hideLegend_ = false; |
| 74 | 240 |
| 75 // This should use tr.ui.b.instantiateTemplate. However, creating | 241 // This should use tr.ui.b.instantiateTemplate. However, creating |
| 76 // svg-namespaced elements inside a template isn't possible. Thus, this | 242 // svg-namespaced elements inside a template isn't possible. Thus, this |
| 77 // hack. | 243 // hack. |
| 78 var template = Polymer.dom(THIS_DOC) | 244 var template = |
| 79 .querySelector('#chart-base-template'); | 245 Polymer.dom(THIS_DOC).querySelector('#chart-base-template'); |
| 80 var svgEl = Polymer.dom(template.content).querySelector('svg'); | 246 var svgEl = Polymer.dom(template.content).querySelector('svg'); |
| 81 for (var i = 0; i < svgEl.children.length; i++) | 247 for (var i = 0; i < Polymer.dom(svgEl).children.length; i++) |
| 82 Polymer.dom(this).appendChild(svgEl.children[i].cloneNode(true)); | 248 Polymer.dom(this).appendChild( |
| 249 Polymer.dom(svgEl.children[i]).cloneNode(true)); |
| 83 | 250 |
| 84 // svg likes to take over width & height properties for some reason. This | 251 // svg likes to take over width & height properties for some reason. This |
| 85 // works around it. | 252 // works around it. |
| 86 Object.defineProperty( | 253 Object.defineProperty( |
| 87 this, 'width', { | 254 this, 'width', { |
| 88 get: function() { | 255 get: function() { |
| 89 return this.width_; | 256 return this.width_; |
| 90 }, | 257 }, |
| 91 set: function(width) { | 258 set: function(width) { |
| 92 this.width_ = width; | 259 this.width_ = width; |
| 93 this.updateContents_(); | 260 this.updateContents_(); |
| 94 } | 261 } |
| 95 }); | 262 }); |
| 96 Object.defineProperty( | 263 Object.defineProperty( |
| 97 this, 'height', { | 264 this, 'height', { |
| 98 get: function() { | 265 get: function() { |
| 99 return this.height_; | 266 return this.height_; |
| 100 }, | 267 }, |
| 101 set: function(height) { | 268 set: function(height) { |
| 102 this.height_ = height; | 269 this.height_ = height; |
| 103 this.updateContents_(); | 270 this.updateContents_(); |
| 104 } | 271 } |
| 105 }); | 272 }); |
| 273 this.addEventListener(DataSeriesEnableChangeEventType, |
| 274 this.onDataSeriesEnableChange_.bind(this)); |
| 275 }, |
| 276 |
| 277 get hideLegend() { |
| 278 return this.hideLegend_; |
| 279 }, |
| 280 |
| 281 set hideLegend(h) { |
| 282 this.hideLegend_ = h; |
| 283 this.updateContents_(); |
| 284 }, |
| 285 |
| 286 isSeriesEnabled: function(key) { |
| 287 return this.getDataSeries(key).enabled; |
| 288 }, |
| 289 |
| 290 onDataSeriesEnableChange_: function(event) { |
| 291 this.getDataSeries(event.key).enabled = event.enabled; |
| 292 this.updateContents_(); |
| 106 }, | 293 }, |
| 107 | 294 |
| 108 get chartTitle() { | 295 get chartTitle() { |
| 109 return this.chartTitle_; | 296 return this.chartTitle_; |
| 110 }, | 297 }, |
| 111 | 298 |
| 112 set chartTitle(chartTitle) { | 299 set chartTitle(chartTitle) { |
| 300 if (chartTitle && !this.chartTitle_) |
| 301 this.margin.top += this.titleMarginPx; |
| 302 else if (this.chartTitle_ && !chartTitle) |
| 303 this.margin.top -= this.titleMarginPx; |
| 304 |
| 113 this.chartTitle_ = chartTitle; | 305 this.chartTitle_ = chartTitle; |
| 114 this.updateContents_(); | 306 this.updateContents_(); |
| 115 }, | 307 }, |
| 116 | 308 |
| 309 get titleMarginPx() { |
| 310 return 20; |
| 311 }, |
| 312 |
| 117 get chartAreaElement() { | 313 get chartAreaElement() { |
| 118 return Polymer.dom(this).querySelector('#chart-area'); | 314 return Polymer.dom(this).querySelector('#chart-area'); |
| 119 }, | 315 }, |
| 120 | 316 |
| 121 setSize: function(size) { | 317 setSize: function(size) { |
| 122 this.width_ = size.width; | 318 this.width_ = size.width; |
| 123 this.height_ = size.height; | 319 this.height_ = size.height; |
| 124 this.updateContents_(); | 320 this.updateContents_(); |
| 125 }, | 321 }, |
| 126 | 322 |
| 127 getMargin_: function() { | |
| 128 var margin = {top: 20, right: 20, bottom: 30, left: 50}; | |
| 129 if (this.chartTitle_) | |
| 130 margin.top += 20; | |
| 131 return margin; | |
| 132 }, | |
| 133 | |
| 134 get margin() { | |
| 135 return this.getMargin_(); | |
| 136 }, | |
| 137 | |
| 138 get chartAreaSize() { | 323 get chartAreaSize() { |
| 139 var margin = this.margin; | |
| 140 return { | 324 return { |
| 141 width: this.width_ - margin.left - margin.right, | 325 width: this.width_ - this.margin.left - this.margin.right, |
| 142 height: this.height_ - margin.top - margin.bottom | 326 height: this.height_ - this.margin.top - this.margin.bottom |
| 143 }; | 327 }; |
| 144 }, | 328 }, |
| 145 | 329 |
| 146 getLegendKeys_: function() { | 330 /** |
| 147 throw new Error('Not implemented'); | 331 * Legend keys can be clickable links instead of plain text. |
| 332 * When a legend key link is clicked, a RequestSelectionChangeEvent is |
| 333 * dispatched containing arbitrary data. ChartBase calls that arbitrary data |
| 334 * the "target" of the legend key link. |
| 335 * In order to turn the legend key for the 'foo' data series into a |
| 336 * clickable link, call customizeLegendTargets({foo: target}). When the user |
| 337 * clicks on the legend key link for 'foo', then a |
| 338 * RequestSelectionChangeEvent will be dispatched, and its |selection| field |
| 339 * will be the |target| value for the 'foo' key in |delta|. |
| 340 * |
| 341 * @param {!Object} delta |
| 342 */ |
| 343 customizeLegendTargets: function(delta) { |
| 344 tr.b.iterItems(delta, function(key, value) { |
| 345 this.getDataSeries(key).target = value; |
| 346 }, this); |
| 347 }, |
| 348 |
| 349 /** |
| 350 * Optional data series can be enabled and disabled using checkboxes. |
| 351 * In order to allow the user to enable/disabled the 'foo' data series, |
| 352 * call customizeOptionalSeries({foo: true}). This will show a checkbox |
| 353 * next to the 'foo' legend key. When the user toggles the checkbox, then a |
| 354 * DataSeriesEnableChangeEvent will be dispatched with its |key| = 'foo'. |
| 355 * ChartBase listens for that event and updates |isSeriesEnabled('foo')| to |
| 356 * reflect the state of that checkbox, and calls updateContents_(). |
| 357 * Subclasses are responsible for implementing updateContents_() in order to |
| 358 * hiding disabled data series -- see BarChart. |
| 359 * |
| 360 * @param {!Object} delta |
| 361 */ |
| 362 customizeOptionalSeries: function(delta) { |
| 363 tr.b.iterItems(delta, function(key, value) { |
| 364 this.getDataSeries(key).optional = value; |
| 365 }, this); |
| 366 }, |
| 367 |
| 368 /** |
| 369 * Data series can be enabled and disabled. |
| 370 * See customizeOptionalSeries() in order to allow the user to |
| 371 * enable/disable data series manually. Callers may call |
| 372 * customizeEnabledSeries({foo: false}) in order to automatically |
| 373 * disable the 'foo' data series, for example. |
| 374 * |
| 375 * @param {!Object} delta |
| 376 */ |
| 377 customizeEnabledSeries: function(delta) { |
| 378 tr.b.iterItems(delta, function(key, value) { |
| 379 this.getDataSeries(key).enabled = value; |
| 380 }, this); |
| 148 }, | 381 }, |
| 149 | 382 |
| 150 updateScales_: function() { | 383 updateScales_: function() { |
| 151 throw new Error('Not implemented'); | 384 throw new Error('Not implemented'); |
| 152 }, | 385 }, |
| 153 | 386 |
| 154 updateContents_: function() { | 387 updateContents_: function() { |
| 155 var margin = this.margin; | |
| 156 | |
| 157 var thisSel = d3.select(this); | 388 var thisSel = d3.select(this); |
| 158 thisSel.attr('width', this.width_); | 389 thisSel.attr('width', this.width_); |
| 159 thisSel.attr('height', this.height_); | 390 thisSel.attr('height', this.height_); |
| 160 | 391 |
| 161 var chartAreaSel = d3.select(this.chartAreaElement); | 392 var chartAreaSel = d3.select(this.chartAreaElement); |
| 162 chartAreaSel.attr('transform', | 393 chartAreaSel.attr('transform', |
| 163 'translate(' + margin.left + ',' + margin.top + ')'); | 394 'translate(' + this.margin.left + ',' + this.margin.top + ')'); |
| 164 | 395 |
| 165 this.updateScales_(); | 396 this.updateScales_(); |
| 166 this.updateTitle_(chartAreaSel); | 397 this.updateTitle_(chartAreaSel); |
| 167 this.updateLegend_(); | 398 this.updateLegend_(); |
| 168 }, | 399 }, |
| 169 | 400 |
| 170 updateTitle_: function(chartAreaSel) { | 401 updateTitle_: function(chartAreaSel) { |
| 171 var titleSel = chartAreaSel.select('#title'); | 402 var titleSel = chartAreaSel.select('#title'); |
| 172 if (!this.chartTitle_) { | 403 if (!this.chartTitle_) { |
| 173 titleSel.style('display', 'none'); | 404 titleSel.style('display', 'none'); |
| 174 return; | 405 return; |
| 175 } | 406 } |
| 176 var width = this.chartAreaSize.width; | 407 var width = this.chartAreaSize.width; |
| 177 titleSel.attr('transform', 'translate(' + width * 0.5 + ',-5)') | 408 titleSel.attr('transform', 'translate(' + width * 0.5 + ',-5)') |
| 178 .style('display', undefined) | 409 .style('display', undefined) |
| 179 .style('text-anchor', 'middle') | 410 .style('text-anchor', 'middle') |
| 180 .attr('class', 'title') | 411 .attr('class', 'title') |
| 181 .attr('width', width) | 412 .attr('width', width) |
| 182 .text(this.chartTitle_); | 413 .text(this.chartTitle_); |
| 183 }, | 414 }, |
| 184 | 415 |
| 185 // TODO(charliea): We should change updateLegend_ so that it ellipsizes the | |
| 186 // series names after a certain point. Otherwise, the series names start | |
| 187 // dipping below the x-axis and continue on outside of the viewport. | |
| 188 updateLegend_: function() { | 416 updateLegend_: function() { |
| 189 var keys = this.getLegendKeys_(); | 417 var chartAreaSel = d3.select(this.chartAreaElement); |
| 190 if (keys === undefined) | 418 chartAreaSel.selectAll('.legend').remove(); |
| 419 if (this.hideLegend) |
| 191 return; | 420 return; |
| 192 | 421 |
| 193 var chartAreaSel = d3.select(this.chartAreaElement); | 422 var series = [...this.seriesByKey_.values()].reverse(); |
| 194 var chartAreaSize = this.chartAreaSize; | 423 var legendEntriesSel = chartAreaSel.selectAll('.legend').data(series); |
| 195 | 424 |
| 196 var legendEntriesSel = chartAreaSel.selectAll('.legend') | 425 var width = this.margin.right - 2; |
| 197 .data(keys.slice().reverse()); | |
| 198 | |
| 199 legendEntriesSel.enter() | 426 legendEntriesSel.enter() |
| 200 .append('g') | 427 .append('foreignObject') |
| 201 .attr('class', 'legend') | 428 .attr('class', 'legend') |
| 202 .attr('transform', function(d, i) { | 429 .attr('x', this.chartAreaSize.width + 2) |
| 203 return 'translate(0,' + i * 20 + ')'; | 430 .attr('width', width) |
| 431 .attr('height', 18) |
| 432 .attr('transform', function(series, i) { |
| 433 return 'translate(0,' + i * 18 + ')'; |
| 204 }) | 434 }) |
| 205 .append('text').text(function(key) { | 435 .append('xhtml:body') |
| 206 return key; | 436 .append('tr-ui-b-chart-legend-key') |
| 207 }); | 437 .property('color', function(series) { |
| 438 var selected = this.currentHighlightedLegendKey === series.key; |
| 439 return getColorOfKey(series.key, selected); |
| 440 }.bind(this)) |
| 441 .property('width', width) |
| 442 .property('target', function(series) { return series.target; }) |
| 443 .property('optional', function(series) { return series.optional; }) |
| 444 .property('enabled', function(series) { return series.enabled; }) |
| 445 .text(function(series) { return series.key; }); |
| 208 legendEntriesSel.exit().remove(); | 446 legendEntriesSel.exit().remove(); |
| 209 | |
| 210 legendEntriesSel.attr('x', chartAreaSize.width - 18) | |
| 211 .attr('width', 18) | |
| 212 .attr('height', 18) | |
| 213 .style('fill', function(key) { | |
| 214 var selected = this.currentHighlightedLegendKey === key; | |
| 215 return getColorOfKey(key, selected); | |
| 216 }.bind(this)); | |
| 217 | |
| 218 legendEntriesSel.selectAll('text') | |
| 219 .attr('x', chartAreaSize.width - 24) | |
| 220 .attr('y', 9) | |
| 221 .attr('dy', '.35em') | |
| 222 .style('text-anchor', 'end') | |
| 223 .text(function(d) { return d; }); | |
| 224 }, | 447 }, |
| 225 | 448 |
| 226 get highlightedLegendKey() { | 449 get highlightedLegendKey() { |
| 227 return this.highlightedLegendKey_; | 450 return this.highlightedLegendKey_; |
| 228 }, | 451 }, |
| 229 | 452 |
| 230 set highlightedLegendKey(highlightedLegendKey) { | 453 set highlightedLegendKey(highlightedLegendKey) { |
| 231 this.highlightedLegendKey_ = highlightedLegendKey; | 454 this.highlightedLegendKey_ = highlightedLegendKey; |
| 232 this.updateHighlight_(); | 455 this.updateHighlight_(); |
| 233 }, | 456 }, |
| (...skipping 30 matching lines...) Expand all Loading... |
| 264 this.style.fill = color; | 487 this.style.fill = color; |
| 265 if (highlighted) | 488 if (highlighted) |
| 266 this.style.fontWeight = 'bold'; | 489 this.style.fontWeight = 'bold'; |
| 267 else | 490 else |
| 268 this.style.fontWeight = ''; | 491 this.style.fontWeight = ''; |
| 269 }); | 492 }); |
| 270 } | 493 } |
| 271 }; | 494 }; |
| 272 | 495 |
| 273 return { | 496 return { |
| 497 DataSeriesEnableChangeEventType: DataSeriesEnableChangeEventType, |
| 274 getColorOfKey: getColorOfKey, | 498 getColorOfKey: getColorOfKey, |
| 275 ChartBase: ChartBase | 499 ChartBase: ChartBase |
| 276 }; | 500 }; |
| 277 }); | 501 }); |
| 278 </script> | 502 </script> |
| OLD | NEW |