From 0fccc8d6a0b712728771ccd6d2ba83f6e2d0546d Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 29 Mar 2021 11:19:50 -0400 Subject: [PATCH 01/10] rename _main legend to _isLegend for clarity --- src/components/legend/draw.js | 28 ++++++++++++------------ src/components/legend/get_legend_data.js | 3 +-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index f91b813c6c4..365a14feed8 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -30,7 +30,7 @@ module.exports = function draw(gd, opts) { // Check whether this is the main legend (ie. called without any opts) if(!opts) { opts = fullLayout.legend || {}; - opts._main = true; + opts._isLegend = true; layer = fullLayout._infolayer; } else { layer = opts.layer; @@ -42,7 +42,7 @@ module.exports = function draw(gd, opts) { if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0; var legendData; - if(opts._main) { + if(opts._isLegend) { if(!gd.calcdata) return; legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts); } else { @@ -52,14 +52,14 @@ module.exports = function draw(gd, opts) { var hiddenSlices = fullLayout.hiddenlabels || []; - if(opts._main && (!fullLayout.showlegend || !legendData.length)) { + if(opts._isLegend && (!fullLayout.showlegend || !legendData.length)) { layer.selectAll('.legend').remove(); fullLayout._topdefs.select('#' + clipId).remove(); return Plots.autoMargin(gd, 'legend'); } var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) { - if(opts._main) s.attr('pointer-events', 'all'); + if(opts._isLegend) s.attr('pointer-events', 'all'); }); var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) { @@ -112,7 +112,7 @@ module.exports = function draw(gd, opts) { }) .each(function() { d3.select(this).call(drawTexts, gd, opts); }) .call(style, gd, opts) - .each(function() { if(opts._main) d3.select(this).call(setupTraceToggle, gd); }); + .each(function() { if(opts._isLegend) d3.select(this).call(setupTraceToggle, gd); }); Lib.syncOrAsync([ Plots.previousPromises, @@ -121,7 +121,7 @@ module.exports = function draw(gd, opts) { // IF expandMargin return a Promise (which is truthy), // we're under a doAutoMargin redraw, so we don't have to // draw the remaining pieces below - if(opts._main && expandMargin(gd)) return; + if(opts._isLegend && expandMargin(gd)) return; var gs = fullLayout._size; var bw = opts.borderwidth; @@ -129,7 +129,7 @@ module.exports = function draw(gd, opts) { var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width; var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight; - if(opts._main && fullLayout.margin.autoexpand) { + if(opts._isLegend && fullLayout.margin.autoexpand) { var lx0 = lx; var ly0 = ly; @@ -146,18 +146,18 @@ module.exports = function draw(gd, opts) { // Set size and position of all the elements that make up a legend: // legend, background and border, scroll box and scroll bar as well as title - if(opts._main) Drawing.setTranslate(legend, lx, ly); + if(opts._isLegend) Drawing.setTranslate(legend, lx, ly); // to be safe, remove previous listeners scrollBar.on('.drag', null); legend.on('wheel', null); - if(!opts._main || opts._height <= opts._maxHeight || gd._context.staticPlot) { + if(!opts._isLegend || opts._height <= opts._maxHeight || gd._context.staticPlot) { // if scrollbar should not be shown. var height = opts._effHeight; - // if not the main legend, let it be its full size - if(!opts._main) height = opts._height; + // if unified hover, let it be its full size + if(!opts._isLegend) height = opts._height; bg.attr({ width: opts._width - bw, @@ -386,7 +386,7 @@ function drawTexts(g, gd, opts) { var trace = legendItem.trace; var isPieLike = Registry.traceIs(trace, 'pie-like'); var traceIndex = trace.index; - var isEditable = opts._main && gd._context.edits.legendText && !isPieLike; + var isEditable = opts._isLegend && gd._context.edits.legendText && !isPieLike; var maxNameLength = opts._maxNameLength; var name; @@ -491,7 +491,7 @@ function setupTraceToggle(g, gd) { } function textLayout(s, g, gd, opts) { - if(!opts._main) s.attr('data-notex', true); // do not process MathJax if not main + if(!opts._isLegend) s.attr('data-notex', true); // do not process MathJax for unified hover svgTextUtils.convertToTspans(s, gd, function() { computeTextDimensions(g, gd, opts); }); @@ -499,7 +499,7 @@ function textLayout(s, g, gd, opts) { function computeTextDimensions(g, gd, opts) { var legendItem = g.data()[0][0]; - if(opts._main && legendItem && !legendItem.trace.showlegend) { + if(opts._isLegend && legendItem && !legendItem.trace.showlegend) { g.remove(); return; } diff --git a/src/components/legend/get_legend_data.js b/src/components/legend/get_legend_data.js index 4fcb91b1e4d..d90d760bfc5 100644 --- a/src/components/legend/get_legend_data.js +++ b/src/components/legend/get_legend_data.js @@ -11,7 +11,6 @@ module.exports = function getLegendData(calcdata, opts) { var lgroupi = 0; var maxNameLength = 0; var i, j; - var main = opts._main; function addOneItem(legendGroup, legendItem) { // each '' legend group is treated as a separate group @@ -37,7 +36,7 @@ module.exports = function getLegendData(calcdata, opts) { var trace = cd0.trace; var lgroup = trace.legendgroup; - if(main && (!trace.visible || !trace.showlegend)) continue; + if(opts._isLegend && (!trace.visible || !trace.showlegend)) continue; if(Registry.traceIs(trace, 'pie-like')) { if(!slicesShown[lgroup]) slicesShown[lgroup] = {}; From 6abbfb2324cf027ed2bd9ff335f48d6e180dda40 Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 29 Mar 2021 11:27:06 -0400 Subject: [PATCH 02/10] use _inHover instead of !_isLegend for clarity --- src/components/fx/hover.js | 1 + src/components/legend/draw.js | 27 ++++++++++++------------ src/components/legend/get_legend_data.js | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 8e345bd9836..76d00608489 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1045,6 +1045,7 @@ function createHoverText(hoverData, opts, gd) { legendOpts.layer = container; // Draw unified hover label + legendOpts._inHover = true; legendDraw(gd, legendOpts); // Position the hover diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 365a14feed8..63254ad62e6 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -30,19 +30,20 @@ module.exports = function draw(gd, opts) { // Check whether this is the main legend (ie. called without any opts) if(!opts) { opts = fullLayout.legend || {}; - opts._isLegend = true; layer = fullLayout._infolayer; } else { layer = opts.layer; clipId += '-hover'; } + var inHover = !!opts._inHover; + if(!layer) return; if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0; var legendData; - if(opts._isLegend) { + if(!inHover) { if(!gd.calcdata) return; legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts); } else { @@ -52,14 +53,14 @@ module.exports = function draw(gd, opts) { var hiddenSlices = fullLayout.hiddenlabels || []; - if(opts._isLegend && (!fullLayout.showlegend || !legendData.length)) { + if(!inHover && (!fullLayout.showlegend || !legendData.length)) { layer.selectAll('.legend').remove(); fullLayout._topdefs.select('#' + clipId).remove(); return Plots.autoMargin(gd, 'legend'); } var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) { - if(opts._isLegend) s.attr('pointer-events', 'all'); + if(!inHover) s.attr('pointer-events', 'all'); }); var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) { @@ -112,7 +113,7 @@ module.exports = function draw(gd, opts) { }) .each(function() { d3.select(this).call(drawTexts, gd, opts); }) .call(style, gd, opts) - .each(function() { if(opts._isLegend) d3.select(this).call(setupTraceToggle, gd); }); + .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); }); Lib.syncOrAsync([ Plots.previousPromises, @@ -121,7 +122,7 @@ module.exports = function draw(gd, opts) { // IF expandMargin return a Promise (which is truthy), // we're under a doAutoMargin redraw, so we don't have to // draw the remaining pieces below - if(opts._isLegend && expandMargin(gd)) return; + if(!inHover && expandMargin(gd)) return; var gs = fullLayout._size; var bw = opts.borderwidth; @@ -129,7 +130,7 @@ module.exports = function draw(gd, opts) { var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width; var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight; - if(opts._isLegend && fullLayout.margin.autoexpand) { + if(!inHover && fullLayout.margin.autoexpand) { var lx0 = lx; var ly0 = ly; @@ -146,18 +147,18 @@ module.exports = function draw(gd, opts) { // Set size and position of all the elements that make up a legend: // legend, background and border, scroll box and scroll bar as well as title - if(opts._isLegend) Drawing.setTranslate(legend, lx, ly); + if(!inHover) Drawing.setTranslate(legend, lx, ly); // to be safe, remove previous listeners scrollBar.on('.drag', null); legend.on('wheel', null); - if(!opts._isLegend || opts._height <= opts._maxHeight || gd._context.staticPlot) { + if(inHover || opts._height <= opts._maxHeight || gd._context.staticPlot) { // if scrollbar should not be shown. var height = opts._effHeight; // if unified hover, let it be its full size - if(!opts._isLegend) height = opts._height; + if(inHover) height = opts._height; bg.attr({ width: opts._width - bw, @@ -386,7 +387,7 @@ function drawTexts(g, gd, opts) { var trace = legendItem.trace; var isPieLike = Registry.traceIs(trace, 'pie-like'); var traceIndex = trace.index; - var isEditable = opts._isLegend && gd._context.edits.legendText && !isPieLike; + var isEditable = !opts._inHover && gd._context.edits.legendText && !isPieLike; var maxNameLength = opts._maxNameLength; var name; @@ -491,7 +492,7 @@ function setupTraceToggle(g, gd) { } function textLayout(s, g, gd, opts) { - if(!opts._isLegend) s.attr('data-notex', true); // do not process MathJax for unified hover + if(opts._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover svgTextUtils.convertToTspans(s, gd, function() { computeTextDimensions(g, gd, opts); }); @@ -499,7 +500,7 @@ function textLayout(s, g, gd, opts) { function computeTextDimensions(g, gd, opts) { var legendItem = g.data()[0][0]; - if(opts._isLegend && legendItem && !legendItem.trace.showlegend) { + if(!opts._inHover && legendItem && !legendItem.trace.showlegend) { g.remove(); return; } diff --git a/src/components/legend/get_legend_data.js b/src/components/legend/get_legend_data.js index d90d760bfc5..7e09fa32337 100644 --- a/src/components/legend/get_legend_data.js +++ b/src/components/legend/get_legend_data.js @@ -36,7 +36,7 @@ module.exports = function getLegendData(calcdata, opts) { var trace = cd0.trace; var lgroup = trace.legendgroup; - if(opts._isLegend && (!trace.visible || !trace.showlegend)) continue; + if(!opts._inHover && (!trace.visible || !trace.showlegend)) continue; if(Registry.traceIs(trace, 'pie-like')) { if(!slicesShown[lgroup]) slicesShown[lgroup] = {}; From ee759471518f53a0bf3a79403cbacee885fa67db Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 31 Mar 2021 16:16:13 -0400 Subject: [PATCH 03/10] better handle of main title --- src/components/legend/draw.js | 53 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 63254ad62e6..5d704cae8d9 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -22,6 +22,8 @@ var getLegendData = require('./get_legend_data'); var style = require('./style'); var helpers = require('./helpers'); +var MAIN_TITLE = 1; + module.exports = function draw(gd, opts) { var fullLayout = gd._fullLayout; var clipId = 'legend' + fullLayout._uid; @@ -85,7 +87,7 @@ module.exports = function draw(gd, opts) { .call(Drawing.font, title.font) .text(title.text); - textLayout(titleEl, scrollBox, gd, opts); // handle mathjax or multi-line text and compute title height + textLayout(titleEl, scrollBox, gd, opts, MAIN_TITLE); // handle mathjax or multi-line text and compute title height } else { scrollBox.selectAll('.legendtitletext').remove(); } @@ -386,7 +388,6 @@ function drawTexts(g, gd, opts) { var legendItem = g.data()[0][0]; var trace = legendItem.trace; var isPieLike = Registry.traceIs(trace, 'pie-like'); - var traceIndex = trace.index; var isEditable = !opts._inHover && gd._context.edits.legendText && !isPieLike; var maxNameLength = opts._maxNameLength; @@ -432,7 +433,7 @@ function drawTexts(g, gd, opts) { update.name = newName; } - return Registry.call('_guiRestyle', gd, update, traceIndex); + return Registry.call('_guiRestyle', gd, update, trace.index); }); } else { textLayout(textEl, g, gd, opts); @@ -491,14 +492,14 @@ function setupTraceToggle(g, gd) { }); } -function textLayout(s, g, gd, opts) { +function textLayout(s, g, gd, opts, aTitle) { if(opts._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover svgTextUtils.convertToTspans(s, gd, function() { - computeTextDimensions(g, gd, opts); + computeTextDimensions(g, gd, opts, aTitle); }); } -function computeTextDimensions(g, gd, opts) { +function computeTextDimensions(g, gd, opts, aTitle) { var legendItem = g.data()[0][0]; if(!opts._inHover && legendItem && !legendItem.trace.showlegend) { g.remove(); @@ -509,7 +510,7 @@ function computeTextDimensions(g, gd, opts) { var mathjaxNode = mathjaxGroup.node(); if(!opts) opts = gd._fullLayout.legend; var bw = opts.borderwidth; - var lineHeight = (legendItem ? opts : opts.title).font.size * LINE_SPACING; + var lineHeight = (aTitle === MAIN_TITLE ? opts.title : opts).font.size * LINE_SPACING; var height, width; if(mathjaxNode) { @@ -518,14 +519,14 @@ function computeTextDimensions(g, gd, opts) { height = mathjaxBB.height; width = mathjaxBB.width; - if(legendItem) { + if(aTitle === MAIN_TITLE) { + Drawing.setTranslate(mathjaxGroup, bw, bw + height * 0.75); + } else { // legend item Drawing.setTranslate(mathjaxGroup, 0, height * 0.25); - } else { // case of title - Drawing.setTranslate(mathjaxGroup, bw, height * 0.75 + bw); } } else { - var textEl = g.select(legendItem ? - '.legendtext' : '.legendtitletext' + var textEl = g.select(aTitle === MAIN_TITLE ? + '.legendtitletext' : '.legendtext' ); var textLines = svgTextUtils.lineCount(textEl); var textNode = textEl.node(); @@ -535,22 +536,26 @@ function computeTextDimensions(g, gd, opts) { // approximation to height offset to center the font // to avoid getBoundingClientRect - var textY = lineHeight * ((textLines - 1) / 2 - 0.3); - if(legendItem) { - var textGap = opts.itemwidth + constants.itemGap * 2; - svgTextUtils.positionText(textEl, textGap, -textY); - } else { // case of title - svgTextUtils.positionText(textEl, constants.titlePad + bw, lineHeight + bw); + if(aTitle === MAIN_TITLE) { + svgTextUtils.positionText(textEl, + bw + constants.titlePad, + bw + lineHeight + ); + } else { // legend item + svgTextUtils.positionText(textEl, + opts.itemwidth + constants.itemGap * 2, + -lineHeight * ((textLines - 1) / 2 - 0.3) + ); } } - if(legendItem) { + if(aTitle === MAIN_TITLE) { + opts._titleWidth = width; + opts._titleHeight = height; + } else { // legend item legendItem.lineHeight = lineHeight; legendItem.height = Math.max(height, 16) + 3; legendItem.width = width; - } else { // case of title - opts._titleWidth = width; - opts._titleHeight = height; } } @@ -599,6 +604,8 @@ function computeLegendDimensions(gd, groups, traces, opts) { var isBelowPlotArea = opts.y < 0 || (opts.y === 0 && yanchor === 'top'); var isAbovePlotArea = opts.y > 1 || (opts.y === 1 && yanchor === 'bottom'); + var traceGroupGap = opts.tracegroupgap; + // - if below/above plot area, give it the maximum potential margin-push value // - otherwise, extend the height of the plot area opts._maxHeight = Math.max( @@ -681,7 +688,7 @@ function computeLegendDimensions(gd, groups, traces, opts) { if((next + bw + groupOffsetX) > opts._maxWidth) { maxRowWidth = Math.max(maxRowWidth, groupOffsetX); groupOffsetX = 0; - groupOffsetY += maxGroupHeightInRow + opts.tracegroupgap; + groupOffsetY += maxGroupHeightInRow + traceGroupGap; maxGroupHeightInRow = offsetY; } From 2daab813cfd65f4cd739c2e32b3543c7e6f5425d Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 5 Apr 2021 15:00:25 -0400 Subject: [PATCH 04/10] rename confusing opts in legend/draw to container i.e. legend/hover --- src/components/legend/draw.js | 250 +++++++++++++++++----------------- 1 file changed, 125 insertions(+), 125 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 5d704cae8d9..3f865788fb1 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -24,21 +24,21 @@ var helpers = require('./helpers'); var MAIN_TITLE = 1; -module.exports = function draw(gd, opts) { +module.exports = function draw(gd, container) { var fullLayout = gd._fullLayout; var clipId = 'legend' + fullLayout._uid; var layer; - // Check whether this is the main legend (ie. called without any opts) - if(!opts) { - opts = fullLayout.legend || {}; + // Check whether this is the main legend (ie. called without any container) + if(!container) { + container = fullLayout.legend || {}; layer = fullLayout._infolayer; } else { - layer = opts.layer; + layer = container.layer; clipId += '-hover'; } - var inHover = !!opts._inHover; + var inHover = !!container._inHover; if(!layer) return; @@ -47,10 +47,10 @@ module.exports = function draw(gd, opts) { var legendData; if(!inHover) { if(!gd.calcdata) return; - legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts); + legendData = fullLayout.showlegend && getLegendData(gd.calcdata, container); } else { - if(!opts.entries) return; - legendData = getLegendData(opts.entries, opts); + if(!container.entries) return; + legendData = getLegendData(container.entries, container); } var hiddenSlices = fullLayout.hiddenlabels || []; @@ -72,22 +72,22 @@ module.exports = function draw(gd, opts) { var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) { s.attr('shape-rendering', 'crispEdges'); }); - bg.call(Color.stroke, opts.bordercolor) - .call(Color.fill, opts.bgcolor) - .style('stroke-width', opts.borderwidth + 'px'); + bg.call(Color.stroke, container.bordercolor) + .call(Color.fill, container.bgcolor) + .style('stroke-width', container.borderwidth + 'px'); var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox'); - var title = opts.title; - opts._titleWidth = 0; - opts._titleHeight = 0; + var title = container.title; + container._titleWidth = 0; + container._titleHeight = 0; if(title.text) { var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext'); titleEl.attr('text-anchor', 'start') .call(Drawing.font, title.font) .text(title.text); - textLayout(titleEl, scrollBox, gd, opts, MAIN_TITLE); // handle mathjax or multi-line text and compute title height + textLayout(titleEl, scrollBox, gd, container, MAIN_TITLE); // handle mathjax or multi-line text and compute title height } else { scrollBox.selectAll('.legendtitletext').remove(); } @@ -113,13 +113,13 @@ module.exports = function draw(gd, opts) { return trace.visible === 'legendonly' ? 0.5 : 1; } }) - .each(function() { d3.select(this).call(drawTexts, gd, opts); }) - .call(style, gd, opts) + .each(function() { d3.select(this).call(drawTexts, gd, container); }) + .call(style, gd, container) .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); }); Lib.syncOrAsync([ Plots.previousPromises, - function() { return computeLegendDimensions(gd, groups, traces, opts); }, + function() { return computeLegendDimensions(gd, groups, traces, container); }, function() { // IF expandMargin return a Promise (which is truthy), // we're under a doAutoMargin redraw, so we don't have to @@ -127,17 +127,17 @@ module.exports = function draw(gd, opts) { if(!inHover && expandMargin(gd)) return; var gs = fullLayout._size; - var bw = opts.borderwidth; + var bw = container.borderwidth; - var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width; - var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight; + var lx = gs.l + gs.w * container.x - FROM_TL[getXanchor(container)] * container._width; + var ly = gs.t + gs.h * (1 - container.y) - FROM_TL[getYanchor(container)] * container._effHeight; if(!inHover && fullLayout.margin.autoexpand) { var lx0 = lx; var ly0 = ly; - lx = Lib.constrain(lx, 0, fullLayout.width - opts._width); - ly = Lib.constrain(ly, 0, fullLayout.height - opts._effHeight); + lx = Lib.constrain(lx, 0, fullLayout.width - container._width); + ly = Lib.constrain(ly, 0, fullLayout.height - container._effHeight); if(lx !== lx0) { Lib.log('Constrain legend.x to make legend fit inside graph'); @@ -155,15 +155,15 @@ module.exports = function draw(gd, opts) { scrollBar.on('.drag', null); legend.on('wheel', null); - if(inHover || opts._height <= opts._maxHeight || gd._context.staticPlot) { + if(inHover || container._height <= container._maxHeight || gd._context.staticPlot) { // if scrollbar should not be shown. - var height = opts._effHeight; + var height = container._effHeight; // if unified hover, let it be its full size - if(inHover) height = opts._height; + if(inHover) height = container._height; bg.attr({ - width: opts._width - bw, + width: container._width - bw, height: height - bw, x: bw / 2, y: bw / 2 @@ -172,7 +172,7 @@ module.exports = function draw(gd, opts) { Drawing.setTranslate(scrollBox, 0, 0); clipPath.select('rect').attr({ - width: opts._width - 2 * bw, + width: container._width - 2 * bw, height: height - 2 * bw, x: bw, y: bw @@ -181,36 +181,36 @@ module.exports = function draw(gd, opts) { Drawing.setClipUrl(scrollBox, clipId, gd); Drawing.setRect(scrollBar, 0, 0, 0, 0); - delete opts._scrollY; + delete container._scrollY; } else { var scrollBarHeight = Math.max(constants.scrollBarMinHeight, - opts._effHeight * opts._effHeight / opts._height); - var scrollBarYMax = opts._effHeight - + container._effHeight * container._effHeight / container._height); + var scrollBarYMax = container._effHeight - scrollBarHeight - 2 * constants.scrollBarMargin; - var scrollBoxYMax = opts._height - opts._effHeight; + var scrollBoxYMax = container._height - container._effHeight; var scrollRatio = scrollBarYMax / scrollBoxYMax; - var scrollBoxY = Math.min(opts._scrollY || 0, scrollBoxYMax); + var scrollBoxY = Math.min(container._scrollY || 0, scrollBoxYMax); // increase the background and clip-path width // by the scrollbar width and margin bg.attr({ - width: opts._width - + width: container._width - 2 * bw + constants.scrollBarWidth + constants.scrollBarMargin, - height: opts._effHeight - bw, + height: container._effHeight - bw, x: bw / 2, y: bw / 2 }); clipPath.select('rect').attr({ - width: opts._width - + width: container._width - 2 * bw + constants.scrollBarWidth + constants.scrollBarMargin, - height: opts._effHeight - 2 * bw, + height: container._effHeight - 2 * bw, x: bw, y: bw + scrollBoxY }); @@ -222,7 +222,7 @@ module.exports = function draw(gd, opts) { // scroll legend by mousewheel or touchpad swipe up/down legend.on('wheel', function() { scrollBoxY = Lib.constrain( - opts._scrollY + + container._scrollY + ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax), 0, scrollBoxYMax); scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); @@ -288,12 +288,12 @@ module.exports = function draw(gd, opts) { } function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) { - opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; + container._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; Drawing.setTranslate(scrollBox, 0, -scrollBoxY); Drawing.setRect( scrollBar, - opts._width, + container._width, constants.scrollBarMargin + scrollBoxY * scrollRatio, constants.scrollBarWidth, scrollBarHeight @@ -320,8 +320,8 @@ module.exports = function draw(gd, opts) { Drawing.setTranslate(legend, newX, newY); - xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, opts.xanchor); - yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor); + xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, container.xanchor); + yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, container.yanchor); }, doneFn: function() { if(xf !== undefined && yf !== undefined) { @@ -384,15 +384,15 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { } } -function drawTexts(g, gd, opts) { +function drawTexts(g, gd, container) { var legendItem = g.data()[0][0]; var trace = legendItem.trace; var isPieLike = Registry.traceIs(trace, 'pie-like'); - var isEditable = !opts._inHover && gd._context.edits.legendText && !isPieLike; - var maxNameLength = opts._maxNameLength; + var isEditable = !container._inHover && gd._context.edits.legendText && !isPieLike; + var maxNameLength = container._maxNameLength; var name; - if(!opts.entries) { + if(!container.entries) { name = isPieLike ? legendItem.label : trace.name; if(trace._meta) { name = Lib.templateString(name, trace._meta); @@ -404,18 +404,18 @@ function drawTexts(g, gd, opts) { var textEl = Lib.ensureSingle(g, 'text', 'legendtext'); textEl.attr('text-anchor', 'start') - .call(Drawing.font, opts.font) + .call(Drawing.font, container.font) .text(isEditable ? ensureLength(name, maxNameLength) : name); - var textGap = opts.itemwidth + constants.itemGap * 2; + var textGap = container.itemwidth + constants.itemGap * 2; svgTextUtils.positionText(textEl, textGap, 0); if(isEditable) { textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name}) - .call(textLayout, g, gd, opts) + .call(textLayout, g, gd, container) .on('edit', function(newName) { this.text(ensureLength(newName, maxNameLength)) - .call(textLayout, g, gd, opts); + .call(textLayout, g, gd, container); var fullInput = legendItem.trace._fullInput || {}; var update = {}; @@ -436,7 +436,7 @@ function drawTexts(g, gd, opts) { return Registry.call('_guiRestyle', gd, update, trace.index); }); } else { - textLayout(textEl, g, gd, opts); + textLayout(textEl, g, gd, container); } } @@ -492,25 +492,25 @@ function setupTraceToggle(g, gd) { }); } -function textLayout(s, g, gd, opts, aTitle) { - if(opts._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover +function textLayout(s, g, gd, container, aTitle) { + if(container._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover svgTextUtils.convertToTspans(s, gd, function() { - computeTextDimensions(g, gd, opts, aTitle); + computeTextDimensions(g, gd, container, aTitle); }); } -function computeTextDimensions(g, gd, opts, aTitle) { +function computeTextDimensions(g, gd, container, aTitle) { var legendItem = g.data()[0][0]; - if(!opts._inHover && legendItem && !legendItem.trace.showlegend) { + if(!container._inHover && legendItem && !legendItem.trace.showlegend) { g.remove(); return; } var mathjaxGroup = g.select('g[class*=math-group]'); var mathjaxNode = mathjaxGroup.node(); - if(!opts) opts = gd._fullLayout.legend; - var bw = opts.borderwidth; - var lineHeight = (aTitle === MAIN_TITLE ? opts.title : opts).font.size * LINE_SPACING; + if(!container) container = gd._fullLayout.legend; + var bw = container.borderwidth; + var lineHeight = (aTitle === MAIN_TITLE ? container.title : container).font.size * LINE_SPACING; var height, width; if(mathjaxNode) { @@ -543,15 +543,15 @@ function computeTextDimensions(g, gd, opts, aTitle) { ); } else { // legend item svgTextUtils.positionText(textEl, - opts.itemwidth + constants.itemGap * 2, + container.itemwidth + constants.itemGap * 2, -lineHeight * ((textLines - 1) / 2 - 0.3) ); } } if(aTitle === MAIN_TITLE) { - opts._titleWidth = width; - opts._titleHeight = height; + container._titleWidth = width; + container._titleHeight = height; } else { // legend item legendItem.lineHeight = lineHeight; legendItem.height = Math.max(height, 16) + 3; @@ -559,17 +559,17 @@ function computeTextDimensions(g, gd, opts, aTitle) { } } -function getTitleSize(opts) { +function getTitleSize(container) { var w = 0; var h = 0; - var side = opts.title.side; + var side = container.title.side; if(side) { if(side.indexOf('left') !== -1) { - w = opts._titleWidth; + w = container._titleWidth; } if(side.indexOf('top') !== -1) { - h = opts._titleHeight; + h = container._titleHeight; } } @@ -586,70 +586,70 @@ function getTitleSize(opts) { * - _width: legend width * - _maxWidth (for orientation:h only): maximum width before starting new row */ -function computeLegendDimensions(gd, groups, traces, opts) { +function computeLegendDimensions(gd, groups, traces, container) { var fullLayout = gd._fullLayout; - if(!opts) opts = fullLayout.legend; + if(!container) container = fullLayout.legend; var gs = fullLayout._size; - var isVertical = helpers.isVertical(opts); - var isGrouped = helpers.isGrouped(opts); + var isVertical = helpers.isVertical(container); + var isGrouped = helpers.isGrouped(container); - var bw = opts.borderwidth; + var bw = container.borderwidth; var bw2 = 2 * bw; var itemGap = constants.itemGap; - var textGap = opts.itemwidth + itemGap * 2; + var textGap = container.itemwidth + itemGap * 2; var endPad = 2 * (bw + itemGap); - var yanchor = getYanchor(opts); - var isBelowPlotArea = opts.y < 0 || (opts.y === 0 && yanchor === 'top'); - var isAbovePlotArea = opts.y > 1 || (opts.y === 1 && yanchor === 'bottom'); + var yanchor = getYanchor(container); + var isBelowPlotArea = container.y < 0 || (container.y === 0 && yanchor === 'top'); + var isAbovePlotArea = container.y > 1 || (container.y === 1 && yanchor === 'bottom'); - var traceGroupGap = opts.tracegroupgap; + var traceGroupGap = container.tracegroupgap; // - if below/above plot area, give it the maximum potential margin-push value // - otherwise, extend the height of the plot area - opts._maxHeight = Math.max( + container._maxHeight = Math.max( (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h, 30 ); var toggleRectWidth = 0; - opts._width = 0; - opts._height = 0; - var titleSize = getTitleSize(opts); + container._width = 0; + container._height = 0; + var titleSize = getTitleSize(container); if(isVertical) { traces.each(function(d) { var h = d[0].height; Drawing.setTranslate(this, bw + titleSize[0], - bw + titleSize[1] + opts._height + h / 2 + itemGap + bw + titleSize[1] + container._height + h / 2 + itemGap ); - opts._height += h; - opts._width = Math.max(opts._width, d[0].width); + container._height += h; + container._width = Math.max(container._width, d[0].width); }); - toggleRectWidth = textGap + opts._width; - opts._width += itemGap + textGap + bw2; - opts._height += endPad; + toggleRectWidth = textGap + container._width; + container._width += itemGap + textGap + bw2; + container._height += endPad; if(isGrouped) { groups.each(function(d, i) { - Drawing.setTranslate(this, 0, i * opts.tracegroupgap); + Drawing.setTranslate(this, 0, i * container.tracegroupgap); }); - opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap; + container._height += (container._lgroupsLength - 1) * container.tracegroupgap; } } else { - var xanchor = getXanchor(opts); - var isLeftOfPlotArea = opts.x < 0 || (opts.x === 0 && xanchor === 'right'); - var isRightOfPlotArea = opts.x > 1 || (opts.x === 1 && xanchor === 'left'); + var xanchor = getXanchor(container); + var isLeftOfPlotArea = container.x < 0 || (container.x === 0 && xanchor === 'right'); + var isRightOfPlotArea = container.x > 1 || (container.x === 1 && xanchor === 'left'); var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea; var hw = fullLayout.width / 2; // - if placed within x-margins, extend the width of the plot area // - else if below/above plot area and anchored in the margin, extend to opposite margin, // - otherwise give it the maximum potential margin-push value - opts._maxWidth = Math.max( + container._maxWidth = Math.max( isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) : isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) : gs.w, @@ -685,7 +685,7 @@ function computeLegendDimensions(gd, groups, traces, opts) { var next = maxWidthInGroup + itemGap; - if((next + bw + groupOffsetX) > opts._maxWidth) { + if((next + bw + groupOffsetX) > container._maxWidth) { maxRowWidth = Math.max(maxRowWidth, groupOffsetX); groupOffsetX = 0; groupOffsetY += maxGroupHeightInRow + traceGroupGap; @@ -697,11 +697,11 @@ function computeLegendDimensions(gd, groups, traces, opts) { groupOffsetX += next; }); - opts._width = Math.max(maxRowWidth, groupOffsetX) + bw; - opts._height = groupOffsetY + maxGroupHeightInRow + endPad; + container._width = Math.max(maxRowWidth, groupOffsetX) + bw; + container._height = groupOffsetY + maxGroupHeightInRow + endPad; } else { var nTraces = traces.size(); - var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < opts._maxWidth; + var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < container._maxWidth; var maxItemHeightInRow = 0; var offsetX = 0; @@ -712,11 +712,11 @@ function computeLegendDimensions(gd, groups, traces, opts) { var w = textGap + d[0].width; var next = (oneRowLegend ? w : maxItemWidth) + itemGap; - if((next + bw + offsetX - itemGap) >= opts._maxWidth) { + if((next + bw + offsetX - itemGap) >= container._maxWidth) { maxRowWidth = Math.max(maxRowWidth, rowWidth); offsetX = 0; offsetY += maxItemHeightInRow; - opts._height += maxItemHeightInRow; + container._height += maxItemHeightInRow; maxItemHeightInRow = 0; } @@ -731,30 +731,30 @@ function computeLegendDimensions(gd, groups, traces, opts) { }); if(oneRowLegend) { - opts._width = offsetX + bw2; - opts._height = maxItemHeightInRow + endPad; + container._width = offsetX + bw2; + container._height = maxItemHeightInRow + endPad; } else { - opts._width = Math.max(maxRowWidth, rowWidth) + bw2; - opts._height += maxItemHeightInRow + endPad; + container._width = Math.max(maxRowWidth, rowWidth) + bw2; + container._height += maxItemHeightInRow + endPad; } } } - opts._width = Math.ceil( + container._width = Math.ceil( Math.max( - opts._width + titleSize[0], - opts._titleWidth + 2 * (bw + constants.titlePad) + container._width + titleSize[0], + container._titleWidth + 2 * (bw + constants.titlePad) ) ); - opts._height = Math.ceil( + container._height = Math.ceil( Math.max( - opts._height + titleSize[1], - opts._titleHeight + 2 * (bw + constants.itemGap) + container._height + titleSize[1], + container._titleHeight + 2 * (bw + constants.itemGap) ) ); - opts._effHeight = Math.min(opts._height, opts._maxHeight); + container._effHeight = Math.min(container._height, container._maxHeight); var edits = gd._context.edits; var isEditable = edits.legendText || edits.legendPosition; @@ -769,28 +769,28 @@ function computeLegendDimensions(gd, groups, traces, opts) { function expandMargin(gd) { var fullLayout = gd._fullLayout; - var opts = fullLayout.legend; - var xanchor = getXanchor(opts); - var yanchor = getYanchor(opts); + var container = fullLayout.legend; + var xanchor = getXanchor(container); + var yanchor = getYanchor(container); return Plots.autoMargin(gd, 'legend', { - x: opts.x, - y: opts.y, - l: opts._width * (FROM_TL[xanchor]), - r: opts._width * (FROM_BR[xanchor]), - b: opts._effHeight * (FROM_BR[yanchor]), - t: opts._effHeight * (FROM_TL[yanchor]) + x: container.x, + y: container.y, + l: container._width * (FROM_TL[xanchor]), + r: container._width * (FROM_BR[xanchor]), + b: container._effHeight * (FROM_BR[yanchor]), + t: container._effHeight * (FROM_TL[yanchor]) }); } -function getXanchor(opts) { - return Lib.isRightAnchor(opts) ? 'right' : - Lib.isCenterAnchor(opts) ? 'center' : +function getXanchor(container) { + return Lib.isRightAnchor(container) ? 'right' : + Lib.isCenterAnchor(container) ? 'center' : 'left'; } -function getYanchor(opts) { - return Lib.isBottomAnchor(opts) ? 'bottom' : - Lib.isMiddleAnchor(opts) ? 'middle' : +function getYanchor(container) { + return Lib.isBottomAnchor(container) ? 'bottom' : + Lib.isMiddleAnchor(container) ? 'middle' : 'top'; } From 61d01e67165dea1dc295a8b6d01a955a480af2ec Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 5 Apr 2021 15:26:36 -0400 Subject: [PATCH 05/10] refactor if without not --- src/components/legend/draw.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 3f865788fb1..e7297b74196 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -30,12 +30,12 @@ module.exports = function draw(gd, container) { var layer; // Check whether this is the main legend (ie. called without any container) - if(!container) { - container = fullLayout.legend || {}; - layer = fullLayout._infolayer; - } else { + if(container) { layer = container.layer; clipId += '-hover'; + } else { + container = fullLayout.legend || {}; + layer = fullLayout._infolayer; } var inHover = !!container._inHover; From f717986d18147417c875af830f0b6d9d3b17dacf Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 5 Apr 2021 15:27:36 -0400 Subject: [PATCH 06/10] let _inHover handle the case of unified hover --- src/components/legend/draw.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index e7297b74196..1c8965a9601 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -30,7 +30,7 @@ module.exports = function draw(gd, container) { var layer; // Check whether this is the main legend (ie. called without any container) - if(container) { + if(container && container._inHover) { layer = container.layer; clipId += '-hover'; } else { From 0a4fd7397e6a16b8bd6f1561dd82a07913a4b175 Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 5 Apr 2021 16:13:22 -0400 Subject: [PATCH 07/10] more of let _inHover handle unified hover in legend draw --- src/components/legend/draw.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 1c8965a9601..3d3a8691709 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -30,7 +30,8 @@ module.exports = function draw(gd, container) { var layer; // Check whether this is the main legend (ie. called without any container) - if(container && container._inHover) { + var inHover = container && container._inHover; + if(inHover) { layer = container.layer; clipId += '-hover'; } else { @@ -38,8 +39,6 @@ module.exports = function draw(gd, container) { layer = fullLayout._infolayer; } - var inHover = !!container._inHover; - if(!layer) return; if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0; From 9c9ee75146a02f865ee412efaa3ee9003d9d3dbd Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 5 Apr 2021 16:25:54 -0400 Subject: [PATCH 08/10] ensure container passed into legend _draw function --- src/components/legend/draw.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 3d3a8691709..b5755bd992a 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -24,18 +24,21 @@ var helpers = require('./helpers'); var MAIN_TITLE = 1; -module.exports = function draw(gd, container) { +module.exports = function draw(gd, opts) { + if(!opts) opts = gd._fullLayout.legend || {}; + return _draw(gd, opts); +}; + +function _draw(gd, container) { var fullLayout = gd._fullLayout; var clipId = 'legend' + fullLayout._uid; var layer; - // Check whether this is the main legend (ie. called without any container) - var inHover = container && container._inHover; + var inHover = container._inHover; if(inHover) { layer = container.layer; clipId += '-hover'; } else { - container = fullLayout.legend || {}; layer = fullLayout._infolayer; } @@ -342,7 +345,7 @@ module.exports = function draw(gd, container) { }); } }], gd); -}; +} function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { var trace = legendItem.data()[0][0].trace; From 7337cb3e4522dd3445f4fd89527f50bad7ebc251 Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 5 Apr 2021 16:39:43 -0400 Subject: [PATCH 09/10] rename legendOpts to mockLegend --- src/components/fx/hover.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 76d00608489..a8738cf3f6e 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1010,10 +1010,10 @@ function createHoverText(hoverData, opts, gd) { }; var mockLayoutOut = {}; legendSupplyDefaults(mockLayoutIn, mockLayoutOut, gd._fullData); - var legendOpts = mockLayoutOut.legend; + var mockLegend = mockLayoutOut.legend; // prepare items for the legend - legendOpts.entries = []; + mockLegend.entries = []; for(var j = 0; j < hoverData.length; j++) { var texts = getHoverLabelText(hoverData[j], true, hovermode, fullLayout, t0); var text = texts[0]; @@ -1039,14 +1039,14 @@ function createHoverText(hoverData, opts, gd) { } pt._distinct = true; - legendOpts.entries.push([pt]); + mockLegend.entries.push([pt]); } - legendOpts.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;}); - legendOpts.layer = container; + mockLegend.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;}); + mockLegend.layer = container; // Draw unified hover label - legendOpts._inHover = true; - legendDraw(gd, legendOpts); + mockLegend._inHover = true; + legendDraw(gd, mockLegend); // Position the hover var ly = Lib.mean(hoverData.map(function(c) {return (c.y0 + c.y1) / 2;})); From ab233979d24b285565016e9cf209ded2c43e2acb Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 5 Apr 2021 16:49:19 -0400 Subject: [PATCH 10/10] rename container to legendObj i.e. to avoid conflict with legend.container --- src/components/legend/draw.js | 244 +++++++++++++++++----------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index b5755bd992a..d5b614a3506 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -29,14 +29,14 @@ module.exports = function draw(gd, opts) { return _draw(gd, opts); }; -function _draw(gd, container) { +function _draw(gd, legendObj) { var fullLayout = gd._fullLayout; var clipId = 'legend' + fullLayout._uid; var layer; - var inHover = container._inHover; + var inHover = legendObj._inHover; if(inHover) { - layer = container.layer; + layer = legendObj.layer; clipId += '-hover'; } else { layer = fullLayout._infolayer; @@ -49,10 +49,10 @@ function _draw(gd, container) { var legendData; if(!inHover) { if(!gd.calcdata) return; - legendData = fullLayout.showlegend && getLegendData(gd.calcdata, container); + legendData = fullLayout.showlegend && getLegendData(gd.calcdata, legendObj); } else { - if(!container.entries) return; - legendData = getLegendData(container.entries, container); + if(!legendObj.entries) return; + legendData = getLegendData(legendObj.entries, legendObj); } var hiddenSlices = fullLayout.hiddenlabels || []; @@ -74,22 +74,22 @@ function _draw(gd, container) { var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) { s.attr('shape-rendering', 'crispEdges'); }); - bg.call(Color.stroke, container.bordercolor) - .call(Color.fill, container.bgcolor) - .style('stroke-width', container.borderwidth + 'px'); + bg.call(Color.stroke, legendObj.bordercolor) + .call(Color.fill, legendObj.bgcolor) + .style('stroke-width', legendObj.borderwidth + 'px'); var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox'); - var title = container.title; - container._titleWidth = 0; - container._titleHeight = 0; + var title = legendObj.title; + legendObj._titleWidth = 0; + legendObj._titleHeight = 0; if(title.text) { var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext'); titleEl.attr('text-anchor', 'start') .call(Drawing.font, title.font) .text(title.text); - textLayout(titleEl, scrollBox, gd, container, MAIN_TITLE); // handle mathjax or multi-line text and compute title height + textLayout(titleEl, scrollBox, gd, legendObj, MAIN_TITLE); // handle mathjax or multi-line text and compute title height } else { scrollBox.selectAll('.legendtitletext').remove(); } @@ -115,13 +115,13 @@ function _draw(gd, container) { return trace.visible === 'legendonly' ? 0.5 : 1; } }) - .each(function() { d3.select(this).call(drawTexts, gd, container); }) - .call(style, gd, container) + .each(function() { d3.select(this).call(drawTexts, gd, legendObj); }) + .call(style, gd, legendObj) .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); }); Lib.syncOrAsync([ Plots.previousPromises, - function() { return computeLegendDimensions(gd, groups, traces, container); }, + function() { return computeLegendDimensions(gd, groups, traces, legendObj); }, function() { // IF expandMargin return a Promise (which is truthy), // we're under a doAutoMargin redraw, so we don't have to @@ -129,17 +129,17 @@ function _draw(gd, container) { if(!inHover && expandMargin(gd)) return; var gs = fullLayout._size; - var bw = container.borderwidth; + var bw = legendObj.borderwidth; - var lx = gs.l + gs.w * container.x - FROM_TL[getXanchor(container)] * container._width; - var ly = gs.t + gs.h * (1 - container.y) - FROM_TL[getYanchor(container)] * container._effHeight; + var lx = gs.l + gs.w * legendObj.x - FROM_TL[getXanchor(legendObj)] * legendObj._width; + var ly = gs.t + gs.h * (1 - legendObj.y) - FROM_TL[getYanchor(legendObj)] * legendObj._effHeight; if(!inHover && fullLayout.margin.autoexpand) { var lx0 = lx; var ly0 = ly; - lx = Lib.constrain(lx, 0, fullLayout.width - container._width); - ly = Lib.constrain(ly, 0, fullLayout.height - container._effHeight); + lx = Lib.constrain(lx, 0, fullLayout.width - legendObj._width); + ly = Lib.constrain(ly, 0, fullLayout.height - legendObj._effHeight); if(lx !== lx0) { Lib.log('Constrain legend.x to make legend fit inside graph'); @@ -157,15 +157,15 @@ function _draw(gd, container) { scrollBar.on('.drag', null); legend.on('wheel', null); - if(inHover || container._height <= container._maxHeight || gd._context.staticPlot) { + if(inHover || legendObj._height <= legendObj._maxHeight || gd._context.staticPlot) { // if scrollbar should not be shown. - var height = container._effHeight; + var height = legendObj._effHeight; // if unified hover, let it be its full size - if(inHover) height = container._height; + if(inHover) height = legendObj._height; bg.attr({ - width: container._width - bw, + width: legendObj._width - bw, height: height - bw, x: bw / 2, y: bw / 2 @@ -174,7 +174,7 @@ function _draw(gd, container) { Drawing.setTranslate(scrollBox, 0, 0); clipPath.select('rect').attr({ - width: container._width - 2 * bw, + width: legendObj._width - 2 * bw, height: height - 2 * bw, x: bw, y: bw @@ -183,36 +183,36 @@ function _draw(gd, container) { Drawing.setClipUrl(scrollBox, clipId, gd); Drawing.setRect(scrollBar, 0, 0, 0, 0); - delete container._scrollY; + delete legendObj._scrollY; } else { var scrollBarHeight = Math.max(constants.scrollBarMinHeight, - container._effHeight * container._effHeight / container._height); - var scrollBarYMax = container._effHeight - + legendObj._effHeight * legendObj._effHeight / legendObj._height); + var scrollBarYMax = legendObj._effHeight - scrollBarHeight - 2 * constants.scrollBarMargin; - var scrollBoxYMax = container._height - container._effHeight; + var scrollBoxYMax = legendObj._height - legendObj._effHeight; var scrollRatio = scrollBarYMax / scrollBoxYMax; - var scrollBoxY = Math.min(container._scrollY || 0, scrollBoxYMax); + var scrollBoxY = Math.min(legendObj._scrollY || 0, scrollBoxYMax); // increase the background and clip-path width // by the scrollbar width and margin bg.attr({ - width: container._width - + width: legendObj._width - 2 * bw + constants.scrollBarWidth + constants.scrollBarMargin, - height: container._effHeight - bw, + height: legendObj._effHeight - bw, x: bw / 2, y: bw / 2 }); clipPath.select('rect').attr({ - width: container._width - + width: legendObj._width - 2 * bw + constants.scrollBarWidth + constants.scrollBarMargin, - height: container._effHeight - 2 * bw, + height: legendObj._effHeight - 2 * bw, x: bw, y: bw + scrollBoxY }); @@ -224,7 +224,7 @@ function _draw(gd, container) { // scroll legend by mousewheel or touchpad swipe up/down legend.on('wheel', function() { scrollBoxY = Lib.constrain( - container._scrollY + + legendObj._scrollY + ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax), 0, scrollBoxYMax); scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); @@ -290,12 +290,12 @@ function _draw(gd, container) { } function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) { - container._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; + legendObj._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; Drawing.setTranslate(scrollBox, 0, -scrollBoxY); Drawing.setRect( scrollBar, - container._width, + legendObj._width, constants.scrollBarMargin + scrollBoxY * scrollRatio, constants.scrollBarWidth, scrollBarHeight @@ -322,8 +322,8 @@ function _draw(gd, container) { Drawing.setTranslate(legend, newX, newY); - xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, container.xanchor); - yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, container.yanchor); + xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, legendObj.xanchor); + yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, legendObj.yanchor); }, doneFn: function() { if(xf !== undefined && yf !== undefined) { @@ -386,15 +386,15 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { } } -function drawTexts(g, gd, container) { +function drawTexts(g, gd, legendObj) { var legendItem = g.data()[0][0]; var trace = legendItem.trace; var isPieLike = Registry.traceIs(trace, 'pie-like'); - var isEditable = !container._inHover && gd._context.edits.legendText && !isPieLike; - var maxNameLength = container._maxNameLength; + var isEditable = !legendObj._inHover && gd._context.edits.legendText && !isPieLike; + var maxNameLength = legendObj._maxNameLength; var name; - if(!container.entries) { + if(!legendObj.entries) { name = isPieLike ? legendItem.label : trace.name; if(trace._meta) { name = Lib.templateString(name, trace._meta); @@ -406,18 +406,18 @@ function drawTexts(g, gd, container) { var textEl = Lib.ensureSingle(g, 'text', 'legendtext'); textEl.attr('text-anchor', 'start') - .call(Drawing.font, container.font) + .call(Drawing.font, legendObj.font) .text(isEditable ? ensureLength(name, maxNameLength) : name); - var textGap = container.itemwidth + constants.itemGap * 2; + var textGap = legendObj.itemwidth + constants.itemGap * 2; svgTextUtils.positionText(textEl, textGap, 0); if(isEditable) { textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name}) - .call(textLayout, g, gd, container) + .call(textLayout, g, gd, legendObj) .on('edit', function(newName) { this.text(ensureLength(newName, maxNameLength)) - .call(textLayout, g, gd, container); + .call(textLayout, g, gd, legendObj); var fullInput = legendItem.trace._fullInput || {}; var update = {}; @@ -438,7 +438,7 @@ function drawTexts(g, gd, container) { return Registry.call('_guiRestyle', gd, update, trace.index); }); } else { - textLayout(textEl, g, gd, container); + textLayout(textEl, g, gd, legendObj); } } @@ -494,25 +494,25 @@ function setupTraceToggle(g, gd) { }); } -function textLayout(s, g, gd, container, aTitle) { - if(container._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover +function textLayout(s, g, gd, legendObj, aTitle) { + if(legendObj._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover svgTextUtils.convertToTspans(s, gd, function() { - computeTextDimensions(g, gd, container, aTitle); + computeTextDimensions(g, gd, legendObj, aTitle); }); } -function computeTextDimensions(g, gd, container, aTitle) { +function computeTextDimensions(g, gd, legendObj, aTitle) { var legendItem = g.data()[0][0]; - if(!container._inHover && legendItem && !legendItem.trace.showlegend) { + if(!legendObj._inHover && legendItem && !legendItem.trace.showlegend) { g.remove(); return; } var mathjaxGroup = g.select('g[class*=math-group]'); var mathjaxNode = mathjaxGroup.node(); - if(!container) container = gd._fullLayout.legend; - var bw = container.borderwidth; - var lineHeight = (aTitle === MAIN_TITLE ? container.title : container).font.size * LINE_SPACING; + if(!legendObj) legendObj = gd._fullLayout.legend; + var bw = legendObj.borderwidth; + var lineHeight = (aTitle === MAIN_TITLE ? legendObj.title : legendObj).font.size * LINE_SPACING; var height, width; if(mathjaxNode) { @@ -545,15 +545,15 @@ function computeTextDimensions(g, gd, container, aTitle) { ); } else { // legend item svgTextUtils.positionText(textEl, - container.itemwidth + constants.itemGap * 2, + legendObj.itemwidth + constants.itemGap * 2, -lineHeight * ((textLines - 1) / 2 - 0.3) ); } } if(aTitle === MAIN_TITLE) { - container._titleWidth = width; - container._titleHeight = height; + legendObj._titleWidth = width; + legendObj._titleHeight = height; } else { // legend item legendItem.lineHeight = lineHeight; legendItem.height = Math.max(height, 16) + 3; @@ -561,17 +561,17 @@ function computeTextDimensions(g, gd, container, aTitle) { } } -function getTitleSize(container) { +function getTitleSize(legendObj) { var w = 0; var h = 0; - var side = container.title.side; + var side = legendObj.title.side; if(side) { if(side.indexOf('left') !== -1) { - w = container._titleWidth; + w = legendObj._titleWidth; } if(side.indexOf('top') !== -1) { - h = container._titleHeight; + h = legendObj._titleHeight; } } @@ -588,70 +588,70 @@ function getTitleSize(container) { * - _width: legend width * - _maxWidth (for orientation:h only): maximum width before starting new row */ -function computeLegendDimensions(gd, groups, traces, container) { +function computeLegendDimensions(gd, groups, traces, legendObj) { var fullLayout = gd._fullLayout; - if(!container) container = fullLayout.legend; + if(!legendObj) legendObj = fullLayout.legend; var gs = fullLayout._size; - var isVertical = helpers.isVertical(container); - var isGrouped = helpers.isGrouped(container); + var isVertical = helpers.isVertical(legendObj); + var isGrouped = helpers.isGrouped(legendObj); - var bw = container.borderwidth; + var bw = legendObj.borderwidth; var bw2 = 2 * bw; var itemGap = constants.itemGap; - var textGap = container.itemwidth + itemGap * 2; + var textGap = legendObj.itemwidth + itemGap * 2; var endPad = 2 * (bw + itemGap); - var yanchor = getYanchor(container); - var isBelowPlotArea = container.y < 0 || (container.y === 0 && yanchor === 'top'); - var isAbovePlotArea = container.y > 1 || (container.y === 1 && yanchor === 'bottom'); + var yanchor = getYanchor(legendObj); + var isBelowPlotArea = legendObj.y < 0 || (legendObj.y === 0 && yanchor === 'top'); + var isAbovePlotArea = legendObj.y > 1 || (legendObj.y === 1 && yanchor === 'bottom'); - var traceGroupGap = container.tracegroupgap; + var traceGroupGap = legendObj.tracegroupgap; // - if below/above plot area, give it the maximum potential margin-push value // - otherwise, extend the height of the plot area - container._maxHeight = Math.max( + legendObj._maxHeight = Math.max( (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h, 30 ); var toggleRectWidth = 0; - container._width = 0; - container._height = 0; - var titleSize = getTitleSize(container); + legendObj._width = 0; + legendObj._height = 0; + var titleSize = getTitleSize(legendObj); if(isVertical) { traces.each(function(d) { var h = d[0].height; Drawing.setTranslate(this, bw + titleSize[0], - bw + titleSize[1] + container._height + h / 2 + itemGap + bw + titleSize[1] + legendObj._height + h / 2 + itemGap ); - container._height += h; - container._width = Math.max(container._width, d[0].width); + legendObj._height += h; + legendObj._width = Math.max(legendObj._width, d[0].width); }); - toggleRectWidth = textGap + container._width; - container._width += itemGap + textGap + bw2; - container._height += endPad; + toggleRectWidth = textGap + legendObj._width; + legendObj._width += itemGap + textGap + bw2; + legendObj._height += endPad; if(isGrouped) { groups.each(function(d, i) { - Drawing.setTranslate(this, 0, i * container.tracegroupgap); + Drawing.setTranslate(this, 0, i * legendObj.tracegroupgap); }); - container._height += (container._lgroupsLength - 1) * container.tracegroupgap; + legendObj._height += (legendObj._lgroupsLength - 1) * legendObj.tracegroupgap; } } else { - var xanchor = getXanchor(container); - var isLeftOfPlotArea = container.x < 0 || (container.x === 0 && xanchor === 'right'); - var isRightOfPlotArea = container.x > 1 || (container.x === 1 && xanchor === 'left'); + var xanchor = getXanchor(legendObj); + var isLeftOfPlotArea = legendObj.x < 0 || (legendObj.x === 0 && xanchor === 'right'); + var isRightOfPlotArea = legendObj.x > 1 || (legendObj.x === 1 && xanchor === 'left'); var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea; var hw = fullLayout.width / 2; // - if placed within x-margins, extend the width of the plot area // - else if below/above plot area and anchored in the margin, extend to opposite margin, // - otherwise give it the maximum potential margin-push value - container._maxWidth = Math.max( + legendObj._maxWidth = Math.max( isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) : isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) : gs.w, @@ -687,7 +687,7 @@ function computeLegendDimensions(gd, groups, traces, container) { var next = maxWidthInGroup + itemGap; - if((next + bw + groupOffsetX) > container._maxWidth) { + if((next + bw + groupOffsetX) > legendObj._maxWidth) { maxRowWidth = Math.max(maxRowWidth, groupOffsetX); groupOffsetX = 0; groupOffsetY += maxGroupHeightInRow + traceGroupGap; @@ -699,11 +699,11 @@ function computeLegendDimensions(gd, groups, traces, container) { groupOffsetX += next; }); - container._width = Math.max(maxRowWidth, groupOffsetX) + bw; - container._height = groupOffsetY + maxGroupHeightInRow + endPad; + legendObj._width = Math.max(maxRowWidth, groupOffsetX) + bw; + legendObj._height = groupOffsetY + maxGroupHeightInRow + endPad; } else { var nTraces = traces.size(); - var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < container._maxWidth; + var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < legendObj._maxWidth; var maxItemHeightInRow = 0; var offsetX = 0; @@ -714,11 +714,11 @@ function computeLegendDimensions(gd, groups, traces, container) { var w = textGap + d[0].width; var next = (oneRowLegend ? w : maxItemWidth) + itemGap; - if((next + bw + offsetX - itemGap) >= container._maxWidth) { + if((next + bw + offsetX - itemGap) >= legendObj._maxWidth) { maxRowWidth = Math.max(maxRowWidth, rowWidth); offsetX = 0; offsetY += maxItemHeightInRow; - container._height += maxItemHeightInRow; + legendObj._height += maxItemHeightInRow; maxItemHeightInRow = 0; } @@ -733,30 +733,30 @@ function computeLegendDimensions(gd, groups, traces, container) { }); if(oneRowLegend) { - container._width = offsetX + bw2; - container._height = maxItemHeightInRow + endPad; + legendObj._width = offsetX + bw2; + legendObj._height = maxItemHeightInRow + endPad; } else { - container._width = Math.max(maxRowWidth, rowWidth) + bw2; - container._height += maxItemHeightInRow + endPad; + legendObj._width = Math.max(maxRowWidth, rowWidth) + bw2; + legendObj._height += maxItemHeightInRow + endPad; } } } - container._width = Math.ceil( + legendObj._width = Math.ceil( Math.max( - container._width + titleSize[0], - container._titleWidth + 2 * (bw + constants.titlePad) + legendObj._width + titleSize[0], + legendObj._titleWidth + 2 * (bw + constants.titlePad) ) ); - container._height = Math.ceil( + legendObj._height = Math.ceil( Math.max( - container._height + titleSize[1], - container._titleHeight + 2 * (bw + constants.itemGap) + legendObj._height + titleSize[1], + legendObj._titleHeight + 2 * (bw + constants.itemGap) ) ); - container._effHeight = Math.min(container._height, container._maxHeight); + legendObj._effHeight = Math.min(legendObj._height, legendObj._maxHeight); var edits = gd._context.edits; var isEditable = edits.legendText || edits.legendPosition; @@ -771,28 +771,28 @@ function computeLegendDimensions(gd, groups, traces, container) { function expandMargin(gd) { var fullLayout = gd._fullLayout; - var container = fullLayout.legend; - var xanchor = getXanchor(container); - var yanchor = getYanchor(container); + var legendObj = fullLayout.legend; + var xanchor = getXanchor(legendObj); + var yanchor = getYanchor(legendObj); return Plots.autoMargin(gd, 'legend', { - x: container.x, - y: container.y, - l: container._width * (FROM_TL[xanchor]), - r: container._width * (FROM_BR[xanchor]), - b: container._effHeight * (FROM_BR[yanchor]), - t: container._effHeight * (FROM_TL[yanchor]) + x: legendObj.x, + y: legendObj.y, + l: legendObj._width * (FROM_TL[xanchor]), + r: legendObj._width * (FROM_BR[xanchor]), + b: legendObj._effHeight * (FROM_BR[yanchor]), + t: legendObj._effHeight * (FROM_TL[yanchor]) }); } -function getXanchor(container) { - return Lib.isRightAnchor(container) ? 'right' : - Lib.isCenterAnchor(container) ? 'center' : +function getXanchor(legendObj) { + return Lib.isRightAnchor(legendObj) ? 'right' : + Lib.isCenterAnchor(legendObj) ? 'center' : 'left'; } -function getYanchor(container) { - return Lib.isBottomAnchor(container) ? 'bottom' : - Lib.isMiddleAnchor(container) ? 'middle' : +function getYanchor(legendObj) { + return Lib.isBottomAnchor(legendObj) ? 'bottom' : + Lib.isMiddleAnchor(legendObj) ? 'middle' : 'top'; }