Skip to content

Commit 204b5c6

Browse files
committed
fix many legend auto-margin push and positioning edge cases
- see #771 - introduce measures _maxHeight, _maxWidth and _effHeight to track margin pushes, scrollbox and horizontal row wrapping - simplify (and fix) legend (x,y) constraint into graph width/height - introduce some l,r,b,y "max" margin-push logic - paves way to expose legend `maxheight` and `maxwidth` - update baselines! + from previous "new legend mocks" commit + and `horizontal_wrap-alll-lines` which now spans the graph's width
1 parent 85578d3 commit 204b5c6

8 files changed

+54
-45
lines changed

src/components/legend/draw.js

+54-45
Original file line numberDiff line numberDiff line change
@@ -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+
*/
477460
function 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

Loading
-515 Bytes
Loading
2.09 KB
Loading
Loading
-11.6 KB
Loading
227 Bytes
Loading
Loading

0 commit comments

Comments
 (0)