Skip to content

Commit 976d9cb

Browse files
committed
trim toggle-rect to more-precise width
... the current width didn't lead to any bugs, but it was too large in most cases. - make horiz. grouped / non-grouped logic more similar - DRY up x/y anchor logic
1 parent 739580a commit 976d9cb

File tree

2 files changed

+105
-133
lines changed

2 files changed

+105
-133
lines changed

src/components/legend/draw.js

+104-132
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,8 @@ module.exports = function draw(gd) {
9898
var gs = fullLayout._size;
9999
var bw = opts.borderwidth;
100100

101-
var lx = gs.l + gs.w * opts.x;
102-
if(Lib.isRightAnchor(opts)) {
103-
lx -= opts._width;
104-
} else if(Lib.isCenterAnchor(opts)) {
105-
lx -= opts._width / 2;
106-
}
101+
var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width;
102+
var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight;
107103

108104

109105
// Make sure the legend left and right sides are visible
@@ -119,12 +115,6 @@ module.exports = function draw(gd) {
119115
legendWidth = Math.min(fullLayout.width - lx, opts._width);
120116
}
121117

122-
var ly = gs.t + gs.h * (1 - opts.y);
123-
if(Lib.isBottomAnchor(opts)) {
124-
ly -= opts._height;
125-
} else if(Lib.isMiddleAnchor(opts)) {
126-
ly -= opts._height / 2;
127-
}
128118

129119
// Make sure the legend top and bottom are visible
130120
// (legends with a scroll bar are not allowed to stretch beyond the extended
@@ -487,6 +477,7 @@ function computeTextDimensions(g, gd) {
487477
function computeLegendDimensions(gd, groups, traces) {
488478
var fullLayout = gd._fullLayout;
489479
var opts = fullLayout.legend;
480+
var isVertical = helpers.isVertical(opts);
490481
var isGrouped = helpers.isGrouped(opts);
491482

492483
var bw = opts.borderwidth;
@@ -495,172 +486,141 @@ function computeLegendDimensions(gd, groups, traces) {
495486
var itemGap = constants.itemGap;
496487
var endPad = 2 * (bw + itemGap);
497488

498-
var extraWidth = 0;
489+
opts._maxWidth = fullLayout._size.w;
490+
491+
var toggleRectWidth = 0;
499492
opts._width = 0;
500493
opts._height = 0;
501494

502-
if(helpers.isVertical(opts)) {
503-
if(isGrouped) {
504-
groups.each(function(d, i) {
505-
Drawing.setTranslate(this, 0, i * opts.tracegroupgap);
506-
});
507-
}
508-
495+
if(isVertical) {
509496
traces.each(function(d) {
510497
var h = d[0].height;
511498
Drawing.setTranslate(this, bw, itemGap + bw + opts._height + h / 2);
512499
opts._height += h;
513500
opts._width = Math.max(opts._width, d[0].width);
514501
});
515502

503+
toggleRectWidth = textGap + opts._width;
516504
opts._width += itemGap + textGap + bw2;
517505
opts._height += endPad;
518506

519507
if(isGrouped) {
508+
groups.each(function(d, i) {
509+
Drawing.setTranslate(this, 0, i * opts.tracegroupgap);
510+
});
520511
opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap;
521512
}
513+
} else {
514+
var maxItemWidth = 0;
515+
var combinedItemWidth = 0;
516+
traces.each(function(d) {
517+
var w = d[0].width + textGap;
518+
maxItemWidth = Math.max(maxItemWidth, w);
519+
combinedItemWidth += w;
520+
});
522521

523-
extraWidth = textGap;
524-
} else if(isGrouped) {
525-
var maxHeight = 0;
526-
var maxWidth = 0;
527-
var maxItems = 0;
528-
var groupData = groups.data();
529-
var i;
530-
531-
for(i = 0; i < groupData.length; i++) {
532-
var group = groupData[i];
533-
var groupWidths = group.map(function(d) { return d[0].width; });
534-
var groupHeight = group.reduce(function(a, b) { return a + b[0].height; }, 0);
535-
maxWidth = Math.max(maxWidth, Lib.aggNums(Math.max, null, groupWidths));
536-
maxHeight = Math.max(maxHeight, groupHeight);
537-
maxItems = Math.max(maxItems, group.length);
538-
}
539-
540-
maxWidth += itemGap + textGap;
522+
if(isGrouped) {
523+
var groupData = groups.data();
524+
var i;
541525

542-
var groupXOffsets = [opts._width];
543-
var groupYOffsets = [];
544-
var rowNum = 0;
545-
for(i = 0; i < groupData.length; i++) {
546-
if(fullLayout._size.w < (bw + opts._width + itemGap + maxWidth)) {
547-
groupXOffsets[groupXOffsets.length - 1] = groupXOffsets[0];
548-
opts._width = maxWidth;
549-
rowNum++;
550-
} else {
551-
opts._width += maxWidth + bw;
526+
var maxGroupHeight = 0;
527+
for(i = 0; i < groupData.length; i++) {
528+
var groupHeight = groupData[i].reduce(function(a, b) { return a + b[0].height; }, 0);
529+
maxGroupHeight = Math.max(maxGroupHeight, groupHeight);
552530
}
553531

554-
var rowYOffset = (rowNum * maxHeight);
555-
rowYOffset += rowNum > 0 ? opts.tracegroupgap : 0;
532+
var groupXOffsets = [opts._width];
533+
var groupYOffsets = [];
534+
var rowNum = 0;
535+
for(i = 0; i < groupData.length; i++) {
536+
if((opts._width + itemGap + maxItemWidth + bw) > opts._maxWidth) {
537+
groupXOffsets[groupXOffsets.length - 1] = groupXOffsets[0];
538+
opts._width = maxItemWidth + itemGap;
539+
rowNum++;
540+
} else {
541+
opts._width += maxItemWidth + itemGap;
542+
}
556543

557-
groupYOffsets.push(rowYOffset);
558-
groupXOffsets.push(opts._width);
559-
}
544+
groupXOffsets.push(opts._width);
545+
groupYOffsets.push(rowNum * maxGroupHeight + (rowNum > 0 ? opts.tracegroupgap : 0));
546+
}
560547

561-
groups.each(function(d, i) {
562-
Drawing.setTranslate(this, groupXOffsets[i], groupYOffsets[i]);
563-
});
548+
groups.each(function(d, i) {
549+
Drawing.setTranslate(this, groupXOffsets[i], groupYOffsets[i]);
550+
});
564551

565-
groups.each(function() {
566-
var group = d3.select(this);
567-
var groupTraces = group.selectAll('g.traces');
568-
var groupHeight = 0;
552+
groups.each(function() {
553+
var group = d3.select(this);
554+
var groupTraces = group.selectAll('g.traces');
555+
var groupHeight = 0;
569556

570-
groupTraces.each(function(d) {
571-
var h = d[0].height;
572-
Drawing.setTranslate(this, 0, itemGap + bw + groupHeight + h / 2);
573-
groupHeight += h;
557+
groupTraces.each(function(d) {
558+
var h = d[0].height;
559+
Drawing.setTranslate(this, 0, itemGap + bw + groupHeight + h / 2);
560+
groupHeight += h;
561+
});
574562
});
575-
});
576563

577-
var maxYLegend = groupYOffsets[groupYOffsets.length - 1] + maxHeight;
578-
opts._height = maxYLegend + endPad;
564+
opts._height = groupYOffsets[groupYOffsets.length - 1] + maxGroupHeight + endPad;
565+
opts._width = Math.max.apply(null, groupXOffsets) + maxItemWidth + textGap + bw2;
566+
toggleRectWidth = maxItemWidth;
567+
} else {
568+
var oneRowLegend = (combinedItemWidth + bw2 + (traces.size() - 1) * itemGap) < opts._maxWidth;
579569

580-
var maxOffset = Math.max.apply(null, groupXOffsets);
581-
opts._width = maxOffset + maxWidth + textGap + bw2;
582-
} else {
583-
var rowHeight = 0;
584-
var maxTraceHeight = 0;
585-
var maxTraceWidth = 0;
586-
var offsetX = 0;
587-
var fullTracesWidth = 0;
570+
var maxRowWidth = 0;
571+
var maxItemHeightInRow = 0;
572+
var offsetX = 0;
573+
var offsetY = 0;
574+
traces.each(function(d) {
575+
var h = d[0].height;
576+
var next = (oneRowLegend ? textGap + d[0].width : maxItemWidth) + itemGap;
577+
578+
if((next + bw + offsetX) > opts._maxWidth) {
579+
maxRowWidth = Math.max(maxRowWidth, offsetX);
580+
offsetX = 0;
581+
offsetY += maxItemHeightInRow;
582+
opts._height += maxItemHeightInRow;
583+
maxItemHeightInRow = 0;
584+
}
588585

589-
// calculate largest width for traces and use for width of all legend items
590-
traces.each(function(d) {
591-
maxTraceWidth = Math.max(maxTraceWidth, textGap + d[0].width);
592-
fullTracesWidth += textGap + d[0].width + itemGap;
593-
});
586+
Drawing.setTranslate(this, bw + offsetX, itemGap + bw + h / 2 + offsetY);
594587

595-
// check if legend fits in one row
596-
var oneRowLegend = fullLayout._size.w > bw + fullTracesWidth - itemGap;
588+
offsetX += next;
589+
maxItemHeightInRow = Math.max(maxItemHeightInRow, h);
590+
});
597591

598-
traces.each(function(d) {
599-
var h = d[0].height;
600-
var traceWidth = oneRowLegend ? textGap + d[0].width : maxTraceWidth;
601-
602-
if((bw + offsetX + itemGap + traceWidth) > fullLayout._size.w) {
603-
offsetX = 0;
604-
rowHeight += maxTraceHeight;
605-
opts._height += maxTraceHeight;
606-
// reset for next row
607-
maxTraceHeight = 0;
592+
if(oneRowLegend) {
593+
opts._width = offsetX + bw2;
594+
opts._height = maxItemHeightInRow + endPad;
595+
toggleRectWidth = null;
596+
} else {
597+
opts._width = Math.max(maxRowWidth, offsetX) + bw;
598+
opts._height += maxItemHeightInRow + endPad;
599+
toggleRectWidth = maxItemWidth;
608600
}
609-
610-
Drawing.setTranslate(this, bw + offsetX, itemGap + bw + h / 2 + rowHeight);
611-
opts._width += itemGap + traceWidth;
612-
613-
// keep track of tallest trace in group
614-
offsetX += itemGap + traceWidth;
615-
maxTraceHeight = Math.max(maxTraceHeight, h);
616-
});
617-
618-
if(oneRowLegend) {
619-
opts._height = maxTraceHeight;
620-
} else {
621-
opts._height += maxTraceHeight;
622601
}
623-
624-
opts._width += bw2;
625-
opts._height += endPad;
626602
}
627603

628604
opts._width = Math.ceil(opts._width);
629605
opts._height = Math.ceil(opts._height);
630606

631-
var isEditable = (
632-
gd._context.edits.legendText ||
633-
gd._context.edits.legendPosition
634-
);
635-
607+
var edits = gd._context.edits;
608+
var isEditable = edits.legendText || edits.legendPosition;
636609
traces.each(function(d) {
610+
var traceToggle = d3.select(this).select('.legendtoggle');
637611
var h = d[0].height;
638-
Drawing.setRect(d3.select(this).select('.legendtoggle'),
639-
0,
640-
-h / 2,
641-
(isEditable ? 0 : opts._width) + extraWidth,
642-
h
643-
);
612+
var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width));
613+
if(!isVertical) w += itemGap / 2;
614+
Drawing.setRect(traceToggle, 0, -h / 2, w, h);
644615
});
645616
}
646617

647618
function expandMargin(gd) {
648619
var fullLayout = gd._fullLayout;
649620
var opts = fullLayout.legend;
621+
var xanchor = getXanchor(opts);
622+
var yanchor = getYanchor(opts);
650623

651-
var xanchor = 'left';
652-
if(Lib.isRightAnchor(opts)) {
653-
xanchor = 'right';
654-
} else if(Lib.isCenterAnchor(opts)) {
655-
xanchor = 'center';
656-
}
657-
658-
var yanchor = 'top';
659-
if(Lib.isBottomAnchor(opts)) {
660-
yanchor = 'bottom';
661-
} else if(Lib.isMiddleAnchor(opts)) {
662-
yanchor = 'middle';
663-
}
664624

665625
Plots.autoMargin(gd, 'legend', {
666626
x: opts.x,
@@ -671,3 +631,15 @@ function expandMargin(gd) {
671631
t: opts._height * (FROM_TL[yanchor])
672632
});
673633
}
634+
635+
function getXanchor(opts) {
636+
return Lib.isRightAnchor(opts) ? 'right' :
637+
Lib.isCenterAnchor(opts) ? 'center' :
638+
'left';
639+
}
640+
641+
function getYanchor(opts) {
642+
return Lib.isBottomAnchor(opts) ? 'bottom' :
643+
Lib.isMiddleAnchor(opts) ? 'middle' :
644+
'top';
645+
}

test/jasmine/tests/legend_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ describe('legend restyle update', function() {
788788
expect(node.attr('height')).toEqual('19');
789789

790790
var w = +node.attr('width');
791-
expect(Math.abs(w - 160)).toBeLessThan(10);
791+
expect(w).toBeWithin(113, 10);
792792
});
793793
}
794794

0 commit comments

Comments
 (0)