@@ -23,16 +23,15 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
2323 var hoveron = trace . hoveron ;
2424 var marker = trace . marker || { } ;
2525
26- // output hover points array
27- var closeData = [ ] ;
26+ // output hover points components
27+ var closeBoxData = [ ] ;
28+ var closePtData ;
2829 // x/y/effective distance functions
2930 var dx , dy , distfn ;
3031 // orientation-specific fields
3132 var posLetter , valLetter , posAxis , valAxis ;
3233 // calcdata item
3334 var di ;
34- // hover point item extended from pointData
35- var pointData2 ;
3635 // loop indices
3736 var i , j ;
3837
@@ -105,7 +104,8 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
105104
106105 // copy out to a new object for each value to label
107106 var val = valAxis . c2p ( di [ attr ] , true ) ;
108- pointData2 = Lib . extendFlat ( { } , pointData ) ;
107+ var pointData2 = Lib . extendFlat ( { } , pointData ) ;
108+
109109 pointData2 [ valLetter + '0' ] = pointData2 [ valLetter + '1' ] = val ;
110110 pointData2 [ valLetter + 'LabelVal' ] = di [ attr ] ;
111111 pointData2 . attr = attr ;
@@ -116,7 +116,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
116116 // only keep name on the first item (median)
117117 pointData . name = '' ;
118118
119- closeData . push ( pointData2 ) ;
119+ closeBoxData . push ( pointData2 ) ;
120120 }
121121 }
122122 }
@@ -125,58 +125,71 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
125125 var xPx = xa . c2p ( xval ) ;
126126 var yPx = ya . c2p ( yval ) ;
127127
128- // do not take jitter into consideration in compare hover modes
129- var kx , ky ;
130- if ( hovermode === 'closest' ) {
131- kx = 'x' ;
132- ky = 'y' ;
133- } else {
134- kx = 'xh' ;
135- ky = 'yh' ;
136- }
137-
138128 dx = function ( di ) {
139129 var rad = Math . max ( 3 , di . mrc || 0 ) ;
140- return Math . max ( Math . abs ( xa . c2p ( di [ kx ] ) - xPx ) - rad , 1 - 3 / rad ) ;
130+ return Math . max ( Math . abs ( xa . c2p ( di . x ) - xPx ) - rad , 1 - 3 / rad ) ;
141131 } ;
142132 dy = function ( di ) {
143133 var rad = Math . max ( 3 , di . mrc || 0 ) ;
144- return Math . max ( Math . abs ( ya . c2p ( di [ ky ] ) - yPx ) - rad , 1 - 3 / rad ) ;
134+ return Math . max ( Math . abs ( ya . c2p ( di . y ) - yPx ) - rad , 1 - 3 / rad ) ;
145135 } ;
146- distfn = Fx . getDistanceFunction ( hovermode , dx , dy ) ;
136+ distfn = Fx . quadrature ( dx , dy ) ;
137+
138+ // show one point per trace
139+ var ijClosest = false ;
140+ var pt ;
147141
148142 for ( i = 0 ; i < cd . length ; i ++ ) {
149143 di = cd [ i ] ;
150144
151145 for ( j = 0 ; j < ( di . pts || [ ] ) . length ; j ++ ) {
152- var pt = di . pts [ j ] ;
146+ pt = di . pts [ j ] ;
153147
154148 var newDistance = distfn ( pt ) ;
155149 if ( newDistance <= pointData . distance ) {
156150 pointData . distance = newDistance ;
157-
158- var xc = xa . c2p ( pt . x , true ) ;
159- var yc = ya . c2p ( pt . y , true ) ;
160- var rad = pt . mrc || 1 ;
161-
162- pointData2 = Lib . extendFlat ( { } , pointData , {
163- // corresponds to index in x/y input data array
164- index : pt . i ,
165- color : marker . color ,
166- x0 : xc - rad ,
167- x1 : xc + rad ,
168- xLabelVal : pt . x ,
169- y0 : yc - rad ,
170- y1 : yc + rad ,
171- yLabelVal : pt . y
172- } ) ;
173-
174- fillHoverText ( pt , trace , pointData2 ) ;
175- closeData . push ( pointData2 ) ;
151+ ijClosest = [ i , j ] ;
176152 }
177153 }
178154 }
155+
156+ if ( ijClosest ) {
157+ di = cd [ ijClosest [ 0 ] ] ;
158+ pt = di . pts [ ijClosest [ 1 ] ] ;
159+
160+ var xc = xa . c2p ( pt . x , true ) ;
161+ var yc = ya . c2p ( pt . y , true ) ;
162+ var rad = pt . mrc || 1 ;
163+
164+ closePtData = Lib . extendFlat ( { } , pointData , {
165+ // corresponds to index in x/y input data array
166+ index : pt . i ,
167+ color : marker . color ,
168+ name : trace . name ,
169+ x0 : xc - rad ,
170+ x1 : xc + rad ,
171+ xLabelVal : pt . x ,
172+ y0 : yc - rad ,
173+ y1 : yc + rad ,
174+ yLabelVal : pt . y
175+ } ) ;
176+ fillHoverText ( pt , trace , closePtData ) ;
177+ }
179178 }
180179
181- return closeData ;
180+ // In closest mode, show only one point or stats for one box, and points have priority
181+ // If there's a point in range and hoveron has points, show the best single point only.
182+ // If hoveron has boxes and there's no point in range (or hoveron doesn't have points), show the box stats.
183+ if ( hovermode === 'closest' ) {
184+ if ( closePtData ) return [ closePtData ] ;
185+ return closeBoxData ;
186+ }
187+
188+ // Otherwise in compare mode, allow a point AND the box stats to be labeled
189+ // If there are multiple boxes in range (ie boxmode = 'overlay') we'll see stats for all of them.
190+ if ( closePtData ) {
191+ closeBoxData . push ( closePtData ) ;
192+ return closeBoxData ;
193+ }
194+ return closeBoxData ;
182195} ;
0 commit comments