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 /// Displays tooltip for the values as user moves the mouse pointer over | |
12 /// values in the chart. It displays all the active values in the data row | |
13 /// and use the value in the dimension as the title. | |
14 @Deprecated('Use Hovercard') | |
15 class ChartTooltip implements ChartBehavior { | |
16 static const _TOOLTIP_OFFSET = 10; | |
17 final String orientation; | |
18 final bool showDimensionValue; | |
19 final bool showMeasureTotal; | |
20 final bool showSelectedMeasure; | |
21 | |
22 ChartArea _area; | |
23 ChartState _state; | |
24 Selection _tooltipRoot; | |
25 SubscriptionsDisposer _disposer = new SubscriptionsDisposer(); | |
26 | |
27 /// Constructs the tooltip. | |
28 /// If [showDimensionValue] is set, displays the dimension value as title. | |
29 /// If [showMeasureTotal] is set, displays the total value. | |
30 ChartTooltip({ | |
31 this.showSelectedMeasure: false, | |
32 this.showDimensionValue: false, | |
33 this.showMeasureTotal: false, | |
34 this.orientation: ORIENTATION_RIGHT}); | |
35 | |
36 /// Sets up listeners for triggering tooltip. | |
37 void init(ChartArea area, Selection _, Selection __) { | |
38 _area = area; | |
39 _state = area.state; | |
40 _disposer.addAll([ | |
41 area.onValueMouseOver.listen(show), | |
42 area.onValueMouseOut.listen(hide) | |
43 ]); | |
44 | |
45 // Tooltip requires host to be position: relative. | |
46 area.host.style.position = 'relative'; | |
47 | |
48 var _scope = new SelectionScope.element(_area.host); | |
49 _scope.append('div')..classed('tooltip'); | |
50 _tooltipRoot = _scope.select('.tooltip'); | |
51 } | |
52 | |
53 void dispose() { | |
54 _disposer.dispose(); | |
55 if (_tooltipRoot != null) _tooltipRoot.remove(); | |
56 } | |
57 | |
58 /// Displays tooltip upon receiving a hover event on an element in chart. | |
59 show(ChartEvent e) { | |
60 _tooltipRoot.first | |
61 ..children.clear() | |
62 ..attributes['dir'] = _area.config.isRTL ? 'rtl' : ''; | |
63 _tooltipRoot.classed('rtl', _area.config.isRTL); | |
64 | |
65 // Display dimension value if set in config. | |
66 if (showDimensionValue) { | |
67 var column = _area.config.dimensions.elementAt(0), | |
68 value = _area.data.rows.elementAt(e.row).elementAt(column), | |
69 formatter = _getFormatterForColumn(column); | |
70 | |
71 _tooltipRoot.append('div') | |
72 ..classed('tooltip-title') | |
73 ..text((formatter != null) ? formatter(value) : value.toString()); | |
74 } | |
75 | |
76 // Display sum of the values in active row if set in config. | |
77 if (showMeasureTotal) { | |
78 var measures = e.series.measures, | |
79 formatter = _getFormatterForColumn(measures.elementAt(0)), | |
80 row = _area.data.rows.elementAt(e.row), | |
81 total = 0; | |
82 for (int i = 0, len = measures.length; i < len; i++) { | |
83 total += row.elementAt(measures.elementAt(i)); | |
84 } | |
85 _tooltipRoot.append('div') | |
86 ..classed('tooltip-total') | |
87 ..text((formatter != null) ? formatter(total) : total.toString()); | |
88 } | |
89 | |
90 // Find the currently selectedMeasures and hoveredMeasures and show | |
91 // tooltip for them, if none is selected/hovered, show all. | |
92 var activeMeasures = []; | |
93 if (showSelectedMeasure) { | |
94 activeMeasures.addAll(_state.selection); | |
95 activeMeasures.add(_state.preview != null ? _state.preview : | |
96 _state.hovered.first); | |
97 if (activeMeasures.isEmpty) { | |
98 for (var series in _area.config.series) { | |
99 activeMeasures.addAll(series.measures); | |
100 } | |
101 } | |
102 activeMeasures.sort(); | |
103 } | |
104 | |
105 var data = (showSelectedMeasure) ? activeMeasures : e.series.measures; | |
106 | |
107 // Create the tooltip items base on the number of measures in the series. | |
108 var items = _tooltipRoot.selectAll('.tooltip-item'). | |
109 data(data); | |
110 items.enter.append('div') | |
111 ..classed('tooltip-item') | |
112 ..classedWithCallback('active', (d, i, c) => | |
113 !showSelectedMeasure && (d == e.column)); | |
114 | |
115 // Display the label for the currently active series. | |
116 var tooltipItems = _tooltipRoot.selectAll('.tooltip-item'); | |
117 tooltipItems.append('div') | |
118 ..classed('tooltip-item-label') | |
119 ..textWithCallback((d, i, c) => _area.data.columns. | |
120 elementAt((showSelectedMeasure) ? d : | |
121 e.series.measures.elementAt(i)).label); | |
122 | |
123 // Display the value of the currently active series | |
124 tooltipItems.append('div') | |
125 ..classed('tooltip-item-value') | |
126 ..styleWithCallback('color', (d, i, c) => | |
127 _area.theme.getColorForKey(d)) | |
128 ..textWithCallback((d, i, c) { | |
129 var formatter = _getFormatterForColumn(d), | |
130 value = _area.data.rows.elementAt(e.row).elementAt(d); | |
131 return (formatter != null) ? formatter(value) : value.toString(); | |
132 }); | |
133 | |
134 math.Point position = computeTooltipPosition( | |
135 new math.Point(e.chartX, e.chartY), | |
136 _tooltipRoot.first.getBoundingClientRect()); | |
137 | |
138 // Set position of the tooltip and display it. | |
139 _tooltipRoot | |
140 ..style('left', '${position.x}px') | |
141 ..style('top', '${position.y}px') | |
142 ..style('opacity', '1'); | |
143 } | |
144 | |
145 static String switchPositionDirection(String direction) => | |
146 direction == ORIENTATION_LEFT | |
147 ? ORIENTATION_RIGHT | |
148 : ORIENTATION_LEFT; | |
149 | |
150 /// Computes the ideal tooltip position based on orientation. | |
151 math.Point computeTooltipPosition( | |
152 math.Point coord, math.Rectangle rect) { | |
153 var x, y, direction; | |
154 direction = _area.config.isRTL && _area.config.switchAxesForRTL | |
155 ? switchPositionDirection(orientation) | |
156 : orientation; | |
157 | |
158 if (direction == ORIENTATION_LEFT) { | |
159 x = coord.x - rect.width - _TOOLTIP_OFFSET; | |
160 y = coord.y + _TOOLTIP_OFFSET; | |
161 } else { | |
162 x = coord.x + _TOOLTIP_OFFSET; | |
163 y = coord.y + _TOOLTIP_OFFSET; | |
164 } | |
165 return boundTooltipPosition( | |
166 new math.Rectangle(x, y, rect.width, rect.height)); | |
167 } | |
168 | |
169 /// Positions the tooltip to be inside of the chart boundary. | |
170 math.Point boundTooltipPosition(math.Rectangle rect) { | |
171 var hostRect = _area.host.getBoundingClientRect(); | |
172 | |
173 var top = rect.top; | |
174 var left = rect.left; | |
175 | |
176 // Checks top and bottom. | |
177 if (rect.top < 0) { | |
178 top += (2 * _TOOLTIP_OFFSET); | |
179 } else if (rect.top + rect.height > hostRect.height) { | |
180 top -= (rect.height + 2 * _TOOLTIP_OFFSET); | |
181 } | |
182 | |
183 // Checks left and right. | |
184 if (rect.left < 0) { | |
185 left += (rect.width + 2 * _TOOLTIP_OFFSET); | |
186 } else if (rect.left + rect.width > hostRect.width) { | |
187 left -= (rect.width + 2 * _TOOLTIP_OFFSET); | |
188 } | |
189 | |
190 return new math.Point(left, top); | |
191 } | |
192 | |
193 FormatFunction _getFormatterForColumn(int column) => | |
194 _area.data.columns.elementAt(column).formatter; | |
195 | |
196 hide(ChartEvent e) { | |
197 if (_tooltipRoot == null) return; | |
198 _tooltipRoot.style('opacity', '0'); | |
199 } | |
200 } | |
201 | |
OLD | NEW |