Skip to content

Commit 680dcd3

Browse files
committed
Fix invisible legends with negative y
* Ensure that when the margins are expended to allocate the legend, there is enough space to clear the layout margins. Fixes plotly#384
1 parent c68ed58 commit 680dcd3

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

src/components/legend/draw.js

+34-16
Original file line numberDiff line numberDiff line change
@@ -184,27 +184,42 @@ module.exports = function draw(gd) {
184184
if(anchorUtils.isRightAnchor(opts)) {
185185
lx -= opts.width;
186186
}
187-
if(anchorUtils.isCenterAnchor(opts)) {
187+
else if(anchorUtils.isCenterAnchor(opts)) {
188188
lx -= opts.width / 2;
189189
}
190190

191191
if(anchorUtils.isBottomAnchor(opts)) {
192192
ly -= opts.height;
193193
}
194-
if(anchorUtils.isMiddleAnchor(opts)) {
194+
else if(anchorUtils.isMiddleAnchor(opts)) {
195195
ly -= opts.height / 2;
196196
}
197197

198+
lx = Math.round(lx);
199+
ly = Math.round(ly);
200+
201+
// Make sure the legend top is below the top margin
202+
if(ly < fullLayout.margin.t) ly = fullLayout.margin.t;
203+
204+
var scrollHeightMax = fullLayout.height - fullLayout.margin.b - ly;
205+
var scrollHeight = Math.min(scrollHeightMax, opts.height);
206+
207+
if(scrollHeight <= 2 * opts.borderwidth) {
208+
console.error('Legend.draw: insufficient space to draw legend');
209+
legend.remove();
210+
return;
211+
}
212+
198213
// Deal with scrolling
199-
var plotHeight = fullLayout.height - fullLayout.margin.b,
200-
scrollheight = Math.min(plotHeight - ly, opts.height),
201-
scrollPosition = scrollBox.attr('data-scroll') ? scrollBox.attr('data-scroll') : 0;
214+
var scrollPosition = scrollBox.attr('data-scroll') ?
215+
scrollBox.attr('data-scroll') :
216+
0;
202217

203218
scrollBox.attr('transform', 'translate(0, ' + scrollPosition + ')');
204219

205220
bg.attr({
206221
width: opts.width - 2 * opts.borderwidth,
207-
height: scrollheight - 2 * opts.borderwidth,
222+
height: scrollHeight - 2 * opts.borderwidth,
208223
x: opts.borderwidth,
209224
y: opts.borderwidth
210225
});
@@ -213,15 +228,15 @@ module.exports = function draw(gd) {
213228

214229
clipPath.select('rect').attr({
215230
width: opts.width,
216-
height: scrollheight,
231+
height: scrollHeight,
217232
x: 0,
218233
y: 0
219234
});
220235

221236
legend.call(Drawing.setClipUrl, clipId);
222237

223238
// If scrollbar should be shown.
224-
if(opts.height - scrollheight > 0 && !gd._context.staticPlot) {
239+
if(opts.height - scrollHeight > 0 && !gd._context.staticPlot) {
225240

226241
bg.attr({
227242
width: opts.width - 2 * opts.borderwidth + constants.scrollBarWidth
@@ -243,21 +258,21 @@ module.exports = function draw(gd) {
243258
scrollBox.attr('data-scroll',0);
244259
}
245260

246-
scrollHandler(0,scrollheight);
261+
scrollHandler(0,scrollHeight);
247262

248263
legend.on('wheel',null);
249264

250265
legend.on('wheel', function() {
251266
var e = d3.event;
252267
e.preventDefault();
253-
scrollHandler(e.deltaY / 20, scrollheight);
268+
scrollHandler(e.deltaY / 20, scrollHeight);
254269
});
255270

256271
scrollBar.on('.drag',null);
257272
scrollBox.on('.drag',null);
258273
var drag = d3.behavior.drag()
259274
.on('drag', function() {
260-
scrollHandler(d3.event.dy, scrollheight);
275+
scrollHandler(d3.event.dy, scrollHeight);
261276
});
262277

263278
scrollBar.call(drag);
@@ -266,12 +281,12 @@ module.exports = function draw(gd) {
266281
}
267282

268283

269-
function scrollHandler(delta, scrollheight) {
284+
function scrollHandler(delta, scrollHeight) {
270285

271-
var scrollBarTrack = scrollheight - constants.scrollBarHeight - 2 * constants.scrollBarMargin,
286+
var scrollBarTrack = scrollHeight - constants.scrollBarHeight - 2 * constants.scrollBarMargin,
272287
translateY = scrollBox.attr('data-scroll'),
273-
scrollBoxY = Lib.constrain(translateY - delta, scrollheight-opts.height, 0),
274-
scrollBarY = -scrollBoxY / (opts.height - scrollheight) * scrollBarTrack + constants.scrollBarMargin;
288+
scrollBoxY = Lib.constrain(translateY - delta, scrollHeight-opts.height, 0),
289+
scrollBarY = -scrollBoxY / (opts.height - scrollHeight) * scrollBarTrack + constants.scrollBarMargin;
275290

276291
scrollBox.attr('data-scroll', scrollBoxY);
277292
scrollBox.attr('transform', 'translate(0, ' + scrollBoxY + ')');
@@ -454,7 +469,10 @@ function repositionLegend(gd, traces) {
454469
opts.height = Math.ceil(opts.height);
455470

456471
// lastly check if the margin auto-expand has changed
457-
Plots.autoMargin(gd, 'legend', {
472+
// (using Plots.autoMarginVertical to ensure the requested margins are
473+
// padded with the layout vertical margins to ensure the legend doesn't
474+
// exceed the plot area)
475+
Plots.autoMarginVertical(gd, 'legend', {
458476
x: opts.x,
459477
y: opts.y,
460478
l: opts.width * ({right: 1, center: 0.5}[xanchor] || 0),

src/plots/plots.js

+30
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,36 @@ plots.autoMargin = function(gd, id, o) {
875875
}
876876
};
877877

878+
// Similar to plots.autoMargin, except that it pads the request with the layout
879+
// margins, so that an object can be drawn without reaching the layout vertical
880+
// margins.
881+
plots.autoMarginVertical = function(gd, id, o) {
882+
var fullLayout = gd._fullLayout;
883+
884+
if(!fullLayout._pushmargin) fullLayout._pushmargin = {};
885+
886+
if(fullLayout.margin.autoexpand !== false) {
887+
if(!o) delete fullLayout._pushmargin[id];
888+
else {
889+
var pad = o.pad === undefined ? 12 : o.pad;
890+
891+
// if the item is too big, just give it enough automargin to
892+
// make sure you can still grab it and bring it back
893+
if(o.l+o.r > fullLayout.width*0.5) o.l = o.r = 0;
894+
if(o.b+o.t > fullLayout.height*0.5) o.b = o.t = 0;
895+
896+
fullLayout._pushmargin[id] = {
897+
l: {val: o.x, size: o.l + pad},
898+
r: {val: o.x, size: o.r + pad},
899+
b: {val: o.y, size: o.b + fullLayout.margin.b},
900+
t: {val: o.y, size: o.t + fullLayout.margin.t}
901+
};
902+
}
903+
904+
if(!gd._replotting) plots.doAutoMargin(gd);
905+
}
906+
};
907+
878908
plots.doAutoMargin = function(gd) {
879909
var fullLayout = gd._fullLayout;
880910
if(!fullLayout._size) fullLayout._size = {};

0 commit comments

Comments
 (0)