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 |