diff --git a/draftlogs/7072-fix.md b/draftlogs/7072-fix.md new file mode 100644 index 00000000000..f363f589e40 --- /dev/null +++ b/draftlogs/7072-fix.md @@ -0,0 +1,2 @@ + - Fix maximum dimensions for legend that is anchored to container [[#7072](https://github.com/plotly/plotly.js/pull/7072)], + with thanks to @attatrol for the contribution! \ No newline at end of file diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 6a8106bbcc1..ec162b20630 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -763,19 +763,39 @@ function computeLegendDimensions(gd, groups, traces, legendObj) { var endPad = 2 * (bw + itemGap); var yanchor = getYanchor(legendObj); - var isBelowPlotArea = legendObj.y < 0 || (legendObj.y === 0 && yanchor === 'top'); - var isAbovePlotArea = legendObj.y > 1 || (legendObj.y === 1 && yanchor === 'bottom'); + let isBelowPlotArea, isAbovePlotArea; var traceGroupGap = legendObj.tracegroupgap; var legendGroupWidths = {}; - const { orientation, yref } = legendObj; + const { orientation, yref, y } = legendObj; let { maxheight } = legendObj; - const useFullLayoutHeight = isBelowPlotArea || isAbovePlotArea || orientation !== "v" || yref !== "paper" + + if (yref === 'paper') { + isBelowPlotArea = y < 0 || (y === 0 && yanchor === 'top'); + isAbovePlotArea = y > 1 || (y === 1 && yanchor === 'bottom'); + } else { + const yPixels = legendObj.y * fullLayout.height; + isBelowPlotArea = yPixels < gs.b || (yPixels === gs.b && yanchor === 'top'); + isAbovePlotArea = yPixels > gs.b + gs.h || (yPixels === gs.b + gs.h && yanchor === 'bottom'); + } + + const useFullLayoutHeight = isBelowPlotArea || isAbovePlotArea || orientation !== "v" || yref !== "paper"; // Set default maxheight here since it depends on values passed in by user maxheight ||= useFullLayoutHeight ? 0.5 : 1; const heightToBeScaled = useFullLayoutHeight ? fullLayout.height : gs.h; - legendObj._maxHeight = Math.max(maxheight > 1 ? maxheight : maxheight * heightToBeScaled, 30); + maxheight = maxheight > 1 ? maxheight : maxheight * heightToBeScaled; + + if (yref === 'container') { + const maxAvailableHeight = yanchor === 'top' + ? y * fullLayout.height + : yanchor === 'bottom' + ? (1 - y) * fullLayout.height + : 2 * Math.min(1 - y, y) * fullLayout.height; // yanchor is 'middle' or 'auto' + maxheight = Math.min(maxheight, maxAvailableHeight); + } + + legendObj._maxHeight = Math.max(maxheight, 30); var toggleRectWidth = 0; legendObj._width = 0; @@ -805,19 +825,30 @@ function computeLegendDimensions(gd, groups, traces, legendObj) { } } else { var xanchor = getXanchor(legendObj); - var isLeftOfPlotArea = legendObj.x < 0 || (legendObj.x === 0 && xanchor === 'right'); - var isRightOfPlotArea = legendObj.x > 1 || (legendObj.x === 1 && xanchor === 'left'); - var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea; - var hw = fullLayout.width / 2; - - // - if placed within x-margins, extend the width of the plot area - // - else if below/above plot area and anchored in the margin, extend to opposite margin, - // - otherwise give it the maximum potential margin-push value - legendObj._maxWidth = Math.max( - isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) : - isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) : - gs.w, - 2 * textGap); + if(legendObj.xref === 'paper') { + var isLeftOfPlotArea = legendObj.x < 0 || (legendObj.x === 0 && xanchor === 'right'); + var isRightOfPlotArea = legendObj.x > 1 || (legendObj.x === 1 && xanchor === 'left'); + var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea; + var hw = fullLayout.width / 2; + + // - if placed within x-margins, extend the width of the plot area + // - else if below/above plot area and anchored in the margin, extend to opposite margin, + // - otherwise give it the maximum potential margin-push value + legendObj._maxWidth = Math.max( + isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) : + isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) : + gs.w, + 2 * textGap); + } else { + if(xanchor === 'right') + legendObj._maxWidth = legendObj.x * fullLayout.width; + else if(xanchor === 'left') + legendObj._maxWidth = (1 - legendObj.x) * fullLayout.width; + else // if (xanchor === 'center') + legendObj._maxWidth = 2 * Math.min(1 - legendObj.x, legendObj.x) * fullLayout.width; + legendObj._maxWidth = Math.max(legendObj._maxWidth, 2 * textGap); + } + var maxItemWidth = 0; var combinedItemWidth = 0; traces.each(function(d) { diff --git a/test/image/baselines/legend_overflow_height.png b/test/image/baselines/legend_overflow_height.png new file mode 100644 index 00000000000..d3e85c9313f Binary files /dev/null and b/test/image/baselines/legend_overflow_height.png differ diff --git a/test/image/baselines/sunburst_coffee.png b/test/image/baselines/sunburst_coffee.png index 523744ad90d..590edb08ba9 100644 Binary files a/test/image/baselines/sunburst_coffee.png and b/test/image/baselines/sunburst_coffee.png differ diff --git a/test/image/baselines/treemap_sunburst_marker_colors.png b/test/image/baselines/treemap_sunburst_marker_colors.png index beebd418791..209427c1b2a 100644 Binary files a/test/image/baselines/treemap_sunburst_marker_colors.png and b/test/image/baselines/treemap_sunburst_marker_colors.png differ diff --git a/test/image/baselines/treemap_textfit.png b/test/image/baselines/treemap_textfit.png index b9b0a41090d..c5822e62271 100644 Binary files a/test/image/baselines/treemap_textfit.png and b/test/image/baselines/treemap_textfit.png differ diff --git a/test/image/mocks/legend_overflow_height.json b/test/image/mocks/legend_overflow_height.json new file mode 100644 index 00000000000..579e17c24a2 --- /dev/null +++ b/test/image/mocks/legend_overflow_height.json @@ -0,0 +1,145 @@ +{ + "data": [ + { + "x": [1, 2, 3], + "y": [40, 50, 60], + "name": "long trace name #0", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [41, 51, 61], + "name": "long trace name #1", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [42, 52, 62], + "name": "long trace name #2", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [43, 53, 63], + "name": "long trace name #3", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [44, 54, 64], + "name": "long trace name #4", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [45, 55, 65], + "name": "long trace name #5", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [46, 56, 66], + "name": "long trace name #6", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [47, 57, 67], + "name": "long trace name #7", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [48, 58, 68], + "name": "long trace name #8", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [49, 59, 69], + "name": "long trace name #9", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [50, 60, 70], + "name": "long trace name #10", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [51, 61, 71], + "name": "long trace name #11", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [52, 62, 72], + "name": "long trace name #12", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [53, 63, 73], + "name": "long trace name #13", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [54, 64, 74], + "name": "long trace name #14", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [55, 65, 75], + "name": "long trace name #15", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [56, 66, 76], + "name": "long trace name #16", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [57, 67, 77], + "name": "long trace name #17", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [58, 68, 78], + "name": "long trace name #18", + "type": "scatter" + }, + { + "x": [1, 2, 3], + "y": [59, 69, 79], + "name": "long trace name #19", + "type": "scatter" + } + ], + "layout": { + "width": 1000, + "height": 400, + "margin": { + "l": 0, + "t": 120, + "r": 0, + "b": 120, + "autoexpand": false + }, + "legend": { + "orientation": "v", + "xref": "container", + "yref": "container", + "xanchor": "left", + "yanchor": "top", + "x": 0.05, + "y": 0.25, + "bgcolor": "lightgrey" + } + } +}