@@ -25,6 +25,8 @@ var Registry = require('../../registry');
25
25
var helpers = require ( './helpers' ) ;
26
26
var constants = require ( './constants' ) ;
27
27
28
+ var legend = require ( '../legend' ) ;
29
+
28
30
// hover labels for multiple horizontal bars get tilted by some angle,
29
31
// then need to be offset differently if they overlap
30
32
var YANGLE = constants . YANGLE ;
@@ -244,7 +246,7 @@ function _hover(gd, evt, subplot, noHoverEvent) {
244
246
245
247
if ( hovermode && ! supportsCompare ) hovermode = 'closest' ;
246
248
247
- if ( [ 'x' , 'y' , 'closest' ] . indexOf ( hovermode ) === - 1 || ! gd . calcdata ||
249
+ if ( [ 'x' , 'y' , 'closest' , 'x unified' , 'y unified' ] . indexOf ( hovermode ) === - 1 || ! gd . calcdata ||
248
250
gd . querySelector ( '.zoombox' ) || gd . _dragging ) {
249
251
return dragElement . unhoverRaw ( gd , evt ) ;
250
252
}
@@ -388,6 +390,9 @@ function _hover(gd, evt, subplot, noHoverEvent) {
388
390
389
391
// within one trace mode can sometimes be overridden
390
392
mode = hovermode ;
393
+ if ( [ 'x unified' , 'y unified' ] . indexOf ( mode ) !== - 1 ) {
394
+ mode = mode . charAt ( 0 ) ;
395
+ }
391
396
392
397
// container for new point, also used to pass info into module.hoverPoints
393
398
pointData = {
@@ -543,7 +548,7 @@ function _hover(gd, evt, subplot, noHoverEvent) {
543
548
var thisSpikeDistance ;
544
549
for ( var i = 0 ; i < pointsData . length ; i ++ ) {
545
550
thisSpikeDistance = pointsData [ i ] . spikeDistance ;
546
- if ( thisSpikeDistance < minDistance && thisSpikeDistance <= spikedistance ) {
551
+ if ( thisSpikeDistance <= minDistance && thisSpikeDistance <= spikedistance ) {
547
552
resultPoint = pointsData [ i ] ;
548
553
minDistance = thisSpikeDistance ;
549
554
}
@@ -661,9 +666,10 @@ function _hover(gd, evt, subplot, noHoverEvent) {
661
666
662
667
var hoverLabels = createHoverText ( hoverData , labelOpts , gd ) ;
663
668
664
- hoverAvoidOverlaps ( hoverLabels , rotateLabels ? 'xa' : 'ya' , fullLayout ) ;
665
-
666
- alignHoverText ( hoverLabels , rotateLabels ) ;
669
+ if ( [ 'x unified' , 'y unified' ] . indexOf ( hovermode ) === - 1 ) {
670
+ hoverAvoidOverlaps ( hoverLabels , rotateLabels ? 'xa' : 'ya' , fullLayout ) ;
671
+ alignHoverText ( hoverLabels , rotateLabels ) ;
672
+ }
667
673
668
674
// TODO: tagName hack is needed to appease geo.js's hack of using evt.target=true
669
675
// we should improve the "fx" API so other plots can use it without these hack.
@@ -712,7 +718,7 @@ function createHoverText(hoverData, opts, gd) {
712
718
var c0 = hoverData [ 0 ] ;
713
719
var xa = c0 . xa ;
714
720
var ya = c0 . ya ;
715
- var commonAttr = hovermode === 'y' ? 'yLabel' : 'xLabel' ;
721
+ var commonAttr = hovermode . charAt ( 0 ) === 'y' ? 'yLabel' : 'xLabel' ;
716
722
var t0 = c0 [ commonAttr ] ;
717
723
var t00 = ( String ( t0 ) || '' ) . split ( ' ' ) [ 0 ] ;
718
724
var outerContainerBB = outerContainer . node ( ) . getBoundingClientRect ( ) ;
@@ -906,11 +912,87 @@ function createHoverText(hoverData, opts, gd) {
906
912
907
913
// remove the "close but not quite" points
908
914
// because of error bars, only take up to a space
909
- hoverData = hoverData . filter ( function ( d ) {
915
+ hoverData = filterClosePoints ( hoverData ) ;
916
+ } ) ;
917
+
918
+ function filterClosePoints ( hoverData ) {
919
+ return hoverData . filter ( function ( d ) {
910
920
return ( d . zLabelVal !== undefined ) ||
911
921
( d [ commonAttr ] || '' ) . split ( ' ' ) [ 0 ] === t00 ;
912
922
} ) ;
913
- } ) ;
923
+ }
924
+
925
+ // Show a single hover label
926
+ if ( [ 'x unified' , 'y unified' ] . indexOf ( hovermode ) !== - 1 ) {
927
+ // Delete leftover hover labels from other hovermodes
928
+ container . selectAll ( 'g.hovertext' ) . remove ( ) ;
929
+
930
+ // similarly to compare mode, we remove the "close but not quite together" points
931
+ hoverData = filterClosePoints ( hoverData ) ;
932
+
933
+ // mock legend
934
+ var mockLayoutIn = {
935
+ showlegend : true ,
936
+ legend : {
937
+ title : { text : t0 } ,
938
+ bgcolor : fullLayout . paper_bgcolor ,
939
+ borderwidth : 1 ,
940
+ tracegroupgap : 7
941
+ }
942
+ } ;
943
+ var mockLayoutOut = { } ;
944
+ legend . supplyLayoutDefaults ( mockLayoutIn , mockLayoutOut , gd . _fullData , fullLayout ) ;
945
+ var legendOpts = mockLayoutOut . legend ;
946
+
947
+ // prepare items for the legend
948
+ legendOpts . entries = [ ] ;
949
+ for ( var j = 0 ; j < hoverData . length ; j ++ ) {
950
+ var texts = getHoverLabelText ( hoverData [ j ] , true , hovermode , fullLayout , t0 ) ;
951
+ var text = texts [ 0 ] ;
952
+ var name = texts [ 1 ] ;
953
+ hoverData [ j ] . text = name + ' : ' + text ;
954
+ hoverData [ j ] . name = name ;
955
+ legendOpts . entries . push ( [ hoverData [ j ] ] ) ;
956
+ }
957
+ legendOpts . layer = container ;
958
+
959
+ // Draw unified hover label
960
+ legend . draw ( gd , legendOpts ) ;
961
+
962
+ // Position the hover
963
+ var ly = Lib . mean ( hoverData . map ( function ( c ) { return ( c . y0 + c . y1 ) / 2 ; } ) ) ;
964
+ var lx = Lib . mean ( hoverData . map ( function ( c ) { return ( c . x0 + c . x1 ) / 2 ; } ) ) ;
965
+ var legendContainer = container . select ( 'g.legend' ) ;
966
+ var tbb = legendContainer . node ( ) . getBoundingClientRect ( ) ;
967
+ lx += xa . _offset ;
968
+ ly += ya . _offset - tbb . height / 2 ;
969
+
970
+ // Change horizontal alignment to end up on screen
971
+ var txWidth = tbb . width + 2 * HOVERTEXTPAD ;
972
+ var anchorStartOK = lx + txWidth <= outerWidth ;
973
+ var anchorEndOK = lx - txWidth >= 0 ;
974
+ if ( ! anchorStartOK && anchorEndOK ) {
975
+ lx -= txWidth ;
976
+ } else {
977
+ lx += 2 * HOVERTEXTPAD ;
978
+ }
979
+
980
+ // Change vertical alignement to end up on screen
981
+ var txHeight = tbb . height + 2 * HOVERTEXTPAD ;
982
+ var overflowTop = ly <= outerTop ;
983
+ var overflowBottom = ly + txHeight >= outerHeight ;
984
+ var canFit = txHeight <= outerHeight ;
985
+ if ( canFit ) {
986
+ if ( overflowTop ) {
987
+ ly = tbb . top - outerTop + 2 * HOVERTEXTPAD ;
988
+ } else if ( overflowBottom ) {
989
+ ly = outerHeight - txHeight ;
990
+ }
991
+ }
992
+ legendContainer . attr ( 'transform' , 'translate(' + lx + ',' + ly + ')' ) ;
993
+
994
+ return legendContainer ;
995
+ }
914
996
915
997
// show all the individual labels
916
998
0 commit comments