-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Add title feature to legend and support legend option for various traces #4386
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
Changes from 3 commits
9174470
706a883
264044f
a9759a2
b3765b4
ce35947
0a437a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,6 +63,19 @@ module.exports = function draw(gd) { | |
.call(Color.fill, opts.bgcolor) | ||
.style('stroke-width', opts.borderwidth + 'px'); | ||
|
||
|
||
var title = fullLayout.legend.title; | ||
gd._fullLayout._legendTitleWidth = 0; | ||
gd._fullLayout._legendTitleHeight = 0; | ||
etpinard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if(title.text) { | ||
var titleEl = Lib.ensureSingle(legend, 'text', 'legendtitletext'); | ||
titleEl.attr('text-anchor', 'start') | ||
.classed('user-select-none', true) | ||
.call(Drawing.font, title.font) | ||
.text(title.text); | ||
|
||
textLayout(titleEl, legend, gd); // handle mathjax or multi-line text and compute title height | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox'); | ||
|
||
var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) { | ||
|
@@ -121,7 +134,7 @@ module.exports = function draw(gd) { | |
} | ||
|
||
// Set size and position of all the elements that make up a legend: | ||
// legend, background and border, scroll box and scroll bar | ||
// legend, background and border, scroll box and scroll bar as well as title | ||
Drawing.setTranslate(legend, lx, ly); | ||
|
||
// to be safe, remove previous listeners | ||
|
@@ -375,18 +388,12 @@ function drawTexts(g, gd) { | |
|
||
svgTextUtils.positionText(textEl, constants.textGap, 0); | ||
|
||
function textLayout(s) { | ||
svgTextUtils.convertToTspans(s, gd, function() { | ||
computeTextDimensions(g, gd); | ||
}); | ||
} | ||
|
||
if(isEditable) { | ||
textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name}) | ||
.call(textLayout) | ||
.call(textLayout, g, gd) | ||
.on('edit', function(newName) { | ||
this.text(ensureLength(newName, maxNameLength)) | ||
.call(textLayout); | ||
.call(textLayout, g, gd); | ||
|
||
var fullInput = legendItem.trace._fullInput || {}; | ||
var update = {}; | ||
|
@@ -407,7 +414,7 @@ function drawTexts(g, gd) { | |
return Registry.call('_guiRestyle', gd, update, traceIndex); | ||
}); | ||
} else { | ||
textLayout(textEl); | ||
textLayout(textEl, g, gd); | ||
} | ||
} | ||
|
||
|
@@ -460,17 +467,25 @@ function setupTraceToggle(g, gd) { | |
}); | ||
} | ||
|
||
function textLayout(s, g, gd) { | ||
svgTextUtils.convertToTspans(s, gd, function() { | ||
computeTextDimensions(g, gd); | ||
}); | ||
} | ||
|
||
function computeTextDimensions(g, gd) { | ||
var legendItem = g.data()[0][0]; | ||
|
||
if(!legendItem.trace.showlegend) { | ||
if(legendItem && !legendItem.trace.showlegend) { | ||
g.remove(); | ||
return; | ||
} | ||
|
||
var mathjaxGroup = g.select('g[class*=math-group]'); | ||
var mathjaxNode = mathjaxGroup.node(); | ||
var opts = gd._fullLayout.legend; | ||
var bw = gd._fullLayout.legend.borderwidth; | ||
var opts = legendItem ? | ||
gd._fullLayout.legend : | ||
gd._fullLayout.legend.title; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not a big fan of making What if you revert There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like the complexities you brought into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... so don't be surprised if I change the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. Please go for it. |
||
var lineHeight = opts.font.size * LINE_SPACING; | ||
var height, width; | ||
|
||
|
@@ -480,24 +495,43 @@ function computeTextDimensions(g, gd) { | |
height = mathjaxBB.height; | ||
width = mathjaxBB.width; | ||
|
||
Drawing.setTranslate(mathjaxGroup, 0, (height / 4)); | ||
if(legendItem) { | ||
Drawing.setTranslate(mathjaxGroup, 0, height * 0.25); | ||
} else { // case of title | ||
Drawing.setTranslate(mathjaxGroup, bw, height * 0.75 + bw); | ||
} | ||
} else { | ||
var text = g.select('.legendtext'); | ||
var textLines = svgTextUtils.lineCount(text); | ||
var textNode = text.node(); | ||
var textEl = g.select(legendItem ? | ||
'.legendtext' : '.legendtitletext' | ||
); | ||
var textLines = svgTextUtils.lineCount(textEl); | ||
var textNode = textEl.node(); | ||
|
||
height = lineHeight * textLines; | ||
width = textNode ? Drawing.bBox(textNode).width : 0; | ||
|
||
// approximation to height offset to center the font | ||
// to avoid getBoundingClientRect | ||
var textY = lineHeight * (0.3 + (1 - textLines) / 2); | ||
svgTextUtils.positionText(text, constants.textGap, textY); | ||
var textY = lineHeight * ((textLines - 1) / 2 - 0.3); | ||
if(legendItem) { | ||
svgTextUtils.positionText(textEl, constants.textGap, -textY); | ||
} else { // case of title | ||
svgTextUtils.positionText(textEl, constants.titlePad + bw, lineHeight + bw); | ||
} | ||
} | ||
|
||
legendItem.lineHeight = lineHeight; | ||
legendItem.height = Math.max(height, 16) + 3; | ||
legendItem.width = width; | ||
if(legendItem) { | ||
legendItem.lineHeight = lineHeight; | ||
legendItem.height = Math.max(height, 16) + 3; | ||
legendItem.width = width; | ||
} else { // case of title | ||
if(opts.side.indexOf('left') !== -1) { | ||
gd._fullLayout._legendTitleWidth = width; | ||
} | ||
if(opts.side.indexOf('top') !== -1) { | ||
gd._fullLayout._legendTitleHeight = height; | ||
} | ||
etpinard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
/* | ||
|
@@ -514,6 +548,9 @@ function computeLegendDimensions(gd, groups, traces) { | |
var fullLayout = gd._fullLayout; | ||
var opts = fullLayout.legend; | ||
var gs = fullLayout._size; | ||
opts._titleWidth = fullLayout._legendTitleWidth; | ||
opts._titleHeight = fullLayout._legendTitleHeight; | ||
etpinard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
var isVertical = helpers.isVertical(opts); | ||
var isGrouped = helpers.isGrouped(opts); | ||
|
||
|
@@ -541,7 +578,10 @@ function computeLegendDimensions(gd, groups, traces) { | |
if(isVertical) { | ||
traces.each(function(d) { | ||
var h = d[0].height; | ||
Drawing.setTranslate(this, bw, itemGap + bw + opts._height + h / 2); | ||
Drawing.setTranslate(this, | ||
bw + opts._titleWidth, | ||
bw + opts._titleHeight + opts._height + h / 2 + itemGap | ||
); | ||
opts._height += h; | ||
opts._width = Math.max(opts._width, d[0].width); | ||
}); | ||
|
@@ -591,7 +631,10 @@ function computeLegendDimensions(gd, groups, traces) { | |
var offsetY = 0; | ||
d3.select(this).selectAll('g.traces').each(function(d) { | ||
var h = d[0].height; | ||
Drawing.setTranslate(this, 0, itemGap + bw + h / 2 + offsetY); | ||
Drawing.setTranslate(this, | ||
opts._titleWidth, | ||
bw + opts._titleHeight + itemGap + h / 2 + offsetY | ||
); | ||
offsetY += h; | ||
maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width); | ||
}); | ||
|
@@ -634,7 +677,10 @@ function computeLegendDimensions(gd, groups, traces) { | |
maxItemHeightInRow = 0; | ||
} | ||
|
||
Drawing.setTranslate(this, bw + offsetX, itemGap + bw + h / 2 + offsetY); | ||
Drawing.setTranslate(this, | ||
bw + opts._titleWidth + offsetX, | ||
bw + opts._titleHeight + offsetY + h / 2 + itemGap | ||
); | ||
|
||
rowWidth = offsetX + w + itemGap; | ||
offsetX += next; | ||
|
@@ -651,8 +697,8 @@ function computeLegendDimensions(gd, groups, traces) { | |
} | ||
} | ||
|
||
opts._width = Math.ceil(opts._width); | ||
opts._height = Math.ceil(opts._height); | ||
opts._width = Math.ceil(opts._width + opts._titleWidth); | ||
opts._height = Math.ceil(opts._height + opts._titleHeight); | ||
|
||
opts._effHeight = Math.min(opts._height, opts._maxHeight); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why we need this extra piece of logic:
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question.
This is to keep the baselines to what was in place before.
Otherwise if there are e.g. one
scatter3d
with other gl3d traces (that now support legend) thelegend
would show up on the graph forscatter3d
.