| OLD | NEW |
| (Empty) |
| 1 // | |
| 2 // Copyright 2014 Google Inc. All rights reserved. | |
| 3 // | |
| 4 // Use of this source code is governed by a BSD-style | |
| 5 // license that can be found in the LICENSE file or at | |
| 6 // https://developers.google.com/open-source/licenses/bsd | |
| 7 // | |
| 8 | |
| 9 part of charted.charts; | |
| 10 | |
| 11 class DefaultChartLegendImpl implements ChartLegend { | |
| 12 static const CLASS_PREFIX = 'chart-legend'; | |
| 13 | |
| 14 final Element host; | |
| 15 final int visibleItemsCount; | |
| 16 final bool showValues; | |
| 17 final SubscriptionsDisposer _disposer = new SubscriptionsDisposer(); | |
| 18 | |
| 19 String _title; | |
| 20 SelectionScope _scope; | |
| 21 Selection _root; | |
| 22 ChartArea _area; | |
| 23 | |
| 24 Iterable<ChartLegendItem> _items; | |
| 25 | |
| 26 DefaultChartLegendImpl( | |
| 27 this.host, this.visibleItemsCount, this.showValues, String title) | |
| 28 : _title = title { | |
| 29 assert(host != null); | |
| 30 } | |
| 31 | |
| 32 void dispose() { | |
| 33 _disposer.dispose(); | |
| 34 } | |
| 35 | |
| 36 /** | |
| 37 * Sets the title of the legend, if the legend is already drawn, updates the | |
| 38 * title on the legend as well. | |
| 39 */ | |
| 40 void set title(String title) { | |
| 41 _title = title; | |
| 42 if (_scope == null) return; | |
| 43 _updateTitle(); | |
| 44 } | |
| 45 | |
| 46 String get title => _title; | |
| 47 | |
| 48 /** Updates the title of the legend. */ | |
| 49 void _updateTitle() { | |
| 50 if (_title.isNotEmpty) { | |
| 51 if (_root.select('.chart-legend-heading').length == 0) { | |
| 52 _root.select('.chart-legend-heading'); | |
| 53 _root.append('div') | |
| 54 ..classed('chart-legend-heading') | |
| 55 ..text(_title); | |
| 56 } else { | |
| 57 _root.select('.chart-legend-heading').text(_title); | |
| 58 } | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 /** Updates the legend base on a new list of ChartLegendItems. */ | |
| 63 update(Iterable<ChartLegendItem> items, ChartArea area) { | |
| 64 assert(items != null); | |
| 65 assert(area == _area || _area == null); | |
| 66 | |
| 67 _area = area; | |
| 68 if (_area.state != null) { | |
| 69 _disposer.add(_area.state.changes.listen(_handleStateChanges)); | |
| 70 } | |
| 71 if (_scope == null) { | |
| 72 _scope = new SelectionScope.element(host); | |
| 73 _root = _scope.selectElements([host]); | |
| 74 } | |
| 75 | |
| 76 _updateTitle(); | |
| 77 _items = items; | |
| 78 _createLegendItems(); | |
| 79 | |
| 80 // Add more item label if there's more items than the max display items. | |
| 81 if ((visibleItemsCount > 0) && (visibleItemsCount < items.length)) { | |
| 82 _root.select('.chart-legend-more').remove(); | |
| 83 _root.append('div') | |
| 84 ..on('mouseover', | |
| 85 (d, i, e) => _displayMoreItem(items.skip(visibleItemsCount))) | |
| 86 ..on('mouseleave', (d, i, e) => _hideMoreItem()) | |
| 87 ..text('${items.length - visibleItemsCount} more...') | |
| 88 ..classed('chart-legend-more'); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 /** Hides extra legend items. */ | |
| 93 void _hideMoreItem() { | |
| 94 var tooltip = _root.select('.chart-legend-more-tooltip'); | |
| 95 tooltip.style('opacity', '0'); | |
| 96 } | |
| 97 | |
| 98 // Displays remaining legend items as a tooltip | |
| 99 void _displayMoreItem(Iterable<ChartLegendItem> items) { | |
| 100 var tooltip = _root.select('.chart-legend-more-tooltip'); | |
| 101 if (tooltip.isEmpty) { | |
| 102 tooltip = _root.select('.chart-legend-more').append('div') | |
| 103 ..classed('chart-legend-more-tooltip'); | |
| 104 } | |
| 105 tooltip.style('opacity', '1'); | |
| 106 | |
| 107 // _createLegendItems(tooltip, 'chart-legend-more', items); | |
| 108 } | |
| 109 | |
| 110 /// Creates legend items starting at the given index. | |
| 111 void _createLegendItems() { | |
| 112 var state = _area.state, | |
| 113 rows = _root.selectAll( | |
| 114 '.chart-legend-row').data(_items, (x) => x.hashCode), | |
| 115 isFirstRender = rows.length == 0; | |
| 116 | |
| 117 var enter = rows.enter.appendWithCallback((d, i, e) { | |
| 118 var row = Namespace.createChildElement('div', e), | |
| 119 color = Namespace.createChildElement('div', e) | |
| 120 ..className = 'chart-legend-color', | |
| 121 label = Namespace.createChildElement('div', e) | |
| 122 ..className = 'chart-legend-label', | |
| 123 value = showValues ? (Namespace.createChildElement('div', e) | |
| 124 ..className = 'chart-legend-value') : null; | |
| 125 | |
| 126 var rowStyles = ['chart-legend-row'].toList(); | |
| 127 | |
| 128 // If this is the first time we are adding rows, | |
| 129 // Update elements before adding them to the DOM. | |
| 130 if (isFirstRender) { | |
| 131 if (state != null) { | |
| 132 if (d.index == state.preview) { | |
| 133 rowStyles.add('chart-legend-hover'); | |
| 134 } | |
| 135 if (state.isSelected(d.index)) { | |
| 136 rowStyles.add('chart-legend-selected'); | |
| 137 } | |
| 138 } | |
| 139 rowStyles.addAll( | |
| 140 d.series.map((ChartSeries x) => 'type-${x.renderer.name}')); | |
| 141 | |
| 142 color.style.setProperty('background-color', d.color); | |
| 143 row.append(color); | |
| 144 label.text = d.label; | |
| 145 row.append(label); | |
| 146 | |
| 147 if (showValues) { | |
| 148 value.text = d.value; | |
| 149 value.style.setProperty('color', d.color); | |
| 150 row.append(value); | |
| 151 } | |
| 152 } | |
| 153 row.classes.addAll(rowStyles); | |
| 154 return row; | |
| 155 }); | |
| 156 | |
| 157 // We have elements in the DOM that need updating. | |
| 158 if (!isFirstRender) { | |
| 159 rows.each((ChartLegendItem d, i, Element e) { | |
| 160 var classes = e.classes; | |
| 161 if (state != null) { | |
| 162 if (d.index == state.preview) { | |
| 163 classes.add('chart-legend-hover'); | |
| 164 } else { | |
| 165 classes.remove('chart-legend-hover'); | |
| 166 } | |
| 167 if (state.isSelected(d.index)) { | |
| 168 classes.add('chart-legend-selected'); | |
| 169 } else { | |
| 170 classes.remove('chart-legend-selected'); | |
| 171 } | |
| 172 } | |
| 173 classes.addAll(d.series.map((x) => 'type-${x.renderer.name}')); | |
| 174 | |
| 175 (e.children[0]).style.setProperty('background-color', d.color); | |
| 176 (e.children[1]).text = d.label; | |
| 177 if (showValues) { | |
| 178 (e.lastChild as Element) | |
| 179 ..text = d.value | |
| 180 ..style.setProperty('color', d.color); | |
| 181 } | |
| 182 }); | |
| 183 } | |
| 184 | |
| 185 if (state != null) { | |
| 186 enter | |
| 187 ..on('mouseover', (d, i, e) => state.preview = d.index) | |
| 188 ..on('mouseout', (d, i, e) { | |
| 189 if (state.preview == d.index) { | |
| 190 state.preview = null; | |
| 191 } | |
| 192 }) | |
| 193 ..on('click', (d, i, e) { | |
| 194 if (state.isSelected(d.index)) { | |
| 195 state.unselect(d.index); | |
| 196 } else { | |
| 197 state.select(d.index); | |
| 198 } | |
| 199 }); | |
| 200 } | |
| 201 | |
| 202 rows.exit.remove(); | |
| 203 } | |
| 204 | |
| 205 /// Update legend to show chart's selection and visibility. | |
| 206 void _handleStateChanges(_) => _createLegendItems(); | |
| 207 } | |
| OLD | NEW |