9
9
'use strict' ;
10
10
11
11
var Axes = require ( '../../plots/cartesian/axes' ) ;
12
+ var Lib = require ( '../../lib' ) ;
12
13
var Fx = require ( '../../components/fx' ) ;
13
14
var Color = require ( '../../components/color' ) ;
14
15
var fillHoverText = require ( '../scatter/fill_hover_text' ) ;
@@ -18,32 +19,44 @@ var DIRSYMBOL = {
18
19
decreasing : '▼'
19
20
} ;
20
21
21
- module . exports = function hoverPoints ( pointData , xval , yval , hovermode ) {
22
+ function hoverPoints ( pointData , xval , yval , hovermode ) {
23
+ var cd = pointData . cd ;
24
+ var trace = cd [ 0 ] . trace ;
25
+
26
+ if ( trace . hoverlabel . split ) {
27
+ return hoverSplit ( pointData , xval , yval , hovermode ) ;
28
+ }
29
+
30
+ return hoverOnPoints ( pointData , xval , yval , hovermode ) ;
31
+ }
32
+
33
+ function getClosestPoint ( pointData , xval , yval , hovermode ) {
22
34
var cd = pointData . cd ;
23
35
var xa = pointData . xa ;
24
- var ya = pointData . ya ;
25
36
var trace = cd [ 0 ] . trace ;
26
37
var t = cd [ 0 ] . t ;
27
38
28
39
var type = trace . type ;
29
40
var minAttr = type === 'ohlc' ? 'l' : 'min' ;
30
41
var maxAttr = type === 'ohlc' ? 'h' : 'max' ;
31
42
43
+ var hoverPseudoDistance , spikePseudoDistance ;
44
+
32
45
// potentially shift xval for grouped candlesticks
33
46
var centerShift = t . bPos || 0 ;
34
- var x0 = xval - centerShift ;
47
+ var shiftPos = function ( di ) { return di . pos + centerShift - xval ; } ;
35
48
36
49
// ohlc and candlestick call displayHalfWidth different things...
37
50
var displayHalfWidth = t . bdPos || t . tickLen ;
38
51
var hoverHalfWidth = t . wHover ;
39
52
40
- // if two items are overlaying, let the narrowest one win
53
+ // if two figures are overlaying, let the narrowest one win
41
54
var pseudoDistance = Math . min ( 1 , displayHalfWidth / Math . abs ( xa . r2c ( xa . range [ 1 ] ) - xa . r2c ( xa . range [ 0 ] ) ) ) ;
42
- var hoverPseudoDistance = pointData . maxHoverDistance - pseudoDistance ;
43
- var spikePseudoDistance = pointData . maxSpikeDistance - pseudoDistance ;
55
+ hoverPseudoDistance = pointData . maxHoverDistance - pseudoDistance ;
56
+ spikePseudoDistance = pointData . maxSpikeDistance - pseudoDistance ;
44
57
45
58
function dx ( di ) {
46
- var pos = di . pos - x0 ;
59
+ var pos = shiftPos ( di ) ;
47
60
return Fx . inbox ( pos - hoverHalfWidth , pos + hoverHalfWidth , hoverPseudoDistance ) ;
48
61
}
49
62
@@ -52,18 +65,13 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
52
65
}
53
66
54
67
function dxy ( di ) { return ( dx ( di ) + dy ( di ) ) / 2 ; }
68
+
55
69
var distfn = Fx . getDistanceFunction ( hovermode , dx , dy , dxy ) ;
56
70
Fx . getClosest ( cd , distfn , pointData ) ;
57
71
58
- // skip the rest (for this trace) if we didn't find a close point
59
- if ( pointData . index === false ) return [ ] ;
60
-
61
- // we don't make a calcdata point if we're missing any piece (x/o/h/l/c)
62
- // so we need to fix the index here to point to the data arrays
63
- var cdIndex = pointData . index ;
64
- var di = cd [ cdIndex ] ;
65
- var i = pointData . index = di . i ;
72
+ if ( pointData . index === false ) return null ;
66
73
74
+ var di = cd [ pointData . index ] ;
67
75
var dir = di . dir ;
68
76
var container = trace [ dir ] ;
69
77
var lc = container . line . color ;
@@ -79,6 +87,81 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
79
87
pointData . spikeDistance = dxy ( di ) * spikePseudoDistance / hoverPseudoDistance ;
80
88
pointData . xSpike = xa . c2p ( di . pos , true ) ;
81
89
90
+ return pointData ;
91
+ }
92
+
93
+ function hoverSplit ( pointData , xval , yval , hovermode ) {
94
+ var cd = pointData . cd ;
95
+ var ya = pointData . ya ;
96
+ var trace = cd [ 0 ] . trace ;
97
+ var t = cd [ 0 ] . t ;
98
+ var closeBoxData = [ ] ;
99
+
100
+ var closestPoint = getClosestPoint ( pointData , xval , yval , hovermode ) ;
101
+ // skip the rest (for this trace) if we didn't find a close point
102
+ if ( ! closestPoint ) return [ ] ;
103
+
104
+ var hoverinfo = trace . hoverinfo ;
105
+ var hoverParts = hoverinfo . split ( '+' ) ;
106
+ var isAll = hoverinfo === 'all' ;
107
+ var hasY = isAll || hoverParts . indexOf ( 'y' ) !== - 1 ;
108
+
109
+ // similar to hoverOnPoints, we return nothing
110
+ // if all or y is not present.
111
+ if ( ! hasY ) return [ ] ;
112
+
113
+ var attrs = [ 'high' , 'open' , 'close' , 'low' ] ;
114
+
115
+ // several attributes can have the same y-coordinate. We will
116
+ // bunch them together in a single text block. For this, we keep
117
+ // a dictionary mapping y-coord -> point data.
118
+ var usedVals = { } ;
119
+
120
+ for ( var i = 0 ; i < attrs . length ; i ++ ) {
121
+ var attr = attrs [ i ] ;
122
+
123
+ var val = trace [ attr ] [ closestPoint . index ] ;
124
+ var valPx = ya . c2p ( val , true ) ;
125
+ var pointData2 ;
126
+ if ( val in usedVals ) {
127
+ pointData2 = usedVals [ val ] ;
128
+ pointData2 . yLabel += '<br>' + t . labels [ attr ] + Axes . hoverLabelText ( ya , val ) ;
129
+ }
130
+ else {
131
+ // copy out to a new object for each new y-value to label
132
+ pointData2 = Lib . extendFlat ( { } , closestPoint ) ;
133
+
134
+ pointData2 . y0 = pointData2 . y1 = valPx ;
135
+ pointData2 . yLabelVal = val ;
136
+ pointData2 . yLabel = t . labels [ attr ] + Axes . hoverLabelText ( ya , val ) ;
137
+
138
+ pointData2 . name = '' ;
139
+
140
+ closeBoxData . push ( pointData2 ) ;
141
+ usedVals [ val ] = pointData2 ;
142
+ }
143
+ }
144
+
145
+ return closeBoxData ;
146
+ }
147
+
148
+ function hoverOnPoints ( pointData , xval , yval , hovermode ) {
149
+ var cd = pointData . cd ;
150
+ var ya = pointData . ya ;
151
+ var trace = cd [ 0 ] . trace ;
152
+ var t = cd [ 0 ] . t ;
153
+
154
+ var closestPoint = getClosestPoint ( pointData , xval , yval , hovermode ) ;
155
+ // skip the rest (for this trace) if we didn't find a close point
156
+ if ( ! closestPoint ) return [ ] ;
157
+
158
+ // we don't make a calcdata point if we're missing any piece (x/o/h/l/c)
159
+ // so we need to fix the index here to point to the data arrays
160
+ var cdIndex = closestPoint . index ;
161
+ var di = cd [ cdIndex ] ;
162
+ var i = closestPoint . index = di . i ;
163
+ var dir = di . dir ;
164
+
82
165
function getLabelLine ( attr ) {
83
166
return t . labels [ attr ] + Axes . hoverLabelText ( ya , trace [ attr ] [ i ] ) ;
84
167
}
@@ -99,11 +182,17 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
99
182
100
183
// don't make .yLabelVal or .text, since we're managing hoverinfo
101
184
// put it all in .extraText
102
- pointData . extraText = textParts . join ( '<br>' ) ;
185
+ closestPoint . extraText = textParts . join ( '<br>' ) ;
103
186
104
187
// this puts the label *and the spike* at the midpoint of the box, ie
105
188
// halfway between open and close, not between high and low.
106
- pointData . y0 = pointData . y1 = ya . c2p ( di . yc , true ) ;
189
+ closestPoint . y0 = closestPoint . y1 = ya . c2p ( di . yc , true ) ;
190
+
191
+ return [ closestPoint ] ;
192
+ }
107
193
108
- return [ pointData ] ;
194
+ module . exports = {
195
+ hoverPoints : hoverPoints ,
196
+ hoverSplit : hoverSplit ,
197
+ hoverOnPoints : hoverOnPoints
109
198
} ;
0 commit comments