OLD | NEW |
1 // | 1 // |
2 // Copyright 2014 Google Inc. All rights reserved. | 2 // Copyright 2014 Google Inc. All rights reserved. |
3 // | 3 // |
4 // Use of this source code is governed by a BSD-style | 4 // Use of this source code is governed by a BSD-style |
5 // license that can be found in the LICENSE file or at | 5 // license that can be found in the LICENSE file or at |
6 // https://developers.google.com/open-source/licenses/bsd | 6 // https://developers.google.com/open-source/licenses/bsd |
7 // | 7 // |
8 | 8 |
9 part of charted.charts; | 9 part of charted.charts; |
10 | 10 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
77 ChartConfig _config; | 77 ChartConfig _config; |
78 bool _autoUpdate = false; | 78 bool _autoUpdate = false; |
79 | 79 |
80 SelectionScope _scope; | 80 SelectionScope _scope; |
81 Selection _svg; | 81 Selection _svg; |
82 Selection visualization; | 82 Selection visualization; |
83 | 83 |
84 Iterable<ChartSeries> _series; | 84 Iterable<ChartSeries> _series; |
85 | 85 |
86 bool _pendingLegendUpdate = false; | 86 bool _pendingLegendUpdate = false; |
| 87 bool _pendingAxisConfigUpdate = false; |
87 List<ChartBehavior> _behaviors = new List<ChartBehavior>(); | 88 List<ChartBehavior> _behaviors = new List<ChartBehavior>(); |
88 Map<ChartSeries, _ChartSeriesInfo> _seriesInfoCache = new Map(); | 89 Map<ChartSeries, _ChartSeriesInfo> _seriesInfoCache = new Map(); |
89 | 90 |
90 StreamController<ChartEvent> _valueMouseOverController; | 91 StreamController<ChartEvent> _valueMouseOverController; |
91 StreamController<ChartEvent> _valueMouseOutController; | 92 StreamController<ChartEvent> _valueMouseOutController; |
92 StreamController<ChartEvent> _valueMouseClickController; | 93 StreamController<ChartEvent> _valueMouseClickController; |
93 StreamController<ChartArea> _chartAxesUpdatedController; | 94 StreamController<ChartArea> _chartAxesUpdatedController; |
94 | 95 |
95 DefaultCartesianAreaImpl( | 96 DefaultCartesianAreaImpl( |
96 this.host, | 97 this.host, |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 _valueMouseOutController = null; | 129 _valueMouseOutController = null; |
129 } | 130 } |
130 if (_valueMouseClickController != null) { | 131 if (_valueMouseClickController != null) { |
131 _valueMouseClickController.close(); | 132 _valueMouseClickController.close(); |
132 _valueMouseClickController = null; | 133 _valueMouseClickController = null; |
133 } | 134 } |
134 if (_chartAxesUpdatedController != null) { | 135 if (_chartAxesUpdatedController != null) { |
135 _chartAxesUpdatedController.close(); | 136 _chartAxesUpdatedController.close(); |
136 _chartAxesUpdatedController = null; | 137 _chartAxesUpdatedController = null; |
137 } | 138 } |
| 139 if (_behaviors.isNotEmpty) { |
| 140 _behaviors.forEach((behavior) => behavior.dispose()); |
| 141 } |
138 } | 142 } |
139 | 143 |
140 static bool isNotInline(Element e) => | 144 static bool isNotInline(Element e) => |
141 e != null && e.getComputedStyle().display != 'inline'; | 145 e != null && e.getComputedStyle().display != 'inline'; |
142 | 146 |
143 /// Set new data for this chart. If [value] is [Observable], subscribes to | 147 /// Set new data for this chart. If [value] is [Observable], subscribes to |
144 /// changes and updates the chart when data changes. | 148 /// changes and updates the chart when data changes. |
145 @override | 149 @override |
146 set data(ChartData value) { | 150 set data(ChartData value) { |
147 _data = value; | 151 _data = value; |
(...skipping 11 matching lines...) Expand all Loading... |
159 @override | 163 @override |
160 ChartData get data => _data; | 164 ChartData get data => _data; |
161 | 165 |
162 /// Set new config for this chart. If [value] is [Observable], subscribes to | 166 /// Set new config for this chart. If [value] is [Observable], subscribes to |
163 /// changes and updates the chart when series or dimensions change. | 167 /// changes and updates the chart when series or dimensions change. |
164 @override | 168 @override |
165 set config(ChartConfig value) { | 169 set config(ChartConfig value) { |
166 _config = value; | 170 _config = value; |
167 _configEventsDisposer.dispose(); | 171 _configEventsDisposer.dispose(); |
168 _pendingLegendUpdate = true; | 172 _pendingLegendUpdate = true; |
| 173 _pendingAxisConfigUpdate = true; |
169 | 174 |
170 if (_config != null && _config is Observable) { | 175 if (_config != null && _config is Observable) { |
171 _configEventsDisposer.add((_config as Observable).changes.listen((_) { | 176 _configEventsDisposer.add((_config as Observable).changes.listen((_) { |
| 177 _pendingAxisConfigUpdate = true; |
172 _pendingLegendUpdate = true; | 178 _pendingLegendUpdate = true; |
173 draw(); | 179 draw(); |
174 })); | 180 })); |
175 } | 181 } |
176 } | 182 } |
177 | 183 |
178 @override | 184 @override |
179 ChartConfig get config => _config; | 185 ChartConfig get config => _config; |
180 | 186 |
181 @override | 187 @override |
(...skipping 11 matching lines...) Expand all Loading... |
193 /// Gets measure axis from cache - creates a new instance of _ChartAxis | 199 /// Gets measure axis from cache - creates a new instance of _ChartAxis |
194 /// if one was not already created for the given [axisId]. | 200 /// if one was not already created for the given [axisId]. |
195 DefaultChartAxisImpl _getMeasureAxis(String axisId) { | 201 DefaultChartAxisImpl _getMeasureAxis(String axisId) { |
196 _measureAxes.putIfAbsent(axisId, () { | 202 _measureAxes.putIfAbsent(axisId, () { |
197 var axisConf = config.getMeasureAxis(axisId), | 203 var axisConf = config.getMeasureAxis(axisId), |
198 axis = axisConf != null | 204 axis = axisConf != null |
199 ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf) | 205 ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf) |
200 : new DefaultChartAxisImpl(this); | 206 : new DefaultChartAxisImpl(this); |
201 return axis; | 207 return axis; |
202 }); | 208 }); |
| 209 |
203 return _measureAxes[axisId]; | 210 return _measureAxes[axisId]; |
204 } | 211 } |
205 | 212 |
206 /// Gets a dimension axis from cache - creates a new instance of _ChartAxis | 213 /// Gets a dimension axis from cache - creates a new instance of _ChartAxis |
207 /// if one was not already created for the given dimension [column]. | 214 /// if one was not already created for the given dimension [column]. |
208 DefaultChartAxisImpl _getDimensionAxis(int column) { | 215 DefaultChartAxisImpl _getDimensionAxis(int column) { |
209 _dimensionAxes.putIfAbsent(column, () { | 216 _dimensionAxes.putIfAbsent(column, () { |
210 var axisConf = config.getDimensionAxis(column), | 217 var axisConf = config.getDimensionAxis(column), |
211 axis = axisConf != null | 218 axis = axisConf != null |
212 ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf) | 219 ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf) |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 'translate(${layout.renderArea.x},${layout.renderArea.y})'; | 317 'translate(${layout.renderArea.x},${layout.renderArea.y})'; |
311 | 318 |
312 selection.each((ChartSeries s, _, Element group) { | 319 selection.each((ChartSeries s, _, Element group) { |
313 _ChartSeriesInfo info = _seriesInfoCache[s]; | 320 _ChartSeriesInfo info = _seriesInfoCache[s]; |
314 if (info == null) { | 321 if (info == null) { |
315 info = _seriesInfoCache[s] = new _ChartSeriesInfo(this, s); | 322 info = _seriesInfoCache[s] = new _ChartSeriesInfo(this, s); |
316 } | 323 } |
317 info.check(); | 324 info.check(); |
318 group.attributes['transform'] = transform; | 325 group.attributes['transform'] = transform; |
319 (s.renderer as CartesianRenderer) | 326 (s.renderer as CartesianRenderer) |
320 .draw(group, schedulePostRender: schedulePostRender); | 327 ?.draw(group, schedulePostRender: schedulePostRender); |
321 }); | 328 }); |
322 | 329 |
323 // A series that was rendered earlier isn't there anymore, remove it | 330 // A series that was rendered earlier isn't there anymore, remove it |
324 selection.exit | 331 selection.exit |
325 ..each((ChartSeries s, _, __) { | 332 ..each((ChartSeries s, _, __) { |
326 var info = _seriesInfoCache.remove(s); | 333 var info = _seriesInfoCache.remove(s); |
327 if (info != null) { | 334 if (info != null) { |
328 info.dispose(); | 335 info.dispose(); |
329 } | 336 } |
330 }) | 337 }) |
331 ..remove(); | 338 ..remove(); |
332 | 339 |
333 // Notify on the stream that the chart has been updated. | 340 // Notify on the stream that the chart has been updated. |
334 isReady = true; | 341 isReady = true; |
335 if (_chartAxesUpdatedController != null) { | 342 if (_chartAxesUpdatedController != null) { |
336 _chartAxesUpdatedController.add(this); | 343 _chartAxesUpdatedController.add(this); |
337 } | 344 } |
338 }); | 345 }); |
339 | 346 |
340 // Save the list of valid series and initialize axes. | 347 // Save the list of valid series and initialize axes. |
341 _series = series; | 348 _series = series; |
| 349 _updateAxisConfig(); |
342 _initAxes(preRender: preRender); | 350 _initAxes(preRender: preRender); |
343 | 351 |
344 // Render the chart, now that the axes layer is already in DOM. | 352 // Render the chart, now that the axes layer is already in DOM. |
345 axesDomainCompleter.complete(); | 353 axesDomainCompleter.complete(); |
346 | 354 |
347 // Updates the legend if required. | 355 // Updates the legend if required. |
348 _updateLegend(); | 356 _updateLegend(); |
349 } | 357 } |
350 | 358 |
351 String _orientRTL(String orientation) => orientation; | 359 String _orientRTL(String orientation) => orientation; |
352 | 360 |
353 /// Initialize the axes - required even if the axes are not being displayed. | 361 /// Initialize the axes - required even if the axes are not being displayed. |
354 _initAxes({bool preRender: false}) { | 362 _initAxes({bool preRender: false}) { |
355 Map measureAxisUsers = <String, Iterable<ChartSeries>>{}; | 363 Map measureAxisUsers = <String, Iterable<ChartSeries>>{}; |
| 364 var keysToRemove = _measureAxes.keys.toList(); |
356 | 365 |
357 // Create necessary measures axes. | 366 // Create necessary measures axes. |
358 // If measure axes were not configured on the series, default is used. | 367 // If measure axes were not configured on the series, default is used. |
359 _series.forEach((ChartSeries s) { | 368 _series.forEach((ChartSeries s) { |
360 var measureAxisIds = | 369 var measureAxisIds = |
361 isNullOrEmpty(s.measureAxisIds) ? MEASURE_AXIS_IDS : s.measureAxisIds; | 370 isNullOrEmpty(s.measureAxisIds) ? MEASURE_AXIS_IDS : s.measureAxisIds; |
362 measureAxisIds.forEach((axisId) { | 371 measureAxisIds.forEach((axisId) { |
| 372 if (keysToRemove.contains(axisId)) { |
| 373 keysToRemove.remove(axisId); |
| 374 } |
363 _getMeasureAxis(axisId); // Creates axis if required | 375 _getMeasureAxis(axisId); // Creates axis if required |
364 var users = measureAxisUsers[axisId]; | 376 var users = measureAxisUsers[axisId]; |
365 if (users == null) { | 377 if (users == null) { |
366 measureAxisUsers[axisId] = [s]; | 378 measureAxisUsers[axisId] = [s]; |
367 } else { | 379 } else { |
368 users.add(s); | 380 users.add(s); |
369 } | 381 } |
370 }); | 382 }); |
371 }); | 383 }); |
372 | 384 |
| 385 for (var key in keysToRemove) { |
| 386 _measureAxes.remove(key); |
| 387 } |
| 388 |
373 // Now that we know a list of series using each measure axis, configure | 389 // Now that we know a list of series using each measure axis, configure |
374 // the input domain of each axis. | 390 // the input domain of each axis. |
375 measureAxisUsers.forEach((id, listOfSeries) { | 391 measureAxisUsers.forEach((id, listOfSeries) { |
376 var sampleCol = listOfSeries.first.measures.first, | 392 var sampleCol = listOfSeries.first.measures.first, |
377 sampleColSpec = data.columns.elementAt(sampleCol), | 393 sampleColSpec = data.columns.elementAt(sampleCol), |
378 axis = _getMeasureAxis(id); | 394 axis = _getMeasureAxis(id); |
379 List domain; | 395 List domain; |
380 | 396 |
381 if (sampleColSpec.useOrdinalScale) { | 397 if (sampleColSpec.useOrdinalScale) { |
382 throw new UnsupportedError( | 398 throw new UnsupportedError( |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 index: i, | 588 index: i, |
573 label: data.columns.elementAt(i).label, | 589 label: data.columns.elementAt(i).label, |
574 series: s, | 590 series: s, |
575 color: theme.getColorForKey(i))); | 591 color: theme.getColorForKey(i))); |
576 }); | 592 }); |
577 | 593 |
578 _config.legend.update(legend, this); | 594 _config.legend.update(legend, this); |
579 _pendingLegendUpdate = false; | 595 _pendingLegendUpdate = false; |
580 } | 596 } |
581 | 597 |
| 598 // Updates the AxisConfig, if configuration chagned since the last time the |
| 599 // AxisConfig was updated. |
| 600 _updateAxisConfig() { |
| 601 if (!_pendingAxisConfigUpdate) return; |
| 602 _series.forEach((ChartSeries s) { |
| 603 var measureAxisIds = |
| 604 isNullOrEmpty(s.measureAxisIds) ? MEASURE_AXIS_IDS : s.measureAxisIds; |
| 605 measureAxisIds.forEach((axisId) { |
| 606 var axis = _getMeasureAxis(axisId); // Creates axis if required |
| 607 axis.config = config.getMeasureAxis(axisId); |
| 608 }); |
| 609 }); |
| 610 |
| 611 int dimensionAxesCount = useTwoDimensionAxes ? 2 : 1; |
| 612 config.dimensions.take(dimensionAxesCount).forEach((int column) { |
| 613 var axis = _getDimensionAxis(column); |
| 614 axis.config = config.getDimensionAxis(column); |
| 615 }); |
| 616 |
| 617 _pendingAxisConfigUpdate = false; |
| 618 } |
| 619 |
582 @override | 620 @override |
583 Stream<ChartEvent> get onMouseUp => | 621 Stream<ChartEvent> get onMouseUp => |
584 host.onMouseUp.map((MouseEvent e) => new DefaultChartEventImpl(e, this)); | 622 host.onMouseUp.map((MouseEvent e) => new DefaultChartEventImpl(e, this)); |
585 | 623 |
586 @override | 624 @override |
587 Stream<ChartEvent> get onMouseDown => host.onMouseDown | 625 Stream<ChartEvent> get onMouseDown => host.onMouseDown |
588 .map((MouseEvent e) => new DefaultChartEventImpl(e, this)); | 626 .map((MouseEvent e) => new DefaultChartEventImpl(e, this)); |
589 | 627 |
590 @override | 628 @override |
591 Stream<ChartEvent> get onMouseOver => host.onMouseOver | 629 Stream<ChartEvent> get onMouseOver => host.onMouseOver |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
733 } | 771 } |
734 } | 772 } |
735 _renderer = _series.renderer; | 773 _renderer = _series.renderer; |
736 } | 774 } |
737 | 775 |
738 dispose() { | 776 dispose() { |
739 _renderer?.dispose(); | 777 _renderer?.dispose(); |
740 _disposer.dispose(); | 778 _disposer.dispose(); |
741 } | 779 } |
742 } | 780 } |
OLD | NEW |