@@ -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