@@ -772,59 +772,138 @@ function createHoverText(hoverData, opts, gd) {
772772 var commonBgColor = commonLabelOpts . bgcolor || Color . defaultLine ;
773773 var commonStroke = commonLabelOpts . bordercolor || Color . contrast ( commonBgColor ) ;
774774 var contrastColor = Color . contrast ( commonBgColor ) ;
775+ var commonLabelFont = {
776+ family : commonLabelOpts . font . family || fontFamily ,
777+ size : commonLabelOpts . font . size || fontSize ,
778+ color : commonLabelOpts . font . color || contrastColor
779+ } ;
775780
776781 lpath . style ( {
777782 fill : commonBgColor ,
778783 stroke : commonStroke
779784 } ) ;
780785
781786 ltext . text ( t0 )
782- . call ( Drawing . font ,
783- commonLabelOpts . font . family || fontFamily ,
784- commonLabelOpts . font . size || fontSize ,
785- commonLabelOpts . font . color || contrastColor
786- )
787+ . call ( Drawing . font , commonLabelFont )
787788 . call ( svgTextUtils . positionText , 0 , 0 )
788789 . call ( svgTextUtils . convertToTspans , gd ) ;
789790
790791 label . attr ( 'transform' , '' ) ;
791792
792793 var tbb = ltext . node ( ) . getBoundingClientRect ( ) ;
794+ var lx , ly ;
795+
793796 if ( hovermode === 'x' ) {
797+ var topsign = xa . side === 'top' ? '-' : '' ;
798+
794799 ltext . attr ( 'text-anchor' , 'middle' )
795800 . call ( svgTextUtils . positionText , 0 , ( xa . side === 'top' ?
796801 ( outerTop - tbb . bottom - HOVERARROWSIZE - HOVERTEXTPAD ) :
797802 ( outerTop - tbb . top + HOVERARROWSIZE + HOVERTEXTPAD ) ) ) ;
798803
799- var topsign = xa . side === 'top' ? '-' : '' ;
800- lpath . attr ( 'd' , 'M0,0' +
801- 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE +
802- 'H' + ( HOVERTEXTPAD + tbb . width / 2 ) +
803- 'v' + topsign + ( HOVERTEXTPAD * 2 + tbb . height ) +
804- 'H-' + ( HOVERTEXTPAD + tbb . width / 2 ) +
805- 'V' + topsign + HOVERARROWSIZE + 'H-' + HOVERARROWSIZE + 'Z' ) ;
806-
807- label . attr ( 'transform' , 'translate(' +
808- ( xa . _offset + ( c0 . x0 + c0 . x1 ) / 2 ) + ',' +
809- ( ya . _offset + ( xa . side === 'top' ? 0 : ya . _length ) ) + ')' ) ;
804+ lx = xa . _offset + ( c0 . x0 + c0 . x1 ) / 2 ;
805+ ly = ya . _offset + ( xa . side === 'top' ? 0 : ya . _length ) ;
806+
807+ var halfWidth = tbb . width / 2 + HOVERTEXTPAD ;
808+
809+ if ( lx < halfWidth ) {
810+ lx = halfWidth ;
811+
812+ lpath . attr ( 'd' , 'M-' + ( halfWidth - HOVERARROWSIZE ) + ',0' +
813+ 'L-' + ( halfWidth - HOVERARROWSIZE * 2 ) + ',' + topsign + HOVERARROWSIZE +
814+ 'H' + ( HOVERTEXTPAD + tbb . width / 2 ) +
815+ 'v' + topsign + ( HOVERTEXTPAD * 2 + tbb . height ) +
816+ 'H-' + halfWidth +
817+ 'V' + topsign + HOVERARROWSIZE +
818+ 'Z' ) ;
819+ } else if ( lx > ( fullLayout . width - halfWidth ) ) {
820+ lx = fullLayout . width - halfWidth ;
821+
822+ lpath . attr ( 'd' , 'M' + ( halfWidth - HOVERARROWSIZE ) + ',0' +
823+ 'L' + halfWidth + ',' + topsign + HOVERARROWSIZE +
824+ 'v' + topsign + ( HOVERTEXTPAD * 2 + tbb . height ) +
825+ 'H-' + halfWidth +
826+ 'V' + topsign + HOVERARROWSIZE +
827+ 'H' + ( halfWidth - HOVERARROWSIZE * 2 ) + 'Z' ) ;
828+ } else {
829+ lpath . attr ( 'd' , 'M0,0' +
830+ 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE +
831+ 'H' + ( HOVERTEXTPAD + tbb . width / 2 ) +
832+ 'v' + topsign + ( HOVERTEXTPAD * 2 + tbb . height ) +
833+ 'H-' + ( HOVERTEXTPAD + tbb . width / 2 ) +
834+ 'V' + topsign + HOVERARROWSIZE +
835+ 'H-' + HOVERARROWSIZE + 'Z' ) ;
836+ }
810837 } else {
811- ltext . attr ( 'text-anchor' , ya . side === 'right' ? 'start' : 'end' )
812- . call ( svgTextUtils . positionText ,
813- ( ya . side === 'right' ? 1 : - 1 ) * ( HOVERTEXTPAD + HOVERARROWSIZE ) ,
814- outerTop - tbb . top - tbb . height / 2 ) ;
838+ var anchor ;
839+ var sgn ;
840+ var leftsign ;
841+ if ( ya . side === 'right' ) {
842+ anchor = 'start' ;
843+ sgn = 1 ;
844+ leftsign = '' ;
845+ lx = xa . _offset + xa . _length ;
846+ } else {
847+ anchor = 'end' ;
848+ sgn = - 1 ;
849+ leftsign = '-' ;
850+ lx = xa . _offset ;
851+ }
852+
853+ ly = ya . _offset + ( c0 . y0 + c0 . y1 ) / 2 ;
854+
855+ ltext . attr ( 'text-anchor' , anchor ) ;
815856
816- var leftsign = ya . side === 'right' ? '' : '-' ;
817857 lpath . attr ( 'd' , 'M0,0' +
818858 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE +
819859 'V' + ( HOVERTEXTPAD + tbb . height / 2 ) +
820860 'h' + leftsign + ( HOVERTEXTPAD * 2 + tbb . width ) +
821861 'V-' + ( HOVERTEXTPAD + tbb . height / 2 ) +
822862 'H' + leftsign + HOVERARROWSIZE + 'V-' + HOVERARROWSIZE + 'Z' ) ;
823863
824- label . attr ( 'transform' , 'translate(' +
825- ( xa . _offset + ( ya . side === 'right' ? xa . _length : 0 ) ) + ',' +
826- ( ya . _offset + ( c0 . y0 + c0 . y1 ) / 2 ) + ')' ) ;
864+ var halfHeight = tbb . height / 2 ;
865+ var lty = outerTop - tbb . top - halfHeight ;
866+ var clipId = 'clip' + fullLayout . _uid + 'commonlabel' + ya . _id ;
867+ var clipPath ;
868+
869+ if ( lx < ( tbb . width + 2 * HOVERTEXTPAD + HOVERARROWSIZE ) ) {
870+ clipPath = 'M-' + ( HOVERARROWSIZE + HOVERTEXTPAD ) + '-' + halfHeight +
871+ 'h-' + ( tbb . width - HOVERTEXTPAD ) +
872+ 'V' + halfHeight +
873+ 'h' + ( tbb . width - HOVERTEXTPAD ) + 'Z' ;
874+
875+ var ltx = tbb . width - lx + HOVERTEXTPAD ;
876+ svgTextUtils . positionText ( ltext , ltx , lty ) ;
877+
878+ // shift each line (except the longest) so that start-of-line
879+ // is always visible
880+ if ( anchor === 'end' ) {
881+ ltext . selectAll ( 'tspan' ) . each ( function ( ) {
882+ var s = d3 . select ( this ) ;
883+ var dummy = Drawing . tester . append ( 'text' )
884+ . text ( s . text ( ) )
885+ . call ( Drawing . font , commonLabelFont ) ;
886+ var dummyBB = dummy . node ( ) . getBoundingClientRect ( ) ;
887+ if ( dummyBB . width < tbb . width ) {
888+ s . attr ( 'x' , ltx - dummyBB . width ) ;
889+ }
890+ dummy . remove ( ) ;
891+ } ) ;
892+ }
893+ } else {
894+ svgTextUtils . positionText ( ltext , sgn * ( HOVERTEXTPAD + HOVERARROWSIZE ) , lty ) ;
895+ clipPath = null ;
896+ }
897+
898+ var textClip = fullLayout . _topclips . selectAll ( '#' + clipId ) . data ( clipPath ? [ 0 ] : [ ] ) ;
899+ textClip . enter ( ) . append ( 'clipPath' ) . attr ( 'id' , clipId ) . append ( 'path' ) ;
900+ textClip . exit ( ) . remove ( ) ;
901+ textClip . select ( 'path' ) . attr ( 'd' , clipPath ) ;
902+ Drawing . setClipUrl ( ltext , clipPath ? clipId : null , gd ) ;
827903 }
904+
905+ label . attr ( 'transform' , 'translate(' + lx + ',' + ly + ')' ) ;
906+
828907 // remove the "close but not quite" points
829908 // because of error bars, only take up to a space
830909 hoverData = hoverData . filter ( function ( d ) {
0 commit comments