99'use strict' ;
1010
1111var Axes = require ( '../../plots/cartesian/axes' ) ;
12+ var Lib = require ( '../../lib' ) ;
1213var Fx = require ( '../../components/fx' ) ;
1314var Color = require ( '../../components/color' ) ;
1415var fillHoverText = require ( '../scatter/fill_hover_text' ) ;
@@ -18,32 +19,44 @@ var DIRSYMBOL = {
1819 decreasing : '▼'
1920} ;
2021
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 ) {
2234 var cd = pointData . cd ;
2335 var xa = pointData . xa ;
24- var ya = pointData . ya ;
2536 var trace = cd [ 0 ] . trace ;
2637 var t = cd [ 0 ] . t ;
2738
2839 var type = trace . type ;
2940 var minAttr = type === 'ohlc' ? 'l' : 'min' ;
3041 var maxAttr = type === 'ohlc' ? 'h' : 'max' ;
3142
43+ var hoverPseudoDistance , spikePseudoDistance ;
44+
3245 // potentially shift xval for grouped candlesticks
3346 var centerShift = t . bPos || 0 ;
34- var x0 = xval - centerShift ;
47+ var shiftPos = function ( di ) { return di . pos + centerShift - xval ; } ;
3548
3649 // ohlc and candlestick call displayHalfWidth different things...
3750 var displayHalfWidth = t . bdPos || t . tickLen ;
3851 var hoverHalfWidth = t . wHover ;
3952
40- // if two items are overlaying, let the narrowest one win
53+ // if two figures are overlaying, let the narrowest one win
4154 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 ;
4457
4558 function dx ( di ) {
46- var pos = di . pos - x0 ;
59+ var pos = shiftPos ( di ) ;
4760 return Fx . inbox ( pos - hoverHalfWidth , pos + hoverHalfWidth , hoverPseudoDistance ) ;
4861 }
4962
@@ -52,18 +65,13 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
5265 }
5366
5467 function dxy ( di ) { return ( dx ( di ) + dy ( di ) ) / 2 ; }
68+
5569 var distfn = Fx . getDistanceFunction ( hovermode , dx , dy , dxy ) ;
5670 Fx . getClosest ( cd , distfn , pointData ) ;
5771
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 ;
6673
74+ var di = cd [ pointData . index ] ;
6775 var dir = di . dir ;
6876 var container = trace [ dir ] ;
6977 var lc = container . line . color ;
@@ -79,6 +87,81 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
7987 pointData . spikeDistance = dxy ( di ) * spikePseudoDistance / hoverPseudoDistance ;
8088 pointData . xSpike = xa . c2p ( di . pos , true ) ;
8189
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+
82165 function getLabelLine ( attr ) {
83166 return t . labels [ attr ] + Axes . hoverLabelText ( ya , trace [ attr ] [ i ] ) ;
84167 }
@@ -99,11 +182,17 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
99182
100183 // don't make .yLabelVal or .text, since we're managing hoverinfo
101184 // put it all in .extraText
102- pointData . extraText = textParts . join ( '<br>' ) ;
185+ closestPoint . extraText = textParts . join ( '<br>' ) ;
103186
104187 // this puts the label *and the spike* at the midpoint of the box, ie
105188 // 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+ }
107193
108- return [ pointData ] ;
194+ module . exports = {
195+ hoverPoints : hoverPoints ,
196+ hoverSplit : hoverSplit ,
197+ hoverOnPoints : hoverOnPoints
109198} ;
0 commit comments