@@ -25,6 +25,7 @@ var styleOne = require('../../traces/pie/style_one');
2525
2626var legend = module . exports = { } ;
2727
28+ var constants = require ( './constants' ) ;
2829legend . layoutAttributes = require ( './attributes' ) ;
2930
3031legend . supplyLayoutDefaults = function ( layoutIn , layoutOut , fullData ) {
@@ -294,12 +295,18 @@ legend.texts = function(context, td, d, i, traces){
294295 text . enter ( ) . append ( 'text' ) . classed ( 'legendtext' , true ) ;
295296 text . attr ( {
296297 x : 40 ,
297- y : 0
298+ y : 0 ,
299+ 'data-unformatted' : name
300+ } )
301+ . style ( {
302+ 'text-anchor' : 'start' ,
303+ '-webkit-user-select' : 'none' ,
304+ '-moz-user-select' : 'none' ,
305+ '-ms-user-select' : 'none' ,
306+ 'user-select' : 'none'
298307 } )
299- . style ( 'text-anchor' , 'start' )
300308 . call ( Drawing . font , fullLayout . legend . font )
301- . text ( name )
302- . attr ( { 'data-unformatted' : name } ) ;
309+ . text ( name ) ;
303310
304311 function textLayout ( s ) {
305312 Plotly . util . convertToTspans ( s , function ( ) {
@@ -464,9 +471,11 @@ legend.draw = function(td) {
464471 var scrollBar = legendsvg . selectAll ( 'rect.scrollbar' )
465472 . data ( [ 0 ] ) ;
466473 scrollBar . enter ( ) . append ( 'rect' )
467- . attr ( 'class' , 'scrollbar' )
468- . attr ( 'rx' , 20 )
469- . attr ( 'ry' , 2 )
474+ . attr ( {
475+ 'class' : 'scrollbar' ,
476+ 'rx' : 20 ,
477+ 'ry' : 2
478+ } )
470479 . call ( Color . fill , '#808BA4' ) ;
471480
472481 var groups = scrollBox . selectAll ( 'g.groups' )
@@ -546,6 +555,78 @@ legend.draw = function(td) {
546555 // Position and size the legend
547556 legend . repositionLegend ( td , traces ) ;
548557
558+ // Scroll section must be executed after repositionLegend.
559+ // It requires the legend width, height, x and y to position the scrollbox
560+ // and these values are mutated in repositionLegend.
561+ var gs = fullLayout . _size ,
562+ lx = gs . l + gs . w * opts . x ,
563+ ly = gs . t + gs . h * ( 1 - opts . y ) ;
564+
565+ // Deal with scrolling
566+ var plotHeight = fullLayout . height - fullLayout . margin . b ,
567+ scrollPosition = scrollBox . attr ( 'viewBox' ) ? scrollBox . attr ( 'viewBox' ) . split ( ' ' ) [ 1 ] : 0 ,
568+ scrollheight = Math . min ( plotHeight - ly , opts . height ) ;
569+
570+ bg . style ( { width : opts . width , height : scrollheight } ) ;
571+ scrollBox . attr ( 'viewBox' , '0 ' + scrollPosition + ' ' + opts . width + ' ' + scrollheight ) ;
572+
573+ legendsvg . call ( Drawing . setRect , lx , ly , opts . width , scrollheight ) ;
574+
575+ if ( td . firstRender && opts . height - scrollheight > 0 && ! td . _context . staticPlot ) {
576+
577+ legendsvg . node ( ) . addEventListener ( 'wheel' , function ( e ) {
578+ e . preventDefault ( ) ;
579+ scrollHandler ( Math . round ( e . deltaY / 15 ) ) ;
580+ } ) ;
581+
582+ scrollBar . node ( ) . addEventListener ( 'mousedown' , function ( e ) {
583+ e . preventDefault ( ) ;
584+
585+ function mMove ( e ) {
586+ if ( e . buttons === 1 ) {
587+ scrollHandler ( e . movementY ) ;
588+ }
589+ }
590+
591+ function mUp ( ) {
592+ scrollBar . node ( ) . removeEventListener ( 'mousemove' , mMove ) ;
593+ window . removeEventListener ( 'mouseup' , mUp ) ;
594+ }
595+
596+ window . addEventListener ( 'mousemove' , mMove ) ;
597+ window . addEventListener ( 'mouseup' , mUp ) ;
598+ } ) ;
599+
600+ // Move scrollbar to starting position on the first render
601+ scrollBar . call (
602+ Drawing . setRect ,
603+ opts . width - ( constants . scrollBarWidth + constants . scrollBarMargin ) ,
604+ constants . scrollBarMargin ,
605+ constants . scrollBarWidth ,
606+ constants . scrollBarHeight
607+ ) ;
608+ }
609+
610+ function scrollHandler ( delta ) {
611+
612+ // Scale movement to simulate native scroll performance
613+ var viewBox = scrollBox . attr ( 'viewBox' ) . split ( ' ' ) ,
614+ scrollBarTrack = scrollheight - constants . scrollBarHeight - 2 * constants . scrollBarMargin ,
615+ scrollBoxY = Lib . constrain ( + viewBox [ 1 ] + delta , 0 , Math . max ( opts . height - scrollheight , 0 ) ) ,
616+ scrollBarY = scrollBoxY / ( opts . height - scrollheight ) * scrollBarTrack + constants . scrollBarMargin ;
617+
618+ viewBox [ 1 ] = scrollBoxY ;
619+
620+ scrollBox . attr ( 'viewBox' , viewBox . join ( ' ' ) ) ;
621+ scrollBar . call (
622+ Drawing . setRect ,
623+ opts . width - ( constants . scrollBarWidth + constants . scrollBarMargin ) ,
624+ scrollBarY ,
625+ constants . scrollBarWidth ,
626+ constants . scrollBarHeight
627+ ) ;
628+ }
629+
549630 if ( td . _context . editable ) {
550631 var xf ,
551632 yf ,
@@ -579,7 +660,7 @@ legend.draw = function(td) {
579660 } ,
580661 doneFn : function ( dragged ) {
581662 Fx . setCursor ( legendsvg ) ;
582- if ( dragged && xf !== undefined && yf !== undefined ) {
663+ if ( dragged && xf !== undefined && yf !== undefined ) {
583664 Plotly . relayout ( td , { 'legend.x' : xf , 'legend.y' : yf } ) ;
584665 }
585666 }
@@ -591,12 +672,10 @@ legend.repositionLegend = function(td, traces){
591672 var fullLayout = td . _fullLayout ,
592673 gs = fullLayout . _size ,
593674 opts = fullLayout . legend ,
594- borderwidth = opts . borderwidth ,
675+ borderwidth = opts . borderwidth ;
595676
596- // add the legend elements, keeping track of the
597- // legend size (in px) as we go
598- legendwidth = 0 ,
599- legendheight = 0 ;
677+ opts . width = 0 ,
678+ opts . height = 0 ,
600679
601680 traces . each ( function ( d ) {
602681 var trace = d [ 0 ] . trace ,
@@ -605,7 +684,7 @@ legend.repositionLegend = function(td, traces){
605684 text = g . selectAll ( '.legendtext' ) ,
606685 tspans = g . selectAll ( '.legendtext>tspan' ) ,
607686 tHeight = opts . font . size * 1.3 ,
608- tLines = tspans [ 0 ] . length || 1 ,
687+ tLines = tspans [ 0 ] . length || 1 ,
609688 tWidth = text . node ( ) && Drawing . bBox ( text . node ( ) ) . width ,
610689 mathjaxGroup = g . select ( 'g[class*=math-group]' ) ,
611690 textY ,
@@ -620,12 +699,12 @@ legend.repositionLegend = function(td, traces){
620699 var mathjaxBB = Drawing . bBox ( mathjaxGroup . node ( ) ) ;
621700 tHeight = mathjaxBB . height ;
622701 tWidth = mathjaxBB . width ;
623- mathjaxGroup . attr ( 'transform' , 'translate(0,' + ( tHeight / 4 ) + ')' ) ;
702+ mathjaxGroup . attr ( 'transform' , 'translate(0,' + ( tHeight / 4 ) + ')' ) ;
624703 }
625704 else {
626705 // approximation to height offset to center the font
627706 // to avoid getBoundingClientRect
628- textY = tHeight * ( 0.3 + ( 1 - tLines ) / 2 ) ;
707+ textY = tHeight * ( 0.3 + ( 1 - tLines ) / 2 ) ;
629708 text . attr ( 'y' , textY ) ;
630709 tspans . attr ( 'y' , textY ) ;
631710 }
@@ -634,22 +713,23 @@ legend.repositionLegend = function(td, traces){
634713
635714 g . attr ( 'transform' ,
636715 'translate(' + borderwidth + ',' +
637- ( 5 + borderwidth + legendheight + tHeightFull / 2 ) +
716+ ( 5 + borderwidth + opts . height + tHeightFull / 2 ) +
638717 ')'
639718 ) ;
640719 bg . attr ( { x : 0 , y : - tHeightFull / 2 , height : tHeightFull } ) ;
641720
642- legendheight += tHeightFull ;
643- legendwidth = Math . max ( legendwidth , tWidth || 0 ) ;
721+ opts . height += tHeightFull ;
722+ opts . width = Math . max ( opts . width , tWidth || 0 ) ;
644723 } ) ;
645724
646- if ( isGrouped ( opts ) ) legendheight += ( opts . _lgroupsLength - 1 ) * opts . tracegroupgap ;
647725
648- traces . selectAll ( '.legendtoggle' )
649- . attr ( 'width' , ( td . _context . editable ? 0 : legendwidth ) + 40 ) ;
726+ opts . width += 45 + borderwidth * 2 ;
727+ opts . height += 10 + borderwidth * 2 ;
728+
729+ if ( isGrouped ( opts ) ) opts . height += ( opts . _lgroupsLength - 1 ) * opts . tracegroupgap ;
650730
651- legendwidth += 45 + borderwidth * 2 ;
652- legendheight += 10 + borderwidth * 2 ;
731+ traces . selectAll ( '.legendtoggle' )
732+ . attr ( 'width' , ( td . _context . editable ? 0 : opts . width ) + 40 ) ;
653733
654734 // now position the legend. for both x,y the positions are recorded as
655735 // fractions of the plot area (left, bottom = 0,0). Outside the plot
@@ -661,83 +741,38 @@ legend.repositionLegend = function(td, traces){
661741 ly = gs . t + gs . h * ( 1 - opts . y ) ;
662742
663743 var xanchor = 'left' ;
664- if ( opts . xanchor === 'right' || ( opts . xanchor === 'auto' && opts . x >= 2 / 3 ) ) {
665- lx -= legendwidth ;
744+ if ( opts . xanchor === 'right' || ( opts . xanchor === 'auto' && opts . x >= 2 / 3 ) ) {
745+ lx -= opts . width ;
666746 xanchor = 'right' ;
667747 }
668- else if ( opts . xanchor === 'center' || ( opts . xanchor === 'auto' && opts . x > 1 / 3 ) ) {
669- lx -= legendwidth / 2 ;
748+ else if ( opts . xanchor === 'center' || ( opts . xanchor === 'auto' && opts . x > 1 / 3 ) ) {
749+ lx -= opts . width / 2 ;
670750 xanchor = 'center' ;
671751 }
672752
673753 var yanchor = 'top' ;
674- if ( opts . yanchor === 'bottom' || ( opts . yanchor === 'auto' && opts . y <= 1 / 3 ) ) {
675- ly -= legendheight ;
754+ if ( opts . yanchor === 'bottom' || ( opts . yanchor === 'auto' && opts . y <= 1 / 3 ) ) {
755+ ly -= opts . width ;
676756 yanchor = 'bottom' ;
677757 }
678- else if ( opts . yanchor === 'middle' || ( opts . yanchor === 'auto' && opts . y < 2 / 3 ) ) {
679- ly -= legendheight / 2 ;
758+ else if ( opts . yanchor === 'middle' || ( opts . yanchor === 'auto' && opts . y < 2 / 3 ) ) {
759+ ly -= opts . height / 2 ;
680760 yanchor = 'middle' ;
681761 }
682762
683763 // make sure we're only getting full pixels
684- legendwidth = Math . ceil ( legendwidth ) ;
685- legendheight = Math . ceil ( legendheight ) ;
764+ opts . width = Math . ceil ( opts . width ) ;
765+ opts . height = Math . ceil ( opts . height ) ;
686766 lx = Math . round ( lx ) ;
687767 ly = Math . round ( ly ) ;
688768
689-
690- var legendsvg = fullLayout . _infolayer . selectAll ( 'svg.legend' ) ,
691- scrollBox = fullLayout . _infolayer . selectAll ( 'svg.legend .scrollbox' ) ,
692- scrollBar = fullLayout . _infolayer . selectAll ( 'svg.legend .scrollbar' ) ,
693- bg = fullLayout . _infolayer . selectAll ( 'svg.legend .bg' ) ;
694-
695- var plotHeight = fullLayout . height - fullLayout . margin . t - fullLayout . margin . b ,
696- scrollheight = Math . min ( plotHeight - ly , legendheight ) ,
697- scrollPosition = scrollBox . attr ( 'viewBox' ) ? scrollBox . attr ( 'viewBox' ) . split ( ' ' ) [ 1 ] : 0 ;
698-
699- legendsvg . node ( ) . addEventListener ( 'wheel' , scrollHandler ) ;
700- legendsvg . call ( Drawing . setRect , lx , ly , legendwidth , scrollheight ) ;
701-
702- bg . style ( { width : legendwidth , height : scrollheight } ) ;
703-
704- scrollBox . attr ( 'viewBox' , '0 ' + scrollPosition + ' ' + legendwidth + ' ' + scrollheight ) ;
705-
706- if ( td . firstRender ) scrollBar . call ( Drawing . setRect , legendwidth - 6 , 10 , 4 , 20 ) ;
707-
708- function scrollHandler ( e ) {
709- e . preventDefault ( ) ;
710-
711- // Scale movement to simulate native scroll performance
712- var scrollDiff = e . deltaY / 25 ,
713- viewBox = scrollBox . attr ( 'viewBox' ) . split ( ' ' ) ;
714-
715- var scrollBoxY = constrain ( 0 , Math . max ( legendheight - scrollheight , 0 ) , + viewBox [ 1 ] + scrollDiff ) ,
716- scrollBarY = scrollBoxY / legendheight * ( scrollheight ) + 10 ;
717-
718- viewBox [ 1 ] = scrollBoxY ;
719-
720- scrollBox . attr ( 'viewBox' , viewBox . join ( ' ' ) ) ;
721- scrollBar . call ( Drawing . setRect , legendwidth - 6 , scrollBarY , 4 , 20 ) ;
722- }
723-
724- function constrain ( min , max , c ) {
725- if ( c <= max && c >= min ) {
726- return c ;
727- } else if ( c > max ) {
728- return max ;
729- } else {
730- return min ;
731- }
732- }
733-
734769 // lastly check if the margin auto-expand has changed
735770 Plots . autoMargin ( td , 'legend' , {
736771 x : opts . x ,
737772 y : opts . y ,
738- l : legendwidth * ( { right :1 , center :0.5 } [ xanchor ] || 0 ) ,
739- r : legendwidth * ( { left :1 , center :0.5 } [ xanchor ] || 0 ) ,
740- b : legendheight * ( { top :1 , middle :0.5 } [ yanchor ] || 0 ) ,
741- t : legendheight * ( { bottom :1 , middle :0.5 } [ yanchor ] || 0 )
773+ l : opts . width * ( { right :1 , center :0.5 } [ xanchor ] || 0 ) ,
774+ r : opts . width * ( { left :1 , center :0.5 } [ xanchor ] || 0 ) ,
775+ b : opts . height * ( { top :1 , middle :0.5 } [ yanchor ] || 0 ) ,
776+ t : opts . height * ( { bottom :1 , middle :0.5 } [ yanchor ] || 0 )
742777 } ) ;
743778} ;
0 commit comments