@@ -101,35 +101,8 @@ module.exports = function draw(gd) {
101101 var lx = gs . l + gs . w * opts . x - FROM_TL [ getXanchor ( opts ) ] * opts . _width ;
102102 var ly = gs . t + gs . h * ( 1 - opts . y ) - FROM_TL [ getYanchor ( opts ) ] * opts . _effHeight ;
103103
104-
105- // Make sure the legend left and right sides are visible
106- var legendWidth = opts . _width ;
107- var legendWidthMax = gs . w ;
108-
109- if ( legendWidth > legendWidthMax ) {
110- lx = gs . l ;
111- legendWidth = legendWidthMax ;
112- } else {
113- if ( lx + legendWidth > fullLayout . width ) lx = fullLayout . width - legendWidth ;
114- if ( lx < 0 ) lx = 0 ;
115- legendWidth = Math . min ( fullLayout . width - lx , opts . _width ) ;
116- }
117-
118-
119- // Make sure the legend top and bottom are visible
120- // (legends with a scroll bar are not allowed to stretch beyond the extended
121- // margins)
122- var legendHeight = opts . _height ;
123- var legendHeightMax = gs . h ;
124-
125- if ( legendHeight > legendHeightMax ) {
126- ly = gs . t ;
127- legendHeight = legendHeightMax ;
128- } else {
129- if ( ly + legendHeight > fullLayout . height ) ly = fullLayout . height - legendHeight ;
130- if ( ly < 0 ) ly = 0 ;
131- legendHeight = Math . min ( fullLayout . height - ly , opts . _height ) ;
132- }
104+ lx = Lib . constrain ( lx , 0 , fullLayout . width - opts . _width ) ;
105+ ly = Lib . constrain ( ly , 0 , fullLayout . height - opts . _effHeight ) ;
133106
134107 // Set size and position of all the elements that make up a legend:
135108 // legend, background and border, scroll box and scroll bar
@@ -139,20 +112,20 @@ module.exports = function draw(gd) {
139112 scrollBar . on ( '.drag' , null ) ;
140113 legend . on ( 'wheel' , null ) ;
141114
142- if ( opts . _height <= legendHeight || gd . _context . staticPlot ) {
115+ if ( opts . _height <= opts . _maxHeight || gd . _context . staticPlot ) {
143116 // if scrollbar should not be shown.
144117 bg . attr ( {
145- width : legendWidth - bw ,
146- height : legendHeight - bw ,
118+ width : opts . _width - bw ,
119+ height : opts . _effHeight - bw ,
147120 x : bw / 2 ,
148121 y : bw / 2
149122 } ) ;
150123
151124 Drawing . setTranslate ( scrollBox , 0 , 0 ) ;
152125
153126 clipPath . select ( 'rect' ) . attr ( {
154- width : legendWidth - 2 * bw ,
155- height : legendHeight - 2 * bw ,
127+ width : opts . _width - 2 * bw ,
128+ height : opts . _effHeight - 2 * bw ,
156129 x : bw ,
157130 y : bw
158131 } ) ;
@@ -163,33 +136,33 @@ module.exports = function draw(gd) {
163136 delete opts . _scrollY ;
164137 } else {
165138 var scrollBarHeight = Math . max ( constants . scrollBarMinHeight ,
166- legendHeight * legendHeight / opts . _height ) ;
167- var scrollBarYMax = legendHeight -
139+ opts . _effHeight * opts . _effHeight / opts . _height ) ;
140+ var scrollBarYMax = opts . _effHeight -
168141 scrollBarHeight -
169142 2 * constants . scrollBarMargin ;
170- var scrollBoxYMax = opts . _height - legendHeight ;
143+ var scrollBoxYMax = opts . _height - opts . _effHeight ;
171144 var scrollRatio = scrollBarYMax / scrollBoxYMax ;
172145
173146 var scrollBoxY = Math . min ( opts . _scrollY || 0 , scrollBoxYMax ) ;
174147
175148 // increase the background and clip-path width
176149 // by the scrollbar width and margin
177150 bg . attr ( {
178- width : legendWidth -
151+ width : opts . _width -
179152 2 * bw +
180153 constants . scrollBarWidth +
181154 constants . scrollBarMargin ,
182- height : legendHeight - bw ,
155+ height : opts . _effHeight - bw ,
183156 x : bw / 2 ,
184157 y : bw / 2
185158 } ) ;
186159
187160 clipPath . select ( 'rect' ) . attr ( {
188- width : legendWidth -
161+ width : opts . _width -
189162 2 * bw +
190163 constants . scrollBarWidth +
191164 constants . scrollBarMargin ,
192- height : legendHeight - 2 * bw ,
165+ height : opts . _effHeight - 2 * bw ,
193166 x : bw ,
194167 y : bw + scrollBoxY
195168 } ) ;
@@ -235,7 +208,7 @@ module.exports = function draw(gd) {
235208
236209 Drawing . setRect (
237210 scrollBar ,
238- legendWidth ,
211+ opts . _width ,
239212 constants . scrollBarMargin + scrollBoxY * scrollRatio ,
240213 constants . scrollBarWidth ,
241214 scrollBarHeight
@@ -474,9 +447,20 @@ function computeTextDimensions(g, gd) {
474447 legendItem . width = width ;
475448}
476449
450+ /*
451+ * Computes in fullLayout.legend:
452+ *
453+ * - _height: legend height including items past scrollbox height
454+ * - _maxHeight: maximum legend height before scrollbox is required
455+ * - _effHeight: legend height w/ or w/o scrollbox
456+ *
457+ * - _width: legend width
458+ * - _maxWidth (for orientation:h only): maximum width before starting new row
459+ */
477460function computeLegendDimensions ( gd , groups , traces ) {
478461 var fullLayout = gd . _fullLayout ;
479462 var opts = fullLayout . legend ;
463+ var gs = fullLayout . _size ;
480464 var isVertical = helpers . isVertical ( opts ) ;
481465 var isGrouped = helpers . isGrouped ( opts ) ;
482466
@@ -486,7 +470,16 @@ function computeLegendDimensions(gd, groups, traces) {
486470 var itemGap = constants . itemGap ;
487471 var endPad = 2 * ( bw + itemGap ) ;
488472
489- opts . _maxWidth = fullLayout . _size . w ;
473+ var yanchor = getYanchor ( opts ) ;
474+ var isBelowPlotArea = opts . y < 0 || ( opts . y === 0 && yanchor === 'top' ) ;
475+ var isAbovePlotArea = opts . y > 1 || ( opts . y === 1 && yanchor === 'bottom' ) ;
476+
477+ // - if below/above plot area, give it the maximum potential margin-push value
478+ // - otherwise, extend the height of the plot area
479+ opts . _maxHeight = Math . max (
480+ ( isBelowPlotArea || isAbovePlotArea ) ? fullLayout . height / 2 : gs . h ,
481+ 30
482+ ) ;
490483
491484 var toggleRectWidth = 0 ;
492485 opts . _width = 0 ;
@@ -511,6 +504,20 @@ function computeLegendDimensions(gd, groups, traces) {
511504 opts . _height += ( opts . _lgroupsLength - 1 ) * opts . tracegroupgap ;
512505 }
513506 } else {
507+ var xanchor = getXanchor ( opts ) ;
508+ var isLeftOfPlotArea = opts . x < 0 || ( opts . x === 0 && xanchor === 'right' ) ;
509+ var isRightOfPlotArea = opts . x > 1 || ( opts . x === 1 && xanchor === 'left' ) ;
510+ var isBeyondPlotAreaX = isAbovePlotArea || isBelowPlotArea ;
511+ var hw = fullLayout . width / 2 ;
512+
513+ // - if placed within x-margins, extend the width of the plot area
514+ // - else if below/above plot area and anchored in the margin, extend to opposite margin,
515+ // - otherwise give it the maximum potential margin-push value
516+ opts . _maxWidth = Math . max (
517+ isLeftOfPlotArea ? ( ( isBeyondPlotAreaX && xanchor === 'left' ) ? gs . l + gs . w : hw ) :
518+ isRightOfPlotArea ? ( ( isBeyondPlotAreaX && yanchor === 'right' ) ? gs . r + gs . w : hw ) :
519+ gs . w ,
520+ 2 * textGap ) ;
514521 var maxItemWidth = 0 ;
515522 var combinedItemWidth = 0 ;
516523 traces . each ( function ( d ) {
@@ -604,6 +611,8 @@ function computeLegendDimensions(gd, groups, traces) {
604611 opts . _width = Math . ceil ( opts . _width ) ;
605612 opts . _height = Math . ceil ( opts . _height ) ;
606613
614+ opts . _effHeight = Math . min ( opts . _height , opts . _maxHeight ) ;
615+
607616 var edits = gd . _context . edits ;
608617 var isEditable = edits . legendText || edits . legendPosition ;
609618 traces . each ( function ( d ) {
@@ -627,8 +636,8 @@ function expandMargin(gd) {
627636 y : opts . y ,
628637 l : opts . _width * ( FROM_TL [ xanchor ] ) ,
629638 r : opts . _width * ( FROM_BR [ xanchor ] ) ,
630- b : opts . _height * ( FROM_BR [ yanchor ] ) ,
631- t : opts . _height * ( FROM_TL [ yanchor ] )
639+ b : opts . _effHeight * ( FROM_BR [ yanchor ] ) ,
640+ t : opts . _effHeight * ( FROM_TL [ yanchor ] )
632641 } ) ;
633642}
634643
0 commit comments