Skip to content

[WIP] Fix issue #384 (invisible legends when y is negative) #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 36 additions & 31 deletions src/components/legend/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,27 +184,42 @@ module.exports = function draw(gd) {
if(anchorUtils.isRightAnchor(opts)) {
lx -= opts.width;
}
if(anchorUtils.isCenterAnchor(opts)) {
else if(anchorUtils.isCenterAnchor(opts)) {
lx -= opts.width / 2;
}

if(anchorUtils.isBottomAnchor(opts)) {
ly -= opts.height;
}
if(anchorUtils.isMiddleAnchor(opts)) {
else if(anchorUtils.isMiddleAnchor(opts)) {
ly -= opts.height / 2;
}

//lx = Math.round(lx);
//ly = Math.round(ly);

// Make sure the legend top is below the top margin
if(ly < fullLayout.margin.t) ly = fullLayout.margin.t;

var scrollHeightMax = fullLayout.height - fullLayout.margin.b - ly;
var scrollHeight = Math.min(scrollHeightMax, opts.height);

if(scrollHeight <= 2 * opts.borderwidth) {
console.error('Legend.draw: insufficient space to draw legend');
legend.remove();
return;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thoughts, it'd be more user-friendly to provide a safe default here. For example:

  • if opts.height >= fullLayout.height, then ly = fullLayout.margin.t; scrollHeight = fullLayout.height;
  • else locate the legend the bottom.


// Deal with scrolling
var plotHeight = fullLayout.height - fullLayout.margin.b,
scrollheight = Math.min(plotHeight - ly, opts.height),
scrollPosition = scrollBox.attr('data-scroll') ? scrollBox.attr('data-scroll') : 0;
var scrollPosition = scrollBox.attr('data-scroll') ?
scrollBox.attr('data-scroll') :
0;

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

bg.attr({
width: opts.width - 2 * opts.borderwidth,
height: scrollheight - 2 * opts.borderwidth,
height: scrollHeight - 2 * opts.borderwidth,
x: opts.borderwidth,
y: opts.borderwidth
});
Expand All @@ -213,21 +228,21 @@ module.exports = function draw(gd) {

clipPath.select('rect').attr({
width: opts.width,
height: scrollheight,
height: scrollHeight,
x: 0,
y: 0
});

legend.call(Drawing.setClipUrl, clipId);

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

bg.attr({
width: opts.width - 2 * opts.borderwidth + constants.scrollBarWidth
});

clipPath.attr({
clipPath.select('rect').attr({
width: opts.width + constants.scrollBarWidth
});

Expand All @@ -243,21 +258,21 @@ module.exports = function draw(gd) {
scrollBox.attr('data-scroll',0);
}

scrollHandler(0,scrollheight);
scrollHandler(0,scrollHeight);

legend.on('wheel',null);

legend.on('wheel', function() {
var e = d3.event;
e.preventDefault();
scrollHandler(e.deltaY / 20, scrollheight);
scrollHandler(e.deltaY / 20, scrollHeight);
});

scrollBar.on('.drag',null);
scrollBox.on('.drag',null);
var drag = d3.behavior.drag()
.on('drag', function() {
scrollHandler(d3.event.dy, scrollheight);
scrollHandler(d3.event.dy, scrollHeight);
});

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


function scrollHandler(delta, scrollheight) {
function scrollHandler(delta, scrollHeight) {

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

scrollBox.attr('data-scroll', scrollBoxY);
scrollBox.attr('transform', 'translate(0, ' + scrollBoxY + ')');
Expand Down Expand Up @@ -368,7 +383,6 @@ function drawTexts(context, gd, d, i, traces) {

function repositionLegend(gd, traces) {
var fullLayout = gd._fullLayout,
gs = fullLayout._size,
opts = fullLayout.legend,
borderwidth = opts.borderwidth;

Expand Down Expand Up @@ -432,42 +446,33 @@ function repositionLegend(gd, traces) {
.attr('width', (gd._context.editable ? 0 : opts.width) + 40);

// now position the legend. for both x,y the positions are recorded as
// fractions of the plot area (left, bottom = 0,0). Outside the plot
// area is allowed but position will be clipped to the page.
// values <1/3 align the low side at that fraction, 1/3-2/3 align the
// center at that fraction, >2/3 align the right at that fraction

var lx = gs.l + gs.w * opts.x,
ly = gs.t + gs.h * (1-opts.y);
// fractions of the plot area (left, bottom = 0,0).

var xanchor = 'left';
if(anchorUtils.isRightAnchor(opts)) {
lx -= opts.width;
xanchor = 'right';
}
if(anchorUtils.isCenterAnchor(opts)) {
lx -= opts.width / 2;
xanchor = 'center';
}

var yanchor = 'top';
if(anchorUtils.isBottomAnchor(opts)) {
ly -= opts.height;
yanchor = 'bottom';
}
if(anchorUtils.isMiddleAnchor(opts)) {
ly -= opts.height / 2;
yanchor = 'middle';
}

// make sure we're only getting full pixels
opts.width = Math.ceil(opts.width);
opts.height = Math.ceil(opts.height);
lx = Math.round(lx);
ly = Math.round(ly);

// lastly check if the margin auto-expand has changed
Plots.autoMargin(gd, 'legend', {
// (using Plots.autoMarginVertical to ensure the requested margins are
// padded with the layout vertical margins to ensure the legend doesn't
// exceed the plot area)
Plots.autoMarginVertical(gd, 'legend', {
x: opts.x,
y: opts.y,
l: opts.width * ({right: 1, center: 0.5}[xanchor] || 0),
Expand Down
30 changes: 30 additions & 0 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,36 @@ plots.autoMargin = function(gd, id, o) {
}
};

// Similar to plots.autoMargin, except that it pads the request with the layout
// margins, so that an object can be drawn without reaching the layout vertical
// margins.
plots.autoMarginVertical = function(gd, id, o) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this?

var fullLayout = gd._fullLayout;

if(!fullLayout._pushmargin) fullLayout._pushmargin = {};

if(fullLayout.margin.autoexpand !== false) {
if(!o) delete fullLayout._pushmargin[id];
else {
var pad = o.pad === undefined ? 12 : o.pad;

// if the item is too big, just give it enough automargin to
// make sure you can still grab it and bring it back
if(o.l+o.r > fullLayout.width*0.5) o.l = o.r = 0;
if(o.b+o.t > fullLayout.height*0.5) o.b = o.t = 0;

fullLayout._pushmargin[id] = {
l: {val: o.x, size: o.l + pad},
r: {val: o.x, size: o.r + pad},
b: {val: o.y, size: o.b + fullLayout.margin.b},
t: {val: o.y, size: o.t + fullLayout.margin.t}
};
}

if(!gd._replotting) plots.doAutoMargin(gd);
}
};

plots.doAutoMargin = function(gd) {
var fullLayout = gd._fullLayout;
if(!fullLayout._size) fullLayout._size = {};
Expand Down